core: allow to redirect confirmation messages to a different console
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.
This commit is contained in:
parent
42bf1ae17b
commit
7d5ceb6416
|
@ -940,10 +940,14 @@
|
|||
<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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue