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