run0: introduce --via-shell for invoking target user's shell, and -i shortcut

-i/--login has exact sudo semantics. But we only document
the short option and advertise expressly specifying
--via-shell --chdir='~' otherwise.
This commit is contained in:
Mike Yuan
2025-04-09 00:55:27 +02:00
parent fa02d58d85
commit e61d2011d1
2 changed files with 69 additions and 15 deletions

View File

@@ -167,6 +167,24 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--via-shell</option></term>
<listitem><para>Invokes the target user's login shell and runs the specified command (if any) via it.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-i</option></term>
<listitem><para>Shortcut for <option>--via-shell --chdir='~'</option>.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
@@ -290,9 +308,12 @@
<para>All command line arguments after the first non-option argument become part of the command line of
the launched process. If no command line is specified an interactive shell is invoked. The shell to
invoke may be controlled via <option>--setenv=SHELL=…</option> and currently defaults to the
<emphasis>originating user's</emphasis> shell (i.e. not the target user's!) if operating locally, or
<filename>/bin/sh</filename> when operating with <option>--machine=</option>.</para>
invoke may be controlled through <option>--via-shell</option> - when specified the target user's shell
is used - or <option>--setenv=SHELL=…</option>. By default, the <emphasis>originating user's</emphasis> shell
is executed if operating locally, or <filename>/bin/sh</filename> when operating with <option>--machine=</option>.</para>
<para>Note that unlike <command>sudo</command>, <command>run0</command> always spawns shells with login shell
semantics, regardless of <option>-i</option>.</para>
</refsect1>
<refsect1>

View File

@@ -104,6 +104,7 @@ static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
static char *arg_shell_prompt_prefix = NULL;
static int arg_lightweight = -1;
static char *arg_area = NULL;
static bool arg_via_shell = false;
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
@@ -215,6 +216,8 @@ static int help_sudo_mode(void) {
" -g --group=GROUP Run as system group\n"
" --nice=NICE Nice level\n"
" -D --chdir=PATH Set working directory\n"
" --via-shell Invoke command via target user's login shell\n"
" -i Shortcut for --via-shell --chdir='~'\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"
@@ -257,7 +260,7 @@ static int add_timer_property(const char *name, const char *val) {
return 0;
}
static char **make_login_shell_cmdline(const char *shell) {
static char** make_login_shell_cmdline(const char *shell) {
_cleanup_free_ char *argv0 = NULL;
assert(shell);
@@ -826,6 +829,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
ARG_PIPE,
ARG_SHELL_PROMPT_PREFIX,
ARG_LIGHTWEIGHT,
ARG_VIA_SHELL,
};
/* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions
@@ -845,6 +849,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
{ "group", required_argument, NULL, 'g' },
{ "nice", required_argument, NULL, ARG_NICE },
{ "chdir", required_argument, NULL, 'D' },
{ "via-shell", no_argument, NULL, ARG_VIA_SHELL },
{ "login", no_argument, NULL, 'i' }, /* compat with sudo, --via-shell + --chdir='~' */
{ "setenv", required_argument, NULL, ARG_SETENV },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "pty", no_argument, NULL, ARG_PTY },
@@ -864,7 +870,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
/* Resetting to 0 forces the invocation of an internal initialization routine of getopt_long()
* that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
optind = 0;
while ((c = getopt_long(argc, argv, "+hVu:g:D:a:", options, NULL)) >= 0)
while ((c = getopt_long(argc, argv, "+hVu:g:D:a:i", options, NULL)) >= 0)
switch (c) {
@@ -981,6 +987,16 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
break;
case 'i':
r = free_and_strdup_warn(&arg_working_directory, "~");
if (r < 0)
return r;
_fallthrough_;
case ARG_VIA_SHELL:
arg_via_shell = true;
break;
case '?':
return -EINVAL;
@@ -1029,9 +1045,11 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
arg_send_sighup = true;
_cleanup_strv_free_ char **l = NULL;
if (argc > optind)
if (argc > optind) {
l = strv_copy(argv + optind);
else {
if (!l)
return log_oom();
} else if (!arg_via_shell) {
const char *e;
e = strv_env_get(arg_environment, "SHELL");
@@ -1056,9 +1074,19 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
}
l = make_login_shell_cmdline(arg_exec_path);
if (!l)
return log_oom();
}
if (arg_via_shell) {
arg_exec_path = strdup(_PATH_BSHELL);
if (!arg_exec_path)
return log_oom();
r = strv_prepend(&l, "-sh");
if (r < 0)
return log_oom();
}
if (!l)
return log_oom();
strv_free_and_replace(arg_cmdline, l);
@@ -1271,10 +1299,8 @@ static int transient_kill_set_properties(sd_bus_message *m) {
static int transient_service_set_properties(sd_bus_message *m, const char *pty_path, int pty_fd) {
int r, send_term; /* tri-state */
/* We disable environment expansion on the server side via ExecStartEx=:.
* ExecStartEx was added relatively recently (v243), and some bugs were fixed only later.
* So use that feature only if required. It will fail with older systemds. */
bool use_ex_prop = !arg_expand_environment;
/* Use ExecStartEx if new exec flags are required. */
bool use_ex_prop = !arg_expand_environment || arg_via_shell;
assert(m);
assert((!!pty_path) == (pty_fd >= 0));
@@ -1461,7 +1487,9 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
_cleanup_strv_free_ char **opts = NULL;
r = exec_command_flags_to_strv(
(arg_expand_environment ? 0 : EXEC_COMMAND_NO_ENV_EXPAND)|(arg_ignore_failure ? EXEC_COMMAND_IGNORE_FAILURE : 0),
(arg_expand_environment ? 0 : EXEC_COMMAND_NO_ENV_EXPAND)|
(arg_ignore_failure ? EXEC_COMMAND_IGNORE_FAILURE : 0)|
(arg_via_shell ? EXEC_COMMAND_VIA_SHELL : 0),
&opts);
if (r < 0)
return log_error_errno(r, "Failed to format execute flags: %m");
@@ -2821,7 +2849,12 @@ static int run(int argc, char* argv[]) {
if (strv_isempty(arg_cmdline))
t = strdup(arg_unit);
else if (startswith(arg_cmdline[0], "-")) {
else if (arg_via_shell) {
if (arg_cmdline[1])
t = quote_command_line(arg_cmdline + 1, SHELL_ESCAPE_EMPTY);
else
t = strjoin("LOGIN", arg_exec_user ? ": " : NULL, arg_exec_user);
} 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);