]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
core: accept "|" ExecStart= prefix to spawn target user's shell
authorMike Yuan <me@yhndnzj.com>
Wed, 9 Apr 2025 13:22:11 +0000 (15:22 +0200)
committerMike Yuan <me@yhndnzj.com>
Wed, 7 May 2025 16:32:19 +0000 (18:32 +0200)
When switching to another user it's oftentimes desirable to also spawn
the target user's shell. sudo supports this via -i flag, run0 currently
doesn't. We don't want to proactively query NSS ourselves, since
that would fall short when operating remotely. Let's instead teach
the service manager to spawn the command using the user's default shell.

I opted for "|" instead of "." in the end because the latter seems
a bit obscure. But happy to change it to something else if a better option
comes up.

TODO
man/systemd.service.xml
src/core/dbus-execute.c
src/core/exec-invoke.c
src/core/load-fragment.c
src/shared/bus-unit-util.c
src/shared/exec-util.c
src/shared/exec-util.h

diff --git a/TODO b/TODO
index d91dbcee286ff8ebc5f330987a212cdb474ebfa1..436363e836a685022ac3dca562d9fdf4f3c0a379 100644 (file)
--- a/TODO
+++ b/TODO
@@ -720,10 +720,6 @@ Features:
 * machined: optionally track nspawn unix-export/ runtime for each machined, and
   then update systemd-ssh-proxy so that it can connect to that.
 
-* add a new ExecStart= flag that inserts the configured user's shell as first
-  word in the command line. (maybe use character '.'). Usecase: tool such as
-  run0 can use that to spawn the target user's default shell.
-
 * introduce mntid_t, and make it 64bit, as apparently the kernel switched to
   64bit mount ids
 
index b03e142725a37eb3471c86214de9a1c5cc832398..cc9350f5916f1938c249530b76f0138a03aa9573 100644 (file)
         <tbody>
           <row>
             <entry><literal>@</literal></entry>
-            <entry>If the executable path is prefixed with <literal>@</literal>, the second specified token will be passed as <constant>argv[0]</constant> to the executed process (instead of the actual filename), followed by the further arguments specified.</entry>
+            <entry>If the executable path is prefixed with <literal>@</literal>, the second specified token will be passed as <constant>argv[0]</constant> to the executed process (instead of the actual filename), followed by the further arguments specified, unless <literal>|</literal> is also specified, in which case it enables login shell semantics for the shell spawned by prefixing <literal>-</literal> to <constant>argv[0]</constant>.</entry>
           </row>
 
           <row>
 
             <entry>Similar to the <literal>+</literal> character discussed above this permits invoking command lines with elevated privileges. However, unlike <literal>+</literal> the <literal>!</literal> character exclusively alters the effect of <varname>User=</varname>, <varname>Group=</varname> and <varname>SupplementaryGroups=</varname>, i.e. only the stanzas that affect user and group credentials. Note that this setting may be combined with <varname>DynamicUser=</varname>, in which case a dynamic user/group pair is allocated before the command is invoked, but credential changing is left to the executed process itself.</entry>
           </row>
+
+          <row>
+            <entry><literal>|</literal></entry>
+
+            <entry>If <literal>|</literal> is specified standalone as executable path, invoke the default shell of <varname>User=</varname>. If specified as a prefix, use the shell (<literal>-c</literal>) to spawn the executable. When <literal>@</literal> is used in conjunction, <constant>argv[0]</constant> of shell will be prefixed with <literal>-</literal> to enable login shell semantics.</entry>
+          </row>
         </tbody>
       </tgroup>
     </table>
 
-    <para><literal>@</literal>, <literal>-</literal>, <literal>:</literal>, and one of
+    <para><literal>@</literal>, <literal>|</literal>, <literal>-</literal>, <literal>:</literal>, and one of
     <literal>+</literal>/<literal>!</literal> may be used together and they can appear in any order.
     However, <literal>+</literal> and <literal>!</literal> may not be specified at the same time.</para>
 
