alsactl: add locking for per-card initialization

Introduce the -K,--lock-dir parameter to specify the locking
directory. If the locking is active, files card<NUM>.lock
are used to serialize the multiple initialization requests
(udev, service).

Allow the relative -O,--lock-file argument (it's default now).

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Jaroslav Kysela 2022-02-02 15:31:33 +01:00
parent 64fa37f09b
commit 8403967669
6 changed files with 79 additions and 12 deletions

View file

@ -14,7 +14,8 @@ alsactl_SOURCES=alsactl.c state.c lock.c utils.c init_parse.c init_ucm.c \
alsactl_CFLAGS=$(AM_CFLAGS) -D__USE_GNU \ alsactl_CFLAGS=$(AM_CFLAGS) -D__USE_GNU \
-DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\" \ -DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\" \
-DSYS_LOCKFILE=\"$(ASOUND_LOCK_DIR)/asound.state.lock\" \ -DSYS_LOCKPATH=\"$(ASOUND_LOCK_DIR)\" \
-DSYS_LOCKFILE=\"asound.state.lock\" \
-DSYS_PIDFILE=\"$(ALSACTL_PIDFILE_DIR)/alsactl.pid\" -DSYS_PIDFILE=\"$(ALSACTL_PIDFILE_DIR)/alsactl.pid\"
noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c \ noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c \

View file

@ -51,6 +51,7 @@ int do_lock = 0;
int use_syslog = 0; int use_syslog = 0;
char *command; char *command;
char *statefile = NULL; char *statefile = NULL;
char *lockpath = SYS_LOCKPATH;
char *lockfile = SYS_LOCKFILE; char *lockfile = SYS_LOCKFILE;
#define TITLE 0x0100 #define TITLE 0x0100
@ -79,6 +80,7 @@ static struct arg args[] = {
{ FILEARG | 'a', "config-dir", "boot / hotplug configuration directory (default " SYS_ASOUND_DIR ")" }, { FILEARG | 'a', "config-dir", "boot / hotplug configuration directory (default " SYS_ASOUND_DIR ")" },
{ 'l', "lock", "use file locking to serialize concurrent access" }, { 'l', "lock", "use file locking to serialize concurrent access" },
{ 'L', "no-lock", "do not use file locking to serialize concurrent access" }, { 'L', "no-lock", "do not use file locking to serialize concurrent access" },
{ FILEARG | 'K', "lock-dir", "lock path (default " SYS_LOCKPATH ")" },
{ FILEARG | 'O', "lock-state-file", "state lock file path (default " SYS_LOCKFILE ")" }, { FILEARG | 'O', "lock-state-file", "state lock file path (default " SYS_LOCKFILE ")" },
{ 'F', "force", "try to restore the matching controls as much as possible" }, { 'F', "force", "try to restore the matching controls as much as possible" },
{ 0, NULL, " (default mode)" }, { 0, NULL, " (default mode)" },
@ -306,6 +308,9 @@ int main(int argc, char *argv[])
case 'L': case 'L':
do_lock = -1; do_lock = -1;
break; break;
case 'K':
lockpath = optarg;
break;
case 'O': case 'O':
lockfile = optarg; lockfile = optarg;
break; break;

View file

@ -1,6 +1,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#define LOCK_TIMEOUT 10
extern int debugflag; extern int debugflag;
extern int force_restore; extern int force_restore;
extern int ignore_nocards; extern int ignore_nocards;
@ -8,6 +10,7 @@ extern int do_lock;
extern int use_syslog; extern int use_syslog;
extern char *command; extern char *command;
extern char *statefile; extern char *statefile;
extern char *lockpath;
extern char *lockfile; extern char *lockfile;
struct snd_card_iterator { struct snd_card_iterator {
@ -50,7 +53,9 @@ int load_configuration(const char *file, snd_config_t **top, int *open_failed);
int init(const char *cfgdir, const char *file, int flags, const char *cardname); int init(const char *cfgdir, const char *file, int flags, const char *cardname);
int init_ucm(int flags, int cardno); int init_ucm(int flags, int cardno);
int state_lock(const char *file, int timeout); int state_lock(const char *file, int timeout);
int state_unlock(int fd, const char *file); int state_unlock(int lock_fd, const char *file);
int card_lock(int card_number, int timeout);
int card_unlock(int lock_fd, int card_number);
int save_state(const char *file, const char *cardname); int save_state(const char *file, const char *cardname);
int load_state(const char *cfgdir, const char *file, int load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags, const char *initfile, int initflags,

View file

@ -32,6 +32,8 @@
#include <sys/stat.h> #include <sys/stat.h>
#include "alsactl.h" #include "alsactl.h"
#define PATH_SIZE 512
static int alarm_flag; static int alarm_flag;
static void signal_handler_alarm(int sig) static void signal_handler_alarm(int sig)
@ -39,7 +41,7 @@ static void signal_handler_alarm(int sig)
alarm_flag = 1; alarm_flag = 1;
} }
static int state_lock_(int lock, int timeout, int _fd) static int state_lock_(const char *lock_file, int lock, int timeout, int _fd)
{ {
int fd = -1, err = 0; int fd = -1, err = 0;
struct flock lck; struct flock lck;
@ -47,7 +49,6 @@ static int state_lock_(int lock, int timeout, int _fd)
char lcktxt[14]; char lcktxt[14];
struct sigaction sig_alarm, sig_alarm_orig; struct sigaction sig_alarm, sig_alarm_orig;
struct itimerval itv; struct itimerval itv;
char *nfile = lockfile;
if (do_lock <= 0) if (do_lock <= 0)
return 0; return 0;
@ -64,20 +65,20 @@ static int state_lock_(int lock, int timeout, int _fd)
fd = _fd; fd = _fd;
} }
while (fd < 0 && timeout-- > 0) { while (fd < 0 && timeout-- > 0) {
fd = open(nfile, O_RDWR); fd = open(lock_file, O_RDWR);
if (!lock && fd < 0) { if (!lock && fd < 0) {
err = -EIO; err = -EIO;
goto out; goto out;
} }
if (fd < 0) { if (fd < 0) {
fd = open(nfile, O_RDWR|O_CREAT|O_EXCL, 0644); fd = open(lock_file, O_RDWR|O_CREAT|O_EXCL, 0644);
if (fd < 0) { if (fd < 0) {
if (errno == EBUSY || errno == EAGAIN) { if (errno == EBUSY || errno == EAGAIN) {
sleep(1); sleep(1);
continue; continue;
} }
if (errno == EEXIST) { if (errno == EEXIST) {
fd = open(nfile, O_RDWR); fd = open(lock_file, O_RDWR);
if (fd >= 0) if (fd >= 0)
break; break;
} }
@ -147,11 +148,21 @@ out:
return err; return err;
} }
static void state_lock_file(char *buf, size_t buflen)
{
if (lockfile[0] == '/')
snprintf(buf, buflen, "%s", lockfile);
else
snprintf(buf, buflen, "%s/%s", lockpath, lockfile);
}
int state_lock(const char *file, int timeout) int state_lock(const char *file, int timeout)
{ {
char fn[PATH_SIZE];
int err; int err;
err = state_lock_(1, timeout, -1); state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 1, timeout, -1);
if (err < 0) if (err < 0)
error("file %s lock error: %s", file, strerror(-err)); error("file %s lock error: %s", file, strerror(-err));
return err; return err;
@ -159,10 +170,41 @@ int state_lock(const char *file, int timeout)
int state_unlock(int _fd, const char *file) int state_unlock(int _fd, const char *file)
{ {
char fn[PATH_SIZE];
int err; int err;
err = state_lock_(0, 10, _fd); state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 0, 10, _fd);
if (err < 0) if (err < 0)
error("file %s unlock error: %s", file, strerror(-err)); error("file %s unlock error: %s", file, strerror(-err));
return err; return err;
} }
static void card_lock_file(char *buf, size_t buflen, int card_number)
{
snprintf(buf, buflen, "%s/card%i.lock", lockpath, card_number);
}
int card_lock(int card_number, int timeout)
{
char fn[PATH_SIZE];
int err;
card_lock_file(fn, sizeof(fn), card_number);
err = state_lock_(fn, 1, timeout, -1);
if (err < 0)
error("card %d lock error: %s", card_number, strerror(-err));
return err;
}
int card_unlock(int _fd, int card_number)
{
char fn[PATH_SIZE];
int err;
card_lock_file(fn, sizeof(fn), card_number);
err = state_lock_(fn, 0, 10, _fd);
if (err < 0)
error("card %d unlock error: %s", card_number, strerror(-err));
return err;
}

View file

@ -1614,7 +1614,7 @@ int save_state(const char *file, const char *cardname)
} }
strcpy(nfile, file); strcpy(nfile, file);
strcat(nfile, ".new"); strcat(nfile, ".new");
lock_fd = state_lock(file, 10); lock_fd = state_lock(file, LOCK_TIMEOUT);
if (lock_fd < 0) { if (lock_fd < 0) {
err = lock_fd; err = lock_fd;
goto out; goto out;
@ -1675,7 +1675,7 @@ int load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags, const char *initfile, int initflags,
const char *cardname, int do_init) const char *cardname, int do_init)
{ {
int err, finalerr = 0, open_failed; int err, finalerr = 0, open_failed, lock_fd;
struct snd_card_iterator iter; struct snd_card_iterator iter;
snd_config_t *config; snd_config_t *config;
const char *cardname1; const char *cardname1;
@ -1695,7 +1695,14 @@ int load_state(const char *cfgdir, const char *file,
while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) { while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
if (!do_init) if (!do_init)
break; break;
lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
if (lock_fd < 0) {
finalerr = lock_fd;
initfailed(iter.card, "lock", err);
continue;
}
err = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname1); err = init(cfgdir, initfile, initflags | FLAG_UCM_FBOOT | FLAG_UCM_BOOT, cardname1);
card_unlock(lock_fd, iter.card);
if (err < 0) { if (err < 0) {
finalerr = err; finalerr = err;
initfailed(iter.card, "init", err); initfailed(iter.card, "init", err);
@ -1712,6 +1719,12 @@ int load_state(const char *cfgdir, const char *file,
if (err < 0) if (err < 0)
goto out; goto out;
while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) { while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
lock_fd = card_lock(iter.card, LOCK_TIMEOUT);
if (lock_fd < 0) {
initfailed(iter.card, "lock", lock_fd);
finalerr = lock_fd;
continue;
}
/* error is ignored */ /* error is ignored */
init_ucm(initflags | FLAG_UCM_FBOOT, iter.card); init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
/* do a check if controls matches state file */ /* do a check if controls matches state file */
@ -1727,6 +1740,7 @@ int load_state(const char *cfgdir, const char *file,
finalerr = err; finalerr = err;
initfailed(iter.card, "restore", err); initfailed(iter.card, "restore", err);
} }
card_unlock(lock_fd, iter.card);
} }
err = finalerr ? finalerr : snd_card_iterator_error(&iter); err = finalerr ? finalerr : snd_card_iterator_error(&iter);
out: out:

View file

@ -211,7 +211,7 @@ int load_configuration(const char *file, snd_config_t **top, int *open_failed)
if (stdio_flag) { if (stdio_flag) {
err = snd_input_stdio_attach(&in, stdin, 0); err = snd_input_stdio_attach(&in, stdin, 0);
} else { } else {
lock_fd = state_lock(file, 10); lock_fd = state_lock(file, LOCK_TIMEOUT);
err = lock_fd >= 0 ? snd_input_stdio_open(&in, file, "r") : lock_fd; err = lock_fd >= 0 ? snd_input_stdio_open(&in, file, "r") : lock_fd;
} }
if (err < 0) { if (err < 0) {