mixer: fade: Handle empty mood strings gracefully.
[paraslash.git] / audiod_command.c
index 7e68d2d223a1c7d734a65ba8675c092116d57be3..bb54dfab87f7965a18f6cccbe6bc4077c147fa29 100644 (file)
@@ -1,8 +1,4 @@
-/*
- * 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_command.c Commands for para_audiod. */
 
 #include <arpa/inet.h>
 #include <sys/un.h>
 #include <netdb.h>
+#include <lopsub.h>
 
+#include "audiod.lsg.h"
 #include "para.h"
-#include "audiod.cmdline.h"
+#include "lsu.h"
+#include "audiod_cmd.lsg.h"
 #include "list.h"
 #include "sched.h"
-#include "ggo.h"
 #include "buffer_tree.h"
 #include "filter.h"
 #include "grab_client.h"
 #include "write.h"
 #include "fd.h"
 #include "version.h"
-#include "audiod.command_list.h"
 
 extern struct sched sched;
 extern char *stat_item_values[NUM_STAT_ITEMS];
 
+/** The maximal number of simultaneous connections. */
+#define MAX_STAT_CLIENTS 50
 
-static struct audiod_command audiod_cmds[] = {DEFINE_AUDIOD_CMD_ARRAY};
+/** Pointer to a command handler function. */
+typedef int (*audiod_cmd_handler_t)(int, struct lls_parse_result *);
 
-/** Iterate over the array of all audiod commands. */
-#define FOR_EACH_COMMAND(c) for (c = 0; audiod_cmds[c].name; c++)
+/** The lopsub user_data pointer. Only the command handler at the moment. */
+struct audiod_command_info {
+       audiod_cmd_handler_t handler; /**< Implementation of the command. */
+};
 
