-/*
- * Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
+/* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
/** \file audiod.c The paraslash's audio daemon. */
#include "recv_cmd.lsg.h"
#include "para.h"
#include "error.h"
+#include "lsu.h"
#include "crypt.h"
#include "list.h"
#include "sched.h"
#define OPT_GIVEN(_name) (lls_opt_given(OPT_RESULT(_name)))
#define OPT_STRING_VAL(_name) (lls_string_val(0, OPT_RESULT(_name)))
#define OPT_UINT32_VAL(_name) (lls_uint32_val(0, OPT_RESULT(_name)))
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
- lls_opt(LSG_AUDIOD_PARA_AUDIOD_OPT_ ## _name, CMD_PTR)))
__printf_2_3 void (*para_log)(int, const char*, ...) = daemon_log;
/** define the array containing all supported audio formats */
* This is needed also in audiod_command.c (for the tasks command), so it can
* not be made static.
*/
-struct sched sched = {.max_fileno = 0};
+struct sched sched = {.timeout = 0};
/* The task for obtaining para_server's status (para_client stat). */
struct status_task {
static void parse_config_or_die(void)
{
- int ret;
- char *cf, *errctx = NULL;
- void *map;
- size_t sz;
+ int i, ret;
+ uint32_t n;
- if (OPT_GIVEN(CONFIG_FILE))
- cf = para_strdup(OPT_STRING_VAL(CONFIG_FILE));
- else {
- char *home = para_homedir();
- cf = make_message("%s/.paraslash/audiod.conf", home);
- free(home);
- }
- ret = mmap_full_file(cf, O_RDONLY, &map, &sz, NULL);
+ ret = lsu_merge_config_file_options(OPT_STRING_VAL(CONFIG_FILE),
+ "audiod.conf", &lpr, CMD_PTR, audiod_suite, 0U /* flags */);
if (ret < 0) {
- if (ret != -E_EMPTY && ret != -ERRNO_TO_PARA_ERROR(ENOENT))
- goto free_cf;
- if (ret == -ERRNO_TO_PARA_ERROR(ENOENT) && OPT_GIVEN(CONFIG_FILE))
- goto free_cf;
- } else {
- int cf_argc;
- char **cf_argv;
- struct lls_parse_result *cf_lpr, *merged_lpr;
- ret = lls(lls_convert_config(map, sz, NULL, &cf_argv, &errctx));
- para_munmap(map, sz);
- if (ret < 0)
- goto free_cf;
- cf_argc = ret;
- ret = lls(lls_parse(cf_argc, cf_argv, CMD_PTR, &cf_lpr, &errctx));
- lls_free_argv(cf_argv);
- if (ret < 0)
- goto free_cf;
- ret = lls(lls_merge(lpr, cf_lpr, CMD_PTR, &merged_lpr,
- &errctx));
- lls_free_parse_result(cf_lpr, CMD_PTR);
- if (ret < 0)
- goto free_cf;
- lls_free_parse_result(lpr, CMD_PTR);
- lpr = merged_lpr;
+ PARA_EMERG_LOG("failed to parse config file: %s\n",
+ para_strerror(-ret));
+ exit(EXIT_FAILURE);
}
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
- if (OPT_GIVEN(USER_ALLOW)) {
- uint32_t n = OPT_GIVEN(USER_ALLOW);
- int i;
-
- uid_whitelist = para_malloc(n * sizeof(uid_t));
- for (i = 0; i < n; i++) {
- const char *arg = lls_string_val(i,
- OPT_RESULT(USER_ALLOW));
- int32_t val;
- struct passwd *pw;
- ret = para_atoi32(arg, &val);
- if (ret >= 0) {
- uid_whitelist[i] = val;
- continue;
- }
- errno = 0; /* see getpwnam(3) */
- pw = getpwnam(arg);
- if (!pw) {
- PARA_EMERG_LOG("invalid username: %s\n", arg);
- free(uid_whitelist);
- goto free_cf;
- }
- uid_whitelist[i] = pw->pw_uid;
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
+ n = OPT_GIVEN(USER_ALLOW);
+ if (n == 0)
+ return;
+ uid_whitelist = arr_alloc(n, sizeof(uid_t));
+ for (i = 0; i < n; i++) {
+ const char *arg = lls_string_val(i, OPT_RESULT(USER_ALLOW));
+ int32_t val;
+ struct passwd *pw;
+ ret = para_atoi32(arg, &val);
+ if (ret >= 0) {
+ uid_whitelist[i] = val;
+ continue;
}
- }
- ret = 0;
-free_cf:
- free(cf);
- if (ret < 0) {
- if (errctx)
- PARA_ERROR_LOG("%s\n", errctx);
- free(errctx);
- lls_free_parse_result(lpr, CMD_PTR);
- PARA_EMERG_LOG("%s\n", para_strerror(-ret));
- exit(EXIT_FAILURE);
+ pw = getpwnam(arg);
+ if (!pw) {
+ PARA_EMERG_LOG("invalid username: %s\n", arg);
+ exit(EXIT_FAILURE);
+ }
+ uid_whitelist[i] = pw->pw_uid;
}
}
task_reap(&s->receiver_node->task);
free(s->receiver_node);
s->receiver_node = NULL;
- stat_task->current_audio_format_num = -1;
+ if (audiod_status == AUDIOD_ON)
+ stat_task->current_audio_format_num = -1;
tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000),
&a->restart_barrier);
}
s->wns = NULL;
}
+static void notify_writers(int error)
+{
+ int i;
+
+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = slot + i;
+ struct audio_format_info *a;
+ int j;
+
+ if (s->format < 0)
+ continue;
+ a = afi + s->format;
+ for (j = 0; j < a->num_writers; j++)
+ task_notify(s->wns[j].task, error);
+ }
+}
+
static void close_filters(struct slot_info *s)
{
int i;
return;
PARA_INFO_LOG("opening %s filters\n", audio_formats[s->format]);
assert(s->fns == NULL);
- s->fns = para_calloc(nf * sizeof(struct filter_node));
+ s->fns = zalloc(nf * sizeof(struct filter_node));
parent = s->receiver_node->btrn;
for (i = 0; i < nf; i++) {
char buf[20];
sprintf(buf, "%s (slot %d)", name, (int)(s - slot));
fn->task = task_register(&(struct task_info) {
.name = buf,
- .pre_select = f->pre_select,
- .post_select = f->post_select,
+ .pre_monitor = f->pre_monitor,
+ .post_monitor = f->post_monitor,
.context = fn,
}, &sched);
parent = fn->btrn;
struct btr_node *parent = s->fns[a->num_filters - 1].btrn;
assert(s->wns == NULL);
- s->wns = para_calloc(PARA_MAX(1U, a->num_writers)
+ s->wns = zalloc(PARA_MAX(1U, a->num_writers)
* sizeof(struct writer_node));
for (i = 0; i < a->num_writers; i++) {
wn = s->wns + i;
if (ret < 0)
return ret;
slot_num = ret;
- rn = para_calloc(sizeof(*rn));
+ rn = zalloc(sizeof(*rn));
rn->receiver = r;
rn->lpr = a->receiver_lpr;
rn->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = name, .context = rn));
ret = r->open(rn);
if (ret < 0) {
+ PARA_ERROR_LOG("could not open %s receiver\n", name);
btr_remove_node(&rn->btrn);
free(rn);
return ret;
audio_formats[format], name, slot_num);
rn->task = task_register(&(struct task_info) {
.name = name,
- .pre_select = r->pre_select,
- .post_select = r->post_select,
+ .pre_monitor = r->pre_monitor,
+ .post_monitor = r->post_monitor,
.context = rn,
}, &sched);
return slot_num;
if (count > 5) {
int s = tv_diff(&diff, &stat_task->sa_time_diff, &tmp);
if (tv_diff(&max_deviation, &tmp, NULL) < 0)
- PARA_WARNING_LOG("time diff jump: %lums\n",
- s * tv2ms(&tmp));
+ PARA_WARNING_LOG("time diff jump: %c%lums\n",
+ s < 0? '-' : '+', tv2ms(&tmp));
}
count++;
sa_time_diff_sign = tv_convex_combination(
{
long unsigned sec, usec;
- if (stat_task->clock_diff_count && itemnum != SI_CURRENT_TIME)
+ if (stat_task->clock_diff_count && itemnum != SI_current_time)
return 1;
free(stat_item_values[itemnum]);
stat_item_values[itemnum] = para_strdup(buf);
stat_client_write_item(itemnum);
switch (itemnum) {
- case SI_STATUS_FLAGS:
+ case SI_status_flags:
stat_task->vss_status = 0;
if (strchr(buf, 'N'))
stat_task->vss_status |= VSS_STATUS_FLAG_NEXT;
if (strchr(buf, 'P'))
stat_task->vss_status |= VSS_STATUS_FLAG_PLAYING;
break;
- case SI_OFFSET:
+ case SI_offset:
stat_task->offset_seconds = atoi(buf);
break;
- case SI_SECONDS_TOTAL:
+ case SI_seconds_total:
stat_task->length_seconds = atoi(buf);
break;
- case SI_STREAM_START:
+ case SI_stream_start:
if (sscanf(buf, "%lu.%lu", &sec, &usec) == 2) {
stat_task->server_stream_start.tv_sec = sec;
stat_task->server_stream_start.tv_usec = usec;
}
break;
- case SI_CURRENT_TIME:
+ case SI_current_time:
if (sscanf(buf, "%lu.%lu", &sec, &usec) == 2) {
struct timeval tv = {sec, usec};
compute_time_diff(&tv);
}
break;
- case SI_FORMAT:
+ case SI_format:
stat_task->current_audio_format_num
= get_audio_format_num(buf);
}
return -E_MISSING_COLON;
*cmd = p + 1;
len = p - txt;
- re = malloc(len + 1);
+ re = alloc(len + 1);
strncpy(re, txt, len);
re[len] = '\0';
ret = get_matching_audio_format_nums(re);
struct lls_parse_result *flpr;
filter_num = filter_setup(cmdline, &cfg, &flpr);
- a->filter_lpr = para_realloc(a->filter_lpr,
- (nf + 1) * sizeof(flpr));
- a->filter_conf = para_realloc(a->filter_conf,
- (nf + 1) * sizeof(void *));
- a->filter_nums = para_realloc(a->filter_nums,
- (nf + 1) * sizeof(unsigned));
+ a->filter_lpr = arr_realloc(a->filter_lpr, nf + 1, sizeof(flpr));
+ a->filter_conf = arr_realloc(a->filter_conf, nf + 1, sizeof(void *));
+ a->filter_nums = arr_realloc(a->filter_nums, nf + 1, sizeof(unsigned));
a->filter_nums[nf] = filter_num;
a->filter_conf[nf] = cfg;
if (a->num_writers > 0)
continue; /* already set up */
a->num_writers = 1;
- a->wids = para_malloc(sizeof(int));
- a->writer_lpr = para_malloc(sizeof(struct lls_parse_result *));
+ a->wids = alloc(sizeof(int));
+ a->writer_lpr = alloc(sizeof(struct lls_parse_result *));
a->wids[0] = check_writer_arg_or_die(NULL, a->writer_lpr);
PARA_INFO_LOG("%s writer: %s (default)\n", audio_formats[i],
writer_name(a->wids[0]));
exit(EXIT_FAILURE);
}
-static int signal_post_select(struct sched *s, void *context)
+static int signal_post_monitor(struct sched *s, void *context)
{
struct signal_task *st = context;
int ret, signum;
ret = task_get_notification(st->task);
if (ret < 0)
return ret;
- signum = para_next_signal(&s->rfds);
+ signum = para_next_signal();
switch (signum) {
case SIGINT:
case SIGTERM:
case SIGHUP:
- PARA_NOTICE_LOG("received signal %d\n", signum);
+ PARA_WARNING_LOG("terminating on signal %d\n", signum);
task_notify_all(s, E_AUDIOD_SIGNAL);
return -E_AUDIOD_SIGNAL;
}
return 0;
}
-static void command_pre_select(struct sched *s, void *context)
+static void command_pre_monitor(struct sched *s, void *context)
{
struct command_task *ct = context;
- para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
+ sched_monitor_readfd(ct->fd, s);
}
-static int command_post_select(struct sched *s, void *context)
+static int command_post_monitor(struct sched *s, void *context)
{
int ret;
struct command_task *ct = context;
ret = task_get_notification(ct->task);
if (ret < 0)
return ret;
- ret = handle_connect(ct->fd, &s->rfds);
+ ret = dispatch_local_connection(ct->fd);
if (ret < 0) {
- PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
if (ret == -E_AUDIOD_TERM) {
task_notify_all(s, -ret);
return ret;
ct->task = task_register(&(struct task_info) {
.name = "command",
- .pre_select = command_pre_select,
- .post_select = command_post_select,
+ .pre_monitor = command_pre_monitor,
+ .post_monitor = command_post_monitor,
.context = ct,
}, &sched);
}
struct slot_info *sl;
close_unused_slots();
- if (audiod_status != AUDIOD_ON ||
- !(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
+ if (audiod_status != AUDIOD_ON)
+ return notify_writers(E_NOT_PLAYING);
+ if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING))
return notify_receivers(E_NOT_PLAYING);
if (!must_start_decoder())
return;
audiod_status_dump(true);
}
-static void status_pre_select(struct sched *s, void *context)
+static void status_pre_monitor(struct sched *s, void *context)
{
struct status_task *st = context;
int i, ret, cafn = stat_task->current_audio_format_num;
}
/* restart the client task if necessary */
-static int status_post_select(struct sched *s, void *context)
+static int status_post_monitor(struct sched *s, void *context)
{
struct status_task *st = context;
int ret;
client_open(argc, argv, &st->ct, NULL, NULL, st->btrn, s);
set_stat_task_restart_barrier(5);
}
- free(stat_item_values[SI_BASENAME]);
- stat_item_values[SI_BASENAME] = para_strdup(
+ free(stat_item_values[SI_basename]);
+ stat_item_values[SI_basename] = para_strdup(
"no connection to para_server");
- stat_client_write_item(SI_BASENAME);
+ stat_client_write_item(SI_basename);
st->last_status_read = *now;
out:
start_stop_decoders();
stat_task->task = task_register(&(struct task_info) {
.name = "stat",
- .pre_select = status_pre_select,
- .post_select = status_post_select,
+ .pre_monitor = status_pre_monitor,
+ .post_monitor = status_post_monitor,
.context = stat_task,
}, &sched);
}
ret = lls(lls_parse(argc, argv, CMD_PTR, &lpr, &errctx));
if (ret < 0)
goto out;
- daemon_set_loglevel(ENUM_STRING_VAL(LOGLEVEL));
+ daemon_set_loglevel(OPT_UINT32_VAL(LOGLEVEL));
daemon_drop_privileges_or_die(OPT_STRING_VAL(USER),
OPT_STRING_VAL(GROUP));
version_handle_flag("audiod", OPT_GIVEN(VERSION));
handle_help_flags();
parse_config_or_die();
- init_random_seed_or_die();
+ crypt_init();
daemon_set_priority(OPT_UINT32_VAL(PRIORITY));
- recv_init();
if (daemon_init_colors_or_die(OPT_UINT32_VAL(COLOR), COLOR_AUTO,
COLOR_NO, OPT_GIVEN(LOGFILE))) {
for (i = 0; i < OPT_GIVEN(LOG_COLOR); i++)
signal_task->task = task_register(&(struct task_info) {
.name = "signal",
- .pre_select = signal_pre_select,
- .post_select = signal_post_select,
+ .pre_monitor = signal_pre_monitor,
+ .post_monitor = signal_post_monitor,
.context = signal_task,
}, &sched);
- sched.default_timeout.tv_sec = 2;
- sched.default_timeout.tv_usec = 999 * 1000;
+ sched.default_timeout = 2999;
ret = schedule(&sched);
audiod_cleanup();
sched_shutdown(&sched);
signal_shutdown(signal_task);
-
+ crypt_shutdown();
out:
lls_free_parse_result(lpr, CMD_PTR);
if (errctx)