Merge branch 'refs/heads/t/command_handler_cleanups'
[paraslash.git] / command.c
index 43f85ca67a5e7b35c56d430f7da75cfdb64c7f94..e8f6948437aac8c0f03b538381afc5ea20b55fe4 100644 (file)
--- a/command.c
+++ b/command.c
 #include "signal.h"
 #include "version.h"
 
+typedef int server_command_handler_t(struct command_context *);
+static server_command_handler_t SERVER_COMMAND_HANDLERS;
+server_command_handler_t AFS_COMMAND_HANDLERS;
+
+/* Defines one command of para_server. */
+struct server_command {
+       /* The name of the command. */
+       const char *name;
+       /* Pointer to the function that handles the command. */
+       server_command_handler_t *handler;
+       /* The privileges a user must have to execute this command. */
+       unsigned int perms;
+       /* One-line description of the command. */
+       const char *description;
+       /* Summary of the command line options. */
+       const char *usage;
+       /* The long help text. */
+       const char *help;
+};
+
 static struct server_command afs_cmds[] = {DEFINE_AFS_CMD_ARRAY};
 static struct server_command server_cmds[] = {DEFINE_SERVER_CMD_ARRAY};
 
@@ -112,21 +132,15 @@ static char *vss_get_status_flags(unsigned int flags)
 static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
                char **result)
 {
-       char mtime[30] = "";
        char *status, *flags; /* vss status info */
        /* nobody updates our version of "now" */
        long offset = (nmmd->offset + 500) / 1000;
        struct timeval current_time;
-       struct tm mtime_tm;
        struct para_buffer b = {.flags = parser_friendly? PBF_SIZE_PREFIX : 0};
 
        /* report real status */
        status = vss_status_tohuman(nmmd->vss_status_flags);
        flags = vss_get_status_flags(nmmd->vss_status_flags);
-       if (nmmd->size) { /* parent currently has an audio file open */
-               localtime_r(&nmmd->mtime, &mtime_tm);
-               strftime(mtime, 29, "%b %d %Y", &mtime_tm);
-       }
        clock_get_realtime(&current_time);
        /*
         * The calls to WRITE_STATUS_ITEM() below never fail because
@@ -134,8 +148,6 @@ static unsigned get_status(struct misc_meta_data *nmmd, int parser_friendly,
         * is not smart enough to prove this and complains nevertheless.
         * Casting the return value to void silences clang.
         */
-       (void)WRITE_STATUS_ITEM(&b, SI_FILE_SIZE, "%zu\n", nmmd->size / 1024);
-       (void)WRITE_STATUS_ITEM(&b, SI_MTIME, "%s\n", mtime);
        (void)WRITE_STATUS_ITEM(&b, SI_STATUS, "%s\n", status);
        (void)WRITE_STATUS_ITEM(&b, SI_STATUS_FLAGS, "%s\n", flags);
        (void)WRITE_STATUS_ITEM(&b, SI_OFFSET, "%li\n", offset);
@@ -159,7 +171,7 @@ static int check_sender_args(int argc, char * const * argv, struct sender_comman
        const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
 
        scd->sender_num = -1;
-       if (argc < 2)
+       if (argc < 3)
                return -E_COMMAND_SYNTAX;
        for (i = 0; senders[i].name; i++)
                if (!strcmp(senders[i].name, argv[1]))
@@ -413,12 +425,14 @@ static int com_version(struct command_context *cc)
        char *msg;
        size_t len;
 
-       if (cc->argc != 1)
-               return -E_COMMAND_SYNTAX;
-       len = xasprintf(&msg, "%s", version_text("server"));
+       if (cc->argc > 1 && strcmp(cc->argv[1], "-v") == 0)
+               len = xasprintf(&msg, "%s", version_text("server"));
+       else
+               len = xasprintf(&msg, "%s\n", version_single_line("server"));
        return send_sb(&cc->scc, msg, len, SBD_OUTPUT, false);
 }
 
+/** These status items are cleared if no audio file is currently open. */
 #define EMPTY_STATUS_ITEMS \
        ITEM(PATH) \
        ITEM(DIRECTORY) \
@@ -445,7 +459,11 @@ static int com_version(struct command_context *cc)
        ITEM(YEAR) \
        ITEM(ALBUM) \
        ITEM(COMMENT) \
-       ITEM(AMPLIFICATION)
+       ITEM(MTIME) \
+       ITEM(FILE_SIZE) \
+       ITEM(CHUNK_TIME) \
+       ITEM(NUM_CHUNKS) \
+       ITEM(AMPLIFICATION) \
 
 /**
  * Write a list of audio-file related status items with empty values.
@@ -775,7 +793,7 @@ static int com_tasks(struct command_context *cc)
  * check if perms are sufficient to exec a command having perms cmd_perms.
  * Returns 0 if perms are sufficient, -E_PERM otherwise.
  */
-static int check_perms(unsigned int perms, struct server_command *cmd_ptr)
+static int check_perms(unsigned int perms, const struct server_command *cmd_ptr)
 {
        PARA_DEBUG_LOG("checking permissions\n");
        return (cmd_ptr->perms & perms) < cmd_ptr->perms ? -E_PERM : 0;
@@ -837,22 +855,23 @@ out:
 
 #define HANDSHAKE_BUFSIZE 4096
 
-static int parse_sb_command(struct command_context *cc, struct iovec *iov)
+static int run_command(struct command_context *cc, struct iovec *iov,
+               const char *peername)
 {
        int ret, i;
        char *p, *end;
+       struct server_command *cmd;
 
-       ret = -E_BAD_CMD;
        if (iov->iov_base == NULL || iov->iov_len == 0)
-               goto out;
+               return -E_BAD_CMD;
        p = iov->iov_base;
        p[iov->iov_len - 1] = '\0'; /* just to be sure */
-       cc->cmd = get_cmd_ptr(p, NULL);
-       if (!cc->cmd)
-               goto out;
-       ret = check_perms(cc->u->perms, cc->cmd);
+       cmd = get_cmd_ptr(p, NULL);
+       if (!cmd)
+               return -E_BAD_CMD;
+       ret = check_perms(cc->u->perms, cmd);
        if (ret < 0)
-               goto out;
+               return ret;
        end = iov->iov_base + iov->iov_len;
        for (i = 0; p < end; i++)
                p += strlen(p) + 1;
@@ -863,9 +882,15 @@ static int parse_sb_command(struct command_context *cc, struct iovec *iov)
                p += strlen(p) + 1;
        }
        cc->argv[cc->argc] = NULL;
-       ret = cc->argc;
-out:
-       free(iov->iov_base);
+       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cmd->name,
+               cc->u->name, peername);
+       ret = cmd->handler(cc);
+       free_argv(cc->argv);
+       mutex_lock(mmd_mutex);
+       mmd->num_commands++;
+       if (ret >= 0 && (cmd->perms & AFS_WRITE))
+               mmd->events++;
+       mutex_unlock(mmd_mutex);
        return ret;
 }
 
@@ -986,17 +1011,10 @@ __noreturn void handle_connect(int fd, const char *peername)
        ret = recv_sb(&cc->scc, SBD_COMMAND, MAX_COMMAND_LEN, &iov);
        if (ret < 0)
                goto net_err;
-       ret = parse_sb_command(cc, &iov);
+       ret = run_command(cc, &iov, peername);
+       free(iov.iov_base);
        if (ret < 0)
                goto err_out;
-       cc->argc = ret;
-       PARA_NOTICE_LOG("calling com_%s() for %s@%s\n", cc->cmd->name,
-               cc->u->name, peername);
-       ret = cc->cmd->handler(cc);
-       free_argv(cc->argv);
-       mutex_lock(mmd_mutex);
-       mmd->num_commands++;
-       mutex_unlock(mmd_mutex);
        if (ret >= 0)
                goto out;
 err_out:
@@ -1008,8 +1026,6 @@ out:
        free(buf);
        free(command);
        mutex_lock(mmd_mutex);
-       if (cc->cmd && (cc->cmd->perms & AFS_WRITE) && ret >= 0)
-               mmd->events++;
        mmd->active_connections--;
        mutex_unlock(mmd_mutex);
        if (ret >= 0) {