-/** The maximal number of simultaneous connections. */
-#define MAX_STAT_CLIENTS 50
+/** Define the user_data pointer as expected by lopsub. */
+#define EXPORT_AUDIOD_CMD_HANDLER(_cmd) \
+       /** Implementation of _cmd. */ \
+       const struct audiod_command_info lsg_audiod_cmd_com_ ## _cmd ## _user_data = { \
+               .handler = com_ ## _cmd \
+       };
 
 /** Flags used for the stat command of para_audiod. */
 enum stat_client_flags {
@@ -63,7 +69,7 @@ struct stat_client {
        int fd;
        /** Bitmask of those status items the client is interested in. */
        uint64_t item_mask;
-       /** See \ref stat_client flags. s*/
+       /** See \ref stat_client flags. */
        unsigned flags;
        /** Its entry in the list of stat clients. */
        struct list_head node;
@@ -73,7 +79,7 @@ static INITIALIZED_LIST_HEAD(client_list);
 static int num_clients;
 
 /** The list of all status items used by para_{server,audiod,gui}. */
-const char *status_item_list[] = {STATUS_ITEM_ARRAY};
+const char *status_item_list[] = {STATUS_ITEMS};
 
 static void dump_stat_client_list(void)
 {
@@ -98,15 +104,19 @@ static void dump_stat_client_list(void)
 static int stat_client_add(int fd, uint64_t mask, int parser_friendly)
 {
        struct stat_client *new_client;
+       int ret;
 
        if (num_clients >= MAX_STAT_CLIENTS) {
                PARA_ERROR_LOG("maximal number of stat clients (%d) exceeded\n",
                        MAX_STAT_CLIENTS);
                return -E_TOO_MANY_CLIENTS;
        }
-       PARA_INFO_LOG("adding client on fd %d\n", fd);
-       new_client = para_calloc(sizeof(struct stat_client));
-       new_client->fd = fd;
+       ret = dup(fd);
+       if (ret < 0)
+               return -ERRNO_TO_PARA_ERROR(errno);
+       new_client = para_calloc(sizeof(*new_client));
+       new_client->fd = ret;
+       PARA_INFO_LOG("adding client on fd %d\n", new_client->fd);
        new_client->item_mask = mask;
        if (parser_friendly)
                new_client->flags = SCF_PARSER_FRIENDLY;
@@ -164,8 +174,7 @@ void stat_client_write_item(int item_num)
                        continue;
                b = (sc->flags & SCF_PARSER_FRIENDLY)? &pfpb : &pb;
                if (!b->buf)
-                       (void)WRITE_STATUS_ITEM(b, item_num, "%s\n",
-                               msg? msg : "");
+                       WRITE_STATUS_ITEM(b, item_num, "%s\n", msg? msg : "");
                ret = write(sc->fd, b->buf, b->offset);
                if (ret == b->offset)
                        continue;
@@ -216,143 +225,56 @@ __malloc static char *audiod_status_string(void)
        return para_strdup(status);
 }
 
-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;
-       }
-       //PARA_CRIT_LOG("oldest slot: %d\n", oldest_slot);
-       return oldest_slot;
-}
-
-__malloc static char *decoder_flags(void)
+static int com_help(int fd, struct lls_parse_result *lpr)
 {
-       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 dump_commands(int fd)
-{
-       char *buf = para_strdup(""), *tmp = NULL;
-       int i;
-       ssize_t ret;
+       char *buf;
+       int ret;
+       const struct lls_opt_result *r =
+               lls_opt_result(LSG_AUDIOD_CMD_HELP_OPT_LONG, lpr);
+       bool long_help = lls_opt_given(r);
 
-       FOR_EACH_COMMAND(i) {
-               tmp = make_message("%s%s\t%s\n", buf, audiod_cmds[i].name,
-                       audiod_cmds[i].description);
-               free(buf);
-               buf = tmp;
-       }
+       lsu_com_help(long_help, lpr, audiod_cmd_suite, NULL, &buf, NULL);
        ret = client_write(fd, buf);
        free(buf);
-       return ret;
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(help)
 
-/*
- * command handlers don't close their fd on errors (ret < 0) so that
- * its caller can send an error message. Otherwise (ret >= 0) it's up
- * to each individual command to close the fd if necessary.
- */
-
-static int com_help(int fd, int argc, char **argv)
-{
-       int i, ret;
-       char *buf;
-       const char *dflt = "No such command. Available commands:\n";
-
-       if (argc < 2) {
-               ret = dump_commands(fd);
-               goto out;
-       }
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[1]))
-                       continue;
-               buf = make_message(
-                       "NAME\n\t%s -- %s\n"
-                       "SYNOPSIS\n\tpara_audioc %s\n"
-                       "DESCRIPTION\n%s\n",
-                       argv[1],
-                       audiod_cmds[i].description,
-                       audiod_cmds[i].usage,
-                       audiod_cmds[i].help
-               );
-               ret = client_write(fd, buf);
-               free(buf);
-               goto out;
-       }
-       ret = client_write(fd, dflt);
-       if (ret > 0)
-               ret = dump_commands(fd);
-out:
-       if (ret >= 0)
-               close(fd);
-       return ret;
-}
-
-static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_tasks(int fd, __a_unused struct lls_parse_result *lpr)
 {
+       int ret;
        char *tl = get_task_list(&sched);
-       int ret = 1;
-       if (tl)
-               ret = client_write(fd, tl);
+
+       if (!tl) /* no tasks registered yet */
+               return 0;
+       ret = client_write(fd, tl);
        free(tl);
-       if (ret > 0)
-               close(fd);
        return ret;
 }
+EXPORT_AUDIOD_CMD_HANDLER(tasks)
 
-static int com_stat(int fd, int argc, char **argv)
+static int com_stat(int fd, struct lls_parse_result *lpr)
 {
        int i, ret, parser_friendly = 0;
        uint64_t mask = 0;
        const uint64_t one = 1;
        struct para_buffer b = {.flags = 0};
+       const struct lls_opt_result *r;
+       unsigned num_inputs;
 
        ret = mark_fd_nonblocking(fd);
        if (ret < 0)
                return ret;
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (arg[0] != '-')
-                       break;
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strncmp(arg, "-p", 2)) {
-                       parser_friendly = 1;
-                       b.flags = PBF_SIZE_PREFIX;
-               }
+       r = lls_opt_result(LSG_AUDIOD_CMD_STAT_OPT_PARSER_FRIENDLY, lpr);
+       if (lls_opt_given(r) > 0) {
+               parser_friendly = 1;
+               b.flags = PBF_SIZE_PREFIX;
        }
-       if (i >= argc)
+       num_inputs = lls_num_inputs(lpr);
+       if (num_inputs == 0)
                mask--; /* set all bits */
