1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 08:55:40 +03:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Lennart Poettering
d585085f57 update TODO 2024-10-25 17:32:19 +02:00
Lennart Poettering
ff4b6a1915 run: drop "-" prefix from command line when generating unit description
Let's not confuse users with the login shell indicator and drop it from
the description. This means a run0 session will now usually show up with
a description of "[run0] /bin/bash" rather than "[run0] -/bin/bash".
2024-10-25 17:32:19 +02:00
Lennart Poettering
d9f68f48f7 run: prefix unit description with our own process name
I think we should try to communicate clearly if something is a run0
session, or a systemd-run invocation. Hence, let's initialize the
description so that the command is prefixed by
program_invocation_short_name.

Effectively this means that our run0 sessions now appear as services
with a description of "[run0] -/bin/bash"
2024-10-25 17:32:19 +02:00
Lennart Poettering
0310b2a60b run: tweak how we name our transient units
The current logic is a bit complex how systemd-run units are called. It
used to be just the unique ID of the dbus connection. Which was nice,
since its system-widely, uniquely assigned to us. But this didn't work
out well, due to direct connections to PID 1 and due to soft reboots.

We nowadays have a better ID to use though, with nicer properties: the
kernel manages a pidfd ID for every process after all, and it's globally
unique, for any process, and regardless of soft reboots. Hence use that
for naming preferably, and just keep one branch with a randomized name
as fallback.
2024-10-25 17:32:19 +02:00
Lennart Poettering
115fac3c29 run0: optionally show superhero emoji on each shell prompt
This makes use of the infra introduced in 229d4a9806 to indicate visually on each prompt that we are in superuser mode temporarily.
pick ad5de3222f userdbctl: add some basic client-side filtering
2024-10-25 17:31:06 +02:00
Lennart Poettering
9d8f5e22f8
Merge pull request #34891 from poettering/run0-pty
run0: add --pty and --pipe switches to force allocation of a pty or pipe
2024-10-25 16:25:01 +02:00
Lennart Poettering
6fb0c52295 ci: add some basic testing of the new --pty and --pipe switches 2024-10-25 14:14:26 +02:00
Lennart Poettering
edd10ab29c run0: add options to force allocation of PTY or of pipe use
Fixes: #33033
2024-10-25 14:14:26 +02:00
Lennart Poettering
988053eac3 tree-wide: use isatty_safe() everywhere 2024-10-25 14:09:38 +02:00
Lennart Poettering
a586f57eb2 update TODO 2024-10-25 13:57:44 +02:00
9 changed files with 197 additions and 82 deletions

8
TODO
View File

@ -129,6 +129,11 @@ Deprecations and removals:
Features:
* $LISTEN_PID, $MAINPID and $SYSTEMD_EXECPID env vars that the service manager
sets should be augmented with $LISTEN_PIDFDID, $MAINPIDFDID and
$SYSTEMD_EXECPIDFD (and similar for other env vars we might send). Also,
MAINPID= in sd_notify() should be augmented with MAINPIDFDID=, and so on.
* port copy.c over to use LabelOps for all labelling.
* port remaining getmntent() users over to libmount. There are subtle
@ -157,9 +162,6 @@ Features:
sd_event_add_child(), and then get rid of many more explicit sigprocmask()
calls.
* maybe set shell.prompt.prefix credential in run0 to some warning emoji,
i.e. ⚠️ or ☢️ or ⚡ or 👊 or 🧑‍🔧 or so.
* introduce new structure Tpm2CombinedPolicy, that combines the various TPm2
policy bits into one structure, i.e. public key info, pcr masks, pcrlock
stuff, pin and so on. Then pass that around in tpm2_seal() and tpm2_unseal().

View File

@ -192,6 +192,35 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--pty</option></term>
<term><option>--pipe</option></term>
<listitem><para>Request allocation of a pseudo TTY for the <command>run0</command> session (in case
of <option>--pty</option>), or request passing the caller's STDIO file descriptors directly through
(in case of <option>--pipe</option>). 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.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--shell-prompt-prefix=<replaceable>STRING</replaceable></option></term>
<listitem><para>Set a shell prompt prefix string. This ultimately controls the
<varname>$SHELL_PROMPT_PREFIX</varname> environment variable for the invoked program, which is
typically imported into the shell prompt. By default if emojis are supported a superhero emoji is
shown (🦸). This default may also be changed (or turned off) by passing the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable to <varname>run0</varname>,
see below. Set to an empty string to disable shell prompt prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--machine=</option></term>
@ -256,7 +285,30 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>By default set to the superhero emoji (if supported), but may be overriden with the
<varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname> environment variable (see below), or the
<option>--shell-prompt-prefix=</option> switch (see above).</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
<para>The following variables may be passed to <command>run0</command>:</para>
<variablelist>
<varlistentry>
<term><varname>$SYSTEMD_RUN_SHELL_PROMPT_PREFIX</varname></term>
<listitem><para>If set, overrides the default shell prompt prefix that <command>run0</command> sets
for the invoked shell (the superhero emoji). Set to an empty string to disable shell prompt
prefixing.</para>
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>

