summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2013-04-05 11:51:51 +0200
committerJaroslav Kysela <perex@perex.cz>2013-04-05 13:47:18 +0200
commitcc5c3357cff87bb805494e23620bd2d675bcccbd (patch)
tree5396c281f7e13bb85fb2d4443eec12d59fbb8d1f
parentb95dd7ef468212c08434f6e7b9987f2936944b09 (diff)
downloadalsa-utils-cc5c3357cff87bb805494e23620bd2d675bcccbd.tar.gz
alsactl: add the daemon mode
For the plug-and-play hardware, like USB devices, it may be helpful to manage the sound state periodically, before the devices are removed. This change implements new commands 'daemon' and 'rdaemon' to save the sound state in defined intervals when the sound controls are changed. The udev rules can notify the daemon using the 'kill' or 'nrestore' commands to rescan available cards in the system. Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--alsactl/Makefile.am2
-rw-r--r--alsactl/alsactl.130
-rw-r--r--alsactl/alsactl.c92
-rw-r--r--alsactl/alsactl.h76
-rw-r--r--alsactl/daemon.c330
-rw-r--r--alsactl/lock.c123
-rw-r--r--alsactl/state.c18
-rw-r--r--alsactl/utils.c77
8 files changed, 671 insertions, 77 deletions
diff --git a/alsactl/Makefile.am b/alsactl/Makefile.am
index c434e44..8090d02 100644
--- a/alsactl/Makefile.am
+++ b/alsactl/Makefile.am
@@ -7,7 +7,7 @@ man_MANS += alsactl_init.7
endif
EXTRA_DIST=alsactl.1 alsactl_init.xml
-alsactl_SOURCES=alsactl.c state.c utils.c init_parse.c
+alsactl_SOURCES=alsactl.c state.c lock.c utils.c init_parse.c daemon.c
alsactl_CFLAGS=$(AM_CFLAGS) -DSYS_ASOUNDRC=\"$(ASOUND_STATE_DIR)/asound.state\"
noinst_HEADERS=alsactl.h list.h init_sysdeps.c init_utils_string.c init_utils_run.c init_sysfs.c
diff --git a/alsactl/alsactl.1 b/alsactl/alsactl.1
index 054f511..36bd8b5 100644
--- a/alsactl/alsactl.1
+++ b/alsactl/alsactl.1
@@ -1,4 +1,4 @@
-.TH ALSACTL 1 "15 May 2001"
+.TH ALSACTL 1 "05 Apr 2013"
.SH NAME
alsactl \- advanced controls for ALSA soundcard driver
@@ -21,9 +21,19 @@ to the configuration file.
configuration file. If restoring fails (eventually partly), the init
action is called.
+\fInrestore\fP is like \fIrestore\fP, but it notifies also the daemon
+to do new rescan for available soundcards.
+
\fIinit\fP tries to initialize all devices to a default state. If device
is not known, error code 99 is returned.
+\fIdaemon\fP manages to save periodically the sound state.
+
+\fIrdaemon\fP like \fIdaemon\fP but restore the sound state at first.
+
+\fIkill\fP notifies the daemon to do the specified operation (quit,
+rescan, save_and_quit).
+
If no soundcards are specified, setup for all cards will be saved or
loaded.
@@ -85,10 +95,26 @@ ALSA_CONFIG_PATH to read different or optimized configuration - may be
useful for "boot" scripts).
.TP
-\fI\-i, \-\-initfile\fP #=#
+\fI\-i, \-\-initfile\fP
The configuration file for init. By default, PREFIX/share/alsa/init/00main
is used.
+.TP
+\fI\-p, \-\-period\fP
+The store period in seconds for the daemon command.
+
+.TP
+\fI\-e, \-\-pid-file\fP
+The pathname to store the process-id file in the HDB UUCP format (ASCII).
+
+.TP
+\fI\-b, \-\-background\fP
+Run the task in background.
+
+.TP
+\fI\-s, \-\-syslog\fP
+Use syslog for messages.
+
.SH FILES
\fI/var/lib/alsa/asound.state\fP (or whatever file you specify with the
\fB\-f\fP flag) is used to store current settings for your
diff --git a/alsactl/alsactl.c b/alsactl/alsactl.c
index 5e15f70..a65835c 100644
--- a/alsactl/alsactl.c
+++ b/alsactl/alsactl.c
@@ -27,16 +27,22 @@
#include <stdio.h>
#include <assert.h>
#include <errno.h>
+#include <syslog.h>
#include <alsa/asoundlib.h>
#include "alsactl.h"
#ifndef SYS_ASOUNDRC
#define SYS_ASOUNDRC "/var/lib/alsa/asound.state"
#endif
+#ifndef SYS_PIDFILE
+#define SYS_PIDFILE "/var/run/alsactl.pid"
+#endif
int debugflag = 0;
int force_restore = 1;
int ignore_nocards = 0;
+int do_lock = 0;
+int use_syslog = 0;
char *command;
char *statefile = NULL;
@@ -49,6 +55,7 @@ static void help(void)
printf(" -v,--version print version of this program\n");
printf("\nAvailable state options:\n");
printf(" -f,--file # configuration file (default " SYS_ASOUNDRC ")\n");
+ printf(" -l,--lock use file locking to serialize concurrent access\n");
printf(" -F,--force try to restore the matching controls as much as possible\n");
printf(" (default mode)\n");
printf(" -g,--ignore ignore 'No soundcards found' error\n");
@@ -58,16 +65,22 @@ static void help(void)
printf(" -r,--runstate # save restore and init state to this file (only errors)\n");
printf(" default settings is 'no file set'\n");
printf(" -R,--remove remove runstate file at first, otherwise append errors\n");
+ printf(" -p,--period store period in seconds for the daemon command\n");
+ printf(" -e,--pid-file pathname for the process id (daemon mode)\n");
printf("\nAvailable init options:\n");
printf(" -E,--env #=# set environment variable for init phase (NAME=VALUE)\n");
printf(" -i,--initfile # main configuation file for init phase (default " DATADIR "/init/00main)\n");
printf("\n");
printf("\nAvailable commands:\n");
- printf(" store <card #> save current driver setup for one or each soundcards\n");
- printf(" to configuration file\n");
- printf(" restore <card #> load current driver setup for one or each soundcards\n");
- printf(" from configuration file\n");
- printf(" init <card #> initialize driver to a default state\n");
+ printf(" store <card #> save current driver setup for one or each soundcards\n");
+ printf(" to configuration file\n");
+ printf(" restore <card #> load current driver setup for one or each soundcards\n");
+ printf(" from configuration file\n");
+ printf(" nrestore <card #> like restore, but notify the daemon to rescan soundcards\n");
+ printf(" init <card #> initialize driver to a default state\n");
+ printf(" daemon <card #> store state periodically for one or each soundcards\n");
+ printf(" rdaemon <card #> like daemon but do the state restore at first\n");
+ printf(" kill <cmd> notify daemon to quit, rescan or save_and_quit\n");
}
int main(int argc, char *argv[])
@@ -76,6 +89,7 @@ int main(int argc, char *argv[])
{
{"help", 0, NULL, 'h'},
{"file", 1, NULL, 'f'},
+ {"lock", 0, NULL, 'l'},
{"env", 1, NULL, 'E'},
{"initfile", 1, NULL, 'i'},
{"no-init-fallback", 0, NULL, 'I'},
@@ -84,6 +98,10 @@ int main(int argc, char *argv[])
{"pedantic", 0, NULL, 'P'},
{"runstate", 0, NULL, 'r'},
{"remove", 0, NULL, 'R'},
+ {"period", 1, NULL, 'p'},
+ {"pid-file", 1, NULL, 'e'},
+ {"background", 0, NULL, 'b'},
+ {"syslog", 0, NULL, 's'},
{"debug", 0, NULL, 'd'},
{"version", 0, NULL, 'v'},
{NULL, 0, NULL, 0},
@@ -97,17 +115,21 @@ int main(int argc, char *argv[])
};
char *cfgfile = SYS_ASOUNDRC;
char *initfile = DATADIR "/init/00main";
+ char *pidfile = SYS_PIDFILE;
char *cardname, ncardname[16];
+ char *cmd;
const char *const *tmp;
int removestate = 0;
int init_fallback = 1; /* new default behavior */
+ int period = 5*60;
+ int background = 0;
int res;
command = argv[0];
while (1) {
int c;
- if ((c = getopt_long(argc, argv, "hdvf:FgE:i:IPr:R", long_option, NULL)) < 0)
+ if ((c = getopt_long(argc, argv, "hdvf:lFgE:i:IPr:Rp:e:bs", long_option, NULL)) < 0)
break;
switch (c) {
case 'h':
@@ -116,6 +138,9 @@ int main(int argc, char *argv[])
case 'f':
cfgfile = optarg;
break;
+ case 'l':
+ do_lock = 1;
+ break;
case 'F':
force_restore = 1;
break;
@@ -143,6 +168,22 @@ int main(int argc, char *argv[])
case 'P':
force_restore = 0;
break;
+ case 'p':
+ period = atoi(optarg);
+ if (period < 10)
+ period = 5*60;
+ else if (period > 24*60*60)
+ period = 24*60*60;
+ break;
+ case 'e':
+ pidfile = optarg;
+ break;
+ case 'b':
+ background = 1;
+ break;
+ case 's':
+ use_syslog = 1;
+ break;
case 'd':
debugflag = 1;
break;
@@ -174,21 +215,50 @@ int main(int argc, char *argv[])
}
}
- if (!strcmp(argv[optind], "init")) {
+ /* the global system file should be always locked */
+ if (strcmp(cfgfile, SYS_ASOUNDRC) == 0)
+ do_lock = 1;
+
+ /* when running in background, use syslog for reports */
+ if (background) {
+ use_syslog = 1;
+ daemon(0, 0);
+ }
+
+ if (use_syslog) {
+ openlog("alsactl", LOG_CONS|LOG_PID, LOG_DAEMON);
+ syslog(LOG_INFO, "alsactl " SND_UTIL_VERSION_STR " daemon started");
+ }
+
+ cmd = argv[optind];
+ if (!strcmp(cmd, "init")) {
res = init(initfile, cardname);
snd_config_update_free_global();
- } else if (!strcmp(argv[optind], "store")) {
+ } else if (!strcmp(cmd, "store")) {
res = save_state(cfgfile, cardname);
- } else if (!strcmp(argv[optind], "restore")) {
+ } else if (!strcmp(cmd, "restore") ||
+ !strcmp(cmd, "rdaemon") ||
+ !strcmp(cmd, "nrestore")) {
if (removestate)
remove(statefile);
res = load_state(cfgfile, initfile, cardname, init_fallback);
+ if (!strcmp(cmd, "rdaemon"))
+ res = state_daemon(cfgfile, cardname, period, pidfile);
+ if (!strcmp(cmd, "nrestore"))
+ res = state_daemon_kill(pidfile, "rescan");
+ } else if (!strcmp(cmd, "daemon")) {
+ res = state_daemon(cfgfile, cardname, period, pidfile);
+ } else if (!strcmp(cmd, "kill")) {
+ res = state_daemon_kill(pidfile, cardname);
} else {
- fprintf(stderr, "alsactl: Unknown command '%s'...\n",
- argv[optind]);
+ fprintf(stderr, "alsactl: Unknown command '%s'...\n", cmd);
res = -ENODEV;
}
snd_config_update_free_global();
+ if (use_syslog) {
+ syslog(LOG_INFO, "alsactl daemon stopped");
+ closelog();
+ }
return res < 0 ? -res : 0;
}
diff --git a/alsactl/alsactl.h b/alsactl/alsactl.h
index be90efb..6db5018 100644
--- a/alsactl/alsactl.h
+++ b/alsactl/alsactl.h
@@ -1,77 +1,37 @@
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;
-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define info(...) do {\
- fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
- fprintf(stdout, __VA_ARGS__); \
- putc('\n', stdout); \
-} while (0)
-#else
-#define info(args...) do {\
- fprintf(stdout, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
- fprintf(stdout, ##args); \
- putc('\n', stdout); \
-} while (0)
-#endif
-
-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define error(...) do {\
- fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
- fprintf(stderr, __VA_ARGS__); \
- putc('\n', stderr); \
-} while (0)
-#else
-#define error(args...) do {\
- fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
- fprintf(stderr, ##args); \
- putc('\n', stderr); \
-} while (0)
-#endif
-
-#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define cerror(cond, ...) do {\
- if (cond || debugflag) { \
- fprintf(stderr, "%s%s: %s:%d: ", debugflag ? "WARNING: " : "", command, __FUNCTION__, __LINE__); \
- fprintf(stderr, __VA_ARGS__); \
- putc('\n', stderr); \
- } \
-} while (0)
-#else
-#define cerror(cond, args...) do {\
- if (cond || debugflag) { \
- fprintf(stderr, "%s%s: %s:%d: ", debugflag ? "WARNING: " : "", command, __FUNCTION__, __LINE__); \
- fprintf(stderr, ##args); \
- putc('\n', stderr); \
- } \
-} while (0)
-#endif
+void info_(const char *fcn, long line, const char *fmt, ...);
+void error_(const char *fcn, long line, const char *fmt, ...);
+void cerror_(const char *fcn, long line, int cond, const char *fmt, ...);
+void dbg_(const char *fcn, long line, const char *fmt, ...);
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
-#define dbg(...) do {\
- if (!debugflag) break; \
- fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
- fprintf(stderr, __VA_ARGS__); \
- putc('\n', stderr); \
-} while (0)
+#define info(...) do { info_(__FUNCTION__, __LINE__, __VA_ARGS__); } while (0)
+#define error(...) do { error_(__FUNCTION__, __LINE__, __VA_ARGS__); } while (0)
+#define cerror(cond, ...) do { cerror_(__FUNCTION__, __LINE__, (cond) != 0, __VA_ARGS__); } while (0)
+#define dbg(...) do { dbg_(__FUNCTION__, __LINE__, __VA_ARGS__); } while (0)
#else
-#define dbg(args...) do {\
- if (!debugflag) break; \
- fprintf(stderr, "%s: %s:%d: ", command, __FUNCTION__, __LINE__); \
- fprintf(stderr, ##args); \
- putc('\n', stderr); \
-} while (0)
+#define info(args...) do { info_(__FUNCTION__, __LINE__, ##args); } while (0)
+#define error(args...) do { error_(__FUNCTION__, __LINE__, ##args); } while (0)
+#define cerror(cond, ...) do { error_(__FUNCTION__, __LINE__, (cond) != 0, ##args); } while (0)
+#define dbg(args...) do { dbg_(__FUNCTION__, __LINE__, ##args); } while (0)
#endif
int init(const char *file, const char *cardname);
+int state_lock(const char *file, int lock, int timeout);
int save_state(const char *file, const char *cardname);
int load_state(const char *file, const char *initfile, const char *cardname,
int do_init);
int power(const char *argv[], int argc);
-int generate_names(const char *cfgfile);
+int state_daemon(const char *file, const char *cardname, int period,
+ const char *pidfile);
+int state_daemon_kill(const char *pidfile, const char *cmd);
/* utils */
diff --git a/alsactl/daemon.c b/alsactl/daemon.c
new file mode 100644
index 0000000..940c080
--- /dev/null
+++ b/alsactl/daemon.c
@@ -0,0 +1,330 @@
+/*
+ * Advanced Linux Sound Architecture Control Program
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <getopt.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
+#include <poll.h>
+#include <alsa/asoundlib.h>
+#include "alsactl.h"
+
+struct card {
+ int index;
+ int pfds;
+ snd_ctl_t *handle;
+};
+
+static int quit = 0;
+static int rescan = 0;
+static int save_now = 0;
+
+static void signal_handler_quit(int sig)
+{
+ quit = 1;
+ signal(sig, signal_handler_quit);
+}
+
+static void signal_handler_save_and_quit(int sig)
+{
+ quit = save_now = 1;
+ signal(sig, signal_handler_quit);
+}
+
+static void signal_handler_rescan(int sig)
+{
+ rescan = 1;
+ signal(sig, signal_handler_rescan);
+}
+
+static void card_free(struct card **card)
+{
+ struct card *c = *card;
+ if (c->handle)
+ snd_ctl_close(c->handle);
+ free(c);
+ *card = NULL;
+}
+
+static void add_card(struct card ***cards, int *count, const char *cardname)
+{
+ struct card *card, **cc;
+ int i, index, findex;
+ char device[16];
+
+ index = snd_card_get_index(cardname);
+ if (index < 0)
+ return;
+ for (i = 0, findex = -1; i < *count; i++) {
+ if ((*cards)[i] == NULL) {
+ findex = i;
+ } else {
+ if ((*cards)[i]->index == index)
+ return;
+ }
+ }
+ card = calloc(1, sizeof(*card));
+ if (card == NULL)
+ return;
+ card->index = index;
+ sprintf(device, "hw:%i", index);
+ if (snd_ctl_open(&card->handle, device, SND_CTL_READONLY|SND_CTL_NONBLOCK) < 0) {
+ card_free(&card);
+ return;
+ }
+ card->pfds = snd_ctl_poll_descriptors_count(card->handle);
+ if (card->pfds < 0) {
+ card_free(&card);
+ return;
+ }
+ if (snd_ctl_subscribe_events(card->handle, 1) < 0) {
+ card_free(&card);
+ return;
+ }
+ if (findex >= 0) {
+ (*cards)[findex] = card;
+ } else {
+ cc = realloc(*cards, sizeof(void *) * (*count + 1));
+ if (cc == NULL) {
+ card_free(&card);
+ return;
+ }
+ cc[*count] = card;
+ *count = *count + 1;
+ *cards = cc;
+ }
+}
+
+static void add_cards(struct card ***cards, int *count)
+{
+ int card = -1;
+ char cardname[16];
+
+ while (1) {
+ if (snd_card_next(&card) < 0)
+ break;
+ if (card < 0)
+ break;
+ if (card >= 0) {
+ sprintf(cardname, "%i", card);
+ add_card(cards, count, cardname);
+ }
+ }
+}
+
+int card_events(struct card *card)
+{
+ int res = 0;
+ snd_ctl_event_t *ev;
+ snd_ctl_event_alloca(&ev);
+
+ while (snd_ctl_read(card->handle, ev) == 1)
+ res = 1;
+ return res;
+}
+
+long read_pid_file(const char *pidfile)
+{
+ int fd, err;
+ char pid_txt[12];
+
+ fd = open(pidfile, O_RDONLY);
+ if (fd >= 0) {
+ err = read(fd, pid_txt, 11);
+ if (err != 11)
+ err = err < 0 ? -errno : -EIO;
+ close(fd);
+ pid_txt[11] = '\0';
+ return atol(pid_txt);
+ } else {
+ return -errno;
+ }
+}
+
+int write_pid_file(const char *pidfile)
+{
+ int fd, err;
+ char pid_txt[12];
+
+ sprintf(pid_txt, "%10li\n", (long)getpid());
+ fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
+ if (fd >= 0) {
+ err = write(fd, pid_txt, 11);
+ if (err != 11) {
+ err = err < 0 ? -errno : -EIO;
+ unlink(pidfile);
+ } else {
+ err = 0;
+ }
+ close(fd);
+ } else {
+ err = -errno;
+ }
+ return err;
+}
+
+int state_daemon_kill(const char *pidfile, const char *cmd)
+{
+ long pid;
+ int sig = SIGHUP;
+
+ if (cmd == NULL) {
+ error("Specify kill command (quit, rescan or save_and_quit)");
+ return -EINVAL;
+ }
+ if (strcmp(cmd, "rescan") == 0)
+ sig = SIGUSR1;
+ else if (strcmp(cmd, "save_and_quit") == 0)
+ sig = SIGUSR2;
+ else if (strcmp(cmd, "quit") == 0)
+ sig = SIGTERM;
+ if (sig == SIGHUP) {
+ error("Unknown kill command '%s'", cmd);
+ return -EINVAL;
+ }
+ pid = read_pid_file(pidfile);
+ if (pid > 0) {
+ if (kill(pid, sig) >= 0)
+ return 0;
+ return -errno;
+ }
+ return 0;
+}
+
+int check_another_instance(const char *pidfile)
+{
+ long pid;
+
+ pid = read_pid_file(pidfile);
+ if (pid >= 0) {
+ /* invoke new card rescan */
+ if (kill(pid, SIGUSR1) >= 0) {
+ usleep(1000);
+ pid = read_pid_file(pidfile);
+ if (pid >= 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int state_daemon(const char *file, const char *cardname, int period,
+ const char *pidfile)
+{
+ int count = 0, pcount, psize = 0, i, j, k, changed = 0;
+ time_t last_write, now;
+ unsigned short revents;
+ struct card **cards = NULL;
+ struct pollfd *pfd = NULL, *pfdn;
+
+ if (check_another_instance(pidfile))
+ return 0;
+ rescan = 1;
+ signal(SIGABRT, signal_handler_quit);
+ signal(SIGTERM, signal_handler_quit);
+ signal(SIGINT, signal_handler_quit);
+ signal(SIGUSR1, signal_handler_rescan);
+ signal(SIGUSR2, signal_handler_save_and_quit);
+ write_pid_file(pidfile);
+ time(&last_write);
+ while (!quit || save_now) {
+ if (save_now)
+ goto save;
+ if (rescan) {
+ if (cardname) {
+ add_card(&cards, &count, cardname);
+ } else {
+ add_cards(&cards, &count);
+ }
+ snd_config_update_free_global();
+ rescan = 0;
+ }
+ for (i = pcount = 0; i < count; i++) {
+ if (cards[i] == NULL)
+ continue;
+ pcount += cards[i]->pfds;
+ }
+ if (pcount > psize) {
+ pfdn = realloc(pfd, sizeof(struct pollfd) * pcount);
+ if (pfdn) {
+ psize = pcount;
+ pfd = pfdn;
+ } else {
+ error("No enough memory...");
+ goto out;
+ }
+ }
+ for (i = j = 0; i < count; i++) {
+ if (cards[i] == NULL)
+ continue;
+ k = snd_ctl_poll_descriptors(cards[i]->handle, pfd + j, pcount - j);
+ if (k != cards[i]->pfds) {
+ error("poll prepare failed: %i", k);
+ goto out;
+ }
+ j += k;
+ }
+ i = poll(pfd, j, (period / 2) * 1000);
+ if (i < 0 && errno == EINTR)
+ continue;
+ if (i < 0) {
+ error("poll failed: %s", strerror(errno));
+ break;
+ }
+ time(&now);
+ for (i = j = 0; i < count; i++) {
+ if (cards[i] == NULL)
+ continue;
+ k = snd_ctl_poll_descriptors_revents(cards[i]->handle,
+ pfd + j, cards[i]->pfds, &revents);
+ if (k < 0) {
+ error("poll post failed: %i\n", k);
+ goto out;
+ }
+ j += cards[i]->pfds;
+ if (revents & POLLIN) {
+ if (card_events(cards[i])) {
+ /* delay the write */
+ if (!changed)
+ last_write = now;
+ changed = 1;
+ }
+ }
+ }
+ if ((now - last_write >= period && changed) || save_now) {
+save:
+ changed = save_now = 0;
+ save_state(file, cardname);
+ }
+ }
+out:
+ free(pfd);
+ remove(pidfile);
+ for (i = 0; i < count; i++)
+ card_free(&cards[i]);
+ free(cards);
+ return 0;
+}
diff --git a/alsactl/lock.c b/alsactl/lock.c
new file mode 100644
index 0000000..d34d013
--- /dev/null
+++ b/alsactl/lock.c
@@ -0,0 +1,123 @@
+/*
+ * Advanced Linux Sound Architecture Control Program
+ * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include "aconfig.h"
+#include "version.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include "alsactl.h"
+
+static int state_lock_(const char *file, int lock, int timeout)
+{
+ int fd = -1, err = 0;
+ struct flock lck;
+ struct stat st;
+ char lcktxt[11];
+ char *nfile;
+
+ if (!do_lock)
+ return 0;
+ nfile = malloc(strlen(file) + 6);
+ if (nfile == NULL) {
+ error("No enough memory...");
+ return -ENOMEM;
+ }
+ strcpy(nfile, file);
+ strcat(nfile, ".lock");
+ lck.l_type = lock ? F_WRLCK : F_UNLCK;
+ lck.l_whence = SEEK_SET;
+ lck.l_start = 0;
+ lck.l_len = 11;
+ lck.l_pid = 0;
+ if (lock) {
+ sprintf(lcktxt, "%10li\n", (long)getpid());
+ } else {
+ sprintf(lcktxt, "%10s\n", "");
+ }
+ while (fd < 0 && timeout-- > 0) {
+ fd = open(nfile, O_RDWR);
+ if (fd < 0) {
+ fd = open(nfile, O_RDWR|O_CREAT|O_EXCL, 0644);
+ if (fd < 0) {
+ if (errno == EBUSY || errno == EAGAIN) {
+ sleep(1);
+ timeout--;
+ } else {
+ err = -errno;
+ goto out;
+ }
+ }
+ }
+ }
+ if (fd < 0 && timeout <= 0) {
+ err = -EBUSY;
+ goto out;
+ }
+ if (fstat(fd, &st) < 0) {
+ err = -errno;
+ goto out;
+ }
+ if (st.st_size != 11) {
+ if (write(fd, lcktxt, 11) != 11) {
+ err = -EIO;
+ goto out;
+ }
+ if (lseek(fd, 0, SEEK_SET)) {
+ err = -errno;
+ goto out;
+ }
+ }
+ while (timeout > 0) {
+ if (fcntl(fd, F_SETLK, &lck) < 0) {
+ sleep(1);
+ timeout--;
+ } else {
+ break;
+ }
+ }
+ if (timeout <= 0) {
+ err = -EBUSY;
+ goto out;
+ }
+ if (write(fd, lcktxt, 11) != 11) {
+ err = -EIO;
+ goto out;
+ }
+out:
+ free(nfile);
+ return err;
+}
+
+int state_lock(const char *file, int lock, int timeout)
+{
+ int err;
+
+ err = state_lock_(file, lock, timeout);
+ if (err < 0)
+ error("file %s %slock error: %s", file,
+ lock ? "" : "un", strerror(-err));
+ return err;
+}
diff --git a/alsactl/state.c b/alsactl/state.c
index 8b309ba..bd53d21 100644
--- a/alsactl/state.c
+++ b/alsactl/state.c
@@ -1562,6 +1562,8 @@ int save_state(const char *file, const char *cardname)
}
strcpy(nfile, file);
strcat(nfile, ".new");
+ if (state_lock(file, 1, 10) != 0)
+ goto out;
}
if (!stdio && (err = snd_input_stdio_open(&in, file, "r")) >= 0) {
err = snd_config_load(config, in);
@@ -1628,12 +1630,13 @@ int save_state(const char *file, const char *cardname)
if (err < 0) {
error("snd_config_save: %s", snd_strerror(err));
} else {
- //unlink(file);
err = rename(nfile, file);
if (err < 0)
error("rename failed: %s (%s)", strerror(-err), file);
}
out:
+ if (!stdio)
+ state_lock(file, 0, 10);
free(nfile);
snd_config_delete(config);
snd_config_update_free_global();
@@ -1646,7 +1649,7 @@ int load_state(const char *file, const char *initfile, const char *cardname,
int err, finalerr = 0;
snd_config_t *config;
snd_input_t *in;
- int stdio;
+ int stdio, locked = 0;
err = snd_config_top(&config);
if (err < 0) {
@@ -1654,13 +1657,18 @@ int load_state(const char *file, const char *initfile, const char *cardname,
return err;
}
stdio = !strcmp(file, "-");
- if (stdio)
+ if (stdio) {
err = snd_input_stdio_attach(&in, stdin, 0);
- else
- err = snd_input_stdio_open(&in, file, "r");
+ } else {
+ err = state_lock(file, 1, 10);
+ locked = err >= 0;
+ err = err >= 0 ? snd_input_stdio_open(&in, file, "r") : err;
+ }
if (err >= 0) {
err = snd_config_load(config, in);
snd_input_close(in);
+ if (locked)
+ state_lock(file, 0, 10);
if (err < 0) {
error("snd_config_load error: %s", snd_strerror(err));
goto out;
diff --git a/alsactl/utils.c b/alsactl/utils.c
index a27eb6e..7ac2db1 100644
--- a/alsactl/utils.c
+++ b/alsactl/utils.c
@@ -27,6 +27,7 @@
#include <errno.h>
#include <ctype.h>
#include <dirent.h>
+#include <syslog.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -100,3 +101,79 @@ void initfailed(int cardnumber, const char *reason, int exitcode)
close(fp);
free(str);
}
+
+static void syslog_(int prio, const char *fcn, long line,
+ const char *fmt, va_list ap)
+{
+ char buf[1024];
+
+ snprintf(buf, sizeof(buf), "%s: %s:%ld", command, fcn, line);
+ buf[sizeof(buf)-1] = '\0';
+ vsnprintf(buf + strlen(buf), sizeof(buf)-strlen(buf), fmt, ap);
+ buf[sizeof(buf)-1] = '\0';
+ syslog(LOG_INFO, buf);
+}
+
+void info_(const char *fcn, long line, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (use_syslog) {
+ syslog_(LOG_INFO, fcn, line, fmt, ap);
+ } else {
+ fprintf(stdout, "%s: %s:%ld: ", command, fcn, line);
+ vfprintf(stdout, fmt, ap);
+ putc('\n', stdout);
+ }
+ va_end(ap);
+}
+
+void error_(const char *fcn, long line, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (use_syslog) {
+ syslog_(LOG_ERR, fcn, line, fmt, ap);
+ } else {
+ fprintf(stderr, "%s: %s:%ld: ", command, fcn, line);
+ vfprintf(stderr, fmt, ap);
+ putc('\n', stderr);
+ }
+ va_end(ap);
+}
+
+void cerror_(const char *fcn, long line, int cond, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!cond && !debugflag)
+ return;
+ if (use_syslog) {
+ syslog_(LOG_ERR, fcn, line, fmt, ap);
+ } else {
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: %s:%ld: ", command, fcn, line);
+ vfprintf(stderr, fmt, ap);
+ putc('\n', stderr);
+ va_end(ap);
+ }
+}
+
+void dbg_(const char *fcn, long line, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!debugflag)
+ return;
+ if (use_syslog) {
+ syslog_(LOG_DEBUG, fcn, line, fmt, ap);
+ } else {
+ va_start(ap, fmt);
+ fprintf(stderr, "%s: %s:%ld: ", command, fcn, line);
+ vfprintf(stderr, fmt, ap);
+ putc('\n', stderr);
+ va_end(ap);
+ }
+}