]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: allow to redirect confirmation messages to a different console
authorFranck Bui <fbui@suse.com>
Wed, 2 Nov 2016 09:38:22 +0000 (10:38 +0100)
committerFranck Bui <fbui@suse.com>
Thu, 17 Nov 2016 17:16:16 +0000 (18:16 +0100)
It's rather hard to parse the confirmation messages (enabled with
systemd.confirm_spawn=true) amongst the status messages and the kernel
ones (if enabled).

This patch gives the possibility to the user to redirect the confirmation
message to a different virtual console, either by giving its name or its path,
so those messages are separated from the other ones and easier to read.

man/systemd.xml
src/core/execute.c
src/core/execute.h
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/mount.c
src/core/service.c
src/core/socket.c
src/core/swap.c

index 7f24a874ed312d85e238390a85bb3bf849383a46..fb67ba7cb3a980265427bc52be385cedea85e1f1 100644 (file)
       <varlistentry>
         <term><varname>systemd.confirm_spawn=</varname></term>
 
-        <listitem><para>Takes a boolean argument. If
-        <option>yes</option>, the system manager (PID 1) asks for
-        confirmation when spawning processes. Defaults to
-        <option>no</option>.</para></listitem>
+        <listitem><para>Takes a boolean argument or a path to the
+        virtual console where the confirmation messages should be
+        emitted. If <option>yes</option>, the system manager (PID 1)
+        asks for confirmation when spawning processes using
+        <option>/dev/console</option>. If a path or a console name
+        (such as <literal>ttyS0</literal>) is provided, the virtual
+        console pointed to by this path or described by the give name
+        will be used instead. Defaults to <option>no</option>.</para></listitem>
       </varlistentry>
 
       <varlistentry>
index f666f7c6ce0de9373b38666cdfcf42456f2eb087..43a0a5cafd75f9582a635dade6e709591f85a3a6 100644 (file)
@@ -624,7 +624,7 @@ static int chown_terminal(int fd, uid_t uid) {
         return 0;
 }
 
