X Tutup
Skip to content

Commit 8c35c10

Browse files
alexlzhucdown
authored andcommitted
core: Add ExecSearchPath parameter to specify the directory relative to which binaries executed by Exec*= should be found
Currently there does not exist a way to specify a path relative to which all binaries executed by Exec should be found. The only way is to specify the absolute path. This change implements the functionality to specify a path relative to which binaries executed by Exec*= can be found. Closes systemd#6308
1 parent 5b32e48 commit 8c35c10

27 files changed

+360
-13
lines changed

man/org.freedesktop.systemd1.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2737,6 +2737,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
27372737
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
27382738
readonly as NoExecPaths = ['...', ...];
27392739
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
2740+
readonly as ExecSearchPath = ['...', ...];
2741+
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
27402742
readonly t MountFlags = ...;
27412743
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
27422744
readonly b PrivateTmp = ...;
@@ -3260,6 +3262,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
32603262

32613263
<!--property NoExecPaths is not documented!-->
32623264

3265+
<!--property ExecSearchPath is not documented!-->
3266+
32633267
<!--property PrivateTmp is not documented!-->
32643268

32653269
<!--property PrivateDevices is not documented!-->
@@ -3858,6 +3862,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
38583862

38593863
<variablelist class="dbus-property" generated="True" extra-ref="NoExecPaths"/>
38603864

3865+
<variablelist class="dbus-property" generated="True" extra-ref="ExecSearchPath"/>
3866+
38613867
<variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
38623868

38633869
<variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
@@ -4567,6 +4573,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
45674573
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
45684574
readonly as NoExecPaths = ['...', ...];
45694575
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
4576+
readonly as ExecSearchPath = ['...', ...];
4577+
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
45704578
readonly t MountFlags = ...;
45714579
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
45724580
readonly b PrivateTmp = ...;
@@ -5118,6 +5126,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
51185126

51195127
<!--property NoExecPaths is not documented!-->
51205128

5129+
<!--property ExecSearchPath is not documented!-->
5130+
51215131
<!--property PrivateTmp is not documented!-->
51225132

51235133
<!--property PrivateDevices is not documented!-->
@@ -5714,6 +5724,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
57145724

57155725
<variablelist class="dbus-property" generated="True" extra-ref="NoExecPaths"/>
57165726

5727+
<variablelist class="dbus-property" generated="True" extra-ref="ExecSearchPath"/>
5728+
57175729
<variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
57185730

57195731
<variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
@@ -6320,6 +6332,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
63206332
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
63216333
readonly as NoExecPaths = ['...', ...];
63226334
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
6335+
readonly as ExecSearchPath = ['...', ...];
6336+
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
63236337
readonly t MountFlags = ...;
63246338
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
63256339
readonly b PrivateTmp = ...;
@@ -6799,6 +6813,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
67996813

68006814
<!--property NoExecPaths is not documented!-->
68016815

6816+
<!--property ExecSearchPath is not documented!-->
6817+
68026818
<!--property PrivateTmp is not documented!-->
68036819

68046820
<!--property PrivateDevices is not documented!-->
@@ -7313,6 +7329,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
73137329

73147330
<variablelist class="dbus-property" generated="True" extra-ref="NoExecPaths"/>
73157331

7332+
<variablelist class="dbus-property" generated="True" extra-ref="ExecSearchPath"/>
7333+
73167334
<variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
73177335

73187336
<variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>
@@ -8040,6 +8058,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
80408058
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
80418059
readonly as NoExecPaths = ['...', ...];
80428060
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
8061+
readonly as ExecSearchPath = ['...', ...];
8062+
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
80438063
readonly t MountFlags = ...;
80448064
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
80458065
readonly b PrivateTmp = ...;
@@ -8505,6 +8525,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
85058525

85068526
<!--property NoExecPaths is not documented!-->
85078527

8528+
<!--property ExecSearchPath is not documented!-->
8529+
85088530
<!--property PrivateTmp is not documented!-->
85098531

85108532
<!--property PrivateDevices is not documented!-->
@@ -9005,6 +9027,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
90059027

90069028
<variablelist class="dbus-property" generated="True" extra-ref="NoExecPaths"/>
90079029

9030+
<variablelist class="dbus-property" generated="True" extra-ref="ExecSearchPath"/>
9031+
90089032
<variablelist class="dbus-property" generated="True" extra-ref="MountFlags"/>
90099033

