]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
New server command: ll to change the log level at runtime.
authorAndre Noll <maan@tuebingen.mpg.de>
Tue, 19 Oct 2021 19:41:45 +0000 (21:41 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Tue, 12 Jul 2022 11:52:24 +0000 (13:52 +0200)
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.

afs.c
client.c
command.c
interactive.c
m4/lls/include/com_ll.m4
m4/lls/makefile
m4/lls/server_cmd.suite.m4
server.c
server.h

diff --git a/afs.c b/afs.c
index 710670255b2ec1cf67b9ab4e74823bfe19dd3a02..21ab9ae87251cce0b88741aecc9b0023755521db 100644 (file)
--- a/afs.c
+++ b/afs.c
@@ -24,6 +24,7 @@
 #include "afs.h"
 #include "net.h"
 #include "server.h"
+#include "daemon.h"
 #include "ipc.h"
 #include "list.h"
 #include "sched.h"
@@ -979,6 +980,15 @@ static void register_command_task(struct sched *s)
        }, 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.
  *
@@ -1003,6 +1013,7 @@ __noreturn void afs_init(int socket_fd)
        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);
index 8caf44839919fb33c1368715c528f507ebc7a5c9..63f09659cff6a1875d1fcbe62e2867d1d61ce5d1 100644 (file)
--- a/client.c
+++ b/client.c
@@ -246,6 +246,12 @@ I9E_DUMMY_COMPLETER(init);
 
 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)
 {
index e3f12931fd9d17fe5489636919a14379d60d0969..bb276a89b4acebf391a0875d8f81e1033d2130de 100644 (file)
--- a/command.c
+++ b/command.c
@@ -10,7 +10,6 @@
 #include <netdb.h>
 #include <lopsub.h>
 
-#include "server.lsg.h"
 #include "para.h"
 #include "error.h"
 #include "lsu.h"
@@ -391,7 +390,6 @@ static int com_si(struct command_context *cc,
                "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(),
@@ -399,7 +397,6 @@ static int com_si(struct command_context *cc,
                mmd->active_connections,
                mmd->num_commands,
                mmd->num_connects,
-               ENUM_STRING_VAL(LOGLEVEL),
                AUDIO_FORMAT_HANDLERS
        );
        mutex_unlock(mmd_mutex);
@@ -590,6 +587,46 @@ static int com_hup(__a_unused struct command_context *cc,
 }
 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)
 {
index 209c5a36f70b194308a21d132c14541a46d51025..069c003944ae1801d9ce9e73570ea6f1cea64353 100644 (file)
@@ -811,6 +811,10 @@ create_matches:
  *
  * \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)
index d100dfa860f5a82115edff40b9a93ac85fd94afc..d7576eaefbc28a0acd19772cea2f85599299de09 100644 (file)
@@ -1,5 +1,6 @@
 [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
index daf6de92525d334d3c1791e4d0b53907ddd7fb7f..a13f079e9b60195240bb48f3b847978263a486bc 100644 (file)
@@ -11,6 +11,7 @@ $(lls_suite_dir)/%.suite: $(lls_m4_dir)/%.suite.m4 | $(lls_suite_dir)
        $(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
index f73d66b26a713ce70ed144e38362a73f5ddcf8c1..8200c6249449f81b5743775f04796462fc912b83 100644 (file)
@@ -169,6 +169,8 @@ aux_info_prefix = Permissions:
                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
index b9be84ceadf7b8e7e58f7e9557efe528b0ea25a8..b9026ad6713e665fe461a6b563f364186dc3e1cc 100644 (file)
--- a/server.c
+++ b/server.c
@@ -172,6 +172,7 @@ static void init_ipc_or_die(void)
        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);
@@ -180,6 +181,9 @@ err_out:
        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.
  *
@@ -622,6 +626,7 @@ static int server_select(int max_fileno, fd_set *readfds, fd_set *writefds,
 {
        int ret;
 
+       daemon_set_loglevel(mmd->loglevel);
        status_refresh();
        mutex_unlock(mmd_mutex);
        ret = para_select(max_fileno + 1, readfds, writefds, timeout_tv);
index da75d86bdf191b130d02da12f49172ac5e0482d7..10bb6172860ddb467c3f9fd26ef6ce9e3cb8aab1 100644 (file)
--- a/server.h
+++ b/server.h
@@ -73,6 +73,8 @@ struct misc_meta_data {
        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;
 };
@@ -80,15 +82,6 @@ struct misc_meta_data {
 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))
@@ -105,10 +98,6 @@ extern struct lls_parse_result *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);