2 * Copyright (C) 2005-2006 Andre Noll <noll@mathematik.tu-darmstadt.de>
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.
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.
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.
19 /** \file audiod_command.c commands for para_audiod */
23 #include "audiod.cmdline.h"
25 #include "close_on_fork.h"
28 #include "grab_client.cmdline.h"
29 #include "grab_client.h"
38 /** defines one command of para_audiod */
39 struct audiod_command {
40 /** the name of the command */
42 /** pointer to the function that handles the command */
43 int (*handler)(int, int, char**);
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.
49 int (*line_handler)(int, char*);
50 /** one-line description of the command */
51 const char *description;
52 /** summary of the command line options */
54 /** the long help text */
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[] = {
71 .description = "switch to next mode",
75 "on -> standby -> off -> on\n"
80 .line_handler = com_grab,
81 .description = "grab the audio stream",
82 .synopsis = "-- grab [grab_options]",
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"
94 .description = "display command list or help for given command",
95 .synopsis = "help [command]",
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"
102 " -- Beatles: Help\n"
108 .description = "kill an active audiod task",
109 .synopsis = "kill task_id [task_id ...]",
112 "call sched_unregister() and the event_handler of the given task(s)\n"
118 .description = "deactivate para_audiod",
122 "Close connection to para_server and stop all decoders.\n"
128 .description = "activate para_audiod",
132 "Establish connection to para_server, retrieve para_server's current\n"
133 "status. If playing, start corresponding decoder. Otherwise stop\n"
140 .description = "enter standby mode",
144 "Stop all decoders but leave connection to para_server open.\n"
150 .description = "print status information",
151 .synopsis = "stat [item1 ...]",
154 "Dump given status items (all if none given) to stdout.\n"
159 .handler = com_tasks,
160 .description = "list current tasks",
164 "print the list of task ids together with the status of each task\n"
170 .description = "terminate audiod",
174 "Stop all decoders, shut down connection to para_server and exit.\n"
182 /** iterate over the array of all audiod commands */
183 #define FOR_EACH_COMMAND(c) for (c = 0; cmds[c].name; c++)
185 static int client_write(int fd, const char *buf)
187 size_t len = strlen(buf);
188 return write(fd, buf, len) != len? -E_CLIENT_WRITE: 1;
191 static char *get_time_string(struct timeval *newest_stime)
193 struct timeval diff, adj_stream_start, tmp;
194 int total = 0, use_server_time = 1,
195 length_seconds = stat_task->length_seconds;
197 if (!stat_task->playing) {
200 return make_message("%s:\n", status_item_list[SI_PLAY_TIME]);
202 if (audiod_status == AUDIOD_OFF)
204 if (stat_task->sa_time_diff_sign > 0)
205 tv_diff(&stat_task->server_stream_start, &stat_task->sa_time_diff,
208 tv_add(&stat_task->server_stream_start, &stat_task->sa_time_diff,
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) {
218 tv_diff(now, &tmp, &diff);
219 total = diff.tv_sec + stat_task->offset_seconds;
220 if (total > length_seconds)
221 total = length_seconds;
226 "%s:%s%d:%02d [%d:%02d] (%d%%/%d:%02d)\n",
227 status_item_list[SI_PLAY_TIME],
228 use_server_time? "~" : "",
231 (length_seconds - total) / 60,
232 (length_seconds - total) % 60,
233 length_seconds? (total * 100 + length_seconds / 2) /
240 __malloc static char *audiod_status_string(void)
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);
247 static struct timeval *wstime(void)
250 struct timeval *max = NULL;
252 struct slot_info *s = &slot[i];
255 if (max && tv_diff(&s->wstime, max, NULL) <= 0)
261 __malloc static char *decoder_flags(void)
264 char flags[MAX_STREAM_SLOTS + 1];
267 struct slot_info *s = &slot[i];
269 if (s->receiver_node)
275 flags[MAX_STREAM_SLOTS] = '\0';
276 return make_message("%s:%s\n", status_item_list[SI_DECODER_FLAGS],
280 static int dump_commands(int fd)
282 char *buf = para_strdup(""), *tmp = NULL;
286 FOR_EACH_COMMAND(i) {
287 tmp = make_message("%s%s\t%s\n", buf, cmds[i].name,
288 cmds[i].description);
292 ret = client_write(fd, buf);
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.
303 static int com_help(int fd, int argc, char **argv)
307 const char *dflt = "No such command. Available commands:\n";
310 ret = dump_commands(fd);
313 FOR_EACH_COMMAND(i) {
314 if (strcmp(cmds[i].name, argv[1]))
318 "SYNOPSIS\n\tpara_audioc %s\n"
325 ret = client_write(fd, buf);
329 ret = client_write(fd, dflt);
331 ret = dump_commands(fd);
338 static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
340 char *tl = get_task_list();
343 ret = client_write(fd, tl);
350 static int com_kill(int fd, int argc, char **argv)
354 return -E_AUDIOD_SYNTAX;
355 for (i = 1; i < argc; i++) {
356 ret = kill_task(argv[i]);
365 static int com_stat(int fd, __a_unused int argc, __a_unused char **argv)
369 long unsigned mask = ~0LU;
373 for (i = 1; i < argc; i++) {
374 ret = stat_item_valid(argv[i]);
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);
385 ret = client_write(fd, ts);
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);
396 ret = client_write(fd, tmp);
401 if (mask & (1 << SI_AUDIOD_STATUS)) {
402 char *s = audiod_status_string();
403 ret = client_write(fd, s);
408 if (mask & (1 << SI_DECODER_FLAGS)) {
409 char *df =decoder_flags();
410 ret = client_write(fd, df);
416 for (i = 0; i < NUM_STAT_ITEMS; i++) {
418 if (!((1 << i) & mask))
420 v = stat_task->stat_item_values[i];
421 tmp = make_message("%s%s%s", buf? buf: "",
422 v? v : "", v? "\n" : "");
426 ret = client_write(fd, buf);
429 ret = stat_client_add(fd, mask);
434 static struct filter_node *find_filter_node(int slot_num, int format, int filternum)
436 struct filter_node *fn;
440 struct slot_info *s = &slot[i];
441 if (s->format < 0 || !s->fc)
443 if (slot_num >= 0 && slot_num != i)
445 if (format >= 0 && s->format != format)
447 if (num_filters(i) < filternum)
451 list_for_each_entry(fn, &s->fc->filters, node)
452 if (filternum <= 0 || j++ == filternum)
459 static int com_grab(int fd, char *cmdline)
461 struct grab_client *gc;
462 struct filter_node *fn;
466 gc = grab_client_new(fd, cmdline, &err);
469 fn = find_filter_node(gc->conf->slot_arg, gc->audio_format_num, gc->conf->filter_num_arg);
471 activate_grab_client(gc, fn);
474 if (err != -E_GC_HELP_GIVEN && err != -E_GC_VERSION_GIVEN)
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]);
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);
496 static int __noreturn com_term(int fd, __a_unused int argc, __a_unused char **argv)
499 clean_exit(EXIT_SUCCESS, "terminating on user request");
502 static int com_on(int fd, __a_unused int argc, __a_unused char **argv)
504 audiod_status = AUDIOD_ON;
509 static int com_off(int fd, __a_unused int argc, __a_unused char **argv)
511 audiod_status = AUDIOD_OFF;
516 static int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
518 audiod_status = AUDIOD_STANDBY;
523 static int com_cycle(int fd, int argc, char **argv)
525 switch (audiod_status) {
527 return com_sb(fd, argc, argv);
530 return com_on(fd, argc, argv);
533 return com_off(fd, argc, argv);
540 static int check_perms(uid_t uid)
544 if (!conf.user_allow_given)
546 for (i = 0; i < conf.user_allow_given; i++)
547 if (uid == conf.user_allow_arg[i])
549 return -E_UCRED_PERM;
552 int handle_connect(int accept_fd)
554 int i, argc, ret, clifd = -1;
555 char *cmd = NULL, *p, *buf = para_calloc(MAXLINE), **argv = NULL;
556 struct sockaddr_un unix_addr;
558 ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un));
562 ret = recv_cred_buffer(clifd, buf, MAXLINE - 1);
565 PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
566 ret = check_perms(ret);
569 ret = -E_INVALID_AUDIOD_CMD;
570 cmd = para_strdup(buf);
571 p = strchr(cmd, '\n');
578 for (i = 0; cmds[i].name; i++) {
580 if (strcmp(cmds[i].name, cmd))
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);
588 for (j = 0; p[j]; j++)
591 PARA_INFO_LOG("cmd: %s, options: %s\n", cmd, p);
592 ret = cmds[i].line_handler(clifd, p);
595 ret = -E_INVALID_AUDIOD_CMD;
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);
609 void audiod_status_dump(void)
611 struct timeval *t = wstime();
612 char *old, *new, *tmp;
614 old = stat_task->stat_item_values[SI_PLAY_TIME];
615 new = get_time_string(t);
617 if (!old || strcmp(old, new)) {
619 stat_client_write(new, SI_PLAY_TIME);
620 stat_task->stat_item_values[SI_PLAY_TIME] = new;
626 old = stat_task->stat_item_values[SI_AUDIOD_UPTIME];
627 if (!old || strcmp(old, new)) {
629 tmp = make_message("%s:%s\n",
630 status_item_list[SI_AUDIOD_UPTIME], new);
631 stat_client_write(tmp, SI_AUDIOD_UPTIME);
633 stat_task->stat_item_values[SI_AUDIOD_UPTIME] = new;
637 old = stat_task->stat_item_values[SI_AUDIOD_STATUS];
638 new = audiod_status_string();
639 if (!old || strcmp(old, new)) {
641 stat_client_write(new, SI_AUDIOD_STATUS);
642 stat_task->stat_item_values[SI_AUDIOD_STATUS] = new;
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;
656 * send empty status list
658 * Send to each connected client the full status item list
661 void dump_empty_status(void)
665 FOR_EACH_STAT_ITEM(i) {
666 char *tmp = make_message("%s:\n", status_item_list[i]);
667 stat_client_write(tmp, i);
669 free(stat_task->stat_item_values[i]);
670 stat_task->stat_item_values[i] = NULL;