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 static char *p_ts
, *p_us
, *p_as
, *p_df
;
612 struct timeval
*t
= wstime();
613 char *us
, *tmp
= get_time_string(t
);
615 if (tmp
&& (!p_ts
|| strcmp(tmp
, p_ts
)))
616 stat_client_write(tmp
, SI_PLAY_TIME
);
621 tmp
= make_message("%s:%s\n", status_item_list
[SI_AUDIOD_UPTIME
], us
);
623 if (!p_us
|| strcmp(p_us
, tmp
))
624 stat_client_write(tmp
, SI_AUDIOD_UPTIME
);
628 tmp
= audiod_status_string();
629 if (!p_as
|| strcmp(p_as
, tmp
))
630 stat_client_write(tmp
, SI_AUDIOD_STATUS
);
634 tmp
= decoder_flags();
635 if (!p_df
|| strcmp(p_df
, tmp
))
636 stat_client_write(tmp
, SI_DECODER_FLAGS
);