diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 1e7e6a82d5..c8fbb01d00 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -1370,15 +1370,18 @@ Configures how to set up standard input, output and error output for the container payload, as well as the /dev/console device for the container. Takes one of - , , , or - . If , a pseudo-TTY is allocated and made available - as /dev/console in the container. It is then bi-directionally connected to the - standard input and output passed to systemd-nspawn. is - similar but only the output of the container is propagated and no input from the caller is read. If - , a pseudo TTY is allocated, but it is not connected anywhere. Finally, in - mode no pseudo TTY is allocated, but the standard input, output and error - output file descriptors passed to systemd-nspawn are passed on — as they are — to - the container payload, see the following paragraph. Defaults to if + , , , + or . If , a pseudo-TTY is + allocated and made available as /dev/console in the container. It is then + bi-directionally connected to the standard input and output passed to + systemd-nspawn. is similar but only the output of the + container is propagated and no input from the caller is read. If , a pseudo + TTY is allocated, but it is not connected anywhere. In mode no pseudo TTY is + allocated, but the standard input, output and error output file descriptors passed to + systemd-nspawn are passed on — as they are — to the container payload, see the + following paragraph. Finally, mode operates like + when systemd-nspawn is invoked on a terminal, and + like otherwise. Defaults to if systemd-nspawn is invoked from a terminal, and otherwise. diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index d86dd23185..60d7439fb1 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -53,12 +53,6 @@ int stub_pid1(sd_id128_t uuid) { assert_se(sigfillset(&fullmask) >= 0); assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0); - /* Surrender the terminal this stub may control so that child processes can have a controlling terminal - * without resorting to setsid hacks. */ - r = ioctl(STDIN_FILENO, TIOCNOTTY); - if (r < 0 && errno != ENOTTY) - return log_error_errno(errno, "Failed to surrender controlling terminal: %m"); - pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork child pid: %m"); @@ -66,7 +60,10 @@ int stub_pid1(sd_id128_t uuid) { if (pid == 0) { /* Return in the child */ assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0); - setsid(); + + if (setsid() < 0) + return log_error_errno(errno, "Failed to become session leader in payload process: %m"); + return 0; } @@ -76,6 +73,12 @@ int stub_pid1(sd_id128_t uuid) { (void) close_all_fds(NULL, 0); log_open(); + if (ioctl(STDIN_FILENO, TIOCNOTTY) < 0) { + if (errno != ENOTTY) + log_warning_errno(errno, "Unexpected error from TIOCNOTTY ioctl in init stub process, ignoring: %m"); + } else + log_warning("Expected TIOCNOTTY to fail, but it succeeded in init stub process, ignoring."); + /* Flush out /proc/self/environ, so that we don't leak the environment from the host into the container. Also, * set $container= and $container_uuid= so that clients in the container that query it from /proc/1/environ * find them set. */ diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 38339478e0..11a82090b0 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -11,10 +11,12 @@ #endif #include #include +#include #include #include #include #include +#include #include #include "sd-bus.h" @@ -254,10 +256,11 @@ STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep); static int handle_arg_console(const char *arg) { if (streq(arg, "help")) { - puts("interactive\n" - "read-only\n" + puts("autopipe\n" + "interactive\n" "passive\n" - "pipe"); + "pipe\n" + "read-only"); return 0; } @@ -267,9 +270,20 @@ static int handle_arg_console(const char *arg) { arg_console_mode = CONSOLE_READ_ONLY; else if (streq(arg, "passive")) arg_console_mode = CONSOLE_PASSIVE; - else if (streq(arg, "pipe")) + else if (streq(arg, "pipe")) { + if (isatty(STDIN_FILENO) > 0 && isatty(STDOUT_FILENO) > 0) + log_full(arg_quiet ? LOG_DEBUG : LOG_NOTICE, + "Console mode 'pipe' selected, but standard input/output are connected to an interactive TTY. " + "Most likely you want to use 'interactive' console mode for proper interactivity and shell job control. " + "Proceeding anyway."); + arg_console_mode = CONSOLE_PIPE; - else + } else if (streq(arg, "autopipe")) { + if (isatty(STDIN_FILENO) > 0 && isatty(STDOUT_FILENO) > 0) + arg_console_mode = CONSOLE_INTERACTIVE; + else + arg_console_mode = CONSOLE_PIPE; + } else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown console mode: %s", optarg); arg_settings_mask |= SETTING_CONSOLE_MODE; @@ -2269,10 +2283,12 @@ static int setup_pts(const char *dest) { } static int setup_stdio_as_dev_console(void) { - int terminal; + _cleanup_close_ int terminal = -1; int r; - terminal = open_terminal("/dev/console", O_RDWR); + /* We open the TTY in O_NOCTTY mode, so that we do not become controller yet. We'll do that later + * explicitly, if we are configured to. */ + terminal = open_terminal("/dev/console", O_RDWR|O_NOCTTY); if (terminal < 0) return log_error_errno(terminal, "Failed to open console: %m"); @@ -2284,6 +2300,7 @@ static int setup_stdio_as_dev_console(void) { /* invalidates 'terminal' on success and failure */ r = rearrange_stdio(terminal, terminal, terminal); + TAKE_FD(terminal); if (r < 0) return log_error_errno(r, "Failed to move console to stdin/stdout/stderr: %m"); @@ -3366,8 +3383,7 @@ static int inner_child( * wait until the parent is ready with the * setup, too... */ if (!barrier_place_and_sync(barrier)) /* #5 */ - return log_error_errno(SYNTHETIC_ERRNO(ESRCH), - "Parent died too early"); + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Parent died too early"); if (arg_chdir) if (chdir(arg_chdir) < 0) @@ -3379,6 +3395,13 @@ static int inner_child( return r; } + if (arg_console_mode != CONSOLE_PIPE) { + /* So far our pty wasn't controlled by any process. Finally, it's time to change that, if we + * are configured for that. Acquire it as controlling tty. */ + if (ioctl(STDIN_FILENO, TIOCSCTTY) < 0) + return log_error_errno(errno, "Failed to acquire controlling TTY: %m"); + } + log_debug("Inner child completed, invoking payload."); /* Now, explicitly close the log, so that we then can close all remaining fds. Closing the log explicitly first