X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=audiod.c;h=3f46acbcee062f71bd5a5d7e582262a5f26117ac;hp=f0ce62587d9c6fc995fe7c99440fa1238e706b49;hb=89e28a4361bd120dfe4202a16d85d03fb1139de8;hpb=4173e7c1c653ec48e7676a2872ce9b4328a2fbcc diff --git a/audiod.c b/audiod.c index f0ce6258..3f46acbc 100644 --- a/audiod.c +++ b/audiod.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -14,6 +14,7 @@ #include "audiod.cmdline.h" #include "list.h" #include "sched.h" +#include "ggo.h" #include "recv.h" #include "filter.h" #include "grab_client.cmdline.h" @@ -65,6 +66,47 @@ struct audio_format_info { * */ struct slot_info slot[MAX_STREAM_SLOTS]; +/** The vss status flags audiod is interested in. */ +enum vss_status_flags { + /** Whether the 'N' flag is set. */ + VSS_STATUS_FLAG_NEXT = 1, + /** The 'P' flag is set. */ + VSS_STATUS_FLAG_PLAYING = 2, +}; + +/** + * The task for obtaining para_server's status (para_client stat). + * + * \sa struct task, struct sched. + */ +struct status_task { + /** The associated task structure of audiod. */ + struct task task; + /** Client data associated with the stat task. */ + struct client_task *ct; + /** Do not restart client command until this time. */ + struct timeval restart_barrier; + /** Last time we received status data from para_server. */ + struct timeval last_status_read; + /** The offset value announced by para_server. */ + int offset_seconds; + /** The length of the current audio file as announced by para_server. */ + int length_seconds; + /** The start of the current stream from the view of para_server. */ + struct timeval server_stream_start; + /** The average time deviation between para_server and para_audiod. */ + struct timeval sa_time_diff; + /** Whether client time is ahead of server time. */ + int sa_time_diff_sign; + /** The 'P' and the 'N' flags as announced by para_server. */ + enum vss_status_flags vss_status; + /** Number of times the clock difference is to be checked. */ + unsigned clock_diff_count; + /** When to start the next check for clock difference. */ + struct timeval clock_diff_barrier; + /** Number of the audio format as announced by para_server. */ + int current_audio_format_num; +}; /** The array of status items sent by para_server. */ char *stat_item_values[NUM_STAT_ITEMS] = {NULL}; @@ -82,7 +124,6 @@ int audiod_status = AUDIOD_ON; struct audiod_args_info conf; static char *socket_name; -static FILE *logfile; static struct audio_format_info afi[NUM_AUDIO_FORMATS]; static struct signal_task signal_task_struct, *sig_task = &signal_task_struct; @@ -137,7 +178,7 @@ char *get_time_string(struct timeval *newest_stime) int total = 0, use_server_time = 1, length_seconds = stat_task->length_seconds; - if (!stat_task->playing) { + if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) { if (length_seconds) return NULL; return make_message("%s:\n", status_item_list[SI_PLAY_TIME]); @@ -180,49 +221,49 @@ out: ); } -/** - * the log function of para_audiod - * - * \param ll loglevel - * \param fmt the format string - */ -void para_log(int ll, const char* fmt,...) +static int want_colors(void) { - va_list argp; - FILE *outfd; - struct tm *tm; - time_t t1; - char str[MAXLINE] = ""; - static char *hostname; - - if (ll < conf.loglevel_arg) - return; - if (!logfile && conf.daemon_given) - return; - if (!hostname) - hostname = para_hostname(); - outfd = logfile? logfile : stderr; - time(&t1); - tm = localtime(&t1); - strftime(str, MAXLINE, "%b %d %H:%M:%S", tm); - fprintf(outfd, "%s %s ", str, hostname); - if (conf.loglevel_arg <= INFO) - fprintf(outfd, "%i ", ll); - va_start(argp, fmt); - vfprintf(outfd, fmt, argp); - va_end(argp); + if (conf.color_arg == color_arg_no) + return 0; + if (conf.color_arg == color_arg_yes) + return 1; + if (conf.logfile_given) + return 0; + return isatty(STDERR_FILENO); } -static char *configfile_exists(void) +static void parse_config_or_die(void) { - char *home = para_homedir(); - char *config_file = make_message("%s/.paraslash/audiod.conf", - home); - free(home); - if (file_exists(config_file)) - return config_file; + int ret; + char *config_file; + struct audiod_cmdline_parser_params params = { + .override = 0, + .initialize = 0, + .check_required = 1, + .check_ambiguity = 0, + .print_errors = 1 + }; + + if (conf.config_file_given) + config_file = para_strdup(conf.config_file_arg); + else { + char *home = para_homedir(); + config_file = make_message("%s/.paraslash/audiod.conf", home); + free(home); + } + ret = file_exists(config_file); + if (conf.config_file_given && !ret) { + PARA_EMERG_LOG("can not read config file %s\n", config_file); + goto err; + } + if (ret) + audiod_cmdline_parser_config_file(config_file, &conf, ¶ms); + free(config_file); + daemon_set_loglevel(conf.loglevel_arg); + return; +err: free(config_file); - return NULL; + exit(EXIT_FAILURE); } static void setup_signal_handling(void) @@ -429,32 +470,54 @@ err: return ret; } +/* return: 0: Not running, 1: Running, -1: Running but eof (or error) */ static int receiver_running(int format) { - int i; + int i, ret = 0; FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->format == format && s->receiver_node) + if (s->format != format) + continue; + if (!s->receiver_node) + continue; + if (s->receiver_node->task.error >= 0) return 1; + ret = -1; } - return 0; + return ret; } -static int open_current_receiver(struct sched *s) +static void open_current_receiver(struct sched *s) { struct timeval diff; - int cafn = stat_task->current_audio_format_num; + int ret, cafn = stat_task->current_audio_format_num; if (cafn < 0 || !stat_task->ct) - return 0; - if (receiver_running(cafn)) - return 0; + return; + /* Do nothing if the 'N' flag is set or the 'P' flag is unset */ + if (stat_task->vss_status != VSS_STATUS_FLAG_PLAYING) + return; + ret = receiver_running(cafn); + if (ret > 0) /* already running and not eof */ + return; + if (ret < 0) { /* eof */ + /* + * para_server uses a zero start time during the announcement + * period, i.e. before it sends the first chunk. Wait until + * this period begins to avoid restarting the receiver that + * belongs to the file just completed. + */ + if (stat_task->server_stream_start.tv_sec) + return; + } if (tv_diff(now, &afi[cafn].restart_barrier, &diff) < 0) { + /* avoid busy loop */ s->timeout = diff; - return 0; + return; } - return open_receiver(cafn) < 0? 0 : 1; + /* start a new receiver */ + open_receiver(cafn); } static unsigned compute_time_diff(const struct timeval *status_time) @@ -523,9 +586,12 @@ static int check_stat_line(char *line, __a_unused void *data) stat_item_values[itemnum] = para_strdup(line); ilen = strlen(status_item_list[itemnum]); switch (itemnum) { - case SI_STATUS: - stat_task->playing = strstr(line, "playing")? 1 : 0; - PARA_INFO_LOG("stat task playing: %d\n", stat_task->playing); + case SI_STATUS_FLAGS: + stat_task->vss_status = 0; + if (strchr(line, 'N')) + stat_task->vss_status |= VSS_STATUS_FLAG_NEXT; + if (strchr(line, 'P')) + stat_task->vss_status |= VSS_STATUS_FLAG_PLAYING; break; case SI_OFFSET: stat_task->offset_seconds = atoi(line + ilen + 1); @@ -596,14 +662,13 @@ static int add_filter(int format, char *cmdline) return filter_num; } -static int init_writers(void) +static int parse_writer_args(void) { int i, ret, nw; char *cmd; struct audio_format_info *a; - init_supported_writers(); - nw = PARA_MAX(1, conf.writer_given); + nw = PARA_MAX(1U, conf.writer_given); PARA_INFO_LOG("maximal number of writers: %d\n", nw); FOR_EACH_AUDIO_FORMAT(i) { a = &afi[i]; @@ -635,16 +700,12 @@ out: return ret; } -static int init_receivers(void) +static int parse_receiver_args(void) { int i, ret, receiver_num; char *cmd = NULL; struct audio_format_info *a; - for (i = 0; receivers[i].name; i++) { - PARA_INFO_LOG("initializing %s receiver\n", receivers[i].name); - receivers[i].init(&receivers[i]); - } for (i = conf.receiver_given - 1; i >= 0; i--) { char *arg = conf.receiver_arg[i]; char *recv_arg = strchr(arg, ':'); @@ -715,12 +776,11 @@ out: return ret; } -static int init_filters(void) +static int parse_filter_args(void) { int i, ret, nf; - filter_init(filters); - nf = PARA_MAX(1, conf.filter_given); + nf = PARA_MAX(1U, conf.filter_given); PARA_INFO_LOG("maximal number of filters: %d\n", nf); FOR_EACH_AUDIO_FORMAT(i) { afi[i].filter_conf = para_malloc(nf * sizeof(void *)); @@ -748,17 +808,17 @@ out: return ret; } -static int init_stream_io(void) +static int parse_stream_args(void) { int ret; - ret = init_writers(); + ret = parse_receiver_args(); if (ret < 0) return ret; - ret = init_receivers(); + ret = parse_filter_args(); if (ret < 0) return ret; - ret = init_filters(); + ret = parse_writer_args(); if (ret < 0) return ret; return 1; @@ -872,13 +932,8 @@ static void close_stat_pipe(void) dump_empty_status(); stat_task->length_seconds = 0; stat_task->offset_seconds = 0; + stat_task->vss_status = 0; audiod_status_dump(); - stat_task->playing = 0; - stat_item_values[SI_BASENAME] = make_message( - "%s: no connection to para_server\n", - status_item_list[SI_BASENAME]); - stat_client_write(stat_item_values[SI_BASENAME], - SI_BASENAME); } /** @@ -939,7 +994,8 @@ static void start_stop_decoders(struct sched *s) FOR_EACH_SLOT(i) try_to_close_slot(i); - if (audiod_status != AUDIOD_ON || !stat_task->playing) + if (audiod_status != AUDIOD_ON || + !(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) return kill_all_decoders(-E_NOT_PLAYING); open_current_receiver(s); FOR_EACH_SLOT(i) { @@ -1026,15 +1082,21 @@ static void status_pre_select(struct sched *s, struct task *t) int argc = 3; PARA_INFO_LOG("clock diff count: %d\n", st->clock_diff_count); st->clock_diff_count--; - client_open(argc, argv, &st->ct); + client_open(argc, argv, &st->ct, NULL); set_stat_task_restart_barrier(2); } else { char *argv[] = {"audiod", "stat", NULL}; int argc = 2; - client_open(argc, argv, &st->ct); + client_open(argc, argv, &st->ct, NULL); set_stat_task_restart_barrier(5); } + free(stat_item_values[SI_BASENAME]); + stat_item_values[SI_BASENAME] = make_message( + "%s: no connection to para_server\n", + status_item_list[SI_BASENAME]); + stat_client_write(stat_item_values[SI_BASENAME], + SI_BASENAME); st->last_status_read = *now; out: start_stop_decoders(s); @@ -1067,6 +1129,38 @@ static void set_initial_status(void) PARA_WARNING_LOG("invalid mode\n"); } +__noreturn static void print_help_and_die(void) +{ + int d = conf.detailed_help_given; + const char **p = d? audiod_args_info_detailed_help + : audiod_args_info_help; + + printf_or_die("%s\n\n", AUDIOD_CMDLINE_PARSER_PACKAGE "-" + AUDIOD_CMDLINE_PARSER_VERSION); + printf_or_die("%s\n\n", audiod_args_info_usage); + for (; *p; p++) + printf_or_die("%s\n", *p); + print_receiver_helps(d); + print_filter_helps(d); + print_writer_helps(d); + exit(0); +} + +static void init_colors_or_die(void) +{ + int ret, i; + + if (!want_colors()) + return; + daemon_set_default_log_colors(); + daemon_set_flag(DF_COLOR_LOG); + for (i = 0; i < conf.log_color_given; i++) { + ret = daemon_set_log_color(conf.log_color_arg[i]); + if (ret < 0) + exit(EXIT_FAILURE); + } +} + /** * the main function of para_audiod * @@ -1079,7 +1173,6 @@ static void set_initial_status(void) * */ int main(int argc, char *argv[]) { - char *config_file; int ret, i; static struct sched s; struct command_task command_task_struct, *cmd_task = &command_task_struct; @@ -1092,30 +1185,31 @@ int main(int argc, char *argv[]) }; valid_fd_012(); - audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms); + if (audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms)) + exit(EXIT_FAILURE); HANDLE_VERSION_FLAG("audiod", conf); - para_drop_privileges(conf.user_arg, conf.group_arg); - config_file = configfile_exists(); - if (config_file) { - params.override = 0; - params.initialize = 0; - params.check_required = 1; - params.check_ambiguity = 0; - params.print_errors = 1; - if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) { - PARA_EMERG_LOG("parse error in config file\n"); - exit(EXIT_FAILURE); - } - free(config_file); + /* init receivers/filters/writers early to make help work */ + recv_init(); + filter_init(); + writer_init(); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); + drop_privileges_or_die(conf.user_arg, conf.group_arg); + parse_config_or_die(); + init_colors_or_die(); + daemon_set_flag(DF_LOG_TIME); + daemon_set_flag(DF_LOG_HOSTNAME); + daemon_set_flag(DF_LOG_LL); + if (conf.logfile_given) { + daemon_set_logfile(conf.logfile_arg); + daemon_open_log_or_die(); } - if (conf.logfile_given) - logfile = open_log(conf.logfile_arg); - log_welcome("para_audiod", conf.loglevel_arg); - i = init_stream_io(); - if (i < 0) { - PARA_EMERG_LOG("init stream io error: %s\n", para_strerror(-i)); + ret = parse_stream_args(); + if (ret < 0) { + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); exit(EXIT_FAILURE); } + log_welcome("para_audiod"); server_uptime(UPTIME_SET); set_initial_status(); FOR_EACH_SLOT(i) @@ -1128,7 +1222,7 @@ int main(int argc, char *argv[]) init_command_task(cmd_task); if (conf.daemon_given) - daemon_init(); + daemonize(); register_task(&sig_task->task); register_task(&cmd_task->task);