]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
run0: introduce --via-shell for invoking target user's shell, and -i shortcut
authorMike Yuan <me@yhndnzj.com>
Tue, 8 Apr 2025 22:55:27 +0000 (00:55 +0200)
committerMike Yuan <me@yhndnzj.com>
Wed, 7 May 2025 16:32:20 +0000 (18:32 +0200)
-i/--login has exact sudo semantics. But we only document
the short option and advertise expressly specifying
--via-shell --chdir='~' otherwise.

man/run0.xml
src/run/run.c

index 262fa45ddc110317a539fecbb94f449ea0aaf2d5..d853e2d4633cbd63f54e5ed393a950999d8c3c16 100644 (file)
         </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>
 
 
     <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>
index 4504c97df19f28135ab901f545eacc99d6b3f3e6..002874d282a8c8b95e221e5f5aced782cd65721b 100644 (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);