@@ -1489,9 +1495,9 @@ ExecStart=/bin/echo $ONE $TWO $THREE</programlisting>
     includes e.g. <varname>$USER</varname>, but not
     <varname>$TERM</varname>).</para>
 
-    <para>Note that shell command lines are not directly supported. If
-    shell command lines are to be used, they need to be passed
-    explicitly to a shell implementation of some kind. Example:</para>
+    <para>Note that shell command lines are not directly supported, and <literal>|</literal> invokes the user's
+    default shell which isn't deterministic. It's recommended to specify a shell implementation explicitly
+    if portability is desired. Example:</para>
     <programlisting>ExecStart=sh -c 'dmesg | tac'</programlisting>
 
     <para>Example:</para>
index 560594d8a158835726c007f6377e6b416eacecc5..f7b2932e07aa36ed67242e3c185742eaf09793b5 100644 (file)
@@ -1472,11 +1472,12 @@ int bus_property_get_exec_ex_command_list(
         return sd_bus_message_close_container(reply);
 }
 
-static char *exec_command_flags_to_exec_chars(ExecCommandFlags flags) {
+static charexec_command_flags_to_exec_chars(ExecCommandFlags flags) {
         return strjoin(FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE)   ? "-" : "",
                        FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND)    ? ":" : "",
                        FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED) ? "+" : "",
-                       FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)        ? "!" : "");
+                       FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)        ? "!" : "",
+                       FLAGS_SET(flags, EXEC_COMMAND_VIA_SHELL)        ? "|" : "");
 }
 
 int bus_set_transient_exec_command(
@@ -1504,30 +1505,58 @@ int bus_set_transient_exec_command(
                 return r;
 
         while ((r = sd_bus_message_enter_container(message, 'r', ex_prop ? "sasas" : "sasb")) > 0) {
-                _cleanup_strv_free_ char **argv = NULL, **ex_opts = NULL;
+                _cleanup_strv_free_ char **argv = NULL;
                 const char *path;
-                int b;
+                ExecCommandFlags command_flags;
 
                 r = sd_bus_message_read(message, "s", &path);
                 if (r < 0)
                         return r;
 
-                if (!filename_or_absolute_path_is_valid(path))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
-                                                 "\"%s\" is neither a valid executable name nor an absolute path",
-                                                 path);
-
                 r = sd_bus_message_read_strv(message, &argv);
                 if (r < 0)
                         return r;
 
-                if (strv_isempty(argv))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
-                                                 "\"%s\" argv cannot be empty", name);
+                if (ex_prop) {
+                        _cleanup_strv_free_ char **ex_opts = NULL;
 
-                r = ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
-                if (r < 0)
-                        return r;
+                        r = sd_bus_message_read_strv(message, &ex_opts);
+                        if (r < 0)
+                                return r;
+
+                        r = exec_command_flags_from_strv(ex_opts, &command_flags);
+                        if (r < 0)
+                                return r;
+                } else {
+                        int b;
+
+                        r = sd_bus_message_read(message, "b", &b);
+                        if (r < 0)
+                                return r;
+
+                        command_flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
+                }
+
+                if (!FLAGS_SET(command_flags, EXEC_COMMAND_VIA_SHELL)) {
+                        if (!filename_or_absolute_path_is_valid(path))
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                         "\"%s\" is neither a valid executable name nor an absolute path",
+                                                         path);
+
+                        if (strv_isempty(argv))
+                                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+                                                         "\"%s\" argv cannot be empty", name);
+                } else {
+                        /* Always normalize path and argv0 to be "sh" */
+                        path = _PATH_BSHELL;
+
+                        if (strv_isempty(argv))
+                                r = strv_extend(&argv, path);
+                        else
+                                r = free_and_strdup(&argv[0], argv[0][0] == '-' ? "-sh" : "sh");
+                        if (r < 0)
+                                return r;
+                }
 
                 r = sd_bus_message_exit_container(message);
                 if (r < 0)