-static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
+static int setup_confirm_stdio(const char *vc, int *_saved_stdin, int *_saved_stdout) {
         _cleanup_close_ int fd = -1, saved_stdin = -1, saved_stdout = -1;
         int r;
 
@@ -639,12 +639,7 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
         if (saved_stdout < 0)
                 return -errno;
 
-        fd = acquire_terminal(
-                        "/dev/console",
-                        false,
-                        false,
-                        false,
-                        DEFAULT_CONFIRM_USEC);
+        fd = acquire_terminal(vc, false, false, false, DEFAULT_CONFIRM_USEC);
         if (fd < 0)
                 return fd;
 
@@ -674,13 +669,13 @@ static int setup_confirm_stdio(int *_saved_stdin, int *_saved_stdout) {
         return 0;
 }
 
-_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
+_printf_(2, 3) static int write_confirm_message(const char *vc, const char *format, ...) {
         _cleanup_close_ int fd = -1;
         va_list ap;
 
         assert(format);
 
-        fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+        fd = open_terminal(vc, O_WRONLY|O_NOCTTY|O_CLOEXEC);
         if (fd < 0)
                 return fd;
 
@@ -713,11 +708,11 @@ static int restore_confirm_stdio(int *saved_stdin, int *saved_stdout) {
         return r;
 }
 
-static int ask_for_confirmation(char *response, char **argv) {
+static int ask_for_confirmation(const char *vc, char *response, char **argv) {
         int saved_stdout = -1, saved_stdin = -1, r;
         _cleanup_free_ char *line = NULL;
 
-        r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
+        r = setup_confirm_stdio(vc, &saved_stdin, &saved_stdout);
         if (r < 0)
                 return r;
 
@@ -2314,20 +2309,21 @@ static int exec_child(
 
         exec_context_tty_reset(context, params);
 
-        if (params->flags & EXEC_CONFIRM_SPAWN) {
+        if (params->confirm_spawn) {
+                const char *vc = params->confirm_spawn;
                 char response;
 
-                r = ask_for_confirmation(&response, argv);
+                r = ask_for_confirmation(vc, &response, argv);
                 if (r == -ETIMEDOUT)
-                        write_confirm_message("Confirmation question timed out, assuming positive response.\n");
+                        write_confirm_message(vc, "Confirmation question timed out, assuming positive response.\n");
                 else if (r < 0)
-                        write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r));
+                        write_confirm_message(vc, "Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r));
                 else if (response == 's') {
-                        write_confirm_message("Skipping execution.\n");
+                        write_confirm_message(vc, "Skipping execution.\n");
                         *exit_status = EXIT_CONFIRM;
                         return -ECANCELED;
                 } else if (response == 'n') {
-                        write_confirm_message("Failing execution.\n");
+                        write_confirm_message(vc, "Failing execution.\n");
                         *exit_status = 0;
                         return 0;
                 }
index 56f880cffef93993937dbea0b7fbad3961204d33..cd7cbcc5ab45a19630c980cae9277a91f8f4e50e 100644 (file)
@@ -226,16 +226,15 @@ static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) {
 }
 
 typedef enum ExecFlags {
-        EXEC_CONFIRM_SPAWN     = 1U << 0,
-        EXEC_APPLY_PERMISSIONS = 1U << 1,
-        EXEC_APPLY_CHROOT      = 1U << 2,
-        EXEC_APPLY_TTY_STDIN   = 1U << 3,
+        EXEC_APPLY_PERMISSIONS = 1U << 0,
+        EXEC_APPLY_CHROOT      = 1U << 1,
+        EXEC_APPLY_TTY_STDIN   = 1U << 2,
 
         /* The following are not used by execute.c, but by consumers internally */
-        EXEC_PASS_FDS          = 1U << 4,
-        EXEC_IS_CONTROL        = 1U << 5,
-        EXEC_SETENV_RESULT     = 1U << 6,
-        EXEC_SET_WATCHDOG      = 1U << 7,
+        EXEC_PASS_FDS          = 1U << 3,
+        EXEC_IS_CONTROL        = 1U << 4,
+        EXEC_SETENV_RESULT     = 1U << 5,
+        EXEC_SET_WATCHDOG      = 1U << 6,
 } ExecFlags;
 
 struct ExecParameters {
@@ -255,6 +254,8 @@ struct ExecParameters {
 
         const char *runtime_prefix;
 
+        const char *confirm_spawn;
+
         usec_t watchdog_usec;
 
         int *idle_pipe;
index f5f7df838d9849c534226c0ab10e456b694b3e79..5f9b1acad3861e885e0f453f8825347168d81fbd 100644 (file)
@@ -68,6 +68,7 @@
 #include "mount-setup.h"
 #include "pager.h"
 #include "parse-util.h"
+#include "path-util.h"
 #include "proc-cmdline.h"
 #include "process-util.h"
 #include "raw-clone.h"
@@ -104,7 +105,7 @@ static bool arg_dump_core = true;
 static int arg_crash_chvt = -1;
 static bool arg_crash_shell = false;
 static bool arg_crash_reboot = false;
-static bool arg_confirm_spawn = false;
+static char *arg_confirm_spawn = NULL;
 static ShowStatus arg_show_status = _SHOW_STATUS_UNSET;
 static bool arg_switched_root = false;
 static bool arg_no_pager = false;
@@ -294,6 +295,28 @@ static int parse_crash_chvt(const char *value) {
         return 0;
 }
 
+static int parse_confirm_spawn(const char *value, char **console) {
+        char *s;
+        int r;
+
+        r = value ? parse_boolean(value) : 1;
+        if (r == 0) {
+                *console = NULL;
+                return 0;
+        }
+
+        if (r > 0) /* on with default tty */
+                s = strdup("/dev/console");
+        else if (is_path(value)) /* on with fully qualified path */
+                s = strdup(value);
+        else /* on with only a tty file name, not a fully qualified path */
+                s = strjoin("/dev/", value);
+        if (!s)
+                return -ENOMEM;
+        *console = s;
+        return 0;
+}
+
 static int set_machine_id(const char *m) {
         sd_id128_t t;
         assert(m);
@@ -355,11 +378,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
         } else if (streq(key, "systemd.confirm_spawn") && value) {
 
-                r = parse_boolean(value);
+                arg_confirm_spawn = mfree(arg_confirm_spawn);
+
+                r = parse_confirm_spawn(value, &arg_confirm_spawn);
                 if (r < 0)
-                        log_warning("Failed to parse confirm spawn switch %s. Ignoring.", value);
-                else
-                        arg_confirm_spawn = r;
+                        log_warning_errno(r, "Failed to parse confirm_spawn switch %s. Ignoring.", value);
 
         } else if (streq(key, "systemd.show_status") && value) {
 
@@ -952,12 +975,11 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_CONFIRM_SPAWN:
-                        r = optarg ? parse_boolean(optarg) : 1;
-                        if (r < 0) {
-                                log_error("Failed to parse confirm spawn boolean %s.", optarg);
-                                return r;
-                        }
-                        arg_confirm_spawn = r;
+                        arg_confirm_spawn = mfree(arg_confirm_spawn);
+
+                        r = parse_confirm_spawn(optarg, &arg_confirm_spawn);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse confirm spawn option: %m");
                         break;
 
                 case ARG_SHOW_STATUS:
@@ -1991,6 +2013,7 @@ finish:
                 arg_default_rlimit[j] = mfree(arg_default_rlimit[j]);
 
         arg_default_unit = mfree(arg_default_unit);
+        arg_confirm_spawn = mfree(arg_confirm_spawn);
         arg_join_controllers = strv_free_free(arg_join_controllers);
         arg_default_environment = strv_free(arg_default_environment);
         arg_syscall_archs = set_free(arg_syscall_archs);
index d9f772d1685f16ad2745a188dcb88c5f05e28780..6ffbbd73894ca7ee0c214e1767d1a551eed1b49c 100644 (file)
@@ -2974,7 +2974,7 @@ void manager_check_finished(Manager *m) {
         manager_close_idle_pipe(m);
 
         /* Turn off confirm spawn now */
-        m->confirm_spawn = false;
+        m->confirm_spawn = NULL;
 
         /* No need to update ask password status when we're going non-interactive */
         manager_close_ask_password(m);
@@ -3159,6 +3159,49 @@ static bool manager_get_show_status(Manager *m, StatusType type) {
         return false;
 }
 
+const char *manager_get_confirm_spawn(Manager *m) {
+        static int last_errno = 0;
+        const char *vc = m->confirm_spawn;
+        struct stat st;
+        int r;
+
+        /* Here's the deal: we want to test the validity of the console but don't want
+         * PID1 to go through the whole console process which might block. But we also
+         * want to warn the user only once if something is wrong with the console so we
+         * cannot do the sanity checks after spawning our children. So here we simply do
+         * really basic tests to hopefully trap common errors.
+         *
+         * If the console suddenly disappear at the time our children will really it
+         * then they will simply fail to acquire it and a positive answer will be
+         * assumed. New children will fallback to /dev/console though.
+         *
+         * Note: TTYs are devices that can come and go any time, and frequently aren't
+         * available yet during early boot (consider a USB rs232 dongle...). If for any
+         * reason the configured console is not ready, we fallback to the default
+         * console. */
+
+        if (!vc || path_equal(vc, "/dev/console"))
+                return vc;
+
+        r = stat(vc, &st);
+        if (r < 0)
+                goto fail;
+
+        if (!S_ISCHR(st.st_mode)) {
+                errno = ENOTTY;
+                goto fail;
+        }
+
+        last_errno = 0;
+        return vc;
+fail:
+        if (last_errno != errno) {
+                last_errno = errno;
+                log_warning_errno(errno, "Failed to open %s: %m, using default console", vc);
+        }
+        return "/dev/console";
+}
+
 void manager_set_first_boot(Manager *m, bool b) {
         assert(m);
 
index 35172fdba9886d6bee05f98fe0b35a84c6091be2..8b3db6e48b015b70813f8f4b8b4df48c6259dee0 100644 (file)
@@ -246,7 +246,7 @@ struct Manager {
         uint8_t return_value;
 
         ShowStatus show_status;
-        bool confirm_spawn;
+        char *confirm_spawn;
         bool no_console_output;
 
         ExecOutput default_std_output, default_std_error;
@@ -403,3 +403,5 @@ void manager_deserialize_gid_refs_one(Manager *m, const char *value);
 
 const char *manager_state_to_string(ManagerState m) _const_;
 ManagerState manager_state_from_string(const char *s) _pure_;
+
+const char *manager_get_confirm_spawn(Manager *m);
index 43e0f1c7466760e20b39fee5ebff50f3e98b3a8f..1c2be28d55d81d1bc343aaf307bf29f1f37d20fb 100644 (file)
@@ -747,7 +747,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
                 return r;
 
         exec_params.environment = UNIT(m)->manager->environment;
-        exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
+        exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(m)->manager);
         exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported;
         exec_params.cgroup_path = UNIT(m)->cgroup_path;
         exec_params.cgroup_delegate = m->cgroup_context.delegate;
index 7aa1fba5720a276f6bac5939e2ed6a5bdf4a42c4..9ad4cf5070db3af282a5f2b4eb1f2597607bb760 100644 (file)
@@ -1335,7 +1335,7 @@ static int service_spawn(
         exec_params.fds = fds;
         exec_params.fd_names = fd_names;
         exec_params.n_fds = n_fds;
-        exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
+        exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager);
         exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
         exec_params.cgroup_path = path;
         exec_params.cgroup_delegate = s->cgroup_context.delegate;
index ebacd74a47baeb3327ad1efb678f5361c72415a6..1a53d47f2178d0665da4c431d4fed89464664c75 100644 (file)
@@ -1778,7 +1778,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
 
         exec_params.argv = argv;
         exec_params.environment = UNIT(s)->manager->environment;
-        exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
+        exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager);
         exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
         exec_params.cgroup_path = UNIT(s)->cgroup_path;
         exec_params.cgroup_delegate = s->cgroup_context.delegate;
index b870ac88e3360457c14df1c868c479bf6922ae74..bf404db8c30e9982aae612e717ebd90b62c631c5 100644 (file)
@@ -636,7 +636,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
                 goto fail;
 
         exec_params.environment = UNIT(s)->manager->environment;
-        exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
+        exec_params.confirm_spawn = manager_get_confirm_spawn(UNIT(s)->manager);
         exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
         exec_params.cgroup_path = UNIT(s)->cgroup_path;
         exec_params.cgroup_delegate = s->cgroup_context.delegate;