View File

@ -80,6 +80,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = "o",
[SPECIAL_GLYPH_BLUE_CIRCLE] = "o",
[SPECIAL_GLYPH_GREEN_CIRCLE] = "o",
[SPECIAL_GLYPH_SUPERHERO] = "S",
},
/* UTF-8 */
@ -149,6 +150,7 @@ const char* special_glyph_full(SpecialGlyph code, bool force_utf) {
[SPECIAL_GLYPH_YELLOW_CIRCLE] = u8"🟡",
[SPECIAL_GLYPH_BLUE_CIRCLE] = u8"🔵",
[SPECIAL_GLYPH_GREEN_CIRCLE] = u8"🟢",
[SPECIAL_GLYPH_SUPERHERO] = u8"🦸",
},
};

View File

@ -55,6 +55,7 @@ typedef enum SpecialGlyph {
SPECIAL_GLYPH_YELLOW_CIRCLE,
SPECIAL_GLYPH_BLUE_CIRCLE,
SPECIAL_GLYPH_GREEN_CIRCLE,
SPECIAL_GLYPH_SUPERHERO,
_SPECIAL_GLYPH_MAX,
_SPECIAL_GLYPH_INVALID = -EINVAL,
} SpecialGlyph;

View File

@ -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 */

View File

@ -85,6 +85,7 @@ static char *arg_exec_path = NULL;
static bool arg_ignore_failure = false;
static char *arg_background = NULL;
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@ -96,6 +97,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_working_directory, freep);
STATIC_DESTRUCTOR_REGISTER(arg_cmdline, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_exec_path, freep);
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
STATIC_DESTRUCTOR_REGISTER(arg_shell_prompt_prefix, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@ -171,6 +173,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 +194,9 @@ 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"
" --shell-prompt-prefix=PREFIX Set $SHELL_PROMPT_PREFIX\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
@ -674,7 +683,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 +779,9 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_NICE,
ARG_SETENV,
ARG_BACKGROUND,
ARG_PTY,
ARG_PIPE,
ARG_SHELL_PROMPT_PREFIX,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
@ -791,6 +803,9 @@ 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 },
{ "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX },
{},
};
@ -883,6 +898,26 @@ 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 ARG_SHELL_PROMPT_PREFIX:
r = free_and_strdup_warn(&arg_shell_prompt_prefix, optarg);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@ -913,7 +948,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;
@ -993,6 +1030,25 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
log_debug_errno(r, "Unable to get terminal background color, not tinting background: %m");
}
if (!arg_shell_prompt_prefix) {
const char *e = secure_getenv("SYSTEMD_RUN_SHELL_PROMPT_PREFIX");
if (e) {
arg_shell_prompt_prefix = strdup(e);
if (!arg_shell_prompt_prefix)
return log_oom();
} else if (emoji_enabled()) {
arg_shell_prompt_prefix = strjoin(special_glyph(SPECIAL_GLYPH_SUPERHERO), " ");
if (!arg_shell_prompt_prefix)
return log_oom();
}
}
if (!isempty(arg_shell_prompt_prefix)) {
r = strv_env_assign(&arg_environment, "SHELL_PROMPT_PREFIX", arg_shell_prompt_prefix);
if (r < 0)
return log_error_errno(r, "Failed to set $SHELL_PROMPT_PREFIX environment variable: %m");
}
return 1;
}
@ -1181,7 +1237,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) {
@ -1345,70 +1401,38 @@ static int transient_timer_set_properties(sd_bus_message *m) {
return 0;
}
static int make_unit_name(sd_bus *bus, UnitType t, char **ret) {
unsigned soft_reboots_count = 0;
const char *unique, *id;
char *p;
static int make_unit_name(UnitType t, char **ret) {
int r;
assert(bus);
assert(t >= 0);
assert(t < _UNIT_TYPE_MAX);
assert(ret);
r = sd_bus_get_unique_name(bus, &unique);
/* Preferably use our PID + pidfd ID as identifier, if available. It's a boot time unique identifier
* managed by the kernel. Unfortunately only new kernels support this, hence we keep some fallback
* logic in place. */
_cleanup_(pidref_done) PidRef self = PIDREF_NULL;
r = pidref_set_self(&self);
if (r < 0)
return log_error_errno(r, "Failed to get reference to my own process: %m");
r = pidref_acquire_pidfd_id(&self);
if (r < 0) {
log_debug_errno(r, "Failed to acquire pidfd ID of myself, defaulting to randomized unit name: %m");
/* We couldn't get the pidfd id. In that case, just pick a random uuid as name */
sd_id128_t rnd;
/* We couldn't get the unique name, which is a pretty
* common case if we are connected to systemd
* directly. In that case, just pick a random uuid as
* name */
r = sd_id128_randomize(&rnd);
if (r < 0)
return log_error_errno(r, "Failed to generate random run unit name: %m");
if (asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t)) < 0)
return log_oom();
return 0;
}
/* We managed to get the unique name, then let's use that to name our transient units. */
id = startswith(unique, ":1."); /* let' strip the usual prefix */
if (!id)
id = startswith(unique, ":"); /* the spec only requires things to start with a colon, hence
* let's add a generic fallback for that. */
if (!id)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Unique name %s has unexpected format.",
unique);
/* The unique D-Bus names are actually unique per D-Bus instance, so on soft-reboot they will wrap
* and start over since the D-Bus broker is restarted. If there's a failed unit left behind that
* hasn't been garbage collected, we'll conflict. Append the soft-reboot counter to avoid clashing. */
if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = bus_get_property_trivial(
bus, bus_systemd_mgr, "SoftRebootsCount", &error, 'u', &soft_reboots_count);
r = asprintf(ret, "run-r" SD_ID128_FORMAT_STR ".%s", SD_ID128_FORMAT_VAL(rnd), unit_type_to_string(t));
} else
r = asprintf(ret, "run-p" PID_FMT "-i%" PRIu64 ".%s", self.pid, self.fd_id, unit_type_to_string(t));
if (r < 0)
log_debug_errno(r,
"Failed to get SoftRebootsCount property, ignoring: %s",
bus_error_message(&error, r));
}
if (soft_reboots_count > 0) {
if (asprintf(&p, "run-u%s-s%u.%s", id, soft_reboots_count, unit_type_to_string(t)) < 0)
return log_oom();
} else {
p = strjoin("run-u", id, ".", unit_type_to_string(t));
if (!p)
return log_oom();
}
*ret = p;
return 0;
}
@ -1818,7 +1842,7 @@ static int start_transient_service(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle unit name: %m");
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
}
@ -2033,7 +2057,7 @@ static int start_transient_scope(sd_bus *bus) {
if (r < 0)
return log_error_errno(r, "Failed to mangle scope name: %m");
} else {
r = make_unit_name(bus, UNIT_SCOPE, &scope);
r = make_unit_name(UNIT_SCOPE, &scope);
if (r < 0)
return r;
}
@ -2332,7 +2356,7 @@ static int start_transient_trigger(sd_bus *bus, const char *suffix) {
break;
}
} else {
r = make_unit_name(bus, UNIT_SERVICE, &service);
r = make_unit_name(UNIT_SERVICE, &service);
if (r < 0)
return r;
@ -2411,16 +2435,30 @@ static int run(int argc, char* argv[]) {
}
if (!arg_description) {
char *t;
_cleanup_free_ char *t = NULL;
if (strv_isempty(arg_cmdline))
t = strdup(arg_unit);
else
else if (startswith(arg_cmdline[0], "-")) {
/* Drop the login shell marker from the command line when generating the description,
* in order to minimize user confusion. */
_cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
if (!l)
return log_oom();
r = free_and_strdup_warn(l + 0, l[0] + 1);
if (r < 0)
return r;
t = quote_command_line(l, SHELL_ESCAPE_EMPTY);
} else
t = quote_command_line(arg_cmdline, SHELL_ESCAPE_EMPTY);
if (!t)
return log_oom();
free_and_replace(arg_description, t);
arg_description = strjoin("[", program_invocation_short_name, "] ", t);
if (!arg_description)
return log_oom();
}
/* For backward compatibility reasons env var expansion is disabled by default for scopes, and

View File

@ -82,7 +82,7 @@ TEST(keymaps) {
#define dump_glyph(x) log_info(STRINGIFY(x) ": %s", special_glyph(x))
TEST(dump_special_glyphs) {
assert_cc(SPECIAL_GLYPH_GREEN_CIRCLE + 1 == _SPECIAL_GLYPH_MAX);
assert_cc(SPECIAL_GLYPH_SUPERHERO + 1 == _SPECIAL_GLYPH_MAX);
log_info("is_locale_utf8: %s", yes_no(is_locale_utf8()));
@ -133,6 +133,7 @@ TEST(dump_special_glyphs) {
dump_glyph(SPECIAL_GLYPH_YELLOW_CIRCLE);
dump_glyph(SPECIAL_GLYPH_BLUE_CIRCLE);
dump_glyph(SPECIAL_GLYPH_GREEN_CIRCLE);
dump_glyph(SPECIAL_GLYPH_SUPERHERO);
}
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -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

View File

@ -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