X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=audiod.c;h=fedeff7af880a009d938e73087edb7ca6c83f267;hp=e73d14135b855b77f361a020d5c0484c4842b13f;hb=eacc2982fbf7e0c0b69508b57f85e839d1ba1013;hpb=5ab451760281b344bfa6e495ab1aea7d80323c3c diff --git a/audiod.c b/audiod.c index e73d1413..fedeff7a 100644 --- a/audiod.c +++ b/audiod.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "para.h" #include "error.h" @@ -68,6 +69,30 @@ struct audio_format_info { struct timeval restart_barrier; }; +/* Describes one instance of a receiver-filter-writer chain. */ +struct slot_info { + /* Number of the audio format in this slot. */ + int format; + /* The stream_start status item announced by para_server. */ + struct timeval server_stream_start; + /* The offset status item announced by para_server. */ + unsigned offset_seconds; + /* The seconds_total status item announced by para_server. */ + unsigned seconds_total; + /* The receiver info associated with this slot. */ + struct receiver_node *receiver_node; + /* The array of filter nodes. */ + struct filter_node *fns; + /* The array of writers attached to the last filter. */ + struct writer_node *wns; +}; + +/** Maximal number of simultaneous instances. */ +#define MAX_STREAM_SLOTS 5 + +/** Iterate over all slots. */ +#define FOR_EACH_SLOT(_slot) for (_slot = 0; _slot < MAX_STREAM_SLOTS; _slot++) + /** * para_audiod uses \p MAX_STREAM_SLOTS different slots, each of which may * be associated with a receiver/filter/writer triple. This array holds all @@ -139,7 +164,7 @@ int audiod_status = AUDIOD_ON; * the gengetopt args_info struct that holds information on all command line * arguments */ -struct audiod_args_info conf; +static struct audiod_args_info conf; static char *socket_name; static struct audio_format_info afi[NUM_AUDIO_FORMATS]; @@ -148,6 +173,8 @@ static struct signal_task *signal_task; static struct status_task status_task_struct; +static uid_t *uid_whitelist; + /** * the task that calls the status command of para_server * @@ -198,6 +225,35 @@ static int get_audio_format_num(const char *name) return -E_UNSUPPORTED_AUDIO_FORMAT; } +/** + * Return the flags for the \a decoder_flags status item. + * + * Allocates a string which contains one octal digit per slot. Bit zero (value + * 1) is set if a receiver is active. Bit one (value 2) and bit three (value 4) + * have the analogous meaning for filter and writer, respectively. + * + * \return String that must be freed by the caller. + */ +__malloc char *audiod_get_decoder_flags(void) +{ + int i; + char flags[MAX_STREAM_SLOTS + 1]; + + FOR_EACH_SLOT(i) { + struct slot_info *s = &slot[i]; + char flag = '0'; + if (s->receiver_node) + flag += 1; + if (s->fns) + flag += 2; + if (s->wns) + flag += 4; + flags[i] = flag; + } + flags[MAX_STREAM_SLOTS] = '\0'; + return para_strdup(flags); +} + static int get_matching_audio_format_nums(const char *re) { int i, ret; @@ -253,7 +309,7 @@ static int get_play_time_slot_num(void) */ char *get_time_string(void) { - int ret, seconds = 0, length; + int ret, seconds = 0, length = stat_task->length_seconds; struct timeval *tmp, sum, sss, /* server stream start */ rstime, /* receiver start time */ wstime, /* writer start time */ @@ -265,19 +321,18 @@ char *get_time_string(void) if (audiod_status == AUDIOD_OFF) goto empty; - if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) { - if (stat_task->length_seconds) /* paused */ + if (stat_task->server_stream_start.tv_sec == 0) { + if (stat_task->vss_status & VSS_STATUS_FLAG_PLAYING) + goto out; /* server is about to change file */ + if (length > 0) /* paused */ return NULL; goto empty; /* stopped */ } - if (audiod_status == AUDIOD_ON && !s) - goto empty; /* * Valid status items and playing, set length and tmp to the stream * start. We use the slot info and fall back to the info from current * status items if no slot info is available. */ - length = stat_task->length_seconds; tmp = &stat_task->server_stream_start; if (s && s->wns && s->wns[0].btrn) { /* writer active in this slot */ btr_get_node_start(s->wns[0].btrn, &wstime); @@ -296,7 +351,7 @@ char *get_time_string(void) tv_diff(tmp, &stat_task->sa_time_diff, &sss); else tv_add(tmp, &stat_task->sa_time_diff, &sss); - if (!s || !s->wns || !s->wns[0].btrn) { + if (!s || !s->wns || !s->wns[0].btrn || wstime.tv_sec == 0) { struct timeval diff; tv_diff(now, &sss, &diff); seconds = diff.tv_sec + stat_task->offset_seconds; @@ -308,7 +363,8 @@ char *get_time_string(void) if (s->receiver_node->btrn) { btr_get_node_start(s->receiver_node->btrn, &rstime); ret = tv_diff(&rstime, &sss, &rskip); - if (ret > 0) { /* audiod was started in the middle of the stream */ + if (ret > 0 && rskip.tv_sec > 2) { + /* audiod was started in the middle of the stream */ tv_add(&wtime, &rskip, &sum); seconds += sum.tv_sec; } else @@ -337,7 +393,7 @@ empty: static void parse_config_or_die(void) { - int ret; + int ret, i; char *config_file; struct audiod_cmdline_parser_params params = { .override = 0, @@ -357,6 +413,7 @@ static void parse_config_or_die(void) ret = file_exists(config_file); if (conf.config_file_given && !ret) { PARA_EMERG_LOG("can not read config file %s\n", config_file); + free(config_file); goto err; } if (ret) { @@ -364,9 +421,29 @@ static void parse_config_or_die(void) daemon_set_loglevel(conf.loglevel_arg); } free(config_file); + if (conf.user_allow_given > 0) { + uid_whitelist = para_malloc(conf.user_allow_given + * sizeof(uid_t)); + for (i = 0; i < conf.user_allow_given; i++) { + int32_t val; + struct passwd *pw; + ret = para_atoi32(conf.user_allow_arg[i], &val); + if (ret >= 0) { + uid_whitelist[i] = val; + continue; + } + errno = 0; /* see getpwnam(3) */ + pw = getpwnam(conf.user_allow_arg[i]); + if (!pw) { + PARA_EMERG_LOG("invalid username: %s\n", + conf.user_allow_arg[i]); + goto err; + } + uid_whitelist[i] = pw->pw_uid; + } + } return; err: - free(config_file); exit(EXIT_FAILURE); } @@ -448,11 +525,11 @@ static void close_filters(struct slot_info *s) return; for (i = a->num_filters - 1; i >= 0; i--) { struct filter_node *fn = s->fns + i; - struct filter *f; + const struct filter *f; if (!fn) continue; - f = filters + fn->filter_num; + f = filter_get(fn->filter_num); if (f->close) f->close(fn); btr_remove_node(&fn->btrn); @@ -511,7 +588,7 @@ static void open_filters(struct slot_info *s) parent = s->receiver_node->btrn; for (i = 0; i < nf; i++) { char buf[20]; - struct filter *f = filters + a->filter_nums[i]; + const struct filter *f = filter_get(a->filter_nums[i]); fn = s->fns + i; fn->filter_num = a->filter_nums[i]; fn->conf = a->filter_conf[i]; @@ -781,7 +858,7 @@ static int add_filter(int format, char *cmdline) a->filter_conf[nf] = cfg; a->num_filters++; PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf, - filters[filter_num].name); + filter_get(filter_num)->name); return filter_num; } @@ -916,20 +993,20 @@ static int init_default_filters(void) } /* add "dec" to audio format name */ tmp = make_message("%sdec", audio_formats[i]); - for (j = 0; filters[j].name; j++) - if (!strcmp(tmp, filters[j].name)) + for (j = 0; filter_get(j); j++) + if (!strcmp(tmp, filter_get(j)->name)) break; free(tmp); ret = -E_UNSUPPORTED_FILTER; - if (!filters[j].name) + if (!filter_get(j)) goto out; - tmp = para_strdup(filters[j].name); + tmp = para_strdup(filter_get(j)->name); ret = add_filter(i, tmp); free(tmp); if (ret < 0) goto out; PARA_INFO_LOG("%s -> default filter: %s\n", audio_formats[i], - filters[j].name); + filter_get(j)->name); } out: return ret; @@ -995,7 +1072,7 @@ static void init_local_sockets(struct command_task *ct) unlink(socket_name); ct->fd[0] = create_local_socket(socket_name, 0); ct->fd[1] = create_local_socket(socket_name, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IWOTH); + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (ct->fd[0] >= 0 || ct->fd[1] >= 0) return; PARA_EMERG_LOG("%s\n", para_strerror(-ct->fd[1])); @@ -1153,10 +1230,15 @@ static void close_slot(int slot_num) static void close_unused_slots(void) { int i; + bool dump = false; FOR_EACH_SLOT(i) - if (must_close_slot(i)) + if (must_close_slot(i)) { close_slot(i); + dump = true; + } + if (dump) + audiod_status_dump(true); } /* @@ -1173,6 +1255,7 @@ static void audiod_cleanup(void) close_unused_slots(); audiod_cmdline_parser_free(&conf); close_stat_clients(); + free(uid_whitelist); } /* @@ -1200,6 +1283,7 @@ static void start_stop_decoders(void) open_writers(sl); activate_grab_clients(&sched); btr_log_tree(sl->receiver_node->btrn, LL_NOTICE); + audiod_status_dump(true); } static void status_pre_select(struct sched *s, void *context) @@ -1364,6 +1448,29 @@ __noreturn static void print_help_and_die(void) exit(0); } +/** + * Lookup the given UID in the whitelist. + * + * The whitelist is the array of arguments to the --user-allow opion. If the + * option was not given, the array is empty, in which case the check succeeds. + * + * \param uid User ID to look up. + * + * \return True if --user-allow was not given, or if uid matches an element of + * the whitelist. + */ +bool uid_is_whitelisted(uid_t uid) +{ + int i; + + if (!conf.user_allow_given) + return true; + for (i = 0; i < conf.user_allow_given; i++) + if (uid == uid_whitelist[i]) + return true; + return false; +} + /** * the main function of para_audiod * @@ -1396,10 +1503,14 @@ int main(int argc, char *argv[]) writer_init(); if (conf.help_given || conf.detailed_help_given) print_help_and_die(); + daemon_set_priority(conf.priority_arg); daemon_drop_privileges_or_die(conf.user_arg, conf.group_arg); parse_config_or_die(); - daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no, - conf.logfile_given, conf.log_color_arg, conf.log_color_given); + if (daemon_init_colors_or_die(conf.color_arg, color_arg_auto, color_arg_no, + conf.logfile_given)) { + for (i = 0; i < conf.log_color_given; i++) + daemon_set_log_color_or_die(conf.log_color_arg[i]); + } init_random_seed_or_die(); daemon_set_flag(DF_LOG_TIME); daemon_set_flag(DF_LOG_HOSTNAME); @@ -1415,7 +1526,7 @@ int main(int argc, char *argv[]) PARA_EMERG_LOG("%s\n", para_strerror(-ret)); exit(EXIT_FAILURE); } - daemon_log_welcome("para_audiod"); + daemon_log_welcome("audiod"); daemon_set_start_time(); set_initial_status(); FOR_EACH_SLOT(i)