X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=audiod.c;h=54863cb6e9eda7faadbe7cbc170249ac73ce2bf8;hp=d78841336893c170f9da7472c717b1e70c9c45bc;hb=704605b466aca3ae6ca6d1e03b6af55e3d245502;hpb=af2518cf1943cb1b6b4b4a32ca687e8f416ed563 diff --git a/audiod.c b/audiod.c index d7884133..54863cb6 100644 --- a/audiod.c +++ b/audiod.c @@ -83,10 +83,6 @@ struct audio_format_info { struct slot_info { /** number of the audio format in this slot */ int format; - /** time of the last successful read from the receiver */ - struct timeval rtime; - /** time the last write to the write fd happend */ - struct timeval wtime; /** writer start time */ struct timeval wstime; /** the receiver info associated with this slot */ @@ -109,30 +105,42 @@ static char *af_status, /* the audio format announced in server status */ *socket_name, *hostname; static char *stat_item_values[NUM_STAT_ITEMS]; static FILE *logfile; -static const struct timeval restart_delay = {0, 300 * 1000}; +static const struct timeval restart_delay = {0, 200 * 1000}; static struct audio_format_info afi[NUM_AUDIO_FORMATS]; static struct timeval *now; static struct signal_task signal_task_struct, *sig_task = &signal_task_struct; +/** + * the task for handling audiod commands + * + * \sa struct task, struct sched + */ struct command_task { + /** the local listening socket */ int fd; + /** the associated task structure */ struct task task; }; -static struct command_task command_task_struct, *cmd_task = &command_task_struct; +/** + * the task for audiod's child (para_client stat) + * + * \sa struct task, struct sched + */ struct status_task { + /** the output of the stat command is read from this fd */ int fd; - struct task task; + /** stat data is stored here */ char buf[STRINGSIZE]; + /** number of bytes loaded in \a buf */ unsigned loaded; -}; -static struct status_task status_task_struct, *stat_task = &status_task_struct; - -struct audiod_task { + /** the associated task structure */ struct task task; + /** do not restart client command until this time */ + struct timeval restart_barrier; }; -static struct audiod_task audiod_task_struct, *at = &audiod_task_struct; +static struct status_task status_task_struct, *stat_task = &status_task_struct; struct signal_task { int fd; @@ -140,13 +148,17 @@ struct signal_task { struct task task; }; - /** 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; @@ -158,10 +170,12 @@ struct audiod_command { 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[] = { { @@ -200,6 +214,16 @@ static struct audiod_command cmds[] = { "\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", @@ -242,6 +266,16 @@ static struct audiod_command cmds[] = { "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", @@ -420,7 +454,8 @@ static void setup_signal_handling(void) para_install_sighandler(SIGTERM); para_install_sighandler(SIGCHLD); para_install_sighandler(SIGHUP); - signal(SIGPIPE, SIG_IGN); + para_install_sighandler(SIGPIPE); +// signal(SIGPIPE, SIG_IGN); } static void audiod_status_dump(void) @@ -474,8 +509,8 @@ static void close_receiver(int slot_num) a = &afi[s->format]; PARA_NOTICE_LOG("closing %s receiver in slot %d (eof = %d)\n", audio_formats[s->format] , slot_num, s->receiver_node->eof); - if (!s->receiver_node->eof) - unregister_task(&s->receiver_node->task); +// if (!s->receiver_node->eof) +// unregister_task(&s->receiver_node->task); a->receiver->close(s->receiver_node); free(s->receiver_node); s->receiver_node = NULL; @@ -489,8 +524,22 @@ static void kill_all_decoders(void) FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->receiver_node) + if (s->wng && !s->wng->eof) { + PARA_INFO_LOG("unregistering writer node group in slot %d\n", + i); + wng_unregister(s->wng); + s->wng->eof = 1; + } + if (s->fc && !s->fc->eof) { + PARA_INFO_LOG("unregistering filter chain in slot %d\n", i); + unregister_task(&s->fc->task); + s->fc->eof = 1; + } + if (s->receiver_node && !s->receiver_node->eof) { + PARA_INFO_LOG("unregistering receiver_node in slot %d\n", i); + unregister_task(&s->receiver_node->task); s->receiver_node->eof = 1; + } } } @@ -538,7 +587,7 @@ static void close_stat_pipe(void) close(stat_task->fd); del_close_on_fork_list(stat_task->fd); stat_task->fd = -1; - kill_all_decoders(); +// kill_all_decoders(); for (i = 0; i < NUM_STAT_ITEMS; i++) { free(stat_item_values[i]); stat_item_values[i] = NULL; @@ -557,7 +606,6 @@ static void close_stat_pipe(void) static void __noreturn clean_exit(int status, const char *msg) { PARA_EMERG_LOG("%s\n", msg); - kill_all_decoders(); if (socket_name) unlink(socket_name); if (stat_task->fd >= 0) @@ -582,6 +630,8 @@ int num_filters(int audio_format_num) static void filter_event_handler(struct task *t) { PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t->ret)); + struct filter_chain *fc = t->private_data; + fc->eof = 1; unregister_task(t); } @@ -601,12 +651,12 @@ static void open_filters(int slot_num) s->fc->inbuf = s->receiver_node->buf; s->fc->in_loaded = &s->receiver_node->loaded; s->fc->input_eof = &s->receiver_node->eof; - s->fc->task.pre_select = filter_pre_select; s->fc->task.event_handler = filter_event_handler; s->fc->task.private_data = s->fc; - s->fc->task.flags = 0; s->fc->eof = 0; + + s->receiver_node->output_eof = &s->fc->eof; sprintf(s->fc->task.status, "filter chain"); for (i = 0; i < nf; i++) { struct filter_node *fn = para_calloc(sizeof(struct filter_node)); @@ -623,8 +673,6 @@ static void open_filters(int slot_num) s->fc->out_loaded = &fn->loaded; } register_task(&s->fc->task); -// PARA_DEBUG_LOG("output loaded for filter chain %p: %p\n", s->fc, -// s->fc->out_loaded); } static struct filter_node *find_filter_node(int slot_num, int format, int filternum) @@ -654,21 +702,14 @@ static struct filter_node *find_filter_node(int slot_num, int format, int filter static void wng_event_handler(struct task *t) { - struct writer_node_group *g = t->private_data; - int i; + struct writer_node_group *wng = t->private_data; PARA_INFO_LOG("%s\n", PARA_STRERROR(-t->ret)); - unregister_task(t); - FOR_EACH_SLOT(i) { - if (slot[i].wng != g) - continue; - wng_close(g); - wng_destroy(g); - slot[i].wng = NULL; - } + wng->eof = 1; + wng_unregister(wng); } -static void open_writer(int slot_num) +static void open_writers(int slot_num) { int ret, i; struct slot_info *s = &slot[slot_num]; @@ -683,7 +724,10 @@ static void open_writer(int slot_num) s->wng->buf = s->fc->outbuf; s->wng->loaded = s->fc->out_loaded; s->wng->input_eof = &s->fc->eof; + s->wng->channels = &s->fc->channels; + s->wng->samplerate = &s->fc->samplerate; s->fc->output_eof = &s->wng->eof; + PARA_INFO_LOG("samplerate: %d\n", *s->wng->samplerate); } else { s->wng->buf = s->receiver_node->buf; s->wng->loaded = &s->receiver_node->loaded; @@ -693,9 +737,13 @@ static void open_writer(int slot_num) for (i = 0; i < a->num_writers; i++) { s->wng->writer_nodes[i].conf = a->writer_conf[i]; s->wng->writer_nodes[i].writer = a->writers[i]; - sprintf(s->wng->writer_nodes[i].task.status, "writer_ node"); + sprintf(s->wng->writer_nodes[i].task.status, "writer_node"); } ret = wng_open(s->wng); + if (ret < 0) { + PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret)); + return; + } s->wstime = *now; current_decoder = slot_num; activate_inactive_grab_clients(slot_num, s->format, &s->fc->filters); @@ -703,9 +751,11 @@ static void open_writer(int slot_num) static void rn_event_handler(struct task *t) { -// struct receiver_node *rn = t->private_data; + struct receiver_node *rn = t->private_data; + PARA_NOTICE_LOG("%s\n", PARA_STRERROR(-t->ret)); unregister_task(t); + rn->eof = 1; } static void open_receiver(int format) @@ -720,8 +770,6 @@ static void open_receiver(int format) clean_exit(EXIT_FAILURE, PARA_STRERROR(-slot_num)); s = &slot[slot_num]; s->format = format; - s->rtime = *now; - s->wtime = s->rtime; s->receiver_node = para_calloc(sizeof(struct receiver_node)); rn = s->receiver_node; rn->receiver = a->receiver; @@ -737,12 +785,10 @@ static void open_receiver(int format) PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n", audio_formats[s->format], a->receiver->name, slot_num); rn->task.private_data = s->receiver_node; - PARA_NOTICE_LOG("rn = %p\n", rn->task.private_data); rn->task.pre_select = a->receiver->pre_select; rn->task.post_select = a->receiver->post_select; rn->task.event_handler = rn_event_handler; - rn->task.flags = 0; - sprintf(rn->task.status, "receiver node"); + sprintf(rn->task.status, "%s receiver node", rn->receiver->name); register_task(&rn->task); } @@ -753,7 +799,7 @@ static int is_frozen(int format) return (tv_diff(now, &a->restart_barrier, NULL) > 0)? 0 : 1; } -static void open_current_receiver(void) +static void open_current_receiver(struct sched *s) { int i; @@ -765,6 +811,13 @@ static void open_current_receiver(void) if (decoder_running(i) || is_frozen(i)) return; open_receiver(i); + /* + * the receiver didn't get a chance to put his fd to the fd set. + * Make the upcoming select() return immediately to avoid a long + * timeout. + */ + s->timeout.tv_sec = 0; + s->timeout.tv_usec = 1; } static void compute_time_diff(const struct timeval *status_time) @@ -796,7 +849,7 @@ static void compute_time_diff(const struct timeval *status_time) count > 10? sign : sign * time_smooth, &diff, &tmp); sa_time_diff = tmp; - PARA_INFO_LOG("time diff (cur/avg): %s%lums/%s%lums\n", + PARA_DEBUG_LOG("time diff (cur/avg): %s%lums/%s%lums\n", sign > 0? "+" : "-", tv2ms(&diff), sa_time_diff_sign ? "+" : "-", @@ -873,37 +926,20 @@ static void handle_signal(int sig) } } -static void check_timeouts(void) -{ - - int slot_num, timeout = conf.stream_timeout_arg; - - FOR_EACH_SLOT(slot_num) { - struct slot_info *s = &slot[slot_num]; - if (s->format < 0) - continue; - /* check read time */ - if (s->receiver_node && - now->tv_sec > s->rtime.tv_sec + timeout) { - PARA_INFO_LOG("%s stream (slot %d) not ready\n", - audio_formats[s->format], slot_num); - s->receiver_node->eof = 1; - } - } -} - -static void close_decoder_if_idle(int slot_num) +static void try_to_close_slot(int slot_num) { struct slot_info *s = &slot[slot_num]; if (s->format < 0) return; - if (!s->fc) + if (s->receiver_node && !s->receiver_node->eof) + return; + if (s->fc && !s->fc->eof) return; - if (s->wng) + if (s->wng && !s->wng->eof) return; - PARA_INFO_LOG("closing all filters in slot %d (filter_chain %p)\n", - slot_num, s->fc); + PARA_INFO_LOG("closing slot %d \n", slot_num); + wng_close(s->wng); close_filters(s->fc); free(s->fc); close_receiver(slot_num); @@ -914,23 +950,32 @@ static void audiod_pre_select(struct sched *s, __a_unused struct task *t) { int i; + t->ret = 1; now = &s->now; - if (audiod_status != AUDIOD_ON) + if (audiod_status != AUDIOD_ON || !playing) kill_all_decoders(); else if (playing) - open_current_receiver(); - check_timeouts(); + open_current_receiver(s); FOR_EACH_SLOT(i) { - struct receiver_node *rn; + struct slot_info *s = &slot[i]; + struct audio_format_info *a; - close_decoder_if_idle(i); - if (slot[i].format < 0) + if (s->format < 0) + continue; + a = &afi[s->format]; + if (!s->receiver_node) continue; - rn = slot[i].receiver_node; - if (rn && rn->loaded && !slot[i].wng) { + if (!a->num_filters) { + if (s->receiver_node->loaded && !s->wng) + open_writers(i); + continue; + } + if (s->receiver_node->loaded && !s->fc) { open_filters(i); - open_writer(i); + continue; } + if (s->fc && *s->fc->out_loaded && !s->wng) + open_writers(i); } } @@ -938,22 +983,20 @@ static void audiod_post_select(struct sched *s, __a_unused struct task *t) { int i; + /* save away the current time for other users */ now = &s->now; - FOR_EACH_SLOT(i) { - struct receiver_node *rn = slot[i].receiver_node; - - if (rn && rn->loaded) - slot[i].rtime = *now; - } + t->ret = 1; + FOR_EACH_SLOT(i) + try_to_close_slot(i); } -static void init_audiod_task(struct audiod_task *at) +static void init_audiod_task(struct task *t) { - at->task.pre_select = audiod_pre_select; - at->task.post_select = audiod_post_select; - at->task.private_data = at; - at->task.flags = 0; - sprintf(at->task.status, "audiod task"); + t->pre_select = audiod_pre_select; + t->post_select = audiod_post_select; + t->event_handler = NULL; + t->private_data = t; + sprintf(t->status, "audiod task"); } static int parse_stream_command(const char *txt, char **cmd) @@ -1016,7 +1059,7 @@ static int init_writers(void) ret = writer_num; goto out; } - a->writers[nw] = &writers[ret]; + a->writers[nw] = &writers[writer_num]; a->writer_conf[nw] = wconf; PARA_INFO_LOG("%s writer #%d: %s\n", audio_formats[ret], nw, writer_names[writer_num]); @@ -1214,6 +1257,33 @@ out: return ret; } +static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv) +{ + char *tl = get_task_list(); + int ret = 1; + if (tl) + ret = client_write(fd, tl); + free(tl); + if (ret > 0) + close(fd); + return ret; +} + +static int com_kill(int fd, int argc, char **argv) +{ + int i, ret = 1; + if (argc < 2) + return -E_AUDIOD_SYNTAX; + for (i = 1; i < argc; i++) { + ret = kill_task(argv[i]); + if (ret < 0) + break; + } + if (ret > 0) + close(fd); + return ret; +} + static int com_stat(int fd, __a_unused int argc, __a_unused char **argv) { int i, ret; @@ -1376,13 +1446,13 @@ static int check_perms(uid_t uid) return -E_UCRED_PERM; } -static int handle_connect(void) +static 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; - ret = para_accept(cmd_task->fd, &unix_addr, sizeof(struct sockaddr_un)); + ret = para_accept(accept_fd, &unix_addr, sizeof(struct sockaddr_un)); if (ret < 0) goto out; clifd = ret; @@ -1433,9 +1503,10 @@ out: return ret; } -static void audiod_get_socket(void) +static int audiod_get_socket(void) { struct sockaddr_un unix_addr; + int fd; if (conf.socket_given) socket_name = para_strdup(conf.socket_arg); @@ -1445,20 +1516,21 @@ static void audiod_get_socket(void) hn); free(hn); } - PARA_NOTICE_LOG("connecting to local socket %s\n", socket_name); + PARA_NOTICE_LOG("local socket: %s\n", socket_name); if (conf.force_given) unlink(socket_name); - cmd_task->fd = create_pf_socket(socket_name, &unix_addr, + fd = create_pf_socket(socket_name, &unix_addr, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH); - if (cmd_task->fd < 0) { + if (fd < 0) { PARA_EMERG_LOG("%s", "can not connect to socket\n"); exit(EXIT_FAILURE); /* do not unlink socket */ } - if (listen(cmd_task->fd , 5) < 0) { + if (listen(fd , 5) < 0) { PARA_EMERG_LOG("%s", "can not listen on socket\n"); exit(EXIT_FAILURE); /* do not unlink socket */ } - add_close_on_fork_list(cmd_task->fd); + add_close_on_fork_list(fd); + return fd; } static int open_stat_pipe(void) @@ -1478,7 +1550,11 @@ static int open_stat_pipe(void) void signal_event_handler(struct task *t) { struct signal_task *st = t->private_data; - handle_signal(st->signum); + + if (t->ret != -E_SIGNAL_CAUGHT) + PARA_ERROR_LOG("%s (ignored)\n", PARA_STRERROR(-t->ret)); + else + handle_signal(st->signum); } void signal_pre_select(struct sched *s, struct task *t) @@ -1503,13 +1579,13 @@ void signal_setup_default(struct signal_task *st) st->task.pre_select = signal_pre_select; st->task.post_select = signal_post_select; st->task.private_data = st; - st->task.flags = 0; sprintf(st->task.status, "signal task"); } static void command_pre_select(struct sched *s, struct task *t) { struct command_task *ct = t->private_data; + t->ret = 1; para_fd_set(ct->fd, &s->rfds, &s->max_fileno); } @@ -1519,12 +1595,12 @@ static void command_post_select(struct sched *s, struct task *t) int ret; struct command_task *ct = t->private_data; + t->ret = 1; /* always successful */ if (audiod_status != AUDIOD_OFF) audiod_status_dump(); - t->ret = 1; /* always successful */ if (!FD_ISSET(ct->fd, &s->rfds)) return; - ret = handle_connect(); + ret = handle_connect(ct->fd); if (ret < 0) PARA_ERROR_LOG("%s\n", PARA_STRERROR(-ret)); } @@ -1533,18 +1609,34 @@ static void init_command_task(struct command_task *ct) { ct->task.pre_select = command_pre_select; ct->task.post_select = command_post_select; + ct->task.event_handler = NULL; ct->task.private_data = ct; - ct->task.flags = 0; + ct->fd = audiod_get_socket(); /* doesn't return on errors */ sprintf(ct->task.status, "command task"); } +static void status_event_handler(__a_unused struct task *t) +{ + struct timeval delay = {1, 0}; + int i; + struct status_task *st = t->private_data; + + PARA_ERROR_LOG("%s\n", PARA_STRERROR(-t->ret)); + close_stat_pipe(); + /* avoid busy loop if server is down */ + tv_add(now, &delay, &st->restart_barrier); + FOR_EACH_AUDIO_FORMAT(i) + afi[i].restart_barrier = st->restart_barrier; +} + static void status_pre_select(struct sched *s, struct task *t) { struct status_task *st = t->private_data; t->ret = 1; if (st->fd >= 0 && audiod_status == AUDIOD_OFF) close_stat_pipe(); - if (st->fd < 0 && audiod_status != AUDIOD_OFF) { + if (st->fd < 0 && audiod_status != AUDIOD_OFF + && tv_diff(now, &st->restart_barrier, NULL) > 0) { st->fd = open_stat_pipe(); st->loaded = 0; st->buf[0] = '\0'; @@ -1556,34 +1648,29 @@ static void status_pre_select(struct sched *s, struct task *t) static void status_post_select(struct sched *s, struct task *t) { struct status_task *st = t->private_data; - int ret; t->ret = 1; if (st->fd < 0 || !FD_ISSET(st->fd, &s->rfds)) return; - ret = read(st->fd, st->buf + st->loaded, - STRINGSIZE - 1 - st->loaded); - if (ret <= 0) { - close_stat_pipe(); - /* avoid busy loop if server is down */ - while (sleep(1) > 0) /* FIXME */ - ; /* try again*/ - } else { - st->buf[ret + st->loaded] = '\0'; - st->loaded = for_each_line(st->buf, ret + st->loaded, - &check_stat_line); + t->ret = read(st->fd, st->buf + st->loaded, STRINGSIZE - 1 - st->loaded); + if (t->ret <= 0) { + if (!t->ret) + t->ret = -E_STATUS_EOF; + return; } + st->buf[t->ret + st->loaded] = '\0'; + st->loaded = for_each_line(st->buf, t->ret + st->loaded, + &check_stat_line); } static void init_status_task(struct status_task *st) { + memset(st, 0, sizeof(struct status_task)); st->task.pre_select = status_pre_select; st->task.post_select = status_post_select; + st->task.event_handler = status_event_handler; st->task.private_data = st; - st->task.flags = 0; - st->loaded = 0; st->fd = -1; - st->buf[0] = '\0'; sprintf(st->task.status, "status task"); } @@ -1609,6 +1696,8 @@ int main(int argc, char *argv[]) char *cf; int ret, i; struct sched s; + struct command_task command_task_struct, *cmd_task = &command_task_struct; + struct task audiod_task_struct, *audiod_task = &audiod_task_struct; init_sched(); @@ -1637,22 +1726,21 @@ int main(int argc, char *argv[]) clear_slot(i); init_grabbing(); setup_signal_handling(); - if (conf.daemon_given) - daemon_init(); - audiod_get_socket(); /* doesn't return on errors */ - signal_setup_default(sig_task); sig_task->task.event_handler = signal_event_handler; init_status_task(stat_task); init_command_task(cmd_task); - init_audiod_task(at); + init_audiod_task(audiod_task); + + if (conf.daemon_given) + daemon_init(); register_task(&sig_task->task); register_task(&cmd_task->task); register_task(&stat_task->task); - register_task(&at->task); - s.default_timeout.tv_sec = 0; + register_task(audiod_task); + s.default_timeout.tv_sec = 3; s.default_timeout.tv_usec = 99 * 1000; ret = sched(&s);