@@ -1542,19 +1571,13 @@ int bus_set_transient_exec_command(
 
                         *c = (ExecCommand) {
                                 .argv = TAKE_PTR(argv),
+                                .flags = command_flags,
                         };
 
                         r = path_simplify_alloc(path, &c->path);
                         if (r < 0)
                                 return r;
 
-                        if (ex_prop) {
-                                r = exec_command_flags_from_strv(ex_opts, &c->flags);
-                                if (r < 0)
-                                        return r;
-                        } else if (b)
-                                c->flags |= EXEC_COMMAND_IGNORE_FAILURE;
-
                         exec_command_append_list(exec_command, TAKE_PTR(c));
                 }
 
@@ -1585,17 +1608,19 @@ int bus_set_transient_exec_command(
                         _cleanup_free_ char *a = NULL, *exec_chars = NULL;
                         UnitWriteFlags esc_flags = UNIT_ESCAPE_SPECIFIERS |
                                 (FLAGS_SET(c->flags, EXEC_COMMAND_NO_ENV_EXPAND) ? UNIT_ESCAPE_EXEC_SYNTAX : UNIT_ESCAPE_EXEC_SYNTAX_ENV);
+                        bool via_shell = FLAGS_SET(c->flags, EXEC_COMMAND_VIA_SHELL);
 
                         exec_chars = exec_command_flags_to_exec_chars(c->flags);
                         if (!exec_chars)
                                 return -ENOMEM;
 
-                        a = unit_concat_strv(c->argv, esc_flags);
+                        a = unit_concat_strv(via_shell ? strv_skip(c->argv, 1) : c->argv, esc_flags);
                         if (!a)
                                 return -ENOMEM;
 
-                        if (streq_ptr(c->path, c->argv ? c->argv[0] : NULL))
-                                fprintf(f, "%s=%s%s\n", written_name, exec_chars, a);
+                        if (via_shell || streq(c->path, c->argv[0]))
+                                fprintf(f, "%s=%s%s%s\n",
+                                        written_name, exec_chars, via_shell && c->argv[0][0] == '-' ? "@" : "", a);
                         else {
                                 _cleanup_free_ char *t = NULL;
                                 const char *p;
index b0bf50257679d52d636f56771a3d9f5e7a82c249..3b7e9aa626a6d2bf7769484ce9e282b72f41ecb6 100644 (file)
@@ -4572,12 +4572,11 @@ int exec_invoke(
                 const CGroupContext *cgroup_context,
                 int *exit_status) {
 
-        _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **joined_exec_search_path = NULL, **accum_env = NULL, **replaced_argv = NULL;
+        _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **joined_exec_search_path = NULL, **accum_env = NULL;
         int r;
         const char *username = NULL, *groupname = NULL;
         _cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *own_user = NULL;
         const char *pwent_home = NULL, *shell = NULL;
-        char **final_argv = NULL;
         dev_t journal_stream_dev = 0;
         ino_t journal_stream_ino = 0;
         bool needs_sandboxing,          /* Do we need to set up full sandboxing? (i.e. all namespacing, all MAC stuff, caps, yadda yadda */
@@ -4807,7 +4806,7 @@ int exec_invoke(
 
                 if (context->user)
                         u = context->user;
-                else if (context->pam_name) {
+                else if (context->pam_name || FLAGS_SET(command->flags, EXEC_COMMAND_VIA_SHELL)) {
                         /* If PAM is enabled but no user name is explicitly selected, then use our own one. */
                         own_user = getusername_malloc();
                         if (!own_user) {
@@ -5406,17 +5405,26 @@ int exec_invoke(
         /* Now that the mount namespace has been set up and privileges adjusted, let's look for the thing we
          * shall execute. */
 
+        const char *path = command->path;
+
+        if (FLAGS_SET(command->flags, EXEC_COMMAND_VIA_SHELL)) {
+                if (shell_is_placeholder(shell)) {
+                        log_debug("Shell prefixing requested for user without default shell, using /bin/sh: %s",
+                                  strna(username));
+                        assert(streq(path, _PATH_BSHELL));
+                } else
+                        path = shell;
+        }
+
         _cleanup_free_ char *executable = NULL;
         _cleanup_close_ int executable_fd = -EBADF;
-        r = find_executable_full(command->path, /* root= */ NULL, context->exec_search_path, false, &executable, &executable_fd);
+        r = find_executable_full(path, /* root= */ NULL, context->exec_search_path, false, &executable, &executable_fd);
         if (r < 0) {
                 *exit_status = EXIT_EXEC;
                 log_struct_errno(LOG_NOTICE, r,
                                  LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED_STR),
-                                 LOG_EXEC_MESSAGE(params,
-                                                  "Unable to locate executable '%s': %m",
-                                                  command->path),
-                                 LOG_ITEM("EXECUTABLE=%s", command->path));
+                                 LOG_EXEC_MESSAGE(params, "Unable to locate executable '%s': %m", path),
+                                 LOG_ITEM("EXECUTABLE=%s", path));
                 /* If the error will be ignored by manager, tune down the log level here. Missing executable
                  * is very much expected in this case. */
                 return r != -ENOMEM && FLAGS_SET(command->flags, EXEC_COMMAND_IGNORE_FAILURE) ? 1 : r;
@@ -5827,10 +5835,13 @@ int exec_invoke(
                 strv_free_and_replace(accum_env, ee);
         }
 
-        if (!FLAGS_SET(command->flags, EXEC_COMMAND_NO_ENV_EXPAND)) {
+        _cleanup_strv_free_ char **replaced_argv = NULL, **argv_via_shell = NULL;
+        char **final_argv = FLAGS_SET(command->flags, EXEC_COMMAND_VIA_SHELL) ? strv_skip(command->argv, 1) : command->argv;
+
+        if (final_argv && !FLAGS_SET(command->flags, EXEC_COMMAND_NO_ENV_EXPAND)) {
                 _cleanup_strv_free_ char **unset_variables = NULL, **bad_variables = NULL;
 
-                r = replace_env_argv(command->argv, accum_env, &replaced_argv, &unset_variables, &bad_variables);
+                r = replace_env_argv(final_argv, accum_env, &replaced_argv, &unset_variables, &bad_variables);
                 if (r < 0) {
                         *exit_status = EXIT_MEMORY;
                         return log_error_errno(r, "Failed to replace environment variables: %m");
@@ -5846,8 +5857,33 @@ int exec_invoke(
                         _cleanup_free_ char *jb = strv_join(bad_variables, ", ");
                         log_warning("Invalid environment variable name evaluates to an empty string: %s", strna(jb));
                 }
-        } else
-                final_argv = command->argv;
+        }
+
+        if (FLAGS_SET(command->flags, EXEC_COMMAND_VIA_SHELL)) {
+                r = strv_extendf(&argv_via_shell, "%s%s", command->argv[0][0] == '-' ? "-" : "", path);
+                if (r < 0) {
+                        *exit_status = EXIT_MEMORY;
+                        return log_oom();
+                }
+
+                if (!strv_isempty(final_argv)) {
+                        _cleanup_free_ char *cmdline_joined = NULL;
+
+                        cmdline_joined = strv_join(final_argv, " ");
+                        if (!cmdline_joined) {
+                                *exit_status = EXIT_MEMORY;
+                                return log_oom();
+                        }
+
+                        r = strv_extend_many(&argv_via_shell, "-c", cmdline_joined);
+                        if (r < 0) {
+                                *exit_status = EXIT_MEMORY;
+                                return log_oom();
+                        }
+                }
+
+                final_argv = argv_via_shell;
+        }
 
         log_command_line(context, params, "Executing", executable, final_argv);
 
index ca95f334079114d85f71cd90e4a80711eace54ba..67048fcd2d53533d2cda4cca0fa30e975c9391ae 100644 (file)
@@ -888,7 +888,7 @@ int config_parse_exec(
         bool semicolon;
 
         do {
-                _cleanup_free_ char *path = NULL, *firstword = NULL;
+                _cleanup_free_ char *firstword = NULL;
 
                 semicolon = false;
 
@@ -915,6 +915,8 @@ int config_parse_exec(
                          *
                          * "-":  Ignore if the path doesn't exist
                          * "@":  Allow overriding argv[0] (supplied as a separate argument)
+                         * "|":  Prefix the cmdline with target user's shell (when combined with "@" invoke
+                         *       login shell semantics)
                          * ":":  Disable environment variable substitution
                          * "+":  Run with full privileges and no sandboxing
                          * "!":  Apply sandboxing except for user/group credentials
@@ -926,6 +928,8 @@ int config_parse_exec(
                                 separate_argv0 = true;
                         else if (*f == ':' && !FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
                                 flags |= EXEC_COMMAND_NO_ENV_EXPAND;
+                        else if (*f == '|' && !FLAGS_SET(flags, EXEC_COMMAND_VIA_SHELL))
+                                flags |= EXEC_COMMAND_VIA_SHELL;
                         else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID)) && !ambient_hack)
                                 flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
                         else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID)) && !ambient_hack)
@@ -947,45 +951,59 @@ int config_parse_exec(
 
                 ignore = FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE);
 
-                r = unit_path_printf(u, f, &path);
-                if (r < 0) {
-                        log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
-                                   "Failed to resolve unit specifiers in '%s'%s: %m",
-                                   f, ignore ? ", ignoring" : "");
-                        return ignore ? 0 : -ENOEXEC;
-                }
+                _cleanup_strv_free_ char **args = NULL;
+                _cleanup_free_ char *path = NULL;
+
+                if (FLAGS_SET(flags, EXEC_COMMAND_VIA_SHELL)) {
+                        /* Use _PATH_BSHELL as placeholder since we can't do NSS lookups in pid1. This would
+                         * be exported to various dbus properties and is used to determine SELinux label -
+                         * which isn't accurate, but is a best-effort thing to assume all shells have more
+                         * or less the same label. */
+                        path = strdup(_PATH_BSHELL);
+                        if (!path)
+                                return log_oom();
 
-                if (isempty(path)) {
-                        log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
-                                   "Empty path in command line%s: %s",
-                                   ignore ? ", ignoring" : "", rvalue);
-                        return ignore ? 0 : -ENOEXEC;
-                }
-                if (!string_is_safe(path)) {
-                        log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
-                                   "Executable path contains special characters%s: %s",
-                                   ignore ? ", ignoring" : "", path);
-                        return ignore ? 0 : -ENOEXEC;
-                }
-                if (path_implies_directory(path)) {
-                        log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
-                                   "Executable path specifies a directory%s: %s",
-                                   ignore ? ", ignoring" : "", path);
-                        return ignore ? 0 : -ENOEXEC;
-                }
+                        if (strv_extend_many(&args, separate_argv0 ? "-sh" : "sh", empty_to_null(f)) < 0)
+                                return log_oom();
+                } else {
+                        r = unit_path_printf(u, f, &path);
+                        if (r < 0) {
+                                log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, r,
+                                           "Failed to resolve unit specifiers in '%s'%s: %m",
+                                           f, ignore ? ", ignoring" : "");
+                                return ignore ? 0 : -ENOEXEC;
+                        }
 
-                if (!filename_or_absolute_path_is_valid(path)) {
-                        log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
-                                   "Neither a valid executable name nor an absolute path%s: %s",
-                                   ignore ? ", ignoring" : "", path);
-                        return ignore ? 0 : -ENOEXEC;
-                }
+                        if (isempty(path)) {
+                                log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
+                                           "Empty path in command line%s: %s",
+                                           ignore ? ", ignoring" : "", rvalue);
+                                return ignore ? 0 : -ENOEXEC;
+                        }
+                        if (!string_is_safe(path)) {
+                                log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
+                                           "Executable path contains special characters%s: %s",
+                                           ignore ? ", ignoring" : "", path);
+                                return ignore ? 0 : -ENOEXEC;
+                        }
+                        if (path_implies_directory(path)) {
+                                log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
+                                           "Executable path specifies a directory%s: %s",
+                                           ignore ? ", ignoring" : "", path);
+                                return ignore ? 0 : -ENOEXEC;
+                        }
 
