mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-09 17:05:41 +01:00
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>
This commit is contained in:
parent
b95dd7ef46
commit
cc5c3357cf
8 changed files with 671 additions and 77 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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 error(...) do {\
|
||||
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 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
|
||||
|
||||
#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)
|
||||
#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 */
|
||||
|
||||
|
|
330
alsactl/daemon.c
Normal file
330
alsactl/daemon.c
Normal file
|
@ -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;
|
||||
}
|
123
alsactl/lock.c
Normal file
123
alsactl/lock.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue