audiod: fix enum of supported audio formats
[paraslash.git] / audiod_command.c
1 /*
2  * Copyright (C) 2005-2006 Andre Noll <noll@mathematik.tu-darmstadt.de>
3  *
4  *     This program is free software; you can redistribute it and/or modify
5  *     it under the terms of the GNU General Public License as published by
6  *     the Free Software Foundation; either version 2 of the License, or
7  *     (at your option) any later version.
8  *
9  *     This program is distributed in the hope that it will be useful,
10  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *     GNU General Public License for more details.
13  *
14  *     You should have received a copy of the GNU General Public License
15  *     along with this program; if not, write to the Free Software
16  *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
17  */
18
19 /** \file audiod_command.c commands for para_audiod */
20
21 #include "para.h"
22
23 #include "audiod.cmdline.h"
24 #include "list.h"
25 #include "close_on_fork.h"
26 #include "sched.h"
27 #include "filter.h"
28 #include "grab_client.cmdline.h"
29 #include "grab_client.h"
30
31 #include "error.h"
32 #include "audiod.h"
33 #include "net.h"
34 #include "daemon.h"
35 #include "string.h"
36 #include "fd.h"
37
38 /** defines one command of para_audiod */
39 struct audiod_command {
40         /** the name of the command */
41         const char *name;
42         /** pointer to the function that handles the command */
43         int (*handler)(int, int, char**);
44         /**
45          * if the command prefers to handle the full line (rather than the usual
46          * argv[] array), it stores a pointer to the corresponding line handling
47          * function here. In this case, the above \a handler pointer must be NULL.
48          */
49         int (*line_handler)(int, char*);
50         /** one-line description of the command */
51         const char *description;
52         /** summary of the command line options */
53         const char *synopsis;
54         /** the long help text */
55         const char *help;
56 };
57 static int com_grab(int, char *);
58 static int com_cycle(int, int, char **);
59 static int com_help(int, int, char **);
60 static int com_kill(int, int, char **);
61 static int com_off(int, int, char **);
62 static int com_on(int, int, char **);
63 static int com_sb(int, int, char **);
64 static int com_stat(int, int, char **);
65 static int com_tasks(int, int, char **);
66 static int com_term(int, int, char **);
67 static struct audiod_command cmds[] = {
68 {
69 .name = "cycle",
70 .handler = com_cycle,
71 .description = "switch to next mode",
72 .synopsis = "cycle",
73 .help =
74
75 "on -> standby -> off -> on\n"
76
77 },
78 {
79 .name = "grab",
80 .line_handler = com_grab,
81 .description = "grab the audio stream",
82 .synopsis = "-- grab [grab_options]",
83 .help =
84
85 "grab ('splice') the audio stream at any position in the filter      \n"
86 "chain and send that data back to the client. Try\n"
87 "\t para_audioc -- grab -h\n"
88 "for the list of available options.\n"
89 },
90
91 {
92 .name = "help",
93 .handler = com_help,
94 .description = "display command list or help for given command",
95 .synopsis = "help [command]",
96 .help =
97
98 "When I was younger, so much younger than today, I never needed\n"
99 "anybody's help in any way. But now these days are gone, I'm not so\n"
100 "self assured. Now I find I've changed my mind and opened up the doors.\n"
101 "\n"
102 "                               -- Beatles: Help\n"
103
104 },
105 {
106 .name = "kill",
107 .handler = com_kill,
108 .description = "kill an active audiod task",
109 .synopsis = "kill task_id [task_id ...]",
110 .help =
111
112 "call sched_unregister() and the event_handler of the given task(s)\n"
113
114 },
115 {
116 .name = "off",
117 .handler = com_off,
118 .description = "deactivate para_audiod",
119 .synopsis = "off",
120 .help =
121
122 "Close connection to para_server and stop all decoders.\n"
123
124 },
125 {
126 .name = "on",
127 .handler = com_on,
128 .description = "activate para_audiod",
129 .synopsis = "on",
130 .help =
131
132 "Establish connection to para_server, retrieve para_server's current\n"
133 "status. If playing, start corresponding decoder. Otherwise stop\n"
134 "all decoders.\n"
135
136 },
137 {
138 .name = "sb",
139 .handler = com_sb,
140 .description = "enter standby mode",
141 .synopsis = "sb",
142 .help =
143
144 "Stop all decoders but leave connection to para_server open.\n"
145
146 },
147 {
148 .name = "stat",
149 .handler = com_stat,
150 .description = "print status information",
151 .synopsis = "stat [item1 ...]",
152 .help =
153
154 "Dump given status items (all if none given) to stdout.\n"
155
156 },
157 {
158 .name = "tasks",
159 .handler = com_tasks,
160 .description = "list current tasks",
161 .synopsis = "tasks",
162 .help =
163
164 "print the list of task ids together with the status of each task\n"
165
166 },
167 {
168 .name = "term",
169 .handler = com_term,
170 .description = "terminate audiod",
171 .synopsis = "term",
172 .help =
173
174 "Stop all decoders, shut down connection to para_server and exit.\n"
175
176 },
177 {
178 .name = NULL,
179 }
180 };
181
182 /** iterate over the array of all audiod commands */
183 #define FOR_EACH_COMMAND(c) for (c = 0; cmds[c].name; c++)
184
185 static int client_write(int fd, const char *buf)
186 {
187         size_t len = strlen(buf);
188         return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1;
189 }
190
191 static char *get_time_string(struct timeval *newest_stime)
192 {
193         struct timeval diff, adj_stream_start, tmp;
194         int total = 0, use_server_time = 1,
195                 length_seconds = stat_task->length_seconds;
196
197         if (!stat_task->playing) {
198                 if (length_seconds)
199                         return NULL;
200                 return make_message("%s:\n", status_item_list[SI_PLAY_TIME]);
201         }
202         if (audiod_status == AUDIOD_OFF)
203                 goto out;
204         if (stat_task->sa_time_diff_sign > 0)
205                 tv_diff(&stat_task->server_stream_start, &stat_task->sa_time_diff,
206                         &adj_stream_start);
207         else
208                 tv_add(&stat_task->server_stream_start, &stat_task->sa_time_diff,
209                         &adj_stream_start);
210         tmp = adj_stream_start;
211         if (newest_stime && audiod_status == AUDIOD_ON) {
212                 tv_diff(newest_stime, &adj_stream_start, &diff);
213                 if (tv2ms(&diff) < 5000) {
214                         tmp = *newest_stime;
215                         use_server_time = 0;
216                 }
217         }
218         tv_diff(now, &tmp, &diff);
219         total = diff.tv_sec + stat_task->offset_seconds;
220         if (total > length_seconds)
221                 total = length_seconds;
222         if (total < 0)
223                 total = 0;
224 out:
225         return make_message(
226                 "%s:%s%d:%02d [%d:%02d] (%d%%/%d:%02d)\n",
227                 status_item_list[SI_PLAY_TIME],
228                 use_server_time? "~" : "",
229                 total / 60,
230                 total % 60,
231                 (length_seconds - total) / 60,
232                 (length_seconds - total) % 60,
233                 length_seconds? (total * 100 + length_seconds / 2) /
234                         length_seconds : 0,
235                 length_seconds / 60,
236                 length_seconds % 60
237         );
238 }
239
240 __malloc static char *audiod_status_string(void)
241 {
242         const char *status = (audiod_status == AUDIOD_ON)?
243                 "on" : (audiod_status == AUDIOD_OFF)? "off": "sb";
244         return make_message("%s:%s\n", status_item_list[SI_AUDIOD_STATUS], status);
245 }
246
247 static struct timeval *wstime(void)
248 {
249         int i;
250         struct timeval *max = NULL;
251         FOR_EACH_SLOT(i) {
252                 struct slot_info *s = &slot[i];
253                 if (!s->wng)
254                         continue;
255                 if (max && tv_diff(&s->wstime, max, NULL) <= 0)
256                         continue;
257                 max = &s->wstime;
258         }
259         return max;
260 }
261 __malloc static char *decoder_flags(void)
262 {
263         int i;
264         char flags[MAX_STREAM_SLOTS + 1];
265
266         FOR_EACH_SLOT(i) {
267                 struct slot_info *s = &slot[i];
268                 char flag = '0';
269                 if (s->receiver_node)
270                         flag += 1;
271                 if (s->wng)
272                         flag += 2;
273                 flags[i] = flag;
274         }
275         flags[MAX_STREAM_SLOTS] = '\0';
276         return make_message("%s:%s\n", status_item_list[SI_DECODER_FLAGS],
277                 flags);
278 }
279
280 static int dump_commands(int fd)
281 {
282         char *buf = para_strdup(""), *tmp = NULL;
283         int i;
284         ssize_t ret;
285
286         FOR_EACH_COMMAND(i) {
287                 tmp = make_message("%s%s\t%s\n", buf, cmds[i].name,
288                         cmds[i].description);
289                 free(buf);
290                 buf = tmp;
291         }
292         ret = client_write(fd, buf);
293         free(buf);
294         return ret;
295 }
296
297 /*
298  * command handlers don't close their fd on errors (ret < 0) so that
299  * its caller can send an error message. Otherwise (ret >= 0) it's up
300  * to each individual command to close the fd if necessary.
301  */
302
303 static int com_help(int fd, int argc, char **argv)
304 {
305         int i, ret;
306         char *buf;
307         const char *dflt = "No such command. Available commands:\n";
308
309         if (argc < 2) {
310                 ret = dump_commands(fd);
311                 goto out;
312         }
313         FOR_EACH_COMMAND(i) {
314                 if (strcmp(cmds[i].name, argv[1]))
315                         continue;
316                 buf = make_message(
317                         "NAME\n\t%s -- %s\n"
318                         "SYNOPSIS\n\tpara_audioc %s\n"
319                         "DESCRIPTION\n%s\n",
320                         argv[1],
321                         cmds[i].description,
322                         cmds[i].synopsis,
323                         cmds[i].help
324                 );
325                 ret = client_write(fd, buf);
326                 free(buf);
327                 goto out;
328         }
329         ret = client_write(fd, dflt);
330         if (ret > 0)
331                 ret = dump_commands(fd);
332 out:
333         if (ret >= 0)
334                 close(fd);
335         return ret;
336 }
337
338 static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
339 {
340         char *tl = get_task_list();
341         int ret = 1;
342         if (tl)
343                 ret = client_write(fd, tl);
344         free(tl);
345         if (ret > 0)
346                 close(fd);
347         return ret;
348 }
349
350 static int com_kill(int fd, int argc, char **argv)
351 {
352         int i, ret = 1;
353         if (argc < 2)
354                 return -E_AUDIOD_SYNTAX;
355         for (i = 1; i < argc; i++) {
356                 ret = kill_task(argv[i]);
357                 if (ret < 0)
358                         break;
359         }
360         if (ret > 0)
361                 close(fd);
362         return ret;
363 }
364
365 static int com_stat(int fd, __a_unused int argc, __a_unused char **argv)
366 {
367         int i, ret;
368         char *buf = NULL;
369         long unsigned mask = ~0LU;
370
371         if (argc > 1) {
372                 mask = 0;
373                 for (i = 1; i < argc; i++) {
374                         ret = stat_item_valid(argv[i]);
375                         if (ret < 0)
376                                 return ret;
377                         mask |= (1 << ret);
378                 }
379         }
380         PARA_INFO_LOG("mask: 0x%lx\n", mask);
381         if (mask & (1 << SI_PLAY_TIME)) {
382                 struct timeval *t = wstime();
383                 char *ts = get_time_string(t);
384                 if (ts) {
385                         ret = client_write(fd, ts);
386                         if (ret < 0)
387                                 goto out;
388                         free(ts);
389                 }
390         }
391         if (mask & (1 << SI_AUDIOD_UPTIME)) {
392                 char *tmp, *us = uptime_str();
393                 tmp = make_message("%s:%s\n",
394                         status_item_list[SI_AUDIOD_UPTIME], us);
395                 free(us);
396                 ret = client_write(fd, tmp);
397                 if (ret < 0)
398                         goto out;
399                 free(tmp);
400         }
401         if (mask & (1 << SI_AUDIOD_STATUS)) {
402                 char *s = audiod_status_string();
403                 ret = client_write(fd, s);
404                 if (ret < 0)
405                         goto out;
406                 free(s);
407         }
408         if (mask & (1 << SI_DECODER_FLAGS)) {
409                 char *df =decoder_flags();
410                 ret = client_write(fd, df);
411                 if (ret < 0)
412                         goto out;
413                 free(df);
414         }
415
416         for (i = 0; i < NUM_STAT_ITEMS; i++) {
417                 char *tmp, *v;
418                 if (!((1 << i) & mask))
419                         continue;
420                 v = stat_task->stat_item_values[i];
421                 tmp = make_message("%s%s%s", buf? buf: "",
422                         v? v : "", v? "\n" : "");
423                 free(buf);
424                 buf = tmp;
425         }
426         ret = client_write(fd, buf);
427 out:
428         if (ret > 0)
429                 ret = stat_client_add(fd, mask);
430         free(buf);
431         return ret;
432 }
433
434 static struct filter_node *find_filter_node(int slot_num, int format, int filternum)
435 {
436         struct filter_node *fn;
437         int i, j;
438
439         FOR_EACH_SLOT(i) {
440                 struct slot_info *s = &slot[i];
441                 if (s->format < 0 || !s->fc)
442                         continue;
443                 if (slot_num >= 0 && slot_num != i)
444                         continue;
445                 if (format >= 0 && s->format != format)
446                         continue;
447                 if (num_filters(i) < filternum)
448                         continue;
449                 /* success */
450                 j = 1;
451                 list_for_each_entry(fn, &s->fc->filters, node)
452                         if (filternum <= 0 || j++ == filternum)
453                                 break;
454                 return fn;
455         }
456         return NULL;
457 }
458
459 static int com_grab(int fd, char *cmdline)
460 {
461         struct grab_client *gc;
462         struct filter_node *fn;
463         int i, err;
464         char *msg;
465
466         gc = grab_client_new(fd, cmdline, &err);
467         if (!gc)
468                 goto err_out;
469         fn = find_filter_node(gc->conf->slot_arg, gc->audio_format_num, gc->conf->filter_num_arg);
470         if (fn)
471                 activate_grab_client(gc, fn);
472         return 1;
473 err_out:
474         if (err != -E_GC_HELP_GIVEN && err != -E_GC_VERSION_GIVEN)
475                 return err;
476         if (err == -E_GC_HELP_GIVEN) {
477                 msg = make_message("%s\n\n", grab_client_args_info_usage);
478                 for (i = 0; grab_client_args_info_help[i]; i++) {
479                         char *tmp = make_message("%s%s\n", msg,
480                                 grab_client_args_info_help[i]);
481                         free(msg);
482                         msg = tmp;
483                 }
484         } else
485                 msg = make_message("%s %s\n",
486                         GRAB_CLIENT_CMDLINE_PARSER_PACKAGE,
487                         GRAB_CLIENT_CMDLINE_PARSER_VERSION);
488         err = client_write(fd, msg);
489         free(msg);
490         if (err < 0)
491                 return err;
492         close(fd);
493         return 1;
494 }
495
496 static int __noreturn com_term(int fd, __a_unused int argc, __a_unused char **argv)
497 {
498         close(fd);
499         clean_exit(EXIT_SUCCESS, "terminating on user request");
500 }
501
502 static int com_on(int fd, __a_unused int argc, __a_unused char **argv)
503 {
504         audiod_status = AUDIOD_ON;
505         close(fd);
506         return 1;
507 }
508
509 static int com_off(int fd, __a_unused int argc, __a_unused char **argv)
510 {
511         audiod_status = AUDIOD_OFF;
512         close(fd);
513         return 1;
514 }
515
516 static int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
517 {
518         audiod_status = AUDIOD_STANDBY;
519         close(fd);
520         return 1;
521 }
522
523 static int com_cycle(int fd, int argc, char **argv)
524 {
525         switch (audiod_status) {
526                 case  AUDIOD_ON:
527                         return com_sb(fd, argc, argv);
528                         break;
529                 case  AUDIOD_OFF:
530                         return com_on(fd, argc, argv);
531                         break;
532                 case  AUDIOD_STANDBY:
533                         return com_off(fd, argc, argv);
534                         break;
535         }
536         close(fd);
537         return 1;
538 }
539
540 static int check_perms(uid_t uid)
541 {
542         int i;
543
544         if (!conf.user_allow_given)
545                 return 1;
546         for (i = 0; i < conf.user_allow_given; i++)
547                 if (uid == conf.user_allow_arg[i])
548                         return 1;
549         return -E_UCRED_PERM;
550 }
551
552 int handle_connect(int accept_fd)
553 {
554         int i, argc, ret, clifd = -1;
555         char *cmd = NULL, *p, *buf = para_calloc(MAXLINE), **argv = NULL;
556         struct sockaddr_un unix_addr;
557
558         ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un));
559         if (ret < 0)
560                 goto out;
561         clifd = ret;
562         ret = recv_cred_buffer(clifd, buf, MAXLINE - 1);
563         if (ret < 0)
564                 goto out;
565         PARA_INFO_LOG("connection from user %i, buf: %s\n",  ret, buf);
566         ret = check_perms(ret);
567         if (ret < 0)
568                 goto out;
569         ret = -E_INVALID_AUDIOD_CMD;
570         cmd = para_strdup(buf);
571         p = strchr(cmd, '\n');
572         if (!p)
573                 p = "";
574         else {
575                 *p = '\0';
576                 p++;
577         }
578         for (i = 0; cmds[i].name; i++) {
579                 int j;
580                 if (strcmp(cmds[i].name, cmd))
581                         continue;
582                 if (cmds[i].handler) {
583                         argc = split_args(buf, &argv, "\n");
584                         PARA_INFO_LOG("argv[0]: %s, argc= %d\n", argv[0], argc);
585                         ret = cmds[i].handler(clifd, argc, argv);
586                         goto out;
587                 }
588                 for (j = 0; p[j]; j++)
589                         if (p[j] == '\n')
590                                 p[j] = ' ';
591                 PARA_INFO_LOG("cmd: %s, options: %s\n", cmd, p);
592                 ret = cmds[i].line_handler(clifd, p);
593                 goto out;
594         }
595         ret = -E_INVALID_AUDIOD_CMD;
596 out:
597         free(cmd);
598         free(buf);
599         free(argv);
600         if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
601                 char *tmp = make_message("%s\n", PARA_STRERROR(-ret));
602                 client_write(clifd, tmp);
603                 free(tmp);
604                 close(clifd);
605         }
606         return ret;
607 }
608
609 void audiod_status_dump(void)
610 {
611         struct timeval *t = wstime();
612         char *old, *new, *tmp;
613
614         old = stat_task->stat_item_values[SI_PLAY_TIME];
615         new = get_time_string(t);
616         if (new) {
617                 if (!old || strcmp(old, new)) {
618                         free(old);
619                         stat_client_write(new, SI_PLAY_TIME);
620                         stat_task->stat_item_values[SI_PLAY_TIME] = new;
621                 } else
622                         free(new);
623         }
624
625         new = uptime_str();
626         old = stat_task->stat_item_values[SI_AUDIOD_UPTIME];
627         if (!old || strcmp(old, new)) {
628                 free(old);
629                 tmp = make_message("%s:%s\n",
630                         status_item_list[SI_AUDIOD_UPTIME], new);
631                 stat_client_write(tmp, SI_AUDIOD_UPTIME);
632                 free(tmp);
633                 stat_task->stat_item_values[SI_AUDIOD_UPTIME] = new;
634         } else
635                 free(new);
636
637         old = stat_task->stat_item_values[SI_AUDIOD_STATUS];
638         new = audiod_status_string();
639         if (!old || strcmp(old, new)) {
640                 free(old);
641                 stat_client_write(new, SI_AUDIOD_STATUS);
642                 stat_task->stat_item_values[SI_AUDIOD_STATUS] = new;
643         } else
644                 free(new);
645
646         old = stat_task->stat_item_values[SI_DECODER_FLAGS];
647         new = decoder_flags();
648         if (!old || strcmp(old, new)) {
649                 stat_client_write(new, SI_DECODER_FLAGS);
650                 stat_task->stat_item_values[SI_DECODER_FLAGS] = new;
651         } else
652                 free(new);
653 }
654
655 /**
656  * send empty status list
657  *
658  * Send to  each connected client the full status item list
659  * with empty values.
660  */
661 void dump_empty_status(void)
662 {
663         int i;
664
665         FOR_EACH_STAT_ITEM(i) {
666                 char *tmp = make_message("%s:\n", status_item_list[i]);
667                 stat_client_write(tmp, i);
668                 free(tmp);
669                 free(stat_task->stat_item_values[i]);
670                 stat_task->stat_item_values[i] = NULL;
671         }
672 }