#include <sys/un.h>
#include <netdb.h>
#include <signal.h>
+#include <pwd.h>
#include "para.h"
#include "error.h"
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
static char *socket_name;
static struct audio_format_info afi[NUM_AUDIO_FORMATS];
-static struct signal_task signal_task_struct, *sig_task = &signal_task_struct;
+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
*
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;
return ret;
}
+static int get_play_time_slot_num(void)
+{
+ int i, oldest_slot = -1;
+ struct timeval oldest_wstime = {0, 0};
+
+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = &slot[i];
+ struct timeval wstime;
+ if (!s->wns || !s->wns[0].btrn)
+ continue;
+ btr_get_node_start(s->wns[0].btrn, &wstime);
+ if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
+ continue;
+ oldest_wstime = wstime;
+ oldest_slot = i;
+ }
+ return oldest_slot;
+}
+
/**
- * Compute the play time based on information of the given slot.
- *
- * \param slot_num The slot number (negative means: no slot).
+ * Compute the play time based on information of the current slot.
*
* This computes a string of the form "0:07 [3:33] (3%/3:40)" using information
* from the status items received from para_server and the start time of the
- * (first) writer of the given slot.
+ * (first) writer of the current slot.
*
* It has to take into account that the stream was probably not started at
* the beginning of the file, that the clock between the server and the client
* host may differ and that playback of the stream was delayed, e.g. because
- * the prebuffer filter is used in the filter configuration of the given slot.
+ * the prebuffer filter is used in the filter configuration.
*
- * If no writer is active in the given slot, or \a slot_num is negative
- * (indicating that para_audiod runs in standby mode), an approximation based
- * only on the status items is computed and the returned string is prefixed
- * with "~".
+ * If no writer is active, for example because para_audiod runs in standby
+ * mode, an approximation based only on the status items is computed and the
+ * returned string is prefixed with "~".
*
* \return A string that must be freed by the caller.
*/
-char *get_time_string(int slot_num)
+char *get_time_string(void)
{
int ret, seconds = 0, length;
struct timeval *tmp, sum, sss, /* server stream start */
wstime, /* writer start time */
wtime, /* now - writer start */
rskip; /* receiver start - sss */
+ int slot_num = get_play_time_slot_num();
struct slot_info *s = slot_num < 0? NULL : &slot[slot_num];
char *msg;
static void parse_config_or_die(void)
{
- int ret;
+ int ret, i;
char *config_file;
struct audiod_cmdline_parser_params params = {
.override = 0,
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) {
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);
}
static void setup_signal_handling(void)
{
- sig_task->fd = para_signal_init();
- PARA_INFO_LOG("signal pipe: fd %d\n", sig_task->fd);
+ signal_task = signal_init_or_die();
para_install_sighandler(SIGINT);
para_install_sighandler(SIGTERM);
para_install_sighandler(SIGHUP);
exit(EXIT_FAILURE);
}
-static void signal_pre_select(struct sched *s, void *context)
-{
- struct signal_task *st = context;
- para_fd_set(st->fd, &s->rfds, &s->max_fileno);
-}
-
static int signal_post_select(struct sched *s, void *context)
{
struct signal_task *st = context;
for (i = 0; i < 2; i++) {
if (ct->fd[i] < 0)
continue;
- ret = handle_connect(ct->fd[i], &s->rfds);
+ ret = handle_connect(ct->fd[i], &s->rfds, uid_whitelist);
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
if (ret == -E_AUDIOD_TERM) {
close_unused_slots();
audiod_cmdline_parser_free(&conf);
close_stat_clients();
+ free(uid_whitelist);
}
/*
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)
if (conf.daemon_given)
daemonize(false /* parent exits immediately */);
- sig_task->task = task_register(&(struct task_info) {
+ signal_task->task = task_register(&(struct task_info) {
.name = "signal",
.pre_select = signal_pre_select,
.post_select = signal_post_select,
- .context = sig_task,
+ .context = signal_task,
}, &sched);
sched.default_timeout.tv_sec = 2;
ret = schedule(&sched);
audiod_cleanup();
sched_shutdown(&sched);
+ signal_shutdown(signal_task);
if (ret < 0)
PARA_EMERG_LOG("%s\n", para_strerror(-ret));