Blame alsactl/daemon.c

Packit 229ac0
/*
Packit 229ac0
 *  Advanced Linux Sound Architecture Control Program
Packit 229ac0
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
Packit 229ac0
 *
Packit 229ac0
 *
Packit 229ac0
 *   This program is free software; you can redistribute it and/or modify
Packit 229ac0
 *   it under the terms of the GNU General Public License as published by
Packit 229ac0
 *   the Free Software Foundation; either version 2 of the License, or
Packit 229ac0
 *   (at your option) any later version.
Packit 229ac0
 *
Packit 229ac0
 *   This program is distributed in the hope that it will be useful,
Packit 229ac0
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 229ac0
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 229ac0
 *   GNU General Public License for more details.
Packit 229ac0
 *
Packit 229ac0
 *   You should have received a copy of the GNU General Public License
Packit 229ac0
 *   along with this program; if not, write to the Free Software
Packit 229ac0
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit 229ac0
 *
Packit 229ac0
 */
Packit 229ac0
Packit 229ac0
#include "aconfig.h"
Packit 229ac0
#include "version.h"
Packit 229ac0
#include <getopt.h>
Packit 229ac0
#include <stdarg.h>
Packit 229ac0
#include <stdio.h>
Packit 229ac0
#include <assert.h>
Packit 229ac0
#include <errno.h>
Packit 229ac0
#include <signal.h>
Packit 229ac0
#include <time.h>
Packit 229ac0
#include <poll.h>
Packit 229ac0
#include <alsa/asoundlib.h>
Packit 229ac0
#include "alsactl.h"
Packit 229ac0
Packit 229ac0
struct id_list {
Packit 229ac0
	snd_ctl_elem_id_t **list;
Packit 229ac0
	int size;
Packit 229ac0
};
Packit 229ac0
Packit 229ac0
struct card {
Packit 229ac0
	int index;
Packit 229ac0
	int pfds;
Packit 229ac0
	snd_ctl_t *handle;
Packit 229ac0
	struct id_list whitelist;
Packit 229ac0
	struct id_list blacklist;
Packit 229ac0
};
Packit 229ac0
Packit 229ac0
static int quit = 0;
Packit 229ac0
static int rescan = 0;
Packit 229ac0
static int save_now = 0;
Packit 229ac0
Packit 229ac0
static void signal_handler_quit(int sig)
Packit 229ac0
{
Packit 229ac0
	quit = 1;
Packit 229ac0
	signal(sig, signal_handler_quit);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void signal_handler_save_and_quit(int sig)
Packit 229ac0
{
Packit 229ac0
	quit = save_now = 1;
Packit 229ac0
	signal(sig, signal_handler_quit);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void signal_handler_rescan(int sig)
Packit 229ac0
{
Packit 229ac0
	rescan = 1;
Packit 229ac0
	signal(sig, signal_handler_rescan);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void free_list(struct id_list *list)
Packit 229ac0
{
Packit 229ac0
	int i;
Packit 229ac0
Packit 229ac0
	for (i = 0; i < list->size; i++)
Packit 229ac0
		free(list->list[i]);
Packit 229ac0
	free(list->list);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void card_free(struct card **card)
Packit 229ac0
{
Packit 229ac0
	struct card *c = *card;
Packit 229ac0
Packit 229ac0
	free_list(&c->blacklist);
Packit 229ac0
	free_list(&c->whitelist);
Packit 229ac0
	if (c->handle)
Packit 229ac0
		snd_ctl_close(c->handle);
Packit 229ac0
	free(c);
Packit 229ac0
	*card = NULL;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void add_card(struct card ***cards, int *count, const char *cardname)
Packit 229ac0
{
Packit 229ac0
	struct card *card, **cc;
Packit 229ac0
	int i, index, findex;
Packit 229ac0
	char device[16];
Packit 229ac0
Packit 229ac0
	index = snd_card_get_index(cardname);
Packit 229ac0
	if (index < 0)
Packit 229ac0
		return;
Packit 229ac0
	for (i = 0, findex = -1; i < *count; i++) {
Packit 229ac0
		if ((*cards)[i] == NULL) {
Packit 229ac0
			findex = i;
Packit 229ac0
		} else {
Packit 229ac0
			if ((*cards)[i]->index == index)
Packit 229ac0
				return;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
	card = calloc(1, sizeof(*card));
Packit 229ac0
	if (card == NULL)
Packit 229ac0
		return;
Packit 229ac0
	card->index = index;
Packit 229ac0
	sprintf(device, "hw:%i", index);
Packit 229ac0
	if (snd_ctl_open(&card->handle, device, SND_CTL_READONLY|SND_CTL_NONBLOCK) < 0) {
Packit 229ac0
		card_free(&card;;
Packit 229ac0
		return;
Packit 229ac0
	}
Packit 229ac0
	card->pfds = snd_ctl_poll_descriptors_count(card->handle);
Packit 229ac0
	if (card->pfds < 0) {
Packit 229ac0
		card_free(&card;;
Packit 229ac0
		return;
Packit 229ac0
	}
Packit 229ac0
	if (snd_ctl_subscribe_events(card->handle, 1) < 0) {
Packit 229ac0
		card_free(&card;;
Packit 229ac0
		return;
Packit 229ac0
	}
Packit 229ac0
	if (findex >= 0) {
Packit 229ac0
		(*cards)[findex] = card;
Packit 229ac0
	} else {
Packit 229ac0
		cc = realloc(*cards, sizeof(void *) * (*count + 1));
Packit 229ac0
		if (cc == NULL) {
Packit 229ac0
			card_free(&card;;
Packit 229ac0
			return;
Packit 229ac0
		}
Packit 229ac0
		cc[*count] = card;
Packit 229ac0
		*count = *count + 1;
Packit 229ac0
		*cards = cc;
Packit 229ac0
	}
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void add_cards(struct card ***cards, int *count)
Packit 229ac0
{
Packit 229ac0
	int card = -1;
Packit 229ac0
	char cardname[16];
Packit 229ac0
Packit 229ac0
	while (1) {
Packit 229ac0
		if (snd_card_next(&card) < 0)
Packit 229ac0
			break;
Packit 229ac0
		if (card < 0)
Packit 229ac0
			break;
Packit 229ac0
		if (card >= 0) {
Packit 229ac0
			sprintf(cardname, "%i", card);
Packit 229ac0
			add_card(cards, count, cardname);
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int compare_ids(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2)
Packit 229ac0
{
Packit 229ac0
	if (id1 == NULL || id2 == NULL)
Packit 229ac0
		return 0;
Packit 229ac0
	return snd_ctl_elem_id_get_interface(id1) == snd_ctl_elem_id_get_interface(id2) &&
Packit 229ac0
	       snd_ctl_elem_id_get_index(id1) == snd_ctl_elem_id_get_index(id2) &&
Packit 229ac0
	       strcmp(snd_ctl_elem_id_get_name(id1), snd_ctl_elem_id_get_name(id2)) == 0 &&
Packit 229ac0
	       snd_ctl_elem_id_get_device(id1) == snd_ctl_elem_id_get_device(id2) &&
Packit 229ac0
	       snd_ctl_elem_id_get_subdevice(id1) == snd_ctl_elem_id_get_subdevice(id2);
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int in_list(struct id_list *list, snd_ctl_elem_id_t *id)
Packit 229ac0
{
Packit 229ac0
	int i;
Packit 229ac0
	snd_ctl_elem_id_t *id1;
Packit 229ac0
Packit 229ac0
	for (i = 0; i < list->size; i++) {
Packit 229ac0
		id1 = list->list[i];
Packit 229ac0
		if (id1 == NULL)
Packit 229ac0
			continue;
Packit 229ac0
		if (compare_ids(id, id1))
Packit 229ac0
			return 1;
Packit 229ac0
	}
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void remove_from_list(struct id_list *list, snd_ctl_elem_id_t *id)
Packit 229ac0
{
Packit 229ac0
	int i;
Packit 229ac0
Packit 229ac0
	for (i = 0; i < list->size; i++) {
Packit 229ac0
		if (compare_ids(id, list->list[i])) {
Packit 229ac0
			free(list->list[i]);
Packit 229ac0
			list->list[i] = NULL;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static void add_to_list(struct id_list *list, snd_ctl_elem_id_t *id)
Packit 229ac0
{
Packit 229ac0
	snd_ctl_elem_id_t *id1;
Packit 229ac0
	snd_ctl_elem_id_t **n;
Packit 229ac0
	int i;
Packit 229ac0
Packit 229ac0
	if (snd_ctl_elem_id_malloc(&id1))
Packit 229ac0
		return;
Packit 229ac0
	snd_ctl_elem_id_copy(id1, id);
Packit 229ac0
	for (i = 0; i < list->size; i++) {
Packit 229ac0
		if (list->list[i] == NULL) {
Packit 229ac0
			list->list[i] = id1;
Packit 229ac0
			return;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
	n = realloc(list->list, sizeof(void *) * (list->size + 1));
Packit 229ac0
	if (n == NULL)
Packit 229ac0
		return;
Packit 229ac0
	n[list->size] = id1;
Packit 229ac0
	list->size++;
Packit 229ac0
	list->list = n;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int check_lists(struct card *card, snd_ctl_elem_id_t *id)
Packit 229ac0
{
Packit 229ac0
	snd_ctl_elem_info_t *info;
Packit 229ac0
	snd_ctl_elem_info_alloca(&info;;
Packit 229ac0
Packit 229ac0
	if (in_list(&card->blacklist, id))
Packit 229ac0
		return 0;
Packit 229ac0
	if (in_list(&card->whitelist, id))
Packit 229ac0
		return 1;
Packit 229ac0
	snd_ctl_elem_info_set_id(info, id);
Packit 229ac0
	if (snd_ctl_elem_info(card->handle, info) < 0)
Packit 229ac0
		return 0;
Packit 229ac0
	if (snd_ctl_elem_info_is_writable(info) ||
Packit 229ac0
	    snd_ctl_elem_info_is_tlv_writable(info)) {
Packit 229ac0
		add_to_list(&card->whitelist, id);
Packit 229ac0
		return 1;
Packit 229ac0
	} else {
Packit 229ac0
		add_to_list(&card->blacklist, id);
Packit 229ac0
		return 0;
Packit 229ac0
	}
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int card_events(struct card *card)
Packit 229ac0
{
Packit 229ac0
	int res = 0;
Packit 229ac0
	snd_ctl_event_t *ev;
Packit 229ac0
	snd_ctl_event_type_t type;
Packit 229ac0
	unsigned int mask;
Packit 229ac0
	snd_ctl_elem_id_t *id;
Packit 229ac0
	snd_ctl_event_alloca(&ev;;
Packit 229ac0
	snd_ctl_elem_id_alloca(&id;;
Packit 229ac0
Packit 229ac0
	while (snd_ctl_read(card->handle, ev) == 1) {
Packit 229ac0
		type = snd_ctl_event_get_type(ev);
Packit 229ac0
		if (type != SND_CTL_EVENT_ELEM)
Packit 229ac0
			continue;
Packit 229ac0
		mask = snd_ctl_event_elem_get_mask(ev);
Packit 229ac0
		snd_ctl_event_elem_get_id(ev, id);
Packit 229ac0
		if (mask == SND_CTL_EVENT_MASK_REMOVE) {
Packit 229ac0
			remove_from_list(&card->whitelist, id);
Packit 229ac0
			remove_from_list(&card->blacklist, id);
Packit 229ac0
			continue;
Packit 229ac0
		}
Packit 229ac0
		if (mask & SND_CTL_EVENT_MASK_INFO) {
Packit 229ac0
			remove_from_list(&card->whitelist, id);
Packit 229ac0
			remove_from_list(&card->blacklist, id);
Packit 229ac0
		}
Packit 229ac0
		if (mask & (SND_CTL_EVENT_MASK_VALUE|
Packit 229ac0
			    SND_CTL_EVENT_MASK_ADD|
Packit 229ac0
			    SND_CTL_EVENT_MASK_TLV)) {
Packit 229ac0
			if (check_lists(card, id))
Packit 229ac0
				res = 1;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
	return res;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static long read_pid_file(const char *pidfile)
Packit 229ac0
{
Packit 229ac0
	int fd, err;
Packit 229ac0
	char pid_txt[12];
Packit 229ac0
Packit 229ac0
	fd = open(pidfile, O_RDONLY);
Packit 229ac0
	if (fd >= 0) {
Packit 229ac0
		err = read(fd, pid_txt, 11);
Packit 229ac0
		if (err != 11)
Packit 229ac0
			err = err < 0 ? -errno : -EIO;
Packit 229ac0
		close(fd);
Packit 229ac0
		pid_txt[11] = '\0';
Packit 229ac0
		return atol(pid_txt);
Packit 229ac0
	} else {
Packit 229ac0
		return -errno;
Packit 229ac0
	}
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int write_pid_file(const char *pidfile)
Packit 229ac0
{
Packit 229ac0
	int fd, err;
Packit 229ac0
	char pid_txt[12];
Packit 229ac0
Packit 229ac0
	sprintf(pid_txt, "%10li\n", (long)getpid());
Packit 229ac0
	fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
Packit 229ac0
	if (fd >= 0) {
Packit 229ac0
		err = write(fd, pid_txt, 11);
Packit 229ac0
		if (err != 11) {
Packit 229ac0
			err = err < 0 ? -errno : -EIO;
Packit 229ac0
			unlink(pidfile);
Packit 229ac0
		} else {
Packit 229ac0
			err = 0;
Packit 229ac0
		}
Packit 229ac0
		close(fd);
Packit 229ac0
	} else {
Packit 229ac0
		err = -errno;
Packit 229ac0
	}
Packit 229ac0
	return err;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
int state_daemon_kill(const char *pidfile, const char *cmd)
Packit 229ac0
{
Packit 229ac0
	long pid;
Packit 229ac0
	int sig = SIGHUP;
Packit 229ac0
Packit 229ac0
	if (cmd == NULL) {
Packit 229ac0
		error("Specify kill command (quit, rescan or save_and_quit)");
Packit 229ac0
		return -EINVAL;
Packit 229ac0
	}
Packit 229ac0
	if (strcmp(cmd, "rescan") == 0)
Packit 229ac0
		sig = SIGUSR1;
Packit 229ac0
	else if (strcmp(cmd, "save_and_quit") == 0)
Packit 229ac0
		sig = SIGUSR2;
Packit 229ac0
	else if (strcmp(cmd, "quit") == 0)
Packit 229ac0
		sig = SIGTERM;
Packit 229ac0
	if (sig == SIGHUP) {
Packit 229ac0
		error("Unknown kill command '%s'", cmd);
Packit 229ac0
		return -EINVAL;
Packit 229ac0
	}
Packit 229ac0
	pid = read_pid_file(pidfile);
Packit 229ac0
	if (pid > 0) {
Packit 229ac0
		if (kill(pid, sig) >= 0)
Packit 229ac0
			return 0;
Packit 229ac0
		return -errno;
Packit 229ac0
	}
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
static int check_another_instance(const char *pidfile)
Packit 229ac0
{
Packit 229ac0
	long pid;
Packit 229ac0
Packit 229ac0
	pid = read_pid_file(pidfile);
Packit 229ac0
	if (pid >= 0) {
Packit 229ac0
		/* invoke new card rescan */
Packit 229ac0
		if (kill(pid, SIGUSR1) >= 0) {
Packit 229ac0
			usleep(1000);
Packit 229ac0
			pid = read_pid_file(pidfile);
Packit 229ac0
			if (pid >= 0)
Packit 229ac0
				return 1;
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
	return 0;
Packit 229ac0
}
Packit 229ac0
Packit 229ac0
int state_daemon(const char *file, const char *cardname, int period,
Packit 229ac0
		 const char *pidfile)
Packit 229ac0
{
Packit 229ac0
	int count = 0, pcount, psize = 0, i, j, k, changed = 0;
Packit 229ac0
	time_t last_write, now;
Packit 229ac0
	unsigned short revents;
Packit 229ac0
	struct card **cards = NULL;
Packit 229ac0
	struct pollfd *pfd = NULL, *pfdn;
Packit 229ac0
Packit 229ac0
	if (check_another_instance(pidfile))
Packit 229ac0
		return 0;
Packit 229ac0
	rescan = 1;
Packit 229ac0
	signal(SIGABRT, signal_handler_quit);
Packit 229ac0
	signal(SIGTERM, signal_handler_quit);
Packit 229ac0
	signal(SIGINT, signal_handler_quit);
Packit 229ac0
	signal(SIGUSR1, signal_handler_rescan);
Packit 229ac0
	signal(SIGUSR2, signal_handler_save_and_quit);
Packit 229ac0
	write_pid_file(pidfile);
Packit 229ac0
	time(&last_write);
Packit 229ac0
	while (!quit || save_now) {
Packit 229ac0
		if (save_now)
Packit 229ac0
			goto save;
Packit 229ac0
		if (rescan) {
Packit 229ac0
			if (cardname) {
Packit 229ac0
				add_card(&cards, &count, cardname);
Packit 229ac0
			} else {
Packit 229ac0
				add_cards(&cards, &count);
Packit 229ac0
			}
Packit 229ac0
			snd_config_update_free_global();
Packit 229ac0
			rescan = 0;
Packit 229ac0
		}
Packit 229ac0
		for (i = pcount = 0; i < count; i++) {
Packit 229ac0
			if (cards[i] == NULL)
Packit 229ac0
				continue;
Packit 229ac0
			pcount += cards[i]->pfds;
Packit 229ac0
		}
Packit 229ac0
		if (pcount > psize) {
Packit 229ac0
			pfdn = realloc(pfd, sizeof(struct pollfd) * pcount);
Packit 229ac0
			if (pfdn) {
Packit 229ac0
				psize = pcount;
Packit 229ac0
				pfd = pfdn;
Packit 229ac0
			} else {
Packit 229ac0
				error("No enough memory...");
Packit 229ac0
				goto out;
Packit 229ac0
			}
Packit 229ac0
		}
Packit 229ac0
		for (i = j = 0; i < count; i++) {
Packit 229ac0
			if (cards[i] == NULL)
Packit 229ac0
				continue;
Packit 229ac0
			k = snd_ctl_poll_descriptors(cards[i]->handle, pfd + j, pcount - j);
Packit 229ac0
			if (k != cards[i]->pfds) {
Packit 229ac0
				error("poll prepare failed: %i", k);
Packit 229ac0
				goto out;
Packit 229ac0
			}
Packit 229ac0
			j += k;
Packit 229ac0
		}
Packit 229ac0
		i = poll(pfd, j, (period / 2) * 1000);
Packit 229ac0
		if (i < 0 && errno == EINTR)
Packit 229ac0
			continue;
Packit 229ac0
		if (i < 0) {
Packit 229ac0
			error("poll failed: %s", strerror(errno));
Packit 229ac0
			break;
Packit 229ac0
		}
Packit 229ac0
		time(&now;;
Packit 229ac0
		for (i = j = 0; i < count; i++) {
Packit 229ac0
			if (cards[i] == NULL)
Packit 229ac0
				continue;
Packit 229ac0
			k = snd_ctl_poll_descriptors_revents(cards[i]->handle,
Packit 229ac0
					pfd + j, cards[i]->pfds, &revents);
Packit 229ac0
			if (k < 0) {
Packit 229ac0
				error("poll post failed: %i\n", k);
Packit 229ac0
				goto out;
Packit 229ac0
			}
Packit 229ac0
			j += cards[i]->pfds;
Packit 229ac0
			if (revents & (POLLERR|POLLNVAL)) {
Packit 229ac0
				card_free(&cards[i]);
Packit 229ac0
			} else if (revents & POLLIN) {
Packit 229ac0
				if (card_events(cards[i])) {
Packit 229ac0
					/* delay the write */
Packit 229ac0
					if (!changed)
Packit 229ac0
						last_write = now;
Packit 229ac0
					changed = 1;
Packit 229ac0
				}
Packit 229ac0
			}
Packit 229ac0
		}
Packit 229ac0
		if ((now - last_write >= period && changed) || save_now) {
Packit 229ac0
save:
Packit 229ac0
			changed = save_now = 0;
Packit 229ac0
			save_state(file, cardname);
Packit 229ac0
		}
Packit 229ac0
	}
Packit 229ac0
out:
Packit 229ac0
	free(pfd);
Packit 229ac0
	remove(pidfile);
Packit 229ac0
	if (cards) {
Packit 229ac0
		for (i = 0; i < count; i++)
Packit 229ac0
			card_free(&cards[i]);
Packit 229ac0
		free(cards);
Packit 229ac0
	}
Packit 229ac0
	return 0;
Packit 229ac0
}