xref: /third_party/alsa-utils/alsactl/lock.c (revision c72fcc34)
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
38static int alarm_flag;
39
40static void signal_handler_alarm(int sig ATTRIBUTE_UNUSED)
41{
42	alarm_flag = 1;
43}
44
45static 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
146out:
147	if (fd >= 0)
148		close(fd);
149	return err;
150}
151
152static 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
160int 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
172int 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
184static 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
189int 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
201int 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