tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

daemon.c (10023B)


      1 /*
      2  *  Advanced Linux Sound Architecture Control Program
      3  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
      4  *
      5  *
      6  *   This program is free software; you can redistribute it and/or modify
      7  *   it under the terms of the GNU General Public License as published by
      8  *   the Free Software Foundation; either version 2 of the License, or
      9  *   (at your option) any later version.
     10  *
     11  *   This program is distributed in the hope that it will be useful,
     12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14  *   GNU General Public License for more details.
     15  *
     16  *   You should have received a copy of the GNU General Public License
     17  *   along with this program; if not, write to the Free Software
     18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
     19  *
     20  */
     21 
     22 #include "aconfig.h"
     23 #include "version.h"
     24 #include <getopt.h>
     25 #include <stdarg.h>
     26 #include <stdio.h>
     27 #include <assert.h>
     28 #include <errno.h>
     29 #include <signal.h>
     30 #include <time.h>
     31 #include <poll.h>
     32 #include <alsa/asoundlib.h>
     33 #include "alsactl.h"
     34 
     35 struct id_list {
     36 	snd_ctl_elem_id_t **list;
     37 	int size;
     38 };
     39 
     40 struct card {
     41 	int index;
     42 	int pfds;
     43 	snd_ctl_t *handle;
     44 	struct id_list whitelist;
     45 	struct id_list blacklist;
     46 };
     47 
     48 static int quit = 0;
     49 static int rescan = 0;
     50 static int save_now = 0;
     51 
     52 static void signal_handler_quit(int sig)
     53 {
     54 	quit = 1;
     55 	signal(sig, signal_handler_quit);
     56 }
     57 
     58 static void signal_handler_save_and_quit(int sig)
     59 {
     60 	quit = save_now = 1;
     61 	signal(sig, signal_handler_quit);
     62 }
     63 
     64 static void signal_handler_rescan(int sig)
     65 {
     66 	rescan = 1;
     67 	signal(sig, signal_handler_rescan);
     68 }
     69 
     70 static void free_list(struct id_list *list)
     71 {
     72 	int i;
     73 
     74 	for (i = 0; i < list->size; i++)
     75 		free(list->list[i]);
     76 	free(list->list);
     77 }
     78 
     79 static void card_free(struct card **card)
     80 {
     81 	struct card *c = *card;
     82 
     83 	free_list(&c->blacklist);
     84 	free_list(&c->whitelist);
     85 	if (c->handle)
     86 		snd_ctl_close(c->handle);
     87 	free(c);
     88 	*card = NULL;
     89 }
     90 
     91 static void add_card(struct card ***cards, int *count, const char *cardname)
     92 {
     93 	struct card *card, **cc;
     94 	int i, index, findex;
     95 	char device[16];
     96 
     97 	index = snd_card_get_index(cardname);
     98 	if (index < 0)
     99 		return;
    100 	for (i = 0, findex = -1; i < *count; i++) {
    101 		if ((*cards)[i] == NULL) {
    102 			findex = i;
    103 		} else {
    104 			if ((*cards)[i]->index == index)
    105 				return;
    106 		}
    107 	}
    108 	card = calloc(1, sizeof(*card));
    109 	if (card == NULL)
    110 		return;
    111 	card->index = index;
    112 	sprintf(device, "hw:%i", index);
    113 	if (snd_ctl_open(&card->handle, device, SND_CTL_READONLY|SND_CTL_NONBLOCK) < 0) {
    114 		card_free(&card);
    115 		return;
    116 	}
    117 	card->pfds = snd_ctl_poll_descriptors_count(card->handle);
    118 	if (card->pfds < 0) {
    119 		card_free(&card);
    120 		return;
    121 	}
    122 	if (snd_ctl_subscribe_events(card->handle, 1) < 0) {
    123 		card_free(&card);
    124 		return;
    125 	}
    126 	if (findex >= 0) {
    127 		(*cards)[findex] = card;
    128 	} else {
    129 		cc = realloc(*cards, sizeof(void *) * (*count + 1));
    130 		if (cc == NULL) {
    131 			card_free(&card);
    132 			return;
    133 		}
    134 		cc[*count] = card;
    135 		*count = *count + 1;
    136 		*cards = cc;
    137 	}
    138 }
    139 
    140 static void add_cards(struct card ***cards, int *count)
    141 {
    142 	int card = -1;
    143 	char cardname[16];
    144 
    145 	while (1) {
    146 		if (snd_card_next(&card) < 0)
    147 			break;
    148 		if (card < 0)
    149 			break;
    150 		if (card >= 0) {
    151 			sprintf(cardname, "%i", card);
    152 			add_card(cards, count, cardname);
    153 		}
    154 	}
    155 }
    156 
    157 static int compare_ids(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2)
    158 {
    159 	if (id1 == NULL || id2 == NULL)
    160 		return 0;
    161 	return snd_ctl_elem_id_get_interface(id1) == snd_ctl_elem_id_get_interface(id2) &&
    162 	       snd_ctl_elem_id_get_index(id1) == snd_ctl_elem_id_get_index(id2) &&
    163 	       strcmp(snd_ctl_elem_id_get_name(id1), snd_ctl_elem_id_get_name(id2)) == 0 &&
    164 	       snd_ctl_elem_id_get_device(id1) == snd_ctl_elem_id_get_device(id2) &&
    165 	       snd_ctl_elem_id_get_subdevice(id1) == snd_ctl_elem_id_get_subdevice(id2);
    166 }
    167 
    168 static int in_list(struct id_list *list, snd_ctl_elem_id_t *id)
    169 {
    170 	int i;
    171 	snd_ctl_elem_id_t *id1;
    172 
    173 	for (i = 0; i < list->size; i++) {
    174 		id1 = list->list[i];
    175 		if (id1 == NULL)
    176 			continue;
    177 		if (compare_ids(id, id1))
    178 			return 1;
    179 	}
    180 	return 0;
    181 }
    182 
    183 static void remove_from_list(struct id_list *list, snd_ctl_elem_id_t *id)
    184 {
    185 	int i;
    186 
    187 	for (i = 0; i < list->size; i++) {
    188 		if (compare_ids(id, list->list[i])) {
    189 			free(list->list[i]);
    190 			list->list[i] = NULL;
    191 		}
    192 	}
    193 }
    194 
    195 static void add_to_list(struct id_list *list, snd_ctl_elem_id_t *id)
    196 {
    197 	snd_ctl_elem_id_t *id1;
    198 	snd_ctl_elem_id_t **n;
    199 	int i;
    200 
    201 	if (snd_ctl_elem_id_malloc(&id1))
    202 		return;
    203 	snd_ctl_elem_id_copy(id1, id);
    204 	for (i = 0; i < list->size; i++) {
    205 		if (list->list[i] == NULL) {
    206 			list->list[i] = id1;
    207 			return;
    208 		}
    209 	}
    210 	n = realloc(list->list, sizeof(void *) * (list->size + 1));
    211 	if (n == NULL)
    212 		return;
    213 	n[list->size] = id1;
    214 	list->size++;
    215 	list->list = n;
    216 }
    217 
    218 static int check_lists(struct card *card, snd_ctl_elem_id_t *id)
    219 {
    220 	snd_ctl_elem_info_t *info;
    221 	snd_ctl_elem_info_alloca(&info);
    222 
    223 	if (in_list(&card->blacklist, id))
    224 		return 0;
    225 	if (in_list(&card->whitelist, id))
    226 		return 1;
    227 	snd_ctl_elem_info_set_id(info, id);
    228 	if (snd_ctl_elem_info(card->handle, info) < 0)
    229 		return 0;
    230 	if (snd_ctl_elem_info_is_writable(info) ||
    231 	    snd_ctl_elem_info_is_tlv_writable(info)) {
    232 		add_to_list(&card->whitelist, id);
    233 		return 1;
    234 	} else {
    235 		add_to_list(&card->blacklist, id);
    236 		return 0;
    237 	}
    238 }
    239 
    240 static int card_events(struct card *card)
    241 {
    242 	int res = 0;
    243 	snd_ctl_event_t *ev;
    244 	snd_ctl_event_type_t type;
    245 	unsigned int mask;
    246 	snd_ctl_elem_id_t *id;
    247 	snd_ctl_event_alloca(&ev);
    248 	snd_ctl_elem_id_alloca(&id);
    249 
    250 	while (snd_ctl_read(card->handle, ev) == 1) {
    251 		type = snd_ctl_event_get_type(ev);
    252 		if (type != SND_CTL_EVENT_ELEM)
    253 			continue;
    254 		mask = snd_ctl_event_elem_get_mask(ev);
    255 		snd_ctl_event_elem_get_id(ev, id);
    256 		if (mask == SND_CTL_EVENT_MASK_REMOVE) {
    257 			remove_from_list(&card->whitelist, id);
    258 			remove_from_list(&card->blacklist, id);
    259 			continue;
    260 		}
    261 		if (mask & SND_CTL_EVENT_MASK_INFO) {
    262 			remove_from_list(&card->whitelist, id);
    263 			remove_from_list(&card->blacklist, id);
    264 		}
    265 		if (mask & (SND_CTL_EVENT_MASK_VALUE|
    266 			    SND_CTL_EVENT_MASK_ADD|
    267 			    SND_CTL_EVENT_MASK_TLV)) {
    268 			if (check_lists(card, id))
    269 				res = 1;
    270 		}
    271 	}
    272 	return res;
    273 }
    274 
    275 static long read_pid_file(const char *pidfile)
    276 {
    277 	int fd, err;
    278 	char pid_txt[12];
    279 
    280 	fd = open(pidfile, O_RDONLY);
    281 	if (fd >= 0) {
    282 		err = read(fd, pid_txt, 11);
    283 		if (err != 11)
    284 			err = err < 0 ? -errno : -EIO;
    285 		close(fd);
    286 		pid_txt[11] = '\0';
    287 		return atol(pid_txt);
    288 	} else {
    289 		return -errno;
    290 	}
    291 }
    292 
    293 static int write_pid_file(const char *pidfile)
    294 {
    295 	int fd, err;
    296 	char pid_txt[12];
    297 
    298 	sprintf(pid_txt, "%10li\n", (long)getpid());
    299 	fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0600);
    300 	if (fd >= 0) {
    301 		err = write(fd, pid_txt, 11);
    302 		if (err != 11) {
    303 			err = err < 0 ? -errno : -EIO;
    304 			unlink(pidfile);
    305 		} else {
    306 			err = 0;
    307 		}
    308 		close(fd);
    309 	} else {
    310 		err = -errno;
    311 	}
    312 	return err;
    313 }
    314 
    315 int state_daemon_kill(const char *pidfile, const char *cmd)
    316 {
    317 	long pid;
    318 	int sig = SIGHUP;
    319 
    320 	if (cmd == NULL) {
    321 		error("Specify kill command (quit, rescan or save_and_quit)");
    322 		return -EINVAL;
    323 	}
    324 	if (strcmp(cmd, "rescan") == 0)
    325 		sig = SIGUSR1;
    326 	else if (strcmp(cmd, "save_and_quit") == 0)
    327 		sig = SIGUSR2;
    328 	else if (strcmp(cmd, "quit") == 0)
    329 		sig = SIGTERM;
    330 	if (sig == SIGHUP) {
    331 		error("Unknown kill command '%s'", cmd);
    332 		return -EINVAL;
    333 	}
    334 	pid = read_pid_file(pidfile);
    335 	if (pid > 0) {
    336 		if (kill(pid, sig) >= 0)
    337 			return 0;
    338 		return -errno;
    339 	}
    340 	return 0;
    341 }
    342 
    343 static int check_another_instance(const char *pidfile)
    344 {
    345 	long pid;
    346 
    347 	pid = read_pid_file(pidfile);
    348 	if (pid >= 0) {
    349 		/* invoke new card rescan */
    350 		if (kill(pid, SIGUSR1) >= 0) {
    351 			usleep(1000);
    352 			pid = read_pid_file(pidfile);
    353 			if (pid >= 0)
    354 				return 1;
    355 		}
    356 	}
    357 	return 0;
    358 }
    359 
    360 int state_daemon(const char *file, const char *cardname, int period,
    361 		 const char *pidfile)
    362 {
    363 	int count = 0, pcount, psize = 0, i, j, k, changed = 0;
    364 	time_t last_write, now;
    365 	unsigned short revents;
    366 	struct card **cards = NULL;
    367 	struct pollfd *pfd = NULL, *pfdn;
    368 
    369 	if (check_another_instance(pidfile))
    370 		return 0;
    371 	rescan = 1;
    372 	signal(SIGABRT, signal_handler_quit);
    373 	signal(SIGTERM, signal_handler_quit);
    374 	signal(SIGINT, signal_handler_quit);
    375 	signal(SIGUSR1, signal_handler_rescan);
    376 	signal(SIGUSR2, signal_handler_save_and_quit);
    377 	write_pid_file(pidfile);
    378 	time(&last_write);
    379 	while (!quit || save_now) {
    380 		if (save_now)
    381 			goto save;
    382 		if (rescan) {
    383 			if (cardname) {
    384 				add_card(&cards, &count, cardname);
    385 			} else {
    386 				add_cards(&cards, &count);
    387 			}
    388 			snd_config_update_free_global();
    389 			rescan = 0;
    390 		}
    391 		for (i = pcount = 0; i < count; i++) {
    392 			if (cards[i] == NULL)
    393 				continue;
    394 			pcount += cards[i]->pfds;
    395 		}
    396 		if (pcount > psize) {
    397 			pfdn = realloc(pfd, sizeof(struct pollfd) * pcount);
    398 			if (pfdn) {
    399 				psize = pcount;
    400 				pfd = pfdn;
    401 			} else {
    402 				error("No enough memory...");
    403 				goto out;
    404 			}
    405 		}
    406 		for (i = j = 0; i < count; i++) {
    407 			if (cards[i] == NULL)
    408 				continue;
    409 			k = snd_ctl_poll_descriptors(cards[i]->handle, pfd + j, pcount - j);
    410 			if (k != cards[i]->pfds) {
    411 				error("poll prepare failed: %i", k);
    412 				goto out;
    413 			}
    414 			j += k;
    415 		}
    416 		i = poll(pfd, j, (period / 2) * 1000);
    417 		if (i < 0 && errno == EINTR)
    418 			continue;
    419 		if (i < 0) {
    420 			error("poll failed: %s", strerror(errno));
    421 			break;
    422 		}
    423 		time(&now);
    424 		for (i = j = 0; i < count; i++) {
    425 			if (cards[i] == NULL)
    426 				continue;
    427 			k = snd_ctl_poll_descriptors_revents(cards[i]->handle,
    428 					pfd + j, cards[i]->pfds, &revents);
    429 			if (k < 0) {
    430 				error("poll post failed: %i\n", k);
    431 				goto out;
    432 			}
    433 			j += cards[i]->pfds;
    434 			if (revents & (POLLERR|POLLNVAL)) {
    435 				card_free(&cards[i]);
    436 			} else if (revents & POLLIN) {
    437 				if (card_events(cards[i])) {
    438 					/* delay the write */
    439 					if (!changed)
    440 						last_write = now;
    441 					changed = 1;
    442 				}
    443 			}
    444 		}
    445 		if ((now - last_write >= period && changed) || save_now) {
    446 save:
    447 			changed = save_now = 0;
    448 			save_state(file, cardname);
    449 		}
    450 	}
    451 out:
    452 	free(pfd);
    453 	remove(pidfile);
    454 	if (cards) {
    455 		for (i = 0; i < count; i++)
    456 			card_free(&cards[i]);
    457 		free(cards);
    458 	}
    459 	return 0;
    460 }