*/
/** \file audiod.c The paraslash's audio daemon. */
+
+#include <netinet/in.h>
+#include <sys/socket.h>
#include <regex.h>
#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
#include <signal.h>
#include "para.h"
PARA_EMERG_LOG("can not read config file %s\n", config_file);
goto err;
}
- if (ret)
+ if (ret) {
audiod_cmdline_parser_config_file(config_file, &conf, ¶ms);
+ daemon_set_loglevel(conf.loglevel_arg);
+ }
free(config_file);
- daemon_set_loglevel(conf.loglevel_arg);
return;
err:
free(config_file);
struct audio_format_info *a = afi + s->format;
if (a->num_filters == 0)
return;
- for (i = 0; i < a->num_filters; i++) {
+ for (i = a->num_filters - 1; i >= 0; i--) {
struct filter_node *fn = s->fns + i;
struct filter *f;
fn->filter_num = a->filter_nums[i];
fn->conf = a->filter_conf[i];
fn->task.pre_select = f->pre_select;
- if (f->new_post_select) {
- fn->task.new_post_select = f->new_post_select;
- fn->task.post_select = NULL;
- } else {
- fn->task.new_post_select = NULL;
- fn->task.post_select = f->post_select;
- }
+ fn->task.post_select = f->post_select;
fn->btrn = btr_new_node(&(struct btr_node_description)
EMBRACE(.name = f->name, .parent = parent,
.handler = f->execute, .context = fn));
assert(s->wns == NULL);
s->wns = para_calloc(PARA_MAX(1U, a->num_writers)
* sizeof(struct writer_node));
- PARA_INFO_LOG("opening %s writers\n", audio_formats[s->format]);
for (i = 0; i < a->num_writers; i++) {
wn = s->wns + i;
wn->conf = a->writer_conf[i];
wn->writer_num = a->writer_nums[i];
register_writer_node(wn, parent, &sched);
+ PARA_NOTICE_LOG("%s writer started in slot %d\n",
+ writer_names[a->writer_nums[i]], (int)(s - slot));
}
}
PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n",
audio_formats[format], r->name, slot_num);
rn->task.pre_select = r->pre_select;
- if (r->new_post_select) {
- rn->task.new_post_select = r->new_post_select;
- rn->task.post_select = NULL;
- } else {
- rn->task.new_post_select = NULL;
- rn->task.post_select = r->post_select;
- }
+ rn->task.post_select = r->post_select;
sprintf(rn->task.status, "%s receiver node", r->name);
register_task(&sched, &rn->task);
return slot_num;
static int parse_filter_args(void)
{
- int i, j, ret, af_mask;
+ int i, j, ret, af_mask, num_matches;
for (i = 0; i < conf.filter_given; i++) {
char *arg;
if (ret < 0)
goto out;
af_mask = ret;
+ num_matches = 0;
FOR_EACH_AUDIO_FORMAT(j) {
if ((af_mask & (1 << j)) == 0) /* no match */
continue;
ret = add_filter(j, arg);
if (ret < 0)
goto out;
+ num_matches++;
}
+ if (num_matches == 0)
+ PARA_WARNING_LOG("ignoring filter spec: %s\n",
+ conf.filter_arg[i]);
}
ret = init_default_filters(); /* use default values for the rest */
out:
case SIGINT:
case SIGTERM:
case SIGHUP:
- PARA_EMERG_LOG("terminating on signal %d\n", signum);
+ PARA_NOTICE_LOG("received signal %d\n", signum);
clean_exit(EXIT_FAILURE, "caught deadly signal");
}
return 0;
static void signal_setup_default(struct signal_task *st)
{
st->task.pre_select = signal_pre_select;
- st->task.new_post_select = signal_post_select;
- st->task.post_select = NULL;
+ st->task.post_select = signal_post_select;
sprintf(st->task.status, "signal task");
}
para_fd_set(ct->fd, &s->rfds, &s->max_fileno);
}
-static void command_post_select(struct sched *s, struct task *t)
+static int command_post_select(struct sched *s, struct task *t)
{
int ret;
struct command_task *ct = container_of(t, struct command_task, task);
static struct timeval last_status_dump;
- struct timeval tmp, delay = {0, 500 * 1000};
-
- tv_add(&last_status_dump, &delay, &tmp);
- if (tv_diff(&tmp, now, NULL) < 0) {
- audiod_status_dump();
- last_status_dump = *now;
- }
+ struct timeval tmp, delay;
+ bool force = true;
ret = handle_connect(ct->fd, &s->rfds);
if (ret < 0)
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
- audiod_status_dump();
+ else if (ret > 0)
+ goto dump;
+
+ /* if last status dump was less than 500ms ago, do nothing */
+ delay.tv_sec = 0;
+ delay.tv_usec = 500 * 1000;
+ tv_add(&last_status_dump, &delay, &tmp);
+ if (tv_diff(now, &tmp, NULL) < 0)
+ return 0;
+
+ /*
+ * If last status dump was more than 5s ago, force update. Otherwise,
+ * update only those items that have changed.
+ */
+ delay.tv_sec = 5;
+ delay.tv_usec = 0;
+ tv_add(&last_status_dump, &delay, &tmp);
+ if (tv_diff(now, &tmp, NULL) < 0)
+ force = false;
+dump:
+ audiod_status_dump(force);
+ last_status_dump = *now;
+ return 1;
}
static void init_command_task(struct command_task *ct)
{
ct->task.pre_select = command_pre_select;
ct->task.post_select = command_post_select;
- ct->task.new_post_select = NULL;
ct->task.error = 0;
ct->fd = audiod_get_socket(); /* doesn't return on errors */
sprintf(ct->task.status, "command task");
stat_task->offset_seconds = 0;
stat_task->vss_status = 0;
stat_task->current_audio_format_num = -1;
- audiod_status_dump();
-}
-
-/**
- * close the connection to para_server and exit
- *
- * \param status the exit status which is passed to exit(3)
- * \param msg the log message
- *
- * Log \a msg with loglevel \p EMERG, close the connection to para_server if
- * open, and call \p exit(status). \a status should be either EXIT_SUCCESS or
- * EXIT_FAILURE.
- *
- * \sa exit(3)
- */
-void __noreturn clean_exit(int status, const char *msg)
-{
- PARA_EMERG_LOG("%s\n", msg);
- if (socket_name)
- unlink(socket_name);
- close_stat_pipe();
- exit(status);
+ audiod_status_dump(true);
}
/* avoid busy loop if server is down */
return true;
}
+static void close_slot(int slot_num)
+{
+ struct slot_info *s = slot + slot_num;
+
+ PARA_INFO_LOG("closing slot %d\n", slot_num);
+ close_writers(s);
+ close_filters(s);
+ close_receiver(slot_num);
+ clear_slot(slot_num);
+}
+
static void close_unused_slots(void)
{
int i;
- FOR_EACH_SLOT(i) {
- struct slot_info *s = slot + i;
- if (!must_close_slot(i))
- continue;
- PARA_INFO_LOG("closing slot %d\n", i);
- close_writers(s);
- close_filters(s);
- close_receiver(i);
- clear_slot(i);
- }
+ FOR_EACH_SLOT(i)
+ if (must_close_slot(i))
+ close_slot(i);
+}
+
+/**
+ * Close the connection to para_server and exit.
+ *
+ * \param status The exit status which is passed to exit(3).
+ * \param msg The log message
+ *
+ * Log \a msg with loglevel \p EMERG, close the connection to para_server and
+ * all slots, and call \p exit(status). \a status should be either EXIT_SUCCESS
+ * or EXIT_FAILURE.
+ *
+ * \sa exit(3).
+ */
+void __noreturn clean_exit(int status, const char *msg)
+{
+ int i;
+
+ if (socket_name)
+ unlink(socket_name);
+ close_stat_pipe();
+ FOR_EACH_SLOT(i)
+ close_slot(i);
+ audiod_cmdline_parser_free(&conf);
+ close_stat_clients();
+ PARA_EMERG_LOG("%s\n", msg);
+ exit(status);
}
/*
}
/* restart the client task if necessary */
-static void status_post_select(struct sched *s, struct task *t)
+static int status_post_select(struct sched *s, struct task *t)
{
struct status_task *st = container_of(t, struct status_task, task);
char *buf;
size_t sz;
int ret;
- if (st->ct->task.error < 0) {
+
+ ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
+ if (ret < 0) {
close_stat_pipe();
goto out;
}
- if (st->ct->status != CL_RECEIVING)
+ if (st->ct->status != CL_EXECUTING)
goto out;
- ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF);
- if (ret <= 0) {
+ if (ret == 0) {
struct timeval diff;
tv_diff(now, &st->last_status_read, &diff);
if (diff.tv_sec > 61)
- task_notify(&st->ct->task, E_AUDIOD_OFF);
+ task_notify(&st->ct->task, E_STATUS_TIMEOUT);
goto out;
}
btr_merge(st->btrn, st->min_iqs);
sz = btr_next_buffer(st->btrn, &buf);
ret = for_each_stat_item(buf, sz, update_item);
if (ret < 0) {
- task_notify(&st->ct->task, E_AUDIOD_OFF);
+ task_notify(&st->ct->task, -ret);
goto out;
}
if (sz != ret) {
st->last_status_read = *now;
out:
start_stop_decoders();
+ return 0;
}
static void init_status_task(struct status_task *st)
__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);
+ struct ggo_help h = DEFINE_GGO_HELP(audiod);
+ bool d = conf.detailed_help_given;
+ unsigned flags;
+
+ flags = d? GPH_STANDARD_FLAGS_DETAILED : GPH_STANDARD_FLAGS;
+ ggo_print_help(&h, flags);
+
+ flags = d? GPH_MODULE_FLAGS_DETAILED : GPH_MODULE_FLAGS;
+ print_receiver_helps(flags);
+ print_filter_helps(flags);
+ print_writer_helps(flags);
exit(0);
}
};
valid_fd_012();
- if (audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms))
- exit(EXIT_FAILURE);
- HANDLE_VERSION_FLAG("audiod", conf);
+ audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms);
+ daemon_set_loglevel(conf.loglevel_arg);
+ version_handle_flag("audiod", conf.version_given);
/* init receivers/filters/writers early to make help work */
recv_init();
filter_init();