<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>
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;
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;
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;
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;
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;
}
}
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 {
const char *runtime_prefix;
+ const char *confirm_spawn;
+
usec_t watchdog_usec;
int *idle_pipe;
#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"
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;
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);
} 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) {
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:
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);
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);
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);
uint8_t return_value;
ShowStatus show_status;
- bool confirm_spawn;
+ char *confirm_spawn;
bool no_console_output;
ExecOutput default_std_output, default_std_error;
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);
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;
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;
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;
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;