90109034
<variablelist class="dbus-property" generated="True" extra-ref="PrivateTmp"/>

man/systemd.exec.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,20 @@
8989

9090
<variablelist class='unit-directives'>
9191

92+
<varlistentry>
93+
<term><varname>ExecSearchPath=</varname></term>
94+
95+
<listitem><para>Takes a colon separated list of absolute paths relative to which the executable
96+
used by the <varname>Exec*=</varname> (e.g. <varname>ExecStart=</varname>,
97+
<varname>ExecStop=</varname>, etc.) properties can be found. <varname>ExecSearchPath=</varname>
98+
overrides <varname>$PATH</varname> if <varname>$PATH</varname> is not supplied by the user through
99+
<varname>Environment=</varname>, <varname>EnvironmentFile=</varname> or
100+
<varname>PassEnvironment=</varname>. Assigning an empty string removes previous assignments
101+
and setting <varname>ExecSearchPath=</varname> to a value multiple times will append
102+
to the previous setting.
103+
</para></listitem>
104+
</varlistentry>
105+
92106
<varlistentry>
93107
<term><varname>WorkingDirectory=</varname></term>
94108

src/analyze/analyze-verify.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ int verify_executable(Unit *u, const ExecCommand *exec, const char *root) {
142142
if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE)
143143
return 0;
144144

145-
r = find_executable_full(exec->path, root, false, NULL, NULL);
145+
r = find_executable_full(exec->path, root, NULL, false, NULL, NULL);
146146
if (r < 0)
147147
return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path);
148148

src/basic/path-util.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -682,8 +682,8 @@ static int find_executable_impl(const char *name, const char *root, char **ret_f
682682
return 0;
683683
}
684684

