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