Skip to content

Commit

Permalink
alsactl: add locking for per-card initialization
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
perexg committed Feb 2, 2022
1 parent 64fa37f commit 8403967
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 12 deletions.
3 changes: 2 additions & 1 deletion alsactl/Makefile.am
Expand Up @@ -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 \
-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\"

noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c \
Expand Down
5 changes: 5 additions & 0 deletions alsactl/alsactl.c
Expand Up @@ -51,6 +51,7 @@ int do_lock = 0;
int use_syslog = 0;
char *command;
char *statefile = NULL;
char *lockpath = SYS_LOCKPATH;
char *lockfile = SYS_LOCKFILE;

#define TITLE 0x0100
Expand Down Expand Up @@ -79,6 +80,7 @@ static struct arg args[] = {
{ FILEARG | 'a', "config-dir", "boot / hotplug configuration directory (default " SYS_ASOUND_DIR ")" },
{ 'l', "lock", "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 ")" },
{ 'F', "force", "try to restore the matching controls as much as possible" },
{ 0, NULL, " (default mode)" },
Expand Down Expand Up @@ -306,6 +308,9 @@ int main(int argc, char *argv[])
case 'L':
do_lock = -1;
break;
case 'K':
lockpath = optarg;
break;
case 'O':
lockfile = optarg;
break;
Expand Down
7 changes: 6 additions & 1 deletion alsactl/alsactl.h
@@ -1,13 +1,16 @@
#include <stdbool.h>
#include <alsa/asoundlib.h>

#define LOCK_TIMEOUT 10

extern int debugflag;
extern int force_restore;
extern int ignore_nocards;
extern int do_lock;
extern int use_syslog;
extern char *command;
extern char *statefile;
extern char *lockpath;
extern char *lockfile;

struct snd_card_iterator {
Expand Down Expand Up @@ -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_ucm(int flags, int cardno);
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 load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags,
Expand Down
56 changes: 49 additions & 7 deletions alsactl/lock.c
Expand Up @@ -32,22 +32,23 @@
#include <sys/stat.h>
#include "alsactl.h"

#define PATH_SIZE 512

static int alarm_flag;

static void signal_handler_alarm(int sig)
{
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;
struct flock lck;
struct stat st;
char lcktxt[14];
struct sigaction sig_alarm, sig_alarm_orig;
struct itimerval itv;
char *nfile = lockfile;

if (do_lock <= 0)
return 0;
Expand All @@ -64,20 +65,20 @@ static int state_lock_(int lock, int timeout, int _fd)
fd = _fd;
}
while (fd < 0 && timeout-- > 0) {
fd = open(nfile, O_RDWR);
fd = open(lock_file, O_RDWR);
if (!lock && fd < 0) {
err = -EIO;
goto out;
}
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 (errno == EBUSY || errno == EAGAIN) {
sleep(1);
continue;
}
if (errno == EEXIST) {
fd = open(nfile, O_RDWR);
fd = open(lock_file, O_RDWR);
if (fd >= 0)
break;
}
Expand Down Expand Up @@ -147,22 +148,63 @@ static int state_lock_(int lock, int timeout, int _fd)
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)
{
char fn[PATH_SIZE];
int err;

err = state_lock_(1, timeout, -1);
state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 1, timeout, -1);
if (err < 0)
error("file %s lock error: %s", file, strerror(-err));
return err;
}

int state_unlock(int _fd, const char *file)
{
char fn[PATH_SIZE];
int err;

err = state_lock_(0, 10, _fd);
state_lock_file(fn, sizeof(fn));
err = state_lock_(fn, 0, 10, _fd);
if (err < 0)
error("file %s unlock error: %s", file, strerror(-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;
}
18 changes: 16 additions & 2 deletions alsactl/state.c
Expand Up @@ -1614,7 +1614,7 @@ int save_state(const char *file, const char *cardname)
}
strcpy(nfile, file);
strcat(nfile, ".new");
lock_fd = state_lock(file, 10);
lock_fd = state_lock(file, LOCK_TIMEOUT);
if (lock_fd < 0) {
err = lock_fd;
goto out;
Expand Down Expand Up @@ -1675,7 +1675,7 @@ int load_state(const char *cfgdir, const char *file,
const char *initfile, int initflags,
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;
snd_config_t *config;
const char *cardname1;
Expand All @@ -1695,7 +1695,14 @@ int load_state(const char *cfgdir, const char *file,
while ((cardname1 = snd_card_iterator_next(&iter)) != NULL) {
if (!do_init)
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);
card_unlock(lock_fd, iter.card);
if (err < 0) {
finalerr = err;
initfailed(iter.card, "init", err);
Expand All @@ -1712,6 +1719,12 @@ int load_state(const char *cfgdir, const char *file,
if (err < 0)
goto out;
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 */
init_ucm(initflags | FLAG_UCM_FBOOT, iter.card);
/* do a check if controls matches state file */
Expand All @@ -1727,6 +1740,7 @@ int load_state(const char *cfgdir, const char *file,
finalerr = err;
initfailed(iter.card, "restore", err);
}
card_unlock(lock_fd, iter.card);
}
err = finalerr ? finalerr : snd_card_iterator_error(&iter);
out:
Expand Down
2 changes: 1 addition & 1 deletion alsactl/utils.c
Expand Up @@ -211,7 +211,7 @@ int load_configuration(const char *file, snd_config_t **top, int *open_failed)
if (stdio_flag) {
err = snd_input_stdio_attach(&in, stdin, 0);
} 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;
}
if (err < 0) {
Expand Down

0 comments on commit 8403967

Please sign in to comment.