-                _cleanup_strv_free_ char **args = NULL;
+                        if (!filename_or_absolute_path_is_valid(path)) {
+                                log_syntax(unit, ignore ? LOG_WARNING : LOG_ERR, filename, line, 0,
+                                           "Neither a valid executable name nor an absolute path%s: %s",
+                                           ignore ? ", ignoring" : "", path);
+                                return ignore ? 0 : -ENOEXEC;
+                        }
 
-                if (!separate_argv0)
-                        if (strv_extend(&args, path) < 0)
-                                return log_oom();
+                        if (!separate_argv0)
+                                if (strv_extend(&args, path) < 0)
+                                        return log_oom();
+                }
 
                 while (!isempty(p)) {
                         _cleanup_free_ char *word = NULL, *resolved = NULL;
index c7bb1b2533480e939dc18f008209d2d741bdd113..63478664535f19646f809f92b262fac3e0a301a3 100644 (file)
@@ -331,17 +331,28 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                         }
                         break;
 
+                case '|':
+                        if (FLAGS_SET(flags, EXEC_COMMAND_VIA_SHELL))
+                                done = true;
+                        else {
+                                flags |= EXEC_COMMAND_VIA_SHELL;
+                                eq++;
+                        }
+                        break;
+
                 default:
                         done = true;
                 }
         } while (!done);
 
