This makes use of the infrastructure introduced in the previous patch.
However, the implementation of the ll command for para_server is more
involved than its audiod counterpart because in the server case we
have to tell two different processes (server and afs) to change their
log level while the calling process, the command handler, does not
need to set the loglevel because it is about to exit anyway.
For the inter-process communication we introduce a new field in the
mmd shared memory area so that command handlers can read the current
value or set a new value. The log level propagates from there via
daemon_set_loglevel() to the server and afs processes during each
iteration of the scheduler loop where para_log() will pick it up to
set the log level threshold for subsequent log events.
The si command handler currently refers to the argument of the
--loglevel server option to include the log level in its output. With
dynamic log levels this no longer works because it always prints the
value from the command line or the config file rather than the run
time log level. Since the new ll command also prints the loglevel
when it is executed with no arguments, we simply remove this line
from the si output and hope that nobody cares.
The si command handler was the last user of the ENUM_STRING_VAL macro
in command.c. Removing the macro also allows us to make CMD_PTR local
to server.c and to remove the lopsub definitions of the server suite
from command.c. However, we still include the lopsub definitions of
the server *command* suite (server_cmd.lsg.h) of course.
We let any authenticated user run the command with no arguments to
report the current loglevel but require full privileges to change
the loglevel. Thus, the check for sufficient privileges needs to be
performed in the command handler.
#include "afs.h"
#include "net.h"
#include "server.h"
+#include "daemon.h"
#include "ipc.h"
#include "list.h"
#include "sched.h"
}, s);
}
+static int afs_select(int max_fileno, fd_set *readfds, fd_set *writefds,
+ struct timeval *timeout_tv)
+{
+ mutex_lock(mmd_mutex);
+ daemon_set_loglevel(mmd->loglevel);
+ mutex_unlock(mmd_mutex);
+ return para_select(max_fileno + 1, readfds, writefds, timeout_tv);
+}
+
/**
* Initialize the audio file selector process.
*
PARA_INFO_LOG("server_socket: %d\n", server_socket);
init_admissible_files(OPT_STRING_VAL(AFS_INITIAL_MODE));
register_command_task(&s);
+ s.select_function = afs_select;
s.default_timeout.tv_sec = 0;
s.default_timeout.tv_usec = 999 * 1000;
ret = write(socket_fd, "\0", 1);
static struct i9e_completer completers[];
+static void ll_completer(struct i9e_completion_info *ci,
+ struct i9e_completion_result *cr)
+{
+ i9e_ll_completer(ci, cr);
+}
+
static void help_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
{
#include <netdb.h>
#include <lopsub.h>
-#include "server.lsg.h"
#include "para.h"
#include "error.h"
#include "lsu.h"
"server_pid: %d\n"
"afs_pid: %d\n"
"connections (active/accepted/total): %u/%u/%u\n"
- "current loglevel: %s\n"
"supported audio formats: %s\n",
ut, mmd->num_played,
(int)getppid(),
mmd->active_connections,
mmd->num_commands,
mmd->num_connects,
- ENUM_STRING_VAL(LOGLEVEL),
AUDIO_FORMAT_HANDLERS
);
mutex_unlock(mmd_mutex);
}
EXPORT_SERVER_CMD_HANDLER(hup);
+static int com_ll(struct command_context *cc, struct lls_parse_result *lpr)
+{
+ unsigned ll, perms;
+ char *errctx;
+ const char *sev[] = {SEVERITIES}, *arg;
+ int ret = lls(lls_check_arg_count(lpr, 0, 1, &errctx));
+
+ if (ret < 0) {
+ send_errctx(cc, errctx);
+ return ret;
+ }
+ if (lls_num_inputs(lpr) == 0) { /* reporting is an unprivileged op. */
+ const char *severity;
+ mutex_lock(mmd_mutex);
+ severity = sev[mmd->loglevel];
+ mutex_unlock(mmd_mutex);
+ return send_sb_va(&cc->scc, SBD_OUTPUT, "%s\n", severity);
+ }
+ /*
+ * Changing the loglevel changes the state of both the afs and the vss,
+ * so we require both AFS_WRITE and VSS_WRITE.
+ */
+ perms = AFS_WRITE | VSS_WRITE;
+ if ((cc->u->perms & perms) != perms)
+ return -ERRNO_TO_PARA_ERROR(EPERM);
+ arg = lls_input(0, lpr);
+ for (ll = 0; ll < NUM_LOGLEVELS; ll++)
+ if (!strcmp(arg, sev[ll]))
+ break;
+ if (ll >= NUM_LOGLEVELS)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ PARA_INFO_LOG("new log level: %s\n", sev[ll]);
+ /* Ask the server and afs processes to adjust their log level. */
+ mutex_lock(mmd_mutex);
+ mmd->loglevel = ll;
+ mutex_unlock(mmd_mutex);
+ return 1;
+}
+EXPORT_SERVER_CMD_HANDLER(ll);
+
static int com_term(__a_unused struct command_context *cc,
__a_unused struct lls_parse_result *lpr)
{
*
* \param ci See struct \ref i9e_completer.
* \param cr See struct \ref i9e_completer.
+ *
+ * This is used by para_client and para_audioc which need the same completion
+ * primitive for the ll server/audiod command. Both define their own completer
+ * which is implemented as a trivial wrapper that calls this function.
*/
void i9e_ll_completer(struct i9e_completion_info *ci,
struct i9e_completion_result *cr)
[subcommand ll]
purpose = Query or set the log level of the daemon
+ m4_ifelse(SUITE, `server_cmd', `aux_info = NO_PERMISSION_REQUIRED')
non-opts-name = [severity]
[description]
If no argument is given, the command prints the severity string (one
$(call SAY, M4 $<)
$(M4) -Pg -I $(lls_m4_include_dir) -D GIT_VERSION=$(GIT_VERSION) \
-D COPYRIGHT_YEAR=$(COPYRIGHT_YEAR) -D LOGLEVELS=$(LOGLEVELS) \
+ -D SUITE=$(basename $(notdir $<)) \
$< > $@
$(lls_suite_dir)/%.lsg.c: $(lls_suite_dir)/%.suite
playlists. Otherwise only the given tables are created.
[/description]
+m4_include(`com_ll.m4')
+
[subcommand jmp]
purpose = reposition the current stream
non-opts-name = n
mmd->active_connections = 0;
mmd->vss_status_flags = VSS_NEXT;
mmd->new_vss_status_flags = VSS_NEXT;
+ mmd->loglevel = OPT_UINT32_VAL(LOGLEVEL);
return;
destroy_mmd_mutex:
mutex_destroy(mmd_mutex);
exit(EXIT_FAILURE);
}
+/** Get a reference to the supercommand of para_server. */
+#define CMD_PTR (lls_cmd(0, server_suite))
+
/**
* (Re-)read the server configuration files.
*
{
int ret;
+ daemon_set_loglevel(mmd->loglevel);
status_refresh();
mutex_unlock(mmd_mutex);
ret = para_select(max_fileno + 1, readfds, writefds, timeout_tv);
char afs_mode_string[MAXLINE];
/** Used by the sender command. */
struct sender_command_data sender_cmd_data;
+ /** Set by the ll command. */
+ int loglevel;
/** Describes the current audio file. */
struct audio_file_data afd;
};
extern pid_t afs_pid;
extern struct lls_parse_result *server_lpr;
-/**
- * Get a reference to the supercommand of para_server.
- *
- * This is needed for parsing the command line and for the ENUM_STRING_VAL()
- * macro below. The latter macro is used in command.c, so CMD_PTR() can not
- * be made local to server.c.
- */
-#define CMD_PTR (lls_cmd(0, server_suite))
-
/** Get the parse result of an option to para_server. */
#define OPT_RESULT(_name) (lls_opt_result( \
LSG_SERVER_PARA_SERVER_OPT_ ## _name, server_lpr))
/** The (first) argument to a server option of type int32. */
#define OPT_INT32_VAL(_name) (lls_int32_val(0, OPT_RESULT(_name)))
-/** Get the string which corresponds to an enum constant. */
-#define ENUM_STRING_VAL(_name) (lls_enum_string_val(OPT_UINT32_VAL(_name), \
- lls_opt(LSG_SERVER_PARA_SERVER_OPT_ ## _name, CMD_PTR)))
-
int handle_connect(int fd);
void parse_config_or_die(bool reload);
char *server_get_tasks(void);