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