1 /*
2 * Advanced Linux Sound Architecture Control Program
3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
4 *
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include "aconfig.h"
23 #include "version.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <signal.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 #include "alsactl.h"
34 #include "os_compat.h"
35
36 #define PATH_SIZE 512
37
38 static int alarm_flag;
39
signal_handler_alarm(int sig ATTRIBUTE_UNUSED)40 static void signal_handler_alarm(int sig ATTRIBUTE_UNUSED)
41 {
42 alarm_flag = 1;
43 }
44
state_lock_(const char *lock_file, int lock, int timeout, int _fd)45 static int state_lock_(const char *lock_file, int lock, int timeout, int _fd)
46 {
47 int fd = -1, err = 0;
48 struct flock lck;
49 struct stat st;
50 char lcktxt[14];
51 struct sigaction sig_alarm, sig_alarm_orig;
52 struct itimerval itv;
53
54 if (do_lock <= 0)
55 return 0;
56
57 lck.l_type = lock ? F_WRLCK : F_UNLCK;
58 lck.l_whence = SEEK_SET;
59 lck.l_start = 0;
60 lck.l_len = 11;
61 lck.l_pid = 0;
62 if (lock) {
63 snprintf(lcktxt, sizeof(lcktxt), "%10li\n", (long)getpid());
64 } else {
65 snprintf(lcktxt, sizeof(lcktxt), "%10s\n", "");
66 fd = _fd;
67 }
68 while (fd < 0 && timeout-- > 0) {
69 fd = open(lock_file, O_RDWR);
70 if (!lock && fd < 0) {
71 err = -EIO;
72 goto out;
73 }
74 if (fd < 0) {
75 fd = open(lock_file, O_RDWR|O_CREAT|O_EXCL, 0644);
76 if (fd < 0) {
77 if (errno == EBUSY || errno == EAGAIN) {
78 sleep(1);
79 continue;
80 }
81 if (errno == EEXIST) {
82 fd = open(lock_file, O_RDWR);
83 if (fd >= 0)
84 break;
85 }
86 err = -errno;
87 goto out;
88 }
89 }
90 }
91 if (fd < 0 && timeout <= 0) {
92 err = -EBUSY;
93 goto out;
94 }
95 if (fstat(fd, &st) < 0) {
96 err = -errno;
97 goto out;
98 }
99 if (st.st_size != 11 || !lock) {
100 if (write(fd, lcktxt, 11) != 11) {
101 err = -EIO;
102 goto out;
103 }
104 if (lock && lseek(fd, 0, SEEK_SET)) {
105 err = -errno;
106 goto out;
107 }
108 }
109 alarm_flag = 0;
110 memset(&sig_alarm, 0, sizeof(sig_alarm));
111 sigemptyset(&sig_alarm.sa_mask);
112 sig_alarm.sa_handler = signal_handler_alarm;
113 if (sigaction(SIGALRM, &sig_alarm, &sig_alarm_orig) < 0) {
114 err = -ENXIO;
115 goto out;
116 }
117 memset(&itv, 0, sizeof(itv));
118 itv.it_value.tv_sec = timeout;
119 if (setitimer(ITIMER_REAL, &itv, NULL) < 0) {
120 err = -ENXIO;
121 sigaction(SIGALRM, &sig_alarm_orig, NULL);
122 goto out;
123 }
124 while (alarm_flag == 0) {
125 if (fcntl(fd, F_SETLKW, &lck) == 0)
126 break;
127 if (errno == EAGAIN || errno == ERESTART)
128 continue;
129 }
130 memset(&itv, 0, sizeof(itv));
131 setitimer(ITIMER_REAL, &itv, NULL);
132 sigaction(SIGALRM, &sig_alarm_orig, NULL);
133 if (alarm_flag) {
134 err = -EBUSY;
135 goto out;
136 }
137 if (lock) {
138 if (write(fd, lcktxt, 11) != 11) {
139 err = -EIO;
140 goto out;
141 }
142 return fd;
143 }
144 err = 0;
145
146 out:
147 if (fd >= 0)
148 close(fd);
149 return err;
150 }
151
state_lock_file(char *buf, size_t buflen)152 static void state_lock_file(char *buf, size_t buflen)
153 {
154 if (lockfile[0] == '/')
155 snprintf(buf, buflen, "%s", lockfile);
156 else
157 snprintf(buf, buflen, "%s/%s", lockpath, lockfile);
158 }
159
state_lock(const char *file, int timeout)160 int state_lock(const char *file, int timeout)
161 {
162 char fn[PATH_SIZE];
163 int err;
164
165 state_lock_file(fn, sizeof(fn));
166 err = state_lock_(fn, 1, timeout, -1);
167 if (err < 0)
168 error("file %s lock error: %s", file, strerror(-err));
169 return err;
170 }
171
state_unlock(int _fd, const char *file)172 int state_unlock(int _fd, const char *file)
173 {
174 char fn[PATH_SIZE];
175 int err;
176
177 state_lock_file(fn, sizeof(fn));
178 err = state_lock_(fn, 0, 10, _fd);
179 if (err < 0)
180 error("file %s unlock error: %s", file, strerror(-err));
181 return err;
182 }
183
card_lock_file(char *buf, size_t buflen, int card_number)184 static void card_lock_file(char *buf, size_t buflen, int card_number)
185 {
186 snprintf(buf, buflen, "%s/card%i.lock", lockpath, card_number);
187 }
188
card_lock(int card_number, int timeout)189 int card_lock(int card_number, int timeout)
190 {
191 char fn[PATH_SIZE];
192 int err;
193
194 card_lock_file(fn, sizeof(fn), card_number);
195 err = state_lock_(fn, 1, timeout, -1);
196 if (err < 0)
197 error("card %d lock error: %s", card_number, strerror(-err));
198 return err;
199 }
200
card_unlock(int _fd, int card_number)201 int card_unlock(int _fd, int card_number)
202 {
203 char fn[PATH_SIZE];
204 int err;
205
206 card_lock_file(fn, sizeof(fn), card_number);
207 err = state_lock_(fn, 0, 10, _fd);
208 if (err < 0)
209 error("card %d unlock error: %s", card_number, strerror(-err));
210 return err;
211 }
212