Systemd/src/basic/path-lookup.c

870 lines
27 KiB
C
Raw Normal View History

/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "alloc-util.h"
#include "fs-util.h"
#include "log.h"
#include "macro.h"
#include "path-lookup.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "tmpfile-util.h"
#include "user-util.h"
int xdg_user_runtime_dir(char **ret, const char *suffix) {
const char *e;
char *j;
assert(ret);
assert(suffix);
e = getenv("XDG_RUNTIME_DIR");
if (!e)
return -ENXIO;
j = path_join(e, suffix);
if (!j)
return -ENOMEM;
*ret = j;
return 0;
}
int xdg_user_config_dir(char **ret, const char *suffix) {
const char *e;
char *j;
int r;
assert(ret);
e = getenv("XDG_CONFIG_HOME");
if (e)
j = path_join(e, suffix);
else {
_cleanup_free_ char *home = NULL;
r = get_home_dir(&home);
if (r < 0)
return r;
j = path_join(home, "/.config", suffix);
}
if (!j)
return -ENOMEM;
*ret = j;
return 0;
}
int xdg_user_data_dir(char **ret, const char *suffix) {
const char *e;
char *j;
int r;
assert(ret);
assert(suffix);
/* We don't treat /etc/xdg/systemd here as the spec
* suggests because we assume that is a link to
* /etc/systemd/ anyway. */
e = getenv("XDG_DATA_HOME");
if (e)
j = path_join(e, suffix);
else {
_cleanup_free_ char *home = NULL;
r = get_home_dir(&home);
if (r < 0)
return r;
j = path_join(home, "/.local/share", suffix);
}
if (!j)
return -ENOMEM;
*ret = j;
return 1;
}
static const char* const user_data_unit_paths[] = {
"/usr/local/lib/systemd/user",
"/usr/local/share/systemd/user",
USER_DATA_UNIT_DIR,
"/usr/lib/systemd/user",
"/usr/share/systemd/user",
NULL
};
static const char* const user_config_unit_paths[] = {
USER_CONFIG_UNIT_DIR,
"/etc/systemd/user",
NULL
};
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
/* Implement the mechanisms defined in
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
*
* We look in both the config and the data dirs because we
* want to encourage that distributors ship their unit files
* as data, and allow overriding as configuration.
*/
const char *e;
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
e = getenv("XDG_CONFIG_DIRS");
if (e)
config_dirs = strv_split(e, ":");
else
config_dirs = strv_new("/etc/xdg");
if (!config_dirs)
return -ENOMEM;
e = getenv("XDG_DATA_DIRS");
if (e)
data_dirs = strv_split(e, ":");
else
data_dirs = strv_new("/usr/local/share",
"/usr/share");
if (!data_dirs)
return -ENOMEM;
2018-10-08 16:59:31 +02:00
*ret_config_dirs = TAKE_PTR(config_dirs);
*ret_data_dirs = TAKE_PTR(data_dirs);
return 0;
}
static char** user_dirs(
const char *persistent_config,
const char *runtime_config,
const char *global_persistent_config,
const char *global_runtime_config,
const char *generator,
const char *generator_early,
const char *generator_late,
const char *transient,
const char *persistent_control,
const char *runtime_control) {
_cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
_cleanup_free_ char *data_home = NULL;
_cleanup_strv_free_ char **res = NULL;
int r;
r = xdg_user_dirs(&config_dirs, &data_dirs);
if (r < 0)
return NULL;
r = xdg_user_data_dir(&data_home, "/systemd/user");
if (r < 0 && r != -ENXIO)
return NULL;
/* Now merge everything we found. */
if (strv_extend(&res, persistent_control) < 0)
return NULL;
if (strv_extend(&res, runtime_control) < 0)
return NULL;
if (strv_extend(&res, transient) < 0)
return NULL;
if (strv_extend(&res, generator_early) < 0)
return NULL;
if (strv_extend(&res, persistent_config) < 0)
return NULL;
if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
return NULL;
/* global config has lower priority than the user config of the same type */
if (strv_extend(&res, global_persistent_config) < 0)
return NULL;
if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
return NULL;
if (strv_extend(&res, runtime_config) < 0)
return NULL;
if (strv_extend(&res, global_runtime_config) < 0)
return NULL;
if (strv_extend(&res, generator) < 0)
return NULL;
if (strv_extend(&res, data_home) < 0)
return NULL;
if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
return NULL;
if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
return NULL;
if (strv_extend(&res, generator_late) < 0)
return NULL;
if (path_strv_make_absolute_cwd(res) < 0)
return NULL;
return TAKE_PTR(res);
}
bool path_is_user_data_dir(const char *path) {
assert(path);
return strv_contains((char**) user_data_unit_paths, path);
}
bool path_is_user_config_dir(const char *path) {
assert(path);
return strv_contains((char**) user_config_unit_paths, path);
}
static int acquire_generator_dirs(
UnitFileScope scope,
const char *tempdir,
char **generator,
char **generator_early,
char **generator_late) {
_cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
const char *prefix;
assert(generator);
assert(generator_early);
assert(generator_late);
assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
if (scope == UNIT_FILE_GLOBAL)
return -EOPNOTSUPP;
if (tempdir)
prefix = tempdir;
else if (scope == UNIT_FILE_SYSTEM)
prefix = "/run/systemd";
else {
/* UNIT_FILE_USER */
const char *e;
e = getenv("XDG_RUNTIME_DIR");
if (!e)
return -ENXIO;
prefix = strjoina(e, "/systemd");
}
x = path_join(prefix, "generator");
if (!x)
return -ENOMEM;
y = path_join(prefix, "generator.early");
if (!y)
return -ENOMEM;
z = path_join(prefix, "generator.late");
if (!z)
return -ENOMEM;
*generator = TAKE_PTR(x);
*generator_early = TAKE_PTR(y);
*generator_late = TAKE_PTR(z);
return 0;
}
static int acquire_transient_dir(
UnitFileScope scope,
const char *tempdir,
char **ret) {
char *transient;
assert(ret);
assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
if (scope == UNIT_FILE_GLOBAL)
return -EOPNOTSUPP;
if (tempdir)
transient = path_join(tempdir, "transient");
else if (scope == UNIT_FILE_SYSTEM)
transient = strdup("/run/systemd/transient");
else
return xdg_user_runtime_dir(ret, "/systemd/transient");
if (!transient)
return -ENOMEM;
*ret = transient;
return 0;
}
static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
_cleanup_free_ char *a = NULL, *b = NULL;
int r;
assert(persistent);
assert(runtime);
switch (scope) {
case UNIT_FILE_SYSTEM:
a = strdup(SYSTEM_CONFIG_UNIT_DIR);
b = strdup("/run/systemd/system");
break;
case UNIT_FILE_GLOBAL:
a = strdup(USER_CONFIG_UNIT_DIR);
b = strdup("/run/systemd/user");
break;
case UNIT_FILE_USER:
r = xdg_user_config_dir(&a, "/systemd/user");
if (r < 0 && r != -ENXIO)
return r;
r = xdg_user_runtime_dir(runtime, "/systemd/user");
if (r < 0) {
if (r != -ENXIO)
return r;
/* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
* directory to NULL */
*runtime = NULL;
}
*persistent = TAKE_PTR(a);
return 0;
default:
assert_not_reached("Hmm, unexpected scope value.");
}
if (!a || !b)
return -ENOMEM;
*persistent = TAKE_PTR(a);
*runtime = TAKE_PTR(b);
return 0;
}
static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
_cleanup_free_ char *a = NULL;
int r;
assert(persistent);
assert(runtime);
switch (scope) {
case UNIT_FILE_SYSTEM: {
_cleanup_free_ char *b = NULL;
a = strdup("/etc/systemd/system.control");
if (!a)
return -ENOMEM;
b = strdup("/run/systemd/system.control");
if (!b)
return -ENOMEM;
*runtime = TAKE_PTR(b);
break;
}
case UNIT_FILE_USER:
r = xdg_user_config_dir(&a, "/systemd/user.control");
if (r < 0 && r != -ENXIO)
return r;
r = xdg_user_runtime_dir(runtime, "/systemd/user.control");
if (r < 0) {
if (r != -ENXIO)
return r;
/* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
* NULL */
*runtime = NULL;
}
break;
case UNIT_FILE_GLOBAL:
return -EOPNOTSUPP;
default:
assert_not_reached("Hmm, unexpected scope value.");
}
*persistent = TAKE_PTR(a);
return 0;
}
static int acquire_attached_dirs(
UnitFileScope scope,
char **ret_persistent,
char **ret_runtime) {
_cleanup_free_ char *a = NULL, *b = NULL;
assert(ret_persistent);
assert(ret_runtime);
/* Portable services are not available to regular users for now. */
if (scope != UNIT_FILE_SYSTEM)
return -EOPNOTSUPP;
a = strdup("/etc/systemd/system.attached");
if (!a)
return -ENOMEM;
b = strdup("/run/systemd/system.attached");
if (!b)
return -ENOMEM;
*ret_persistent = TAKE_PTR(a);
*ret_runtime = TAKE_PTR(b);
return 0;
}
static int patch_root_prefix(char **p, const char *root_dir) {
char *c;
assert(p);
if (!*p)
return 0;
c = path_join(root_dir, *p);
if (!c)
return -ENOMEM;
free_and_replace(*p, c);
return 0;
}
static int patch_root_prefix_strv(char **l, const char *root_dir) {
char **i;
int r;
if (!root_dir)
return 0;
STRV_FOREACH(i, l) {
r = patch_root_prefix(i, root_dir);
if (r < 0)
return r;
}
return 0;
}
static int get_paths_from_environ(const char *var, char ***paths, bool *append) {
const char *e;
int r;
assert(var);
assert(paths);
assert(append);
*append = false;
e = getenv(var);
if (e) {
const char *k;
k = endswith(e, ":");
if (k) {
e = strndupa(e, k - e);
*append = true;
}
/* FIXME: empty components in other places should be rejected. */
r = path_split_and_make_absolute(e, paths);
if (r < 0)
return r;
}
return 0;
}
int lookup_paths_init(
LookupPaths *p,
UnitFileScope scope,
LookupPathsFlags flags,
const char *root_dir) {
_cleanup_(rmdir_and_freep) char *tempdir = NULL;
_cleanup_free_ char
*root = NULL,
*persistent_config = NULL, *runtime_config = NULL,
*global_persistent_config = NULL, *global_runtime_config = NULL,
*generator = NULL, *generator_early = NULL, *generator_late = NULL,
*transient = NULL,
*persistent_control = NULL, *runtime_control = NULL,
*persistent_attached = NULL, *runtime_attached = NULL;
bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
int r;
assert(p);
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
#if HAVE_SPLIT_USR
flags |= LOOKUP_PATHS_SPLIT_USR;
#endif
if (!empty_or_root(root_dir)) {
if (scope == UNIT_FILE_USER)
return -EINVAL;
r = is_dir(root_dir, true);
if (r < 0)
return r;
if (r == 0)
return -ENOTDIR;
root = strdup(root_dir);
if (!root)
return -ENOMEM;
}
if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
if (r < 0)
return log_debug_errno(r, "Failed to create temporary directory: %m");
}
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
if (r < 0)
return r;
if (scope == UNIT_FILE_USER) {
r = acquire_config_dirs(UNIT_FILE_GLOBAL, &global_persistent_config, &global_runtime_config);
if (r < 0)
return r;
}
if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
r = acquire_generator_dirs(scope, tempdir,
&generator, &generator_early, &generator_late);
2017-10-04 16:01:32 +02:00
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
return r;
}
/* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
r = acquire_transient_dir(scope, tempdir, &transient);
2017-10-04 16:01:32 +02:00
if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
return r;
/* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
if (r < 0 && r != -EOPNOTSUPP)
return r;
r = acquire_attached_dirs(scope, &persistent_attached, &runtime_attached);
if (r < 0 && r != -EOPNOTSUPP)
return r;
/* First priority is whatever has been passed to us via env vars */
r = get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths, &append);
if (r < 0)
return r;
if (!paths || append) {
/* Let's figure something out. */
_cleanup_strv_free_ char **add = NULL;
/* For the user units we include share/ in the search
* path in order to comply with the XDG basedir spec.
* For the system stuff we avoid such nonsense. OTOH
* we include /lib in the search path for the system
* stuff but avoid it for user stuff. */
switch (scope) {
case UNIT_FILE_SYSTEM:
add = strv_new(
/* If you modify this you also want to modify
* systemdsystemunitpath= in systemd.pc.in! */
STRV_IFNOTNULL(persistent_control),
STRV_IFNOTNULL(runtime_control),
STRV_IFNOTNULL(transient),
STRV_IFNOTNULL(generator_early),
persistent_config,
SYSTEM_CONFIG_UNIT_DIR,
"/etc/systemd/system",
STRV_IFNOTNULL(persistent_attached),
runtime_config,
"/run/systemd/system",
STRV_IFNOTNULL(runtime_attached),
STRV_IFNOTNULL(generator),
"/usr/local/lib/systemd/system",
SYSTEM_DATA_UNIT_PATH,
"/usr/lib/systemd/system",
STRV_IFNOTNULL(flags & LOOKUP_PATHS_SPLIT_USR ? "/lib/systemd/system" : NULL),
STRV_IFNOTNULL(generator_late));
break;
case UNIT_FILE_GLOBAL:
add = strv_new(
/* If you modify this you also want to modify
* systemduserunitpath= in systemd.pc.in, and
* the arrays in user_dirs() above! */
STRV_IFNOTNULL(persistent_control),
STRV_IFNOTNULL(runtime_control),
STRV_IFNOTNULL(transient),
STRV_IFNOTNULL(generator_early),
persistent_config,
USER_CONFIG_UNIT_DIR,
"/etc/systemd/user",
runtime_config,
"/run/systemd/user",
STRV_IFNOTNULL(generator),
"/usr/local/share/systemd/user",
"/usr/share/systemd/user",
"/usr/local/lib/systemd/user",
USER_DATA_UNIT_DIR,
"/usr/lib/systemd/user",
STRV_IFNOTNULL(generator_late));
break;
case UNIT_FILE_USER:
add = user_dirs(persistent_config, runtime_config,
global_persistent_config, global_runtime_config,
generator, generator_early, generator_late,
transient,
persistent_control, runtime_control);
break;
default:
assert_not_reached("Hmm, unexpected scope?");
}
if (!add)
return -ENOMEM;
if (paths) {
r = strv_extend_strv(&paths, add, true);
if (r < 0)
return r;
} else
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
* and don't have to copy anything */
paths = TAKE_PTR(add);
}
r = patch_root_prefix(&persistent_config, root);
if (r < 0)
return r;
r = patch_root_prefix(&runtime_config, root);
if (r < 0)
return r;
r = patch_root_prefix(&generator, root);
if (r < 0)
return r;
r = patch_root_prefix(&generator_early, root);
if (r < 0)
return r;
r = patch_root_prefix(&generator_late, root);
if (r < 0)
return r;
r = patch_root_prefix(&transient, root);
if (r < 0)
return r;
r = patch_root_prefix(&persistent_control, root);
if (r < 0)
return r;
r = patch_root_prefix(&runtime_control, root);
if (r < 0)
return r;
r = patch_root_prefix(&persistent_attached, root);
if (r < 0)
return r;
r = patch_root_prefix(&runtime_attached, root);
if (r < 0)
return r;
r = patch_root_prefix_strv(paths, root);
if (r < 0)
return -ENOMEM;
*p = (LookupPaths) {
core: stop removing non-existent and duplicate lookup paths When we would iterate over the lookup paths for each unit, making the list as short as possible was important for performance. With the current cache, it doesn't matter much. Two classes of paths were being removed: - paths which don't exist in the filesystem - paths which symlink to a path earlier in the search list Both of those points cause problems with the caching code: - if a user creates a directory that didn't exist before and puts units there, now we will notice the new mtime an properly load the unit. When the path was removed from list, we wouldn't. - we now properly detect whether a unit path is on the path or not. Before, if e.g. /lib/systemd/system, /usr/lib/systemd/systemd were both on the path, and /lib was a symlink to /usr/lib, the second directory would be pruned from the path. Then, the code would think that a symlink /etc/systemd/system/foo.service→/lib/systemd/system/foo.service is an alias, but /etc/systemd/system/foo.service→/usr/lib/systemd/system/foo.service would be considered a link (in the systemctl link sense). Removing the pruning has a slight negative performance impact in case of usr-merge systems which have systemd compiled with non-usr-merge paths. Non-usr-merge systems are deprecated, and this impact should be very small, so I think it's OK. If it turns out to be an issue, the loop in function that builds the cache could be improved to skip over "duplicate" directories with same logic that the cache pruning did before. I didn't want to add this, becuase it complicates the code to improve a corner case. Fixes #13272.
2019-08-26 08:58:41 +02:00
.search_path = strv_uniq(TAKE_PTR(paths)),
.persistent_config = TAKE_PTR(persistent_config),
.runtime_config = TAKE_PTR(runtime_config),
.generator = TAKE_PTR(generator),
.generator_early = TAKE_PTR(generator_early),
.generator_late = TAKE_PTR(generator_late),
.transient = TAKE_PTR(transient),
.persistent_control = TAKE_PTR(persistent_control),
.runtime_control = TAKE_PTR(runtime_control),
.persistent_attached = TAKE_PTR(persistent_attached),
.runtime_attached = TAKE_PTR(runtime_attached),
.root_dir = TAKE_PTR(root),
.temporary_dir = TAKE_PTR(tempdir),
};
return 0;
}
void lookup_paths_free(LookupPaths *p) {
if (!p)
return;
p->search_path = strv_free(p->search_path);
p->persistent_config = mfree(p->persistent_config);
p->runtime_config = mfree(p->runtime_config);
p->persistent_attached = mfree(p->persistent_attached);
p->runtime_attached = mfree(p->runtime_attached);
p->generator = mfree(p->generator);
p->generator_early = mfree(p->generator_early);
p->generator_late = mfree(p->generator_late);
p->transient = mfree(p->transient);
p->persistent_control = mfree(p->persistent_control);
p->runtime_control = mfree(p->runtime_control);
p->root_dir = mfree(p->root_dir);
p->temporary_dir = mfree(p->temporary_dir);
}
core: stop removing non-existent and duplicate lookup paths When we would iterate over the lookup paths for each unit, making the list as short as possible was important for performance. With the current cache, it doesn't matter much. Two classes of paths were being removed: - paths which don't exist in the filesystem - paths which symlink to a path earlier in the search list Both of those points cause problems with the caching code: - if a user creates a directory that didn't exist before and puts units there, now we will notice the new mtime an properly load the unit. When the path was removed from list, we wouldn't. - we now properly detect whether a unit path is on the path or not. Before, if e.g. /lib/systemd/system, /usr/lib/systemd/systemd were both on the path, and /lib was a symlink to /usr/lib, the second directory would be pruned from the path. Then, the code would think that a symlink /etc/systemd/system/foo.service→/lib/systemd/system/foo.service is an alias, but /etc/systemd/system/foo.service→/usr/lib/systemd/system/foo.service would be considered a link (in the systemctl link sense). Removing the pruning has a slight negative performance impact in case of usr-merge systems which have systemd compiled with non-usr-merge paths. Non-usr-merge systems are deprecated, and this impact should be very small, so I think it's OK. If it turns out to be an issue, the loop in function that builds the cache could be improved to skip over "duplicate" directories with same logic that the cache pruning did before. I didn't want to add this, becuase it complicates the code to improve a corner case. Fixes #13272.
2019-08-26 08:58:41 +02:00
void lookup_paths_log(LookupPaths *p) {
assert(p);
if (strv_isempty(p->search_path)) {
log_debug("Ignoring unit files.");
p->search_path = strv_free(p->search_path);
} else {
_cleanup_free_ char *t;
t = strv_join(p->search_path, "\n\t");
core: stop removing non-existent and duplicate lookup paths When we would iterate over the lookup paths for each unit, making the list as short as possible was important for performance. With the current cache, it doesn't matter much. Two classes of paths were being removed: - paths which don't exist in the filesystem - paths which symlink to a path earlier in the search list Both of those points cause problems with the caching code: - if a user creates a directory that didn't exist before and puts units there, now we will notice the new mtime an properly load the unit. When the path was removed from list, we wouldn't. - we now properly detect whether a unit path is on the path or not. Before, if e.g. /lib/systemd/system, /usr/lib/systemd/systemd were both on the path, and /lib was a symlink to /usr/lib, the second directory would be pruned from the path. Then, the code would think that a symlink /etc/systemd/system/foo.service→/lib/systemd/system/foo.service is an alias, but /etc/systemd/system/foo.service→/usr/lib/systemd/system/foo.service would be considered a link (in the systemctl link sense). Removing the pruning has a slight negative performance impact in case of usr-merge systems which have systemd compiled with non-usr-merge paths. Non-usr-merge systems are deprecated, and this impact should be very small, so I think it's OK. If it turns out to be an issue, the loop in function that builds the cache could be improved to skip over "duplicate" directories with same logic that the cache pruning did before. I didn't want to add this, becuase it complicates the code to improve a corner case. Fixes #13272.
2019-08-26 08:58:41 +02:00
log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
}
}
char **generator_binary_paths(UnitFileScope scope) {
bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
int r;
/* First priority is whatever has been passed to us via env vars */
r = get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths, &append);
if (r < 0)
return NULL;
if (!paths || append) {
_cleanup_strv_free_ char **add = NULL;
switch (scope) {
case UNIT_FILE_SYSTEM:
add = strv_new("/run/systemd/system-generators",
"/etc/systemd/system-generators",
"/usr/local/lib/systemd/system-generators",
SYSTEM_GENERATOR_DIR);
break;
case UNIT_FILE_GLOBAL:
case UNIT_FILE_USER:
add = strv_new("/run/systemd/user-generators",
"/etc/systemd/user-generators",
"/usr/local/lib/systemd/user-generators",
USER_GENERATOR_DIR);
break;
default:
assert_not_reached("Hmm, unexpected scope.");
}
if (!add)
return NULL;
if (paths) {
r = strv_extend_strv(&paths, add, true);
if (r < 0)
return NULL;
} else
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
* and don't have to copy anything */
paths = TAKE_PTR(add);
}
return TAKE_PTR(paths);
}
char **env_generator_binary_paths(bool is_system) {
bool append = false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
_cleanup_strv_free_ char **add = NULL;
int r;
/* First priority is whatever has been passed to us via env vars */
r = get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths, &append);
if (r < 0)
return NULL;
if (!paths || append) {
if (is_system)
add = strv_new("/run/systemd/system-environment-generators",
"/etc/systemd/system-environment-generators",
"/usr/local/lib/systemd/system-environment-generators",
SYSTEM_ENV_GENERATOR_DIR);
else
add = strv_new("/run/systemd/user-environment-generators",
"/etc/systemd/user-environment-generators",
"/usr/local/lib/systemd/user-environment-generators",
USER_ENV_GENERATOR_DIR);
if (!add)
return NULL;
}
if (paths) {
r = strv_extend_strv(&paths, add, true);
if (r < 0)
return NULL;
} else
/* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
* and don't have to copy anything */
paths = TAKE_PTR(add);
return TAKE_PTR(paths);
}