/*
- * Copyright (C) 1997-2014 Andre Noll <maan@tuebingen.mpg.de>
+ * Copyright (C) 1997 Andre Noll <maan@tuebingen.mpg.de>
*
* Licensed under the GPL v2. For licencing details see COPYING.
*/
#include "fd.h"
#include "ipc.h"
#include "user_list.h"
-#include "server_command_list.h"
-#include "afs_command_list.h"
+#include "server.command_list.h"
+#include "afs.command_list.h"
#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);
*
* The nonblock flag must be disabled for the file descriptor given by \a scc.
*
- * Stream cipher encryption is automatically activated if neccessary via the
+ * Stream cipher encryption is automatically activated if necessary via the
* sideband transformation, depending on the value of \a band.
*
* \return Standard.
}
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)
for (i = 0; i < 10; i++) {
mutex_lock(mmd_mutex);
if (mmd->sender_cmd_data.cmd_num >= 0) {
+ /* another sender command is active, retry in 100ms */
+ struct timespec ts = {.tv_nsec = 100 * 1000 * 1000};
mutex_unlock(mmd_mutex);
- usleep(100 * 1000);
+ nanosleep(&ts, NULL);
continue;
}
mmd->sender_cmd_data = scd;
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) \
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.
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)
promille += 1000 * i / mmd->afd.afhi.seconds_total;
if (promille < 0)
promille = 0;
- if (promille > 1000) {
+ if (promille > 1000) {
mmd->new_vss_status_flags |= VSS_NEXT;
goto out;
}
if (i > 100)
i = 100;
PARA_INFO_LOG("jumping to %lu%%\n", i);
- mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50)/ 100;
- PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n",
+ mmd->repos_request = (mmd->afd.afhi.chunks_total * i + 50) / 100;
+ PARA_INFO_LOG("sent: %lu, offset before jmp: %lu\n",
mmd->chunks_sent, mmd->offset);
mmd->new_vss_status_flags |= VSS_REPOS;
mmd->new_vss_status_flags &= ~VSS_NEXT;
return ret;
}
+static int com_tasks(struct command_context *cc)
+{
+ char *tl = server_get_tasks();
+ int ret = 1;
+
+ if (tl)
+ ret = send_sb(&cc->scc, tl, strlen(tl), SBD_OUTPUT, false);
+ return ret;
+}
+
/*
* 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;
#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;
}
* \param fd The file descriptor to send output to.
* \param peername Identifies the connecting peer.
*
- * Whenever para_server accepts an incoming tcp connection on
- * the port it listens on, it forks and the resulting child
- * calls this function.
+ * Whenever para_server accepts an incoming tcp connection on the port it
+ * listens on, it forks and the resulting child calls this function.
*
- * An RSA-based challenge/response is used to authenticate
- * the peer. It that authentication succeeds, a random
- * session key is generated and sent back to the peer,
- * encrypted with its RSA public key. From this point on,
- * all transfers are crypted with this session key.
+ * An RSA-based challenge/response is used to authenticate the peer. It that
+ * authentication succeeds, a random session key is generated and sent back to
+ * the peer, encrypted with its RSA public key. From this point on, all
+ * transfers are crypted with this session key.
*
- * Next it is checked if the peer supplied a valid server command or a command
- * for the audio file selector. If yes, and if the user has sufficient
+ * Next it is checked if the peer supplied a valid server command or a command
+ * for the audio file selector. If yes, and if the user has sufficient
* permissions to execute that command, the function calls the corresponding
* command handler which does argument checking and further processing.
*
- * In order to cope with a DOS attacks, a timeout is set up
- * which terminates the function if the connection was not
- * authenticated when the timeout expires.
+ * In order to cope with a DOS attacks, a timeout is set up which terminates
+ * the function if the connection was not authenticated when the timeout
+ * expires.
*
* \sa alarm(2), crypt.c, crypt.h
*/
goto net_err;
/* send Welcome message */
ret = write_va_buffer(fd, "This is para_server, version "
- PACKAGE_VERSION ".\n"
+ PACKAGE_VERSION ".\n"
"Features: sideband,aes_ctr128\n"
);
if (ret < 0)
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) {