diff options
-rw-r--r-- | man/systemd.service.xml | 39 | ||||
-rw-r--r-- | src/basic/path-util.h | 6 | ||||
-rw-r--r-- | src/core/load-fragment.c | 46 | ||||
-rw-r--r-- | src/test/test-execute.c | 5 | ||||
-rw-r--r-- | test/meson.build | 1 | ||||
-rw-r--r-- | test/test-execute/exec-basic.service | 13 |
6 files changed, 86 insertions, 24 deletions
diff --git a/man/systemd.service.xml b/man/systemd.service.xml index b68d351dff..e89cfe3f0e 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -301,8 +301,9 @@ <varname>ExecStop=</varname> line set. (Services lacking both <varname>ExecStart=</varname> and <varname>ExecStop=</varname> are not valid.)</para> - <para>For each of the specified commands, the first argument must be an absolute path to an - executable. Optionally, this filename may be prefixed with a number of special characters:</para> + <para>For each of the specified commands, the first argument must be either an absolute path to an executable + or a simple file name without any slashes. Optionally, this filename may be prefixed with a number of special + characters:</para> <table> <title>Special executable prefixes</title> @@ -1004,11 +1005,9 @@ <literal>&</literal>, and <emphasis>other elements of shell syntax are not supported</emphasis>.</para> - <para>The command to execute must be an absolute path name. It may - contain spaces, but control characters are not allowed.</para> + <para>The command to execute may contain spaces, but control characters are not allowed.</para> - <para>The command line accepts <literal>%</literal> specifiers as - described in + <para>The command line accepts <literal>%</literal> specifiers as described in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para> <para>Basic environment variable substitution is supported. Use @@ -1022,10 +1021,20 @@ For this type of expansion, quotes are respected when splitting into words, and afterwards removed.</para> + <para>If the command is not a full (absolute) path, it will be resolved to a full path using a + fixed search path determinted at compilation time. Searched directories include + <filename>/usr/local/bin/</filename>, <filename>/usr/bin/</filename>, <filename>/bin/</filename> + on systems using split <filename>/usr/bin/</filename> and <filename>/bin/</filename> + directories, and their <filename>sbin/</filename> counterparts on systems using split + <filename>bin/</filename> and <filename>sbin/</filename>. It is thus safe to use just the + executable name in case of executables located in any of the "standard" directories, and an + absolute path must be used in other cases. Using an absolute path is recommended to avoid + ambiguity.</para> + <para>Example:</para> <programlisting>Environment="ONE=one" 'TWO=two two' -ExecStart=/bin/echo $ONE $TWO ${TWO}</programlisting> +ExecStart=echo $ONE $TWO ${TWO}</programlisting> <para>This will execute <command>/bin/echo</command> with four arguments: <literal>one</literal>, <literal>two</literal>, @@ -1035,7 +1044,7 @@ ExecStart=/bin/echo $ONE $TWO ${TWO}</programlisting> <programlisting>Environment=ONE='one' "TWO='two two' too" THREE= ExecStart=/bin/echo ${ONE} ${TWO} ${THREE} ExecStart=/bin/echo $ONE $TWO $THREE</programlisting> - <para>This results in <filename>echo</filename> being + <para>This results in <filename>/bin/echo</filename> being called twice, the first time with arguments <literal>'one'</literal>, <literal>'two two' too</literal>, <literal></literal>, @@ -1061,27 +1070,27 @@ ExecStart=/bin/echo $ONE $TWO $THREE</programlisting> <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=/bin/sh -c 'dmesg | tac'</programlisting> + <programlisting>ExecStart=sh -c 'dmesg | tac'</programlisting> <para>Example:</para> - <programlisting>ExecStart=/bin/echo one ; /bin/echo "two two"</programlisting> + <programlisting>ExecStart=echo one ; echo "two two"</programlisting> - <para>This will execute <command>/bin/echo</command> two times, + <para>This will execute <command>echo</command> two times, each time with one argument: <literal>one</literal> and <literal>two two</literal>, respectively. Because two commands are specified, <varname>Type=oneshot</varname> must be used.</para> <para>Example:</para> - <programlisting>ExecStart=/bin/echo / >/dev/null & \; \ -/bin/ls</programlisting> + <programlisting>ExecStart=echo / >/dev/null & \; \ +ls</programlisting> - <para>This will execute <command>/bin/echo</command> + <para>This will execute <command>echo</command> with five arguments: <literal>/</literal>, <literal>>/dev/null</literal>, <literal>&</literal>, <literal>;</literal>, and - <literal>/bin/ls</literal>.</para> + <literal>ls</literal>.</para> <table> <title>C escapes supported in command lines and environment variables</title> diff --git a/src/basic/path-util.h b/src/basic/path-util.h index 73f1769fd9..d5b62e295e 100644 --- a/src/basic/path-util.h +++ b/src/basic/path-util.h @@ -30,17 +30,23 @@ #if HAVE_SPLIT_BIN # define PATH_SBIN_BIN(x) x "sbin:" x "bin" +# define PATH0_SBIN_BIN(x) x "sbin\0" x "bin" #else +# define PATH0_SBIN_BIN(x) x "bin" # define PATH_SBIN_BIN(x) x "bin" #endif #define DEFAULT_PATH_NORMAL PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/") +#define DEFAULT_PATH0_NORMAL PATH0_SBIN_BIN("/usr/local/") "\0" PATH0_SBIN_BIN("/usr/") #define DEFAULT_PATH_SPLIT_USR DEFAULT_PATH_NORMAL ":" PATH_SBIN_BIN("/") +#define DEFAULT_PATH0_SPLIT_USR DEFAULT_PATH0_NORMAL "\0" PATH0_SBIN_BIN("/") #if HAVE_SPLIT_USR # define DEFAULT_PATH DEFAULT_PATH_SPLIT_USR +# define DEFAULT_PATH_NULSTR DEFAULT_PATH0_SPLIT_USR #else # define DEFAULT_PATH DEFAULT_PATH_NORMAL +# define DEFAULT_PATH_NULSTR DEFAULT_PATH0_NORMAL #endif bool is_path(const char *p) _pure_; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 887eb1cf49..f9d340df23 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -652,23 +652,51 @@ int config_parse_exec( } if (!string_is_safe(path)) { log_syntax(unit, LOG_ERR, filename, line, 0, - "Executable path contains special characters%s: %s", - ignore ? ", ignoring" : "", rvalue); - return ignore ? 0 : -ENOEXEC; - } - if (!path_is_absolute(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "Executable path is not absolute%s: %s", - ignore ? ", ignoring" : "", rvalue); + "Executable name contains special characters%s: %s", + ignore ? ", ignoring" : "", path); return ignore ? 0 : -ENOEXEC; } if (endswith(path, "/")) { log_syntax(unit, LOG_ERR, filename, line, 0, "Executable path specifies a directory%s: %s", - ignore ? ", ignoring" : "", rvalue); + ignore ? ", ignoring" : "", path); return ignore ? 0 : -ENOEXEC; } + if (!path_is_absolute(path)) { + const char *prefix; + bool found = false; + + if (!filename_is_valid(path)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Neither a valid executable name nor an absolute path%s: %s", + ignore ? ", ignoring" : "", path); + return ignore ? 0 : -ENOEXEC; + } + + /* Resolve a single-component name to a full path */ + NULSTR_FOREACH(prefix, DEFAULT_PATH_NULSTR) { + _cleanup_free_ char *fullpath = NULL; + + fullpath = strjoin(prefix, "/", path); + if (!fullpath) + return log_oom(); + + if (access(fullpath, F_OK) >= 0) { + free_and_replace(path, fullpath); + found = true; + break; + } + } + + if (!found) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Executable \"%s\" not found in path \"%s\"%s", + path, DEFAULT_PATH, ignore ? ", ignoring" : ""); + return ignore ? 0 : -ENOEXEC; + } + } + if (!separate_argv0) { char *w = NULL; diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 81f9b7c344..4c15f1acd5 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -549,6 +549,10 @@ static void test_exec_capabilityboundingset(Manager *m) { test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED); } +static void test_exec_basic(Manager *m) { + test(m, "exec-basic.service", 0, CLD_EXITED); +} + static void test_exec_ambientcapabilities(Manager *m) { int r; @@ -648,6 +652,7 @@ static int run_tests(UnitFileScope scope, const test_function_t *tests) { int main(int argc, char *argv[]) { _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; static const test_function_t user_tests[] = { + test_exec_basic, test_exec_ambientcapabilities, test_exec_bindpaths, test_exec_capabilityboundingset, diff --git a/test/meson.build b/test/meson.build index 809bd44a93..fd4ac11a39 100644 --- a/test/meson.build +++ b/test/meson.build @@ -45,6 +45,7 @@ test_data_files = ''' sockets.target son.service sysinit.target + test-execute/exec-basic.service test-execute/exec-ambientcapabilities-merge-nfsnobody.service test-execute/exec-ambientcapabilities-merge-nobody.service test-execute/exec-ambientcapabilities-merge.service diff --git a/test/test-execute/exec-basic.service b/test/test-execute/exec-basic.service new file mode 100644 index 0000000000..456f06951a --- /dev/null +++ b/test/test-execute/exec-basic.service @@ -0,0 +1,13 @@ +[Unit] +Description=Test for basic execution + +[Service] +ExecStart=touch /tmp/a ; /bin/touch /tmp/b ; touch /tmp/c +ExecStart=test -f /tmp/a +ExecStart=!test -f /tmp/b +ExecStart=!!test -f /tmp/c +ExecStart=+test -f /tmp/c +ExecStartPost=rm /tmp/a /tmp/b /tmp/c + +PrivateTmp=true +Type=oneshot |