Compare commits
9 Commits
8e5c5c1f48
...
18fe53b40a
Author | SHA1 | Date |
---|---|---|
![]() |
18fe53b40a | |
![]() |
7c78827c19 | |
![]() |
1b88fa8ae4 | |
![]() |
2f7d51ee01 | |
![]() |
74c2faff3d | |
![]() |
354be2190f | |
![]() |
d17ae799f2 | |
![]() |
13d9fecdd0 | |
![]() |
d9ac4b925d |
4
TODO
4
TODO
|
@ -735,10 +735,6 @@ Features:
|
||||||
* machined: optionally track nspawn unix-export/ runtime for each machined, and
|
* machined: optionally track nspawn unix-export/ runtime for each machined, and
|
||||||
then update systemd-ssh-proxy so that it can connect to that.
|
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
|
* introduce mntid_t, and make it 64bit, as apparently the kernel switched to
|
||||||
64bit mount ids
|
64bit mount ids
|
||||||
|
|
||||||
|
|
21
man/run0.xml
21
man/run0.xml
|
@ -167,6 +167,18 @@
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--login-environment</option></term>
|
||||||
|
<term><option>-i</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Invokes the target user's login shell and runs the specified command (if any) via it.
|
||||||
|
If <option>-D/--chdir=</option> is not specified, additionally switch to the target user's home directory
|
||||||
|
even for the root user.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v258"/>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
|
<term><option>--setenv=<replaceable>NAME</replaceable>[=<replaceable>VALUE</replaceable>]</option></term>
|
||||||
|
|
||||||
|
@ -290,9 +302,12 @@
|
||||||
|
|
||||||
<para>All command line arguments after the first non-option argument become part of the command line of
|
<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
|
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
|
invoke may be controlled via <option>-i/--login-environment</option> - when specified the target user's shell
|
||||||
<emphasis>originating user's</emphasis> shell (i.e. not the target user's!) if operating locally, or
|
is used - or <option>--setenv=SHELL=…</option>. By default, the <emphasis>originating user's</emphasis> shell
|
||||||
<filename>/bin/sh</filename> when operating with <option>--machine=</option>.</para>
|
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>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|
|
@ -1397,7 +1397,7 @@
|
||||||
<tbody>
|
<tbody>
|
||||||
<row>
|
<row>
|
||||||
<entry><literal>@</literal></entry>
|
<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>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
|
@ -1420,14 +1420,19 @@
|
||||||
|
|
||||||
<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>
|
<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>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><literal>|</literal></entry>
|
||||||
|
|
||||||
|
<entry>If <literal>|</literal> is specified standalone as executable path, invoke the user's default shell. 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>
|
</tbody>
|
||||||
</tgroup>
|
</tgroup>
|
||||||
</table>
|
</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>/<literal>!!</literal> may be used together and they can appear in any
|
<literal>+</literal>/<literal>!</literal> may be used together and they can appear in any order.
|
||||||
order. However, only one of <literal>+</literal>, <literal>!</literal>, <literal>!!</literal> may be used at a
|
However, <literal>+</literal> and <literal>!</literal> may not be specified at the same time.</para>
|
||||||
time.</para>
|
|
||||||
|
|
||||||
<para>For each command, the first argument must be either an absolute path to an executable or a simple
|
<para>For each command, the first argument must be either an absolute path to an executable or a simple
|
||||||
file name without any slashes. If the command is not a full (absolute) path, it will be resolved to a
|
file name without any slashes. If the command is not a full (absolute) path, it will be resolved to a
|
||||||
|
@ -1490,11 +1495,6 @@ ExecStart=/bin/echo $ONE $TWO $THREE</programlisting>
|
||||||
includes e.g. <varname>$USER</varname>, but not
|
includes e.g. <varname>$USER</varname>, but not
|
||||||
<varname>$TERM</varname>).</para>
|
<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>
|
|
||||||
<programlisting>ExecStart=sh -c 'dmesg | tac'</programlisting>
|
|
||||||
|
|
||||||
<para>Example:</para>
|
<para>Example:</para>
|
||||||
|
|
||||||
<programlisting>ExecStart=echo one
|
<programlisting>ExecStart=echo one
|
||||||
|
|
|
@ -884,9 +884,12 @@ int replace_env_argv(
|
||||||
char ***ret_bad_variables) {
|
char ***ret_bad_variables) {
|
||||||
|
|
||||||
_cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
|
_cleanup_strv_free_ char **n = NULL, **unset_variables = NULL, **bad_variables = NULL;
|
||||||
size_t k = 0, l = 0;
|
size_t k = 0, l;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(!strv_isempty(argv));
|
||||||
|
assert(ret);
|
||||||
|
|
||||||
l = strv_length(argv);
|
l = strv_length(argv);
|
||||||
|
|
||||||
n = new(char*, l+1);
|
n = new(char*, l+1);
|
||||||
|
|
|
@ -1470,11 +1470,12 @@ int bus_property_get_exec_ex_command_list(
|
||||||
return sd_bus_message_close_container(reply);
|
return sd_bus_message_close_container(reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *exec_command_flags_to_exec_chars(ExecCommandFlags flags) {
|
static char* exec_command_flags_to_exec_chars(ExecCommandFlags flags) {
|
||||||
return strjoin(FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE) ? "-" : "",
|
return strjoin(FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE) ? "-" : "",
|
||||||
FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND) ? ":" : "",
|
FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND) ? ":" : "",
|
||||||
FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED) ? "+" : "",
|
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_PREFIX_SHELL) ? "|" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
int bus_set_transient_exec_command(
|
int bus_set_transient_exec_command(
|
||||||
|
@ -1502,30 +1503,58 @@ int bus_set_transient_exec_command(
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
while ((r = sd_bus_message_enter_container(message, 'r', ex_prop ? "sasas" : "sasb")) > 0) {
|
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;
|
const char *path;
|
||||||
int b;
|
ExecCommandFlags command_flags;
|
||||||
|
|
||||||
r = sd_bus_message_read(message, "s", &path);
|
r = sd_bus_message_read(message, "s", &path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!path_is_absolute(path) && !filename_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);
|
r = sd_bus_message_read_strv(message, &argv);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (strv_isempty(argv))
|
if (ex_prop) {
|
||||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
_cleanup_strv_free_ char **ex_opts = NULL;
|
||||||
"\"%s\" argv cannot be empty", name);
|
|
||||||
|
|
||||||
r = ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
|
r = sd_bus_message_read_strv(message, &ex_opts);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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_PREFIX_SHELL)) {
|
||||||
|
if (!path_is_absolute(path) && !filename_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 = "/bin/sh";
|
||||||
|
|
||||||
|
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);
|
r = sd_bus_message_exit_container(message);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -1540,19 +1569,13 @@ int bus_set_transient_exec_command(
|
||||||
|
|
||||||
*c = (ExecCommand) {
|
*c = (ExecCommand) {
|
||||||
.argv = TAKE_PTR(argv),
|
.argv = TAKE_PTR(argv),
|
||||||
|
.flags = command_flags,
|
||||||
};
|
};
|
||||||
|
|
||||||
r = path_simplify_alloc(path, &c->path);
|
r = path_simplify_alloc(path, &c->path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
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));
|
exec_command_append_list(exec_command, TAKE_PTR(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1583,17 +1606,19 @@ int bus_set_transient_exec_command(
|
||||||
_cleanup_free_ char *a = NULL, *exec_chars = NULL;
|
_cleanup_free_ char *a = NULL, *exec_chars = NULL;
|
||||||
UnitWriteFlags esc_flags = UNIT_ESCAPE_SPECIFIERS |
|
UnitWriteFlags esc_flags = UNIT_ESCAPE_SPECIFIERS |
|
||||||
(FLAGS_SET(c->flags, EXEC_COMMAND_NO_ENV_EXPAND) ? UNIT_ESCAPE_EXEC_SYNTAX : UNIT_ESCAPE_EXEC_SYNTAX_ENV);
|
(FLAGS_SET(c->flags, EXEC_COMMAND_NO_ENV_EXPAND) ? UNIT_ESCAPE_EXEC_SYNTAX : UNIT_ESCAPE_EXEC_SYNTAX_ENV);
|
||||||
|
bool prefix_shell = FLAGS_SET(c->flags, EXEC_COMMAND_PREFIX_SHELL);
|
||||||
|
|
||||||
exec_chars = exec_command_flags_to_exec_chars(c->flags);
|
exec_chars = exec_command_flags_to_exec_chars(c->flags);
|
||||||
if (!exec_chars)
|
if (!exec_chars)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
a = unit_concat_strv(c->argv, esc_flags);
|
a = unit_concat_strv(prefix_shell ? strv_skip(c->argv, 1) : c->argv, esc_flags);
|
||||||
if (!a)
|
if (!a)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (streq_ptr(c->path, c->argv ? c->argv[0] : NULL))
|
if (prefix_shell || streq(c->path, c->argv[0]))
|
||||||
fprintf(f, "%s=%s%s\n", written_name, exec_chars, a);
|
fprintf(f, "%s=%s%s%s\n",
|
||||||
|
written_name, exec_chars, prefix_shell && c->argv[0][0] == '-' ? "@" : "", a);
|
||||||
else {
|
else {
|
||||||
_cleanup_free_ char *t = NULL;
|
_cleanup_free_ char *t = NULL;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
|
@ -4649,12 +4649,11 @@ int exec_invoke(
|
||||||
const CGroupContext *cgroup_context,
|
const CGroupContext *cgroup_context,
|
||||||
int *exit_status) {
|
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;
|
int r;
|
||||||
const char *username = NULL, *groupname = NULL;
|
const char *username = NULL, *groupname = NULL;
|
||||||
_cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *own_user = NULL;
|
_cleanup_free_ char *home_buffer = NULL, *memory_pressure_path = NULL, *own_user = NULL;
|
||||||
const char *pwent_home = NULL, *shell = NULL;
|
const char *pwent_home = NULL, *shell = NULL;
|
||||||
char **final_argv = NULL;
|
|
||||||
dev_t journal_stream_dev = 0;
|
dev_t journal_stream_dev = 0;
|
||||||
ino_t journal_stream_ino = 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 */
|
bool needs_sandboxing, /* Do we need to set up full sandboxing? (i.e. all namespacing, all MAC stuff, caps, yadda yadda */
|
||||||
|
@ -4896,7 +4895,7 @@ int exec_invoke(
|
||||||
|
|
||||||
if (context->user)
|
if (context->user)
|
||||||
u = context->user;
|
u = context->user;
|
||||||
else if (context->pam_name) {
|
else if (context->pam_name || FLAGS_SET(command->flags, EXEC_COMMAND_PREFIX_SHELL)) {
|
||||||
/* If PAM is enabled but no user name is explicitly selected, then use our own one. */
|
/* If PAM is enabled but no user name is explicitly selected, then use our own one. */
|
||||||
own_user = getusername_malloc();
|
own_user = getusername_malloc();
|
||||||
if (!own_user) {
|
if (!own_user) {
|
||||||
|
@ -5501,17 +5500,28 @@ int exec_invoke(
|
||||||
/* Now that the mount namespace has been set up and privileges adjusted, let's look for the thing we
|
/* Now that the mount namespace has been set up and privileges adjusted, let's look for the thing we
|
||||||
* shall execute. */
|
* shall execute. */
|
||||||
|
|
||||||
|
const char *path = command->path;
|
||||||
|
|
||||||
|
if (FLAGS_SET(command->flags, EXEC_COMMAND_PREFIX_SHELL)) {
|
||||||
|
if (!shell) {
|
||||||
|
log_exec_debug(context, params,
|
||||||
|
"Shell prefixing requested for user without default shell, using /bin/sh: %s",
|
||||||
|
strna(username));
|
||||||
|
assert(streq(path, "/bin/sh"));
|
||||||
|
} else
|
||||||
|
path = shell;
|
||||||
|
}
|
||||||
|
|
||||||
_cleanup_free_ char *executable = NULL;
|
_cleanup_free_ char *executable = NULL;
|
||||||
_cleanup_close_ int executable_fd = -EBADF;
|
_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) {
|
if (r < 0) {
|
||||||
*exit_status = EXIT_EXEC;
|
*exit_status = EXIT_EXEC;
|
||||||
log_exec_struct_errno(context, params, LOG_NOTICE, r,
|
log_exec_struct_errno(context, params, LOG_NOTICE, r,
|
||||||
LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED_STR),
|
LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED_STR),
|
||||||
LOG_EXEC_MESSAGE(params,
|
LOG_EXEC_MESSAGE(params,
|
||||||
"Unable to locate executable '%s': %m",
|
"Unable to locate executable '%s': %m", path),
|
||||||
command->path),
|
LOG_ITEM("EXECUTABLE=%s", path));
|
||||||
LOG_ITEM("EXECUTABLE=%s", command->path));
|
|
||||||
/* If the error will be ignored by manager, tune down the log level here. Missing executable
|
/* If the error will be ignored by manager, tune down the log level here. Missing executable
|
||||||
* is very much expected in this case. */
|
* is very much expected in this case. */
|
||||||
return r != -ENOMEM && FLAGS_SET(command->flags, EXEC_COMMAND_IGNORE_FAILURE) ? 1 : r;
|
return r != -ENOMEM && FLAGS_SET(command->flags, EXEC_COMMAND_IGNORE_FAILURE) ? 1 : r;
|
||||||
|
@ -5935,10 +5945,13 @@ int exec_invoke(
|
||||||
strv_free_and_replace(accum_env, ee);
|
strv_free_and_replace(accum_env, ee);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FLAGS_SET(command->flags, EXEC_COMMAND_NO_ENV_EXPAND)) {
|
_cleanup_strv_free_ char **replaced_argv = NULL, **prefixed_argv = NULL;
|
||||||
|
char **final_argv = FLAGS_SET(command->flags, EXEC_COMMAND_PREFIX_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;
|
_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) {
|
if (r < 0) {
|
||||||
*exit_status = EXIT_MEMORY;
|
*exit_status = EXIT_MEMORY;
|
||||||
return log_exec_error_errno(context,
|
return log_exec_error_errno(context,
|
||||||
|
@ -5963,8 +5976,33 @@ int exec_invoke(
|
||||||
"Invalid environment variable name evaluates to an empty string: %s",
|
"Invalid environment variable name evaluates to an empty string: %s",
|
||||||
strna(jb));
|
strna(jb));
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
final_argv = command->argv;
|
|
||||||
|
if (FLAGS_SET(command->flags, EXEC_COMMAND_PREFIX_SHELL)) {
|
||||||
|
r = strv_extendf(&prefixed_argv, "%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(&prefixed_argv, "-c", cmdline_joined);
|
||||||
|
if (r < 0) {
|
||||||
|
*exit_status = EXIT_MEMORY;
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final_argv = prefixed_argv;
|
||||||
|
}
|
||||||
|
|
||||||
log_command_line(context, params, "Executing", executable, final_argv);
|
log_command_line(context, params, "Executing", executable, final_argv);
|
||||||
|
|
||||||
|
|
|
@ -888,7 +888,7 @@ int config_parse_exec(
|
||||||
bool semicolon;
|
bool semicolon;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
_cleanup_free_ char *path = NULL, *firstword = NULL;
|
_cleanup_free_ char *firstword = NULL;
|
||||||
|
|
||||||
semicolon = false;
|
semicolon = false;
|
||||||
|
|
||||||
|
@ -915,6 +915,8 @@ int config_parse_exec(
|
||||||
*
|
*
|
||||||
* "-": Ignore if the path doesn't exist
|
* "-": Ignore if the path doesn't exist
|
||||||
* "@": Allow overriding argv[0] (supplied as a separate argument)
|
* "@": 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
|
* ":": Disable environment variable substitution
|
||||||
* "+": Run with full privileges and no sandboxing
|
* "+": Run with full privileges and no sandboxing
|
||||||
* "!": Apply sandboxing except for user/group credentials
|
* "!": Apply sandboxing except for user/group credentials
|
||||||
|
@ -926,6 +928,8 @@ int config_parse_exec(
|
||||||
separate_argv0 = true;
|
separate_argv0 = true;
|
||||||
else if (*f == ':' && !FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
|
else if (*f == ':' && !FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
|
||||||
flags |= EXEC_COMMAND_NO_ENV_EXPAND;
|
flags |= EXEC_COMMAND_NO_ENV_EXPAND;
|
||||||
|
else if (*f == '|' && !FLAGS_SET(flags, EXEC_COMMAND_PREFIX_SHELL))
|
||||||
|
flags |= EXEC_COMMAND_PREFIX_SHELL;
|
||||||
else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID)) && !ambient_hack)
|
else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID)) && !ambient_hack)
|
||||||
flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
|
flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
|
||||||
else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID)) && !ambient_hack)
|
else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID)) && !ambient_hack)
|
||||||
|
@ -947,46 +951,60 @@ int config_parse_exec(
|
||||||
|
|
||||||
ignore = FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE);
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (!(path_is_absolute(path) ? path_is_valid(path) : filename_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cleanup_strv_free_ char **args = NULL;
|
_cleanup_strv_free_ char **args = NULL;
|
||||||
|
_cleanup_free_ char *path = NULL;
|
||||||
|
|
||||||
if (!separate_argv0)
|
if (FLAGS_SET(flags, EXEC_COMMAND_PREFIX_SHELL)) {
|
||||||
if (strv_extend(&args, path) < 0)
|
/* Use /bin/sh 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("/bin/sh");
|
||||||
|
if (!path)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
|
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 (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 (!(path_is_absolute(path) ? path_is_valid(path) : filename_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();
|
||||||
|
}
|
||||||
|
|
||||||
while (!isempty(p)) {
|
while (!isempty(p)) {
|
||||||
_cleanup_free_ char *word = NULL, *resolved = NULL;
|
_cleanup_free_ char *word = NULL, *resolved = NULL;
|
||||||
|
|
||||||
|
|
|
@ -3204,7 +3204,7 @@ int service_deserialize_exec_command(
|
||||||
bool control, found = false, last = false;
|
bool control, found = false, last = false;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
enum ExecCommandState {
|
enum {
|
||||||
STATE_EXEC_COMMAND_TYPE,
|
STATE_EXEC_COMMAND_TYPE,
|
||||||
STATE_EXEC_COMMAND_INDEX,
|
STATE_EXEC_COMMAND_INDEX,
|
||||||
STATE_EXEC_COMMAND_PATH,
|
STATE_EXEC_COMMAND_PATH,
|
||||||
|
@ -3230,6 +3230,7 @@ int service_deserialize_exec_command(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
|
||||||
case STATE_EXEC_COMMAND_TYPE:
|
case STATE_EXEC_COMMAND_TYPE:
|
||||||
id = service_exec_command_from_string(arg);
|
id = service_exec_command_from_string(arg);
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
|
@ -3237,11 +3238,12 @@ int service_deserialize_exec_command(
|
||||||
|
|
||||||
state = STATE_EXEC_COMMAND_INDEX;
|
state = STATE_EXEC_COMMAND_INDEX;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_EXEC_COMMAND_INDEX:
|
case STATE_EXEC_COMMAND_INDEX:
|
||||||
/* PID 1234 is serialized as either '1234' or '+1234'. The second form is used to
|
/* ExecCommand index 1234 is serialized as either '1234' or '+1234'. The second form
|
||||||
* mark the last command in a sequence. We warn if the deserialized command doesn't
|
* is used to mark the last command in a sequence. We warn if the deserialized command
|
||||||
* match what we have loaded from the unit, but we don't need to warn if that is the
|
* doesn't match what we have loaded from the unit, but we don't need to warn if
|
||||||
* last command. */
|
* that is the last command. */
|
||||||
|
|
||||||
r = safe_atou(arg, &idx);
|
r = safe_atou(arg, &idx);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -3250,15 +3252,18 @@ int service_deserialize_exec_command(
|
||||||
|
|
||||||
state = STATE_EXEC_COMMAND_PATH;
|
state = STATE_EXEC_COMMAND_PATH;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_EXEC_COMMAND_PATH:
|
case STATE_EXEC_COMMAND_PATH:
|
||||||
path = TAKE_PTR(arg);
|
path = TAKE_PTR(arg);
|
||||||
state = STATE_EXEC_COMMAND_ARGS;
|
state = STATE_EXEC_COMMAND_ARGS;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_EXEC_COMMAND_ARGS:
|
case STATE_EXEC_COMMAND_ARGS:
|
||||||
r = strv_extend(&argv, arg);
|
r = strv_extend(&argv, arg);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,7 @@ static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||||
static char *arg_shell_prompt_prefix = NULL;
|
static char *arg_shell_prompt_prefix = NULL;
|
||||||
static int arg_lightweight = -1;
|
static int arg_lightweight = -1;
|
||||||
static char *arg_area = NULL;
|
static char *arg_area = NULL;
|
||||||
|
static bool arg_login_environment = false;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_description, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep);
|
||||||
|
@ -212,6 +213,7 @@ static int help_sudo_mode(void) {
|
||||||
" -g --group=GROUP Run as system group\n"
|
" -g --group=GROUP Run as system group\n"
|
||||||
" --nice=NICE Nice level\n"
|
" --nice=NICE Nice level\n"
|
||||||
" -D --chdir=PATH Set working directory\n"
|
" -D --chdir=PATH Set working directory\n"
|
||||||
|
" -i --login-environment Invoke command via target user's login shell and home\n"
|
||||||
" --setenv=NAME[=VALUE] Set environment variable\n"
|
" --setenv=NAME[=VALUE] Set environment variable\n"
|
||||||
" --background=COLOR Set ANSI color for background\n"
|
" --background=COLOR Set ANSI color for background\n"
|
||||||
" --pty Request allocation of a pseudo TTY for stdio\n"
|
" --pty Request allocation of a pseudo TTY for stdio\n"
|
||||||
|
@ -254,7 +256,7 @@ static int add_timer_property(const char *name, const char *val) {
|
||||||
return 0;
|
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;
|
_cleanup_free_ char *argv0 = NULL;
|
||||||
|
|
||||||
assert(shell);
|
assert(shell);
|
||||||
|
@ -842,6 +844,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||||
{ "group", required_argument, NULL, 'g' },
|
{ "group", required_argument, NULL, 'g' },
|
||||||
{ "nice", required_argument, NULL, ARG_NICE },
|
{ "nice", required_argument, NULL, ARG_NICE },
|
||||||
{ "chdir", required_argument, NULL, 'D' },
|
{ "chdir", required_argument, NULL, 'D' },
|
||||||
|
{ "login-environment", no_argument, NULL, 'i' },
|
||||||
{ "setenv", required_argument, NULL, ARG_SETENV },
|
{ "setenv", required_argument, NULL, ARG_SETENV },
|
||||||
{ "background", required_argument, NULL, ARG_BACKGROUND },
|
{ "background", required_argument, NULL, ARG_BACKGROUND },
|
||||||
{ "pty", no_argument, NULL, ARG_PTY },
|
{ "pty", no_argument, NULL, ARG_PTY },
|
||||||
|
@ -861,7 +864,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()
|
/* 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). */
|
* that checks for GNU extensions in optstring ('-' or '+' at the beginning). */
|
||||||
optind = 0;
|
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) {
|
switch (c) {
|
||||||
|
|
||||||
|
@ -975,6 +978,10 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
arg_login_environment = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -991,7 +998,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!arg_working_directory) {
|
if (!arg_working_directory) {
|
||||||
if (arg_exec_user) {
|
if (arg_exec_user || arg_login_environment) {
|
||||||
/* When switching to a specific user, also switch to its home directory. */
|
/* When switching to a specific user, also switch to its home directory. */
|
||||||
arg_working_directory = strdup("~");
|
arg_working_directory = strdup("~");
|
||||||
if (!arg_working_directory)
|
if (!arg_working_directory)
|
||||||
|
@ -1023,9 +1030,11 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||||
arg_send_sighup = true;
|
arg_send_sighup = true;
|
||||||
|
|
||||||
_cleanup_strv_free_ char **l = NULL;
|
_cleanup_strv_free_ char **l = NULL;
|
||||||
if (argc > optind)
|
if (argc > optind) {
|
||||||
l = strv_copy(argv + optind);
|
l = strv_copy(argv + optind);
|
||||||
else {
|
if (!l)
|
||||||
|
return log_oom();
|
||||||
|
} else if (!arg_login_environment) {
|
||||||
const char *e;
|
const char *e;
|
||||||
|
|
||||||
e = strv_env_get(arg_environment, "SHELL");
|
e = strv_env_get(arg_environment, "SHELL");
|
||||||
|
@ -1050,9 +1059,19 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
l = make_login_shell_cmdline(arg_exec_path);
|
l = make_login_shell_cmdline(arg_exec_path);
|
||||||
|
if (!l)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_login_environment) {
|
||||||
|
arg_exec_path = strdup("/bin/sh");
|
||||||
|
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);
|
strv_free_and_replace(arg_cmdline, l);
|
||||||
|
|
||||||
|
@ -1092,6 +1111,12 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
|
||||||
if (strv_extend(&arg_property, "PAMName=systemd-run0") < 0)
|
if (strv_extend(&arg_property, "PAMName=systemd-run0") < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
|
/* The service manager ignores SIGPIPE for all spawned processes by default. Let's explicitly override
|
||||||
|
* that here, since we're primarily invoked in interactive environments, and the termination of
|
||||||
|
* local terminal session should be acknowledged by remote even for --pipe stdio. */
|
||||||
|
if (strv_extend(&arg_property, "IgnoreSIGPIPE=no") < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
if (!arg_background && arg_stdio == ARG_STDIO_PTY && shall_tint_background()) {
|
if (!arg_background && arg_stdio == ARG_STDIO_PTY && shall_tint_background()) {
|
||||||
double hue;
|
double hue;
|
||||||
|
|
||||||
|
@ -1260,10 +1285,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) {
|
static int transient_service_set_properties(sd_bus_message *m, const char *pty_path, int pty_fd) {
|
||||||
int r, send_term; /* tri-state */
|
int r, send_term; /* tri-state */
|
||||||
|
|
||||||
/* We disable environment expansion on the server side via ExecStartEx=:.
|
/* Use ExecStartEx if new exec flags are required. */
|
||||||
* ExecStartEx was added relatively recently (v243), and some bugs were fixed only later.
|
bool use_ex_prop = !arg_expand_environment || arg_login_environment;
|
||||||
* So use that feature only if required. It will fail with older systemds. */
|
|
||||||
bool use_ex_prop = !arg_expand_environment;
|
|
||||||
|
|
||||||
assert(m);
|
assert(m);
|
||||||
assert((!!pty_path) == (pty_fd >= 0));
|
assert((!!pty_path) == (pty_fd >= 0));
|
||||||
|
@ -1450,7 +1473,9 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p
|
||||||
_cleanup_strv_free_ char **opts = NULL;
|
_cleanup_strv_free_ char **opts = NULL;
|
||||||
|
|
||||||
r = exec_command_flags_to_strv(
|
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_login_environment ? EXEC_COMMAND_PREFIX_SHELL : 0),
|
||||||
&opts);
|
&opts);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to format execute flags: %m");
|
return log_error_errno(r, "Failed to format execute flags: %m");
|
||||||
|
@ -2810,7 +2835,12 @@ static int run(int argc, char* argv[]) {
|
||||||
|
|
||||||
if (strv_isempty(arg_cmdline))
|
if (strv_isempty(arg_cmdline))
|
||||||
t = strdup(arg_unit);
|
t = strdup(arg_unit);
|
||||||
else if (startswith(arg_cmdline[0], "-")) {
|
else if (arg_login_environment) {
|
||||||
|
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,
|
/* Drop the login shell marker from the command line when generating the description,
|
||||||
* in order to minimize user confusion. */
|
* in order to minimize user confusion. */
|
||||||
_cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
|
_cleanup_strv_free_ char **l = strv_copy(arg_cmdline);
|
||||||
|
|
|
@ -331,17 +331,28 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '|':
|
||||||
|
if (FLAGS_SET(flags, EXEC_COMMAND_PREFIX_SHELL))
|
||||||
|
done = true;
|
||||||
|
else {
|
||||||
|
flags |= EXEC_COMMAND_PREFIX_SHELL;
|
||||||
|
eq++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
} while (!done);
|
} 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_PREFIX_SHELL))) {
|
||||||
/* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
|
/* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
|
||||||
is_ex_prop = true;
|
is_ex_prop = true;
|
||||||
|
|
||||||
upgraded_name = strjoin(field, "Ex");
|
upgraded_name = strjoin(field, "Ex");
|
||||||
if (!upgraded_name)
|
if (!upgraded_name)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
field = upgraded_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_ex_prop) {
|
if (is_ex_prop) {
|
||||||
|
@ -350,21 +361,36 @@ 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");
|
return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (explicit_path) {
|
if (FLAGS_SET(flags, EXEC_COMMAND_PREFIX_SHELL)) {
|
||||||
|
path = strdup("/bin/sh");
|
||||||
|
if (!path)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
} else if (explicit_path) {
|
||||||
r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
|
r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse path: %m");
|
return log_error_errno(r, "Failed to parse path: %m");
|
||||||
|
if (r == 0)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No executable path specified, refusing.");
|
||||||
|
if (isempty(eq))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Got empty command line, refusing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
|
r = strv_split_full(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse command line: %m");
|
return log_error_errno(r, "Failed to parse command line: %m");
|
||||||
|
|
||||||
|
if (FLAGS_SET(flags, EXEC_COMMAND_PREFIX_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");
|
r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
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)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
|
|
@ -488,6 +488,7 @@ static const char* const exec_command_strings[] = {
|
||||||
"privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */
|
"privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */
|
||||||
"no-setuid", /* EXEC_COMMAND_NO_SETUID */
|
"no-setuid", /* EXEC_COMMAND_NO_SETUID */
|
||||||
"no-env-expand", /* EXEC_COMMAND_NO_ENV_EXPAND */
|
"no-env-expand", /* EXEC_COMMAND_NO_ENV_EXPAND */
|
||||||
|
"prefix-shell", /* EXEC_COMMAND_PREFIX_SHELL */
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_cc((1 << ELEMENTSOF(exec_command_strings)) - 1 == _EXEC_COMMAND_FLAGS_ALL);
|
assert_cc((1 << ELEMENTSOF(exec_command_strings)) - 1 == _EXEC_COMMAND_FLAGS_ALL);
|
||||||
|
|
|
@ -49,8 +49,9 @@ typedef enum ExecCommandFlags {
|
||||||
EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
|
EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
|
||||||
EXEC_COMMAND_NO_SETUID = 1 << 2,
|
EXEC_COMMAND_NO_SETUID = 1 << 2,
|
||||||
EXEC_COMMAND_NO_ENV_EXPAND = 1 << 3,
|
EXEC_COMMAND_NO_ENV_EXPAND = 1 << 3,
|
||||||
|
EXEC_COMMAND_PREFIX_SHELL = 1 << 4,
|
||||||
_EXEC_COMMAND_FLAGS_INVALID = -EINVAL,
|
_EXEC_COMMAND_FLAGS_INVALID = -EINVAL,
|
||||||
_EXEC_COMMAND_FLAGS_ALL = (1 << 4) -1,
|
_EXEC_COMMAND_FLAGS_ALL = (1 << 5) -1,
|
||||||
} ExecCommandFlags;
|
} ExecCommandFlags;
|
||||||
|
|
||||||
int exec_command_flags_from_strv(char * const *ex_opts, ExecCommandFlags *ret);
|
int exec_command_flags_from_strv(char * const *ex_opts, ExecCommandFlags *ret);
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
Environment=SHLVL=100
|
||||||
|
ExecStartPre=-|false
|
||||||
|
ExecStart=|@echo with login shell $$SHELL: lvl $$SHLVL
|
||||||
|
ExecStart=:|"str='with normal shell'" printenv str
|
||||||
|
ExecStart=|echo YAY! >/tmp/TEST-07-PID1.prefix-shell.flag
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
set -eux
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
# shellcheck source=test/units/util.sh
|
||||||
|
. "$(dirname "$0")"/util.sh
|
||||||
|
|
||||||
|
systemd-run --wait -p ExecStartPre="|true" \
|
||||||
|
-p ExecStartPre="|@echo a >/tmp/TEST-07-PID1.prefix-shell.flag" \
|
||||||
|
true
|
||||||
|
assert_eq "$(cat /tmp/TEST-07-PID1.prefix-shell.flag)" "a"
|
||||||
|
rm /tmp/TEST-07-PID1.prefix-shell.flag
|
||||||
|
|
||||||
|
systemctl start prefix-shell.service
|
||||||
|
assert_eq "$(cat /tmp/TEST-07-PID1.prefix-shell.flag)" "YAY!"
|
||||||
|
|
||||||
|
journalctl --sync
|
||||||
|
journalctl -b -u prefix-shell.service --grep "with login shell .*: lvl 101"
|
||||||
|
journalctl -b -u prefix-shell.service --grep "with normal shell"
|
|
@ -256,10 +256,20 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
|
||||||
# Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
|
# Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
|
||||||
assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
|
assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
|
||||||
|
|
||||||
|
# Test shell prefixing
|
||||||
|
assert_eq "$(run0 ${tu:+"--user=$tu"} --setenv=SHLVL=10 printenv SHLVL)" "10"
|
||||||
|
if [[ ! -v ASAN_OPTIONS ]]; then
|
||||||
|
assert_eq "$(run0 ${tu:+"--user=$tu"} --setenv=SHLVL=10 -i echo \$SHLVL)" "11"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "$tu" ]]; then
|
if [[ -n "$tu" ]]; then
|
||||||
# Validate that $SHELL is set to login shell of target user when cmdline is supplied (not invoking shell)
|
# Validate that $SHELL is set to login shell of target user when cmdline is supplied (not invoking shell)
|
||||||
TARGET_LOGIN_SHELL="$(getent passwd "$tu" | cut -d: -f7)"
|
TARGET_LOGIN_SHELL="$(getent passwd "$tu" | cut -d: -f7)"
|
||||||
assert_eq "$(run0 --user="$tu" printenv SHELL)" "$TARGET_LOGIN_SHELL"
|
assert_eq "$(run0 --user="$tu" printenv SHELL)" "$TARGET_LOGIN_SHELL"
|
||||||
|
# ... or when the command is chained by login shell
|
||||||
|
if [[ ! -v ASAN_OPTIONS ]]; then
|
||||||
|
assert_eq "$(run0 --user="$tu" -i printenv SHELL)" "$TARGET_LOGIN_SHELL"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
# Let's chain a couple of run0 calls together, for fun
|
# Let's chain a couple of run0 calls together, for fun
|
||||||
|
|
Loading…
Reference in New Issue