diff --git a/man/run0.xml b/man/run0.xml index 16438b54cbf..422ca6bebbf 100644 --- a/man/run0.xml +++ b/man/run0.xml @@ -192,6 +192,21 @@ + + + + + Request allocation of a pseudo TTY for the run0 session (in case + of ), or request passing the caller's STDIO file descriptors directly through + (in case of ). If neither switch is specified, or if both switches are + specified, the mode will be picked automatically: if standard input, standard output and standard + error output are all connected to a TTY then a pseudo TTY is allocated, otherwise the relevant file + descriptors are passed through directly. + + + + + diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index d659937f3bf..41d10357ede 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -290,7 +290,7 @@ static int handle_arg_console(const char *arg) { else if (streq(arg, "passive")) arg_console_mode = CONSOLE_PASSIVE; else if (streq(arg, "pipe")) { - if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO)) + if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO)) 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. " @@ -298,7 +298,7 @@ static int handle_arg_console(const char *arg) { arg_console_mode = CONSOLE_PIPE; } else if (streq(arg, "autopipe")) { - if (isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO)) + if (isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO)) arg_console_mode = CONSOLE_INTERACTIVE; else arg_console_mode = CONSOLE_PIPE; @@ -5981,7 +5981,7 @@ static int run(int argc, char *argv[]) { umask(0022); if (arg_console_mode < 0) - arg_console_mode = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) ? + arg_console_mode = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) ? CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY; if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */ diff --git a/src/run/run.c b/src/run/run.c index 5d2298a5f6c..529e9d1bcce 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -171,6 +171,10 @@ static int help_sudo_mode(void) { if (r < 0) return log_oom(); + /* NB: Let's not go overboard with short options: we try to keep a modicum of compatibility with + * sudo's short switches, hence please do not introduce new short switches unless they have a roughly + * equivalent purpose on sudo. Use long options for everything private to run0. */ + printf("%s [OPTIONS...] COMMAND [ARGUMENTS...]\n" "\n%sElevate privileges interactively.%s\n\n" " -h --help Show this help\n" @@ -188,6 +192,8 @@ static int help_sudo_mode(void) { " -D --chdir=PATH Set working directory\n" " --setenv=NAME[=VALUE] Set environment variable\n" " --background=COLOR Set ANSI color for background\n" + " --pty Request allocation of a pseudo TTY for stdio\n" + " --pipe Request direct pipe for stdio\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -674,7 +680,7 @@ static int parse_argv(int argc, char *argv[]) { /* If we both --pty and --pipe are specified we'll automatically pick --pty if we are connected fully * to a TTY and pick direct fd passing otherwise. This way, we automatically adapt to usage in a shell * pipeline, but we are neatly interactive with tty-level isolation otherwise. */ - arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? + arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT; @@ -770,6 +776,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { ARG_NICE, ARG_SETENV, ARG_BACKGROUND, + ARG_PTY, + ARG_PIPE, }; /* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions @@ -791,6 +799,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { { "chdir", required_argument, NULL, 'D' }, { "setenv", required_argument, NULL, ARG_SETENV }, { "background", required_argument, NULL, ARG_BACKGROUND }, + { "pty", no_argument, NULL, ARG_PTY }, + { "pipe", no_argument, NULL, ARG_PIPE }, {}, }; @@ -883,6 +893,20 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { break; + case ARG_PTY: + if (IN_SET(arg_stdio, ARG_STDIO_DIRECT, ARG_STDIO_AUTO)) /* if --pipe is already used, upgrade to auto mode */ + arg_stdio = ARG_STDIO_AUTO; + else + arg_stdio = ARG_STDIO_PTY; + break; + + case ARG_PIPE: + if (IN_SET(arg_stdio, ARG_STDIO_PTY, ARG_STDIO_AUTO)) /* If --pty is already used, upgrade to auto mode */ + arg_stdio = ARG_STDIO_AUTO; + else + arg_stdio = ARG_STDIO_DIRECT; + break; + case '?': return -EINVAL; @@ -913,7 +937,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { arg_wait = true; arg_aggressive_gc = true; - arg_stdio = isatty_safe(STDIN_FILENO) && isatty(STDOUT_FILENO) && isatty(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT; + if (IN_SET(arg_stdio, ARG_STDIO_NONE, ARG_STDIO_AUTO)) + arg_stdio = isatty_safe(STDIN_FILENO) && isatty_safe(STDOUT_FILENO) && isatty_safe(STDERR_FILENO) ? ARG_STDIO_PTY : ARG_STDIO_DIRECT; + arg_expand_environment = false; arg_send_sighup = true; @@ -1181,7 +1207,7 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p if (r < 0) return bus_log_create_error(r); - send_term = isatty_safe(STDIN_FILENO) || isatty(STDOUT_FILENO) || isatty(STDERR_FILENO); + send_term = isatty_safe(STDIN_FILENO) || isatty_safe(STDOUT_FILENO) || isatty_safe(STDERR_FILENO); } if (send_term) { diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh index 3ef9c88d792..903ddde70ef 100755 --- a/test/units/TEST-74-AUX-UTILS.run.sh +++ b/test/units/TEST-74-AUX-UTILS.run.sh @@ -261,4 +261,14 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the assert_eq "$(run0 -D / pwd)" "/" assert_eq "$(run0 --user=testuser pwd)" "/home/testuser" assert_eq "$(run0 -D / --user=testuser pwd)" "/" + + # Verify that all combinations of --pty/--pipe come to the sam results + assert_eq "$(run0 echo -n foo)" "foo" + assert_eq "$(run0 --pty echo -n foo)" "foo" + assert_eq "$(run0 --pipe echo -n foo)" "foo" + assert_eq "$(run0 --pipe --pty echo -n foo)" "foo" + + # Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not + assert_neq "$(run0 --pty tty < /dev/null)" "not a tty" + assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty" fi diff --git a/test/units/util.sh b/test/units/util.sh index 80ae8ffc5b5..51e0ad1ec0e 100755 --- a/test/units/util.sh +++ b/test/units/util.sh @@ -39,6 +39,15 @@ assert_eq() {( fi )} +assert_neq() {( + set +ex + + if [[ "${1?}" = "${2?}" ]]; then + echo "FAIL: not expected: '$2' actual: '$1'" >&2 + exit 1 + fi +)} + assert_le() {( set +ex