base64: Use para_isspace() everywhere.
[paraslash.git] / audiod.c
index 1f226265b0c9c79593f255bba4805cfdb8a53e9a..fedeff7af880a009d938e73087edb7ca6c83f267 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -14,6 +14,7 @@
 #include <sys/un.h>
 #include <netdb.h>
 #include <signal.h>
+#include <pwd.h>
 
 #include "para.h"
 #include "error.h"
@@ -163,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];
@@ -172,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
  *
@@ -306,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 */
@@ -318,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);
@@ -349,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;
@@ -361,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
@@ -390,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,
@@ -410,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) {
@@ -417,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);
 }
 
@@ -501,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);
@@ -564,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];
@@ -834,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;
 }
 
@@ -969,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;
@@ -1048,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]));
@@ -1206,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);
 }
 
 /*
@@ -1226,6 +1255,7 @@ static void audiod_cleanup(void)
        close_unused_slots();
        audiod_cmdline_parser_free(&conf);
        close_stat_clients();
+       free(uid_whitelist);
 }
 
 /*
@@ -1253,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)
@@ -1417,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
  *
@@ -1449,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);
@@ -1468,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)