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