#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};
{
}
-static void mmd_dup(struct misc_meta_data *new_mmd)
-{
- mutex_lock(mmd_mutex);
- *new_mmd = *mmd;
- mutex_unlock(mmd_mutex);
-}
-
/*
- * Compute human readable string containing vss status for given integer value.
+ * Compute human readable vss status text.
*
- * We don't want to use vss_playing() and friends here because we take a
- * snapshot of the mmd struct and use the copy for computing the state of the
- * vss. If the real data were used, we would take the mmd lock for a rather
- * long time or risk to get an inconsistent view.
+ * We can't call vss_playing() and friends here because those functions read
+ * the flags from the primary mmd structure, so calling them from command
+ * handler context would require to take the mmd lock. At the time the function
+ * is called we already took a copy of the mmd structure and want to use the
+ * flags value of the copy for computing the vss status text.
*/
static char *vss_status_tohuman(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(¤t_time);
/*
* The calls to WRITE_STATUS_ITEM() below never fail because
* 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);
static int check_sender_args(int argc, char * const * argv, struct sender_command_data *scd)
{
int i;
- /* this has to match sender.h */
- const char *subcmds[] = {"add", "delete", "allow", "deny", "on", "off", NULL};
+ const char *subcmds[] = {SENDER_SUBCOMMANDS 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]))
if (!senders[scd->sender_num].client_cmds[scd->cmd_num])
return -E_SENDER_CMD;
switch (scd->cmd_num) {
- case SENDER_ON:
- case SENDER_OFF:
+ case SENDER_on:
+ case SENDER_off:
if (argc != 3)
return -E_COMMAND_SYNTAX;
break;
- case SENDER_DENY:
- case SENDER_ALLOW:
+ case SENDER_deny:
+ case SENDER_allow:
if (argc != 4 || parse_cidr(argv[3], scd->host,
sizeof(scd->host), &scd->netmask) == NULL)
return -E_COMMAND_SYNTAX;
break;
- case SENDER_ADD:
- case SENDER_DELETE:
+ case SENDER_add:
+ case SENDER_delete:
if (argc != 4)
return -E_COMMAND_SYNTAX;
return parse_fec_url(argv[3], scd);
}
switch (scd.cmd_num) {
- case SENDER_ADD:
- case SENDER_DELETE:
+ case SENDER_add:
+ case SENDER_delete:
assert(senders[scd.sender_num].resolve_target);
ret = senders[scd.sender_num].resolve_target(cc->argv[3], &scd);
if (ret < 0)
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);
}
if (i != cc->argc)
return -E_COMMAND_SYNTAX;
for (;;) {
- mmd_dup(nmmd);
+ /*
+ * Copy the mmd structure to minimize the time we hold the mmd
+ * lock.
+ */
+ mutex_lock(mmd_mutex);
+ *nmmd = *mmd;
+ mutex_unlock(mmd_mutex);
ret = get_status(nmmd, parser_friendly, &s);
ret = send_sb(&cc->scc, s, ret, SBD_OUTPUT, false);
if (ret < 0)
#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;
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;
}
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:
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) {