685-
int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd) {
686-
int last_error, r;
685+
int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd) {
686+
int last_error = -ENOENT, r = 0;
687687
const char *p = NULL;
688688

689689
assert(name);
@@ -698,7 +698,27 @@ int find_executable_full(const char *name, const char *root, bool use_path_envva
698698
if (!p)
699699
p = DEFAULT_PATH;
700700

701-
last_error = -ENOENT;
701+
if (exec_search_path) {
702+
char **element;
703+
704+
STRV_FOREACH(element, exec_search_path) {
705+
_cleanup_free_ char *full_path = NULL;
706+
if (!path_is_absolute(*element))
707+
continue;
708+
full_path = path_join(*element, name);
709+
if (!full_path)
710+
return -ENOMEM;
711+
712+
r = find_executable_impl(full_path, root, ret_filename, ret_fd);
713+
if (r < 0) {
714+
if (r != -EACCES)
715+
last_error = r;
716+
continue;
717+
}
718+
return 0;
719+
}
720+
return last_error;
721+
}
702722

703723
/* Resolve a single-component name to a full path */
704724
for (;;) {

src/basic/path-util.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ int path_strv_make_absolute_cwd(char **l);
9999
char** path_strv_resolve(char **l, const char *root);
100100
char** path_strv_resolve_uniq(char **l, const char *root);
101101

102-
int find_executable_full(const char *name, const char *root, bool use_path_envvar, char **ret_filename, int *ret_fd);
102+
int find_executable_full(const char *name, const char *root, char **exec_search_path, bool use_path_envvar, char **ret_filename, int *ret_fd);
103103
static inline int find_executable(const char *name, char **ret_filename) {
104-
return find_executable_full(name, /* root= */ NULL, true, ret_filename, NULL);
104+
return find_executable_full(name, /* root= */ NULL, NULL, true, ret_filename, NULL);
105105
}
106106

107107
bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update);

src/core/dbus-execute.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,6 +1161,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
11611161
SD_BUS_PROPERTY("InaccessiblePaths", "as", NULL, offsetof(ExecContext, inaccessible_paths), SD_BUS_VTABLE_PROPERTY_CONST),
11621162
SD_BUS_PROPERTY("ExecPaths", "as", NULL, offsetof(ExecContext, exec_paths), SD_BUS_VTABLE_PROPERTY_CONST),
11631163
SD_BUS_PROPERTY("NoExecPaths", "as", NULL, offsetof(ExecContext, no_exec_paths), SD_BUS_VTABLE_PROPERTY_CONST),
1164+
SD_BUS_PROPERTY("ExecSearchPath", "as", NULL, offsetof(ExecContext, exec_search_path), SD_BUS_VTABLE_PROPERTY_CONST),
11641165
SD_BUS_PROPERTY("MountFlags", "t", bus_property_get_ulong, offsetof(ExecContext, mount_flags), SD_BUS_VTABLE_PROPERTY_CONST),
11651166
SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
11661167
SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -3140,6 +3141,36 @@ int bus_exec_context_set_transient_property(
31403141

31413142
return 1;
31423143

3144+
} else if (streq(name, "ExecSearchPath")) {
3145+
_cleanup_strv_free_ char **l = NULL;
3146+
char **p;
3147+
3148+
r = sd_bus_message_read_strv(message, &l);
3149+
if (r < 0)
3150+
return r;
3151+
3152+
STRV_FOREACH(p, l) {
3153+
if (!path_is_absolute(*p) || !path_is_normalized(*p) || strchr(*p, ':'))
3154+
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name);
3155+
}
3156+
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
3157+
if (strv_isempty(l)) {
3158+
c->exec_search_path = strv_free(c->exec_search_path);
3159+
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "ExecSearchPath=");
3160+
} else {
3161+
_cleanup_free_ char *joined = NULL;
3162+
r = strv_extend_strv(&c->exec_search_path, l, true);
3163+
if (r < 0)
3164+
return -ENOMEM;
3165+
joined = strv_join(c->exec_search_path, ":");
3166+
if (!joined)
3167+
return log_oom();
3168+
unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "ExecSearchPath=%s", joined);
3169+
}
3170+
}
3171+
3172+
return 1;
3173+
31433174
} else if (STR_IN_SET(name, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) {
31443175
_cleanup_strv_free_ char **l = NULL;
31453176
char **p;

src/core/execute.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3742,7 +3742,7 @@ static int exec_child(
37423742
int user_lookup_fd,
37433743
int *exit_status) {
37443744

3745-
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **replaced_argv = NULL;
3745+
_cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **joined_exec_search_path = NULL, **accum_env = NULL, **replaced_argv = NULL;
37463746
int r, ngids = 0, exec_fd;
37473747
_cleanup_free_ gid_t *supplementary_gids = NULL;
37483748
const char *username = NULL, *groupname = NULL;
@@ -4158,8 +4158,31 @@ static int exec_child(
41584158
return log_oom();
41594159
}
41604160

4161+
/* The PATH variable is set to the default path in params->environment.
4162+
* However, this is overridden if user specified fields have PATH set.
4163+
* The intention is to also override PATH if the user does
4164+
* not specify PATH and the user has specified ExecSearchPath
4165+
*/
4166+
4167+
if (!strv_isempty(context->exec_search_path)) {
4168+
_cleanup_free_ char *joined = NULL;
4169+
4170+
joined = strv_join(context->exec_search_path, ":");
4171+
if (!joined) {
4172+
*exit_status = EXIT_MEMORY;
4173+
return log_oom();
4174+
}
4175+
4176+
r = strv_env_assign(&joined_exec_search_path, "PATH", joined);
4177+
if (r < 0) {
4178+
*exit_status = EXIT_MEMORY;
4179+
return log_oom();
4180+
}
4181+
}
4182+
41614183
accum_env = strv_env_merge(params->environment,
41624184
our_env,
4185+
joined_exec_search_path,
41634186
pass_env,
41644187
context->environment,
41654188
files_env);
@@ -4350,7 +4373,7 @@ static int exec_child(
43504373

43514374
_cleanup_free_ char *executable = NULL;
43524375
_cleanup_close_ int executable_fd = -1;
4353-
r = find_executable_full(command->path, /* root= */ NULL, false, &executable, &executable_fd);
4376+
r = find_executable_full(command->path, /* root= */ NULL, context->exec_search_path, false, &executable, &executable_fd);
43544377
if (r < 0) {
43554378
if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) {
43564379
log_unit_struct_errno(unit, LOG_INFO, r,
@@ -4926,6 +4949,7 @@ void exec_context_done(ExecContext *c) {
49264949
c->inaccessible_paths = strv_free(c->inaccessible_paths);
49274950
c->exec_paths = strv_free(c->exec_paths);
49284951
c->no_exec_paths = strv_free(c->no_exec_paths);
4952+
c->exec_search_path = strv_free(c->exec_search_path);
49294953

49304954
bind_mount_free_many(c->bind_mounts, c->n_bind_mounts);
49314955
c->bind_mounts = NULL;
@@ -5597,6 +5621,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
55975621
strv_dump(f, prefix, "InaccessiblePaths", c->inaccessible_paths);
55985622
strv_dump(f, prefix, "ExecPaths", c->exec_paths);
55995623
strv_dump(f, prefix, "NoExecPaths", c->no_exec_paths);
5624+
strv_dump(f, prefix, "ExecSearchPath", c->exec_search_path);
56005625

56015626
for (size_t i = 0; i < c->n_bind_mounts; i++)
56025627
fprintf(f, "%s%s: %s%s:%s:%s\n", prefix,

src/core/execute.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ struct ExecContext {
254254
char *smack_process_label;
255255

256256
char **read_write_paths, **read_only_paths, **inaccessible_paths, **exec_paths, **no_exec_paths;
257+
char **exec_search_path;
257258
unsigned long mount_flags;
258259
BindMount *bind_mounts;
259260
size_t n_bind_mounts;

src/core/load-fragment-gperf.gperf.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
{{type}}.InaccessiblePaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.inaccessible_paths)
106106
{{type}}.ExecPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.exec_paths)
107107
{{type}}.NoExecPaths, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.no_exec_paths)
108+
{{type}}.ExecSearchPath, config_parse_colon_separated_paths, 0, offsetof({{type}}, exec_context.exec_search_path)
108109
{{type}}.BindPaths, config_parse_bind_paths, 0, offsetof({{type}}, exec_context)
109110
{{type}}.BindReadOnlyPaths, config_parse_bind_paths, 0, offsetof({{type}}, exec_context)
110111
{{type}}.TemporaryFileSystem, config_parse_temporary_filesystems, 0, offsetof({{type}}, exec_context)

src/core/load-fragment.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,64 @@ int config_parse_unit_path_printf(
305305
return config_parse_path(unit, filename, line, section, section_line, lvalue, ltype, k, data, userdata);
306306
}
307307

308+
int config_parse_colon_separated_paths(
309+
const char *unit,
310+
const char *filename,
311+
unsigned line,
312+
const char *section,
313+
unsigned section_line,
314+
const char *lvalue,
315+
int ltype,
316+
const char *rvalue,
317+
void *data,
318+
void *userdata) {
319+
char ***sv = data;
320+
const Unit *u = userdata;
321+
int r;
322+
323+
assert(filename);
324+
assert(lvalue);
325+
assert(rvalue);
326+
assert(data);
327+
328+
if (isempty(rvalue)) {
329+
/* Empty assignment resets the list */
330+
*sv = strv_free(*sv);
331+
return 0;
332+
}
333+
334+
for (const char *p = rvalue;;) {
335+
_cleanup_free_ char *word = NULL, *k = NULL;
336+
337+
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
338+
if (r == -ENOMEM)
339+
return log_oom();
340+
if (r < 0) {
341+
log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to extract first word, ignoring: %s", rvalue);
342+
return 0;
343+
}
344+
if (r == 0)
345+
break;
346+
347+
r = unit_path_printf(u, word, &k);
348+
if (r < 0) {
349+
log_syntax(unit, LOG_WARNING, filename, line, r,
350+
"Failed to resolve unit specifiers in '%s', ignoring: %m", word);
351+
return 0;
352+
}
353+
354+
r = path_simplify_and_warn(k, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
355+
if (r < 0)
356+
return 0;
357+
358+
r = strv_consume(sv, TAKE_PTR(k));
359+
if (r < 0)
360+
return log_oom();
361+
}
362+
363+
return 0;
364+
}
365+
308366
int config_parse_unit_path_strv_printf(
309367
const char *unit,
310368
const char *filename,
@@ -5915,6 +5973,7 @@ void unit_dump_config_items(FILE *f) {
59155973
{ config_parse_string, "STRING" },
59165974
{ config_parse_path, "PATH" },
59175975
{ config_parse_unit_path_printf, "PATH" },
5976+
{ config_parse_colon_separated_paths, "PATH" },
59185977
{ config_parse_strv, "STRING [...]" },
59195978
{ config_parse_exec_nice, "NICE" },
59205979
{ config_parse_exec_oom_score_adjust, "OOMSCOREADJUST" },

0 commit comments

Comments
 (0)
X Tutup