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 }