X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=audiod_command.c;h=855e5cea102e51076b50ad345d014096d9c08e4a;hp=fe26b663256aaa8083268bee75ac2f2c5fbb5c68;hb=14960ed632ff6a07e5d4ae4a6b02f0f1da19a5b9;hpb=8a8cd0f5bb40dcfad68608193e8c57decd90b25e diff --git a/audiod_command.c b/audiod_command.c index fe26b663..855e5cea 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -1,25 +1,15 @@ /* - * Copyright (C) 2005-2006 Andre Noll + * Copyright (C) 2005-2007 Andre Noll * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Licensed under the GPL v2. For licencing details see COPYING. */ /** \file audiod_command.c commands for para_audiod */ -#include "para.h" +#include +#include +#include "para.h" #include "audiod.cmdline.h" #include "list.h" #include "close_on_fork.h" @@ -34,153 +24,10 @@ #include "daemon.h" #include "string.h" #include "fd.h" - -/** defines one command of para_audiod */ -struct audiod_command { - /** the name of the command */ - const char *name; - /** pointer to the function that handles the command */ - int (*handler)(int, int, char**); - /** - * if the command prefers to handle the full line (rather than the usual - * argv[] array), it stores a pointer to the corresponding line handling - * function here. In this case, the above \a handler pointer must be NULL. - */ - int (*line_handler)(int, char*); - /** one-line description of the command */ - const char *description; - /** summary of the command line options */ - const char *synopsis; - /** the long help text */ - const char *help; -}; -static int com_grab(int, char *); -static int com_cycle(int, int, char **); -static int com_help(int, int, char **); -static int com_kill(int, int, char **); -static int com_off(int, int, char **); -static int com_on(int, int, char **); -static int com_sb(int, int, char **); -static int com_stat(int, int, char **); -static int com_tasks(int, int, char **); -static int com_term(int, int, char **); -static struct audiod_command cmds[] = { -{ -.name = "cycle", -.handler = com_cycle, -.description = "switch to next mode", -.synopsis = "cycle", -.help = - -"on -> standby -> off -> on\n" - -}, -{ -.name = "grab", -.line_handler = com_grab, -.description = "grab the audio stream", -.synopsis = "-- grab [grab_options]", -.help = - -"grab ('splice') the audio stream at any position in the filter \n" -"chain and send that data back to the client. Try\n" -"\t para_audioc -- grab -h\n" -"for the list of available options.\n" -}, - -{ -.name = "help", -.handler = com_help, -.description = "display command list or help for given command", -.synopsis = "help [command]", -.help = - -"When I was younger, so much younger than today, I never needed\n" -"anybody's help in any way. But now these days are gone, I'm not so\n" -"self assured. Now I find I've changed my mind and opened up the doors.\n" -"\n" -" -- Beatles: Help\n" - -}, -{ -.name = "kill", -.handler = com_kill, -.description = "kill an active audiod task", -.synopsis = "kill task_id [task_id ...]", -.help = - -"call sched_unregister() and the event_handler of the given task(s)\n" - -}, -{ -.name = "off", -.handler = com_off, -.description = "deactivate para_audiod", -.synopsis = "off", -.help = - -"Close connection to para_server and stop all decoders.\n" - -}, -{ -.name = "on", -.handler = com_on, -.description = "activate para_audiod", -.synopsis = "on", -.help = - -"Establish connection to para_server, retrieve para_server's current\n" -"status. If playing, start corresponding decoder. Otherwise stop\n" -"all decoders.\n" - -}, -{ -.name = "sb", -.handler = com_sb, -.description = "enter standby mode", -.synopsis = "sb", -.help = - -"Stop all decoders but leave connection to para_server open.\n" - -}, -{ -.name = "stat", -.handler = com_stat, -.description = "print status information", -.synopsis = "stat [item1 ...]", -.help = - -"Dump given status items (all if none given) to stdout.\n" - -}, -{ -.name = "tasks", -.handler = com_tasks, -.description = "list current tasks", -.synopsis = "tasks", -.help = - -"print the list of task ids together with the status of each task\n" - -}, -{ -.name = "term", -.handler = com_term, -.description = "terminate audiod", -.synopsis = "term", -.help = - -"Stop all decoders, shut down connection to para_server and exit.\n" - -}, -{ -.name = NULL, -} -}; +#include "audiod_command_list.h" /** iterate over the array of all audiod commands */ -#define FOR_EACH_COMMAND(c) for (c = 0; cmds[c].name; c++) +#define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++) static int client_write(int fd, const char *buf) { @@ -223,7 +70,7 @@ static char *get_time_string(struct timeval *newest_stime) total = 0; out: return make_message( - "%s:%s%d:%02d [%d:%02d] (%d%%/%d:%02d)\n", + "%s: %s%d:%02d [%d:%02d] (%d%%/%d:%02d)\n", status_item_list[SI_PLAY_TIME], use_server_time? "~" : "", total / 60, @@ -241,7 +88,7 @@ __malloc static char *audiod_status_string(void) { const char *status = (audiod_status == AUDIOD_ON)? "on" : (audiod_status == AUDIOD_OFF)? "off": "sb"; - return make_message("%s:%s\n", status_item_list[SI_AUDIOD_STATUS], status); + return make_message("%s: %s\n", status_item_list[SI_AUDIOD_STATUS], status); } static struct timeval *wstime(void) @@ -273,7 +120,7 @@ __malloc static char *decoder_flags(void) flags[i] = flag; } flags[MAX_STREAM_SLOTS] = '\0'; - return make_message("%s:%s\n", status_item_list[SI_DECODER_FLAGS], + return make_message("%s: %s\n", status_item_list[SI_DECODER_FLAGS], flags); } @@ -284,8 +131,8 @@ static int dump_commands(int fd) ssize_t ret; FOR_EACH_COMMAND(i) { - tmp = make_message("%s%s\t%s\n", buf, cmds[i].name, - cmds[i].description); + tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name, + audiod_cmds[i].description); free(buf); buf = tmp; } @@ -300,7 +147,7 @@ static int dump_commands(int fd) * to each individual command to close the fd if necessary. */ -static int com_help(int fd, int argc, char **argv) +int com_help(int fd, int argc, char **argv) { int i, ret; char *buf; @@ -311,16 +158,16 @@ static int com_help(int fd, int argc, char **argv) goto out; } FOR_EACH_COMMAND(i) { - if (strcmp(cmds[i].name, argv[1])) + if (strcmp(audiod_cmds[i].name, argv[1])) continue; buf = make_message( "NAME\n\t%s -- %s\n" "SYNOPSIS\n\tpara_audioc %s\n" "DESCRIPTION\n%s\n", argv[1], - cmds[i].description, - cmds[i].synopsis, - cmds[i].help + audiod_cmds[i].description, + audiod_cmds[i].usage, + audiod_cmds[i].help ); ret = client_write(fd, buf); free(buf); @@ -335,7 +182,7 @@ out: return ret; } -static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv) +int com_tasks(int fd, __a_unused int argc, __a_unused char **argv) { char *tl = get_task_list(); int ret = 1; @@ -347,7 +194,7 @@ static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv) return ret; } -static int com_kill(int fd, int argc, char **argv) +int com_kill(int fd, int argc, char **argv) { int i, ret = 1; if (argc < 2) @@ -362,7 +209,7 @@ static int com_kill(int fd, int argc, char **argv) return ret; } -static int com_stat(int fd, __a_unused int argc, __a_unused char **argv) +int com_stat(int fd, __a_unused int argc, __a_unused char **argv) { int i, ret; char *buf = NULL; @@ -390,7 +237,7 @@ static int com_stat(int fd, __a_unused int argc, __a_unused char **argv) } if (mask & (1 << SI_AUDIOD_UPTIME)) { char *tmp, *us = uptime_str(); - tmp = make_message("%s:%s\n", + tmp = make_message("%s: %s\n", status_item_list[SI_AUDIOD_UPTIME], us); free(us); ret = client_write(fd, tmp); @@ -406,14 +253,13 @@ static int com_stat(int fd, __a_unused int argc, __a_unused char **argv) free(s); } if (mask & (1 << SI_DECODER_FLAGS)) { - char *df =decoder_flags(); + char *df = decoder_flags(); ret = client_write(fd, df); if (ret < 0) goto out; free(df); } - - for (i = 0; i < NUM_STAT_ITEMS; i++) { + FOR_EACH_STATUS_ITEM(i) { char *tmp, *v; if (!((1 << i) & mask)) continue; @@ -456,7 +302,7 @@ static struct filter_node *find_filter_node(int slot_num, int format, int filter return NULL; } -static int com_grab(int fd, char *cmdline) +int com_grab(int fd, char *cmdline) { struct grab_client *gc; struct filter_node *fn; @@ -493,34 +339,34 @@ err_out: return 1; } -static int __noreturn com_term(int fd, __a_unused int argc, __a_unused char **argv) +__noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv) { close(fd); clean_exit(EXIT_SUCCESS, "terminating on user request"); } -static int com_on(int fd, __a_unused int argc, __a_unused char **argv) +int com_on(int fd, __a_unused int argc, __a_unused char **argv) { audiod_status = AUDIOD_ON; close(fd); return 1; } -static int com_off(int fd, __a_unused int argc, __a_unused char **argv) +int com_off(int fd, __a_unused int argc, __a_unused char **argv) { audiod_status = AUDIOD_OFF; close(fd); return 1; } -static int com_sb(int fd, __a_unused int argc, __a_unused char **argv) +int com_sb(int fd, __a_unused int argc, __a_unused char **argv) { audiod_status = AUDIOD_STANDBY; close(fd); return 1; } -static int com_cycle(int fd, int argc, char **argv) +int com_cycle(int fd, int argc, char **argv) { switch (audiod_status) { case AUDIOD_ON: @@ -549,11 +395,27 @@ static int check_perms(uid_t uid) return -E_UCRED_PERM; } +/** + * handle arriving connections on the local socket + * + * \param accept_fd the fd to call accept() on + * + * This is called whenever para_audiod's main task detects an incoming + * connection by the readability of \a accept_fd. This function reads the + * command sent by the peer, checks the connecting user's permissions by using + * unix socket credentials (if supported by the OS) and calls the corresponding + * command handler if permissions are OK. + * + * \return positive on success, negative on errors + * + * \sa para_accept(), recv_cred_buffer() + * */ int handle_connect(int accept_fd) { int i, argc, ret, clifd = -1; char *cmd = NULL, *p, *buf = para_calloc(MAXLINE), **argv = NULL; struct sockaddr_un unix_addr; + uid_t uid; ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un)); if (ret < 0) @@ -562,8 +424,9 @@ int handle_connect(int accept_fd) ret = recv_cred_buffer(clifd, buf, MAXLINE - 1); if (ret < 0) goto out; + uid = ret; PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf); - ret = check_perms(ret); + ret = check_perms(uid); if (ret < 0) goto out; ret = -E_INVALID_AUDIOD_CMD; @@ -575,21 +438,21 @@ int handle_connect(int accept_fd) *p = '\0'; p++; } - for (i = 0; cmds[i].name; i++) { + for (i = 0; audiod_cmds[i].name; i++) { int j; - if (strcmp(cmds[i].name, cmd)) + if (strcmp(audiod_cmds[i].name, cmd)) continue; - if (cmds[i].handler) { + if (audiod_cmds[i].handler) { argc = split_args(buf, &argv, "\n"); PARA_INFO_LOG("argv[0]: %s, argc= %d\n", argv[0], argc); - ret = cmds[i].handler(clifd, argc, argv); + ret = audiod_cmds[i].handler(clifd, argc, argv); goto out; } for (j = 0; p[j]; j++) if (p[j] == '\n') p[j] = ' '; PARA_INFO_LOG("cmd: %s, options: %s\n", cmd, p); - ret = cmds[i].line_handler(clifd, p); + ret = audiod_cmds[i].line_handler(clifd, p); goto out; } ret = -E_INVALID_AUDIOD_CMD; @@ -605,36 +468,71 @@ out: } return ret; } - +/** + * send the current audiod status to all connected stat clients + */ void audiod_status_dump(void) { - static char *p_ts, *p_us, *p_as, *p_df; struct timeval *t = wstime(); - char *us, *tmp = get_time_string(t); - - if (tmp && (!p_ts || strcmp(tmp, p_ts))) - stat_client_write(tmp, SI_PLAY_TIME); - free(p_ts); - p_ts = tmp; + char *old, *new, *tmp; + + old = stat_task->stat_item_values[SI_PLAY_TIME]; + new = get_time_string(t); + if (new) { + if (!old || strcmp(old, new)) { + free(old); + stat_client_write(new, SI_PLAY_TIME); + stat_task->stat_item_values[SI_PLAY_TIME] = new; + } else + free(new); + } - us = uptime_str(); - tmp = make_message("%s:%s\n", status_item_list[SI_AUDIOD_UPTIME], us); - free(us); - if (!p_us || strcmp(p_us, tmp)) + new = uptime_str(); + old = stat_task->stat_item_values[SI_AUDIOD_UPTIME]; + if (!old || strcmp(old, new)) { + free(old); + tmp = make_message("%s: %s\n", + status_item_list[SI_AUDIOD_UPTIME], new); stat_client_write(tmp, SI_AUDIOD_UPTIME); - free(p_us); - p_us = tmp; - - tmp = audiod_status_string(); - if (!p_as || strcmp(p_as, tmp)) - stat_client_write(tmp, SI_AUDIOD_STATUS); - free(p_as); - p_as = tmp; - - tmp = decoder_flags(); - if (!p_df || strcmp(p_df, tmp)) - stat_client_write(tmp, SI_DECODER_FLAGS); - free(p_df); - p_df = tmp; + free(tmp); + stat_task->stat_item_values[SI_AUDIOD_UPTIME] = new; + } else + free(new); + + old = stat_task->stat_item_values[SI_AUDIOD_STATUS]; + new = audiod_status_string(); + if (!old || strcmp(old, new)) { + free(old); + stat_client_write(new, SI_AUDIOD_STATUS); + stat_task->stat_item_values[SI_AUDIOD_STATUS] = new; + } else + free(new); + + old = stat_task->stat_item_values[SI_DECODER_FLAGS]; + new = decoder_flags(); + if (!old || strcmp(old, new)) { + free(old); + stat_client_write(new, SI_DECODER_FLAGS); + stat_task->stat_item_values[SI_DECODER_FLAGS] = new; + } else + free(new); } +/** + * send empty status list + * + * Send to each connected client the full status item list + * with empty values. + */ +void dump_empty_status(void) +{ + int i; + + FOR_EACH_STATUS_ITEM(i) { + char *tmp = make_message("%s:\n", status_item_list[i]); + stat_client_write(tmp, i); + free(tmp); + free(stat_task->stat_item_values[i]); + stat_task->stat_item_values[i] = NULL; + } +}