-       for (; i < argc; i++) {
-               ret = stat_item_valid(argv[i]);
+       for (i = 0; i < num_inputs; i++) {
+               ret = stat_item_valid(lls_input(i, lpr));
                if (ret < 0)
                        return ret;
                mask |= (one << ret);
@@ -362,91 +284,77 @@ static int com_stat(int fd, int argc, char **argv)
                char *item = stat_item_values[i];
                if (!((one << i) & mask))
                        continue;
-               (void)WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : "");
+               WRITE_STATUS_ITEM(&b, i, "%s\n", item? item : "");
        }
        ret = client_write(fd, b.buf);
        if (ret >= 0)
                ret = stat_client_add(fd, mask, parser_friendly);
        free(b.buf);
-       return ret;
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(stat)
 
-static int com_grab(int fd, int argc, char **argv)
+static int com_grab(int fd, struct lls_parse_result *lpr)
 {
-       return grab_client_new(fd, argc, argv, &sched);
+       int ret = grab_client_new(fd, lpr, &sched);
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(grab)
 
-static int com_term(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
-       close(fd);
        return -E_AUDIOD_TERM;
 }
+EXPORT_AUDIOD_CMD_HANDLER(term)
 
-static int com_on(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_on(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_ON;
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(on)
 
-static int com_off(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_OFF;
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(off)
 
-static int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        audiod_status = AUDIOD_STANDBY;
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(sb)
 
-static int com_cycle(int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, __a_unused struct lls_parse_result *lpr)
 {
        switch (audiod_status) {
-               case  AUDIOD_ON:
-                       return com_sb(fd, argc, argv);
-                       break;
-               case  AUDIOD_OFF:
-                       return com_on(fd, argc, argv);
-                       break;
-               case  AUDIOD_STANDBY:
-                       return com_off(fd, argc, argv);
-                       break;
+               case  AUDIOD_ON: audiod_status = AUDIOD_STANDBY; break;
+               case  AUDIOD_OFF: audiod_status = AUDIOD_ON; break;
+               case  AUDIOD_STANDBY: audiod_status = AUDIOD_OFF; break;
        }
-       close(fd);
        return 1;
 }
+EXPORT_AUDIOD_CMD_HANDLER(cycle)
 
-static int com_version(int fd, int argc, char **argv)
+static int com_version(int fd, struct lls_parse_result *lpr)
 {
        int ret;
        char *msg;
+       const struct lls_opt_result *r_v;
 
-       if (argc > 1 && strcmp(argv[1], "-v") == 0)
+       r_v = lls_opt_result(LSG_AUDIOD_CMD_VERSION_OPT_VERBOSE, lpr);
+       if (lls_opt_given(r_v))
                msg = make_message("%s", version_text("audiod"));
        else
                msg = make_message("%s\n", version_single_line("audiod"));
        ret = client_write(fd, msg);
        free(msg);
-       if (ret >= 0)
-               close(fd);
-       return ret;
-}
-
-static int check_perms(uid_t uid)
-{
-       int i;
-
-       if (!conf.user_allow_given)
-               return 1;
-       for (i = 0; i < conf.user_allow_given; i++)
-               if (uid == conf.user_allow_arg[i])
-                       return 1;
-       return -E_UCRED_PERM;
+       return ret < 0? ret : 0;
 }
+EXPORT_AUDIOD_CMD_HANDLER(version)
 
 /**
  * Handle arriving connections on the local socket.
@@ -463,14 +371,18 @@ static int check_perms(uid_t uid)
  * \return Positive on success, negative on errors, zero if there was no
  * connection to accept.
  *
- * \sa para_accept(), recv_cred_buffer()
+ * \sa \ref para_accept(), \ref recv_cred_buffer().
  * */
 int handle_connect(int accept_fd, fd_set *rfds)
 {
-       int i, argc, ret, clifd;
+       int argc, ret, clifd;
        char buf[MAXLINE], **argv = NULL;
        struct sockaddr_un unix_addr;
        uid_t uid;
+       const struct lls_command *cmd;
+       struct lls_parse_result *lpr;
+       char *errctx = NULL;
+       const struct audiod_command_info *aci;
 
        ret = para_accept(accept_fd, rfds, &unix_addr, sizeof(struct sockaddr_un), &clifd);
        if (ret <= 0)
@@ -479,30 +391,39 @@ int handle_connect(int accept_fd, fd_set *rfds)
        if (ret < 0)
                goto out;
        uid = ret;
-       PARA_INFO_LOG("connection from user %i, buf: %s\n",  ret, buf);
-       ret = check_perms(uid);
-       if (ret < 0)
+       PARA_INFO_LOG("connection from UID %d, buf: %s\n", ret, buf);
+       ret = -E_UCRED_PERM;
+       if (!uid_is_whitelisted(uid))
                goto out;
        ret = create_argv(buf, "\n", &argv);
        if (ret <= 0)
                goto out;
        argc = ret;
-       //PARA_INFO_LOG("argv[0]: %s, argc = %d\n", argv[0], argc);
-       FOR_EACH_COMMAND(i) {
-               if (strcmp(audiod_cmds[i].name, argv[0]))
-                       continue;
-               ret = audiod_cmds[i].handler(clifd, argc, argv);
+       ret = lls(lls_lookup_subcmd(argv[0], audiod_cmd_suite, &errctx));
+       if (ret < 0)
                goto out;
-       }
-       ret = -E_INVALID_AUDIOD_CMD;
+       cmd = lls_cmd(ret, audiod_cmd_suite);
+       ret = lls(lls_parse(argc, argv, cmd, &lpr, &errctx));
+       if (ret < 0)
+               goto out;
+       aci = lls_user_data(cmd);
+       ret = aci->handler(clifd, lpr);
+       lls_free_parse_result(lpr, cmd);
 out:
        free_argv(argv);
-       if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
-               char *tmp = make_message("%s\n", para_strerror(-ret));
+       if (ret < 0 && ret != -E_CLIENT_WRITE) {
+               char *tmp;
+               if (errctx) {
+                       tmp = make_message("%s\n", errctx);
+                       free(errctx);
+                       client_write(clifd, tmp);
+                       free(tmp);
+               }
+               tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
                free(tmp);
-               close(clifd);
        }
+       close(clifd);
        return ret;
 }
 
@@ -513,44 +434,43 @@ out:
  */
 void audiod_status_dump(bool force)
 {
-       int slot_num = get_play_time_slot_num();
        char *old, *new;
 
-       old = stat_item_values[SI_PLAY_TIME];
-       new = get_time_string(slot_num);
+       old = stat_item_values[SI_play_time];
+       new = get_time_string();
        if (new) {
                if (force || !old || strcmp(old, new)) {
                        free(old);
-                       stat_item_values[SI_PLAY_TIME] = new;
-                       stat_client_write_item(SI_PLAY_TIME);
+                       stat_item_values[SI_play_time] = new;
+                       stat_client_write_item(SI_play_time);
                } else
                        free(new);
        }
 
        new = daemon_get_uptime_str(now);
-       old = stat_item_values[SI_AUDIOD_UPTIME];
+       old = stat_item_values[SI_audiod_uptime];
        if (force || !old || strcmp(old, new)) {
                free(old);
-               stat_item_values[SI_AUDIOD_UPTIME] = new;
-               stat_client_write_item(SI_AUDIOD_UPTIME);
+               stat_item_values[SI_audiod_uptime] = new;
+               stat_client_write_item(SI_audiod_uptime);
        } else
                free(new);
 
-       old = stat_item_values[SI_AUDIOD_STATUS];
+       old = stat_item_values[SI_audiod_status];
        new = audiod_status_string();
        if (force || !old || strcmp(old, new)) {
                free(old);
-               stat_item_values[SI_AUDIOD_STATUS] = new;
-               stat_client_write_item(SI_AUDIOD_STATUS);
+               stat_item_values[SI_audiod_status] = new;
+               stat_client_write_item(SI_audiod_status);
        } else
                free(new);
 
-       old = stat_item_values[SI_DECODER_FLAGS];
-       new = decoder_flags();
+       old = stat_item_values[SI_decoder_flags];
+       new = audiod_get_decoder_flags();
        if (force || !old || strcmp(old, new)) {
                free(old);
-               stat_item_values[SI_DECODER_FLAGS] = new;
-               stat_client_write_item(SI_DECODER_FLAGS);
+               stat_item_values[SI_decoder_flags] = new;
+               stat_client_write_item(SI_decoder_flags);
        } else
                free(new);
 }