Merge branch 'refs/heads/t/i9e'
[paraslash.git] / audiod_command.c
index 0fe2e5f..278e6ef 100644 (file)
 extern struct sched sched;
 extern char *stat_item_values[NUM_STAT_ITEMS];
 
+typedef int audiod_command_handler_t(int, int, char **);
+static audiod_command_handler_t AUDIOD_COMMAND_HANDLERS;
+
 /* Defines one command of para_audiod. */
 struct audiod_command {
        const char *name;
        /* Pointer to the function that handles the command. */
-       int (*handler)(int, int, char **);
+       /*
+        * Command handlers must never never close their file descriptor. A
+        * positive return value tells audiod that the status items have
+        * changed. In this case audiod will send an updated version of all
+        * status items to to each connected stat client.
+        */
+       audiod_command_handler_t *handler;
        /* One-line description. */
        const char *description;
        /* Summary of the command line options. */
@@ -75,7 +84,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;
@@ -110,15 +119,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;
@@ -176,8 +189,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;
@@ -245,17 +257,10 @@ static int dump_commands(int fd)
        return ret;
 }
 
-/*
- * 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);
@@ -277,13 +282,11 @@ static int com_help(int fd, int argc, char **argv)
                free(buf);
                goto out;
        }
-       ret = client_write(fd, dflt);
+       ret = client_write(fd, "No such command. Available commands:\n");
        if (ret > 0)
                ret = dump_commands(fd);
 out:
-       if (ret >= 0)
-               close(fd);
-       return ret;
+       return ret < 0? ret : 0;
 }
 
 static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
@@ -294,9 +297,7 @@ static int com_tasks(int fd, __a_unused int argc, __a_unused char **argv)
        if (tl)
                ret = client_write(fd, tl);
        free(tl);
-       if (ret > 0)
-               close(fd);
-       return ret;
+       return ret < 0? ret : 0;
 }
 
 static int com_stat(int fd, int argc, char **argv)
@@ -335,48 +336,45 @@ 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;
 }
 
 static int com_grab(int fd, int argc, char **argv)
 {
-       return grab_client_new(fd, argc, argv, &sched);
+       int ret = grab_client_new(fd, argc, argv, &sched);
+       return ret < 0? ret : 0;
 }
 
-static int com_term(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_term(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
 {
-       close(fd);
        return -E_AUDIOD_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 int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_ON;
-       close(fd);
        return 1;
 }
 
-static int com_off(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_off(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_OFF;
-       close(fd);
        return 1;
 }
 
-static int com_sb(int fd, __a_unused int argc, __a_unused char **argv)
+static int com_sb(__a_unused int fd, __a_unused int argc, __a_unused char **argv)
 {
        audiod_status = AUDIOD_STANDBY;
-       close(fd);
        return 1;
 }
 
-static int com_cycle(int fd, int argc, char **argv)
+static int com_cycle(__a_unused int fd, int argc, char **argv)
 {
        switch (audiod_status) {
                case  AUDIOD_ON:
@@ -389,7 +387,6 @@ static int com_cycle(int fd, int argc, char **argv)
                        return com_off(fd, argc, argv);
                        break;
        }
-       close(fd);
        return 1;
 }
 
@@ -404,9 +401,7 @@ static int com_version(int fd, int argc, char **argv)
                msg = make_message("%s\n", version_single_line("audiod"));
        ret = client_write(fd, msg);
        free(msg);
-       if (ret >= 0)
-               close(fd);
-       return ret;
+       return ret < 0? ret : 0;
 }
 
 static int check_perms(uid_t uid, uid_t *whitelist)
@@ -453,7 +448,7 @@ int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist)
        if (ret < 0)
                goto out;
        uid = ret;
-       PARA_INFO_LOG("connection from user %i, buf: %s\n",  ret, buf);
+       PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
        ret = check_perms(uid, uid_whitelist);
        if (ret < 0)
                goto out;
@@ -471,12 +466,12 @@ int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist)
        ret = -E_INVALID_AUDIOD_CMD;
 out:
        free_argv(argv);
-       if (clifd > 0 && ret < 0 && ret != -E_CLIENT_WRITE) {
+       if (ret < 0 && ret != -E_CLIENT_WRITE) {
                char *tmp = make_message("%s\n", para_strerror(-ret));
                client_write(clifd, tmp);
                free(tmp);
-               close(clifd);
        }
+       close(clifd);
        return ret;
 }