-        if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID))) {
+        if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_VIA_SHELL))) {
                 /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
                 is_ex_prop = true;
+
                 upgraded_name = strjoin(field, "Ex");
                 if (!upgraded_name)
                         return log_oom();
+                field = upgraded_name;
         }
 
         if (is_ex_prop) {
@@ -350,7 +361,12 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                         return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
         }
 
-        if (explicit_path) {
+        if (FLAGS_SET(flags, EXEC_COMMAND_VIA_SHELL)) {
+                path = strdup(_PATH_BSHELL);
+                if (!path)
+                        return log_oom();
+
+        } else if (explicit_path) {
                 r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
                 if (r < 0)
                         return log_error_errno(r, "Failed to parse path: %m");
@@ -364,11 +380,17 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
         if (r < 0)
                 return log_error_errno(r, "Failed to parse command line: %m");
 
+        if (FLAGS_SET(flags, EXEC_COMMAND_VIA_SHELL)) {
+                r = strv_prepend(&l, explicit_path ? "-sh" : "sh");
+                if (r < 0)
+                        return log_oom();
+        }
+
         r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
         if (r < 0)
                 return bus_log_create_error(r);
 
-        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, upgraded_name ?: field);
+        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
         if (r < 0)
                 return bus_log_create_error(r);
 
index 75f24aef985260f0f5108875c3d3cf60278ca3f0..13cd99e74faea630e38259371882c33fc19b64ef 100644 (file)
@@ -487,6 +487,7 @@ static const char* const exec_command_strings[] = {
         "privileged",     /* EXEC_COMMAND_FULLY_PRIVILEGED */
         "no-setuid",      /* EXEC_COMMAND_NO_SETUID */
         "no-env-expand",  /* EXEC_COMMAND_NO_ENV_EXPAND */
+        "via-shell",      /* EXEC_COMMAND_VIA_SHELL */
 };
 
 assert_cc((1 << ELEMENTSOF(exec_command_strings)) - 1 == _EXEC_COMMAND_FLAGS_ALL);
index 93d9e8c11184fbe5856f42bc0278fdfe23e0976a..52acf342c33e3ee2b74f79ee6aa963125676370a 100644 (file)
@@ -49,8 +49,9 @@ typedef enum ExecCommandFlags {
         EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
         EXEC_COMMAND_NO_SETUID        = 1 << 2,
         EXEC_COMMAND_NO_ENV_EXPAND    = 1 << 3,
+        EXEC_COMMAND_VIA_SHELL        = 1 << 4,
         _EXEC_COMMAND_FLAGS_INVALID   = -EINVAL,
-        _EXEC_COMMAND_FLAGS_ALL       = (1 << 4) -1,
+        _EXEC_COMMAND_FLAGS_ALL       = (1 << 5) -1,
 } ExecCommandFlags;
 
 int exec_command_flags_from_strv(char * const *ex_opts, ExecCommandFlags *ret);