summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/manager.c2
-rw-r--r--src/shared/path-lookup.c107
-rw-r--r--src/shared/path-lookup.h2
-rw-r--r--src/test/test-path-lookup.c6
4 files changed, 100 insertions, 17 deletions
diff --git a/src/core/manager.c b/src/core/manager.c
index d48b41d88f..55f2f49a06 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -1120,6 +1120,7 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
if (r < 0)
return r;
+ lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
/* If we will deserialize make sure that during enumeration
@@ -2540,6 +2541,7 @@ int manager_reload(Manager *m) {
if (q < 0 && r >= 0)
r = q;
+ lookup_paths_reduce(&m->lookup_paths);
manager_build_unit_path_cache(m);
/* First, enumerate what we can from all config files */
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index 083e467475..eeabdd1ecd 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -412,6 +412,22 @@ static int patch_root_prefix(char **p, const char *root_dir) {
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;
+}
+
int lookup_paths_init(
LookupPaths *p,
UnitFileScope scope,
@@ -579,23 +595,11 @@ int lookup_paths_init(
if (r < 0)
return r;
- if (!path_strv_resolve_uniq(l, root))
+ r = patch_root_prefix_strv(l, root);
+ if (r < 0)
return -ENOMEM;
- if (strv_isempty(l)) {
- log_debug("Ignoring unit files.");
- l = strv_free(l);
- } else {
- _cleanup_free_ char *t;
-
- t = strv_join(l, "\n\t");
- if (!t)
- return -ENOMEM;
-
- log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
- }
-
- p->search_path = l;
+ p->search_path = strv_uniq(l);
l = NULL;
p->persistent_config = persistent_config;
@@ -634,6 +638,79 @@ void lookup_paths_free(LookupPaths *p) {
p->root_dir = mfree(p->root_dir);
}
+int lookup_paths_reduce(LookupPaths *p) {
+ _cleanup_free_ struct stat *stats = NULL;
+ size_t n_stats = 0, allocated = 0;
+ unsigned c = 0;
+ int r;
+
+ assert(p);
+
+ /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
+ * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
+ * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
+ * account when following symlinks. When we have no root path set this restriction does not apply however. */
+
+ if (!p->search_path)
+ return 0;
+
+ while (p->search_path[c]) {
+ struct stat st;
+ unsigned k;
+
+ if (p->root_dir)
+ r = lstat(p->search_path[c], &st);
+ else
+ r = stat(p->search_path[c], &st);
+ if (r < 0) {
+ if (errno == ENOENT)
+ goto remove_item;
+
+ /* If something we don't grok happened, let's better leave it in. */
+ log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]);
+ c++;
+ continue;
+ }
+
+ for (k = 0; k < n_stats; k++) {
+ if (stats[k].st_dev == st.st_dev &&
+ stats[k].st_ino == st.st_ino)
+ break;
+ }
+
+ if (k < n_stats) /* Is there already an entry with the same device/inode? */
+ goto remove_item;
+
+ if (!GREEDY_REALLOC(stats, allocated, n_stats+1))
+ return -ENOMEM;
+
+ stats[n_stats++] = st;
+ c++;
+ continue;
+
+ remove_item:
+ free(p->search_path[c]);
+ memmove(p->search_path + c,
+ p->search_path + c + 1,
+ (strv_length(p->search_path + c + 1) + 1) * sizeof(char*));
+ }
+
+ 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");
+ if (!t)
+ return -ENOMEM;
+
+ log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
+ }
+
+ return 0;
+}
+
int lookup_paths_mkdir_generator(LookupPaths *p) {
int r, q;
diff --git a/src/shared/path-lookup.h b/src/shared/path-lookup.h
index 27be1d8be8..5a5d734deb 100644
--- a/src/shared/path-lookup.h
+++ b/src/shared/path-lookup.h
@@ -52,6 +52,8 @@ char **generator_paths(UnitFileScope scope);
int lookup_paths_init(LookupPaths *p, UnitFileScope scope, const char *root_dir);
+int lookup_paths_reduce(LookupPaths *p);
+
int lookup_paths_mkdir_generator(LookupPaths *p);
void lookup_paths_trim_generator(LookupPaths *p);
diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c
index 5575a364f2..5ee6f4ebb2 100644
--- a/src/test/test-path-lookup.c
+++ b/src/test/test-path-lookup.c
@@ -36,8 +36,8 @@ static void test_paths(UnitFileScope scope) {
assert_se(mkdtemp(template));
assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0);
- assert_se(lookup_paths_init(&lp_without_env, scope, NULL) == 0);
-
+ assert_se(lookup_paths_init(&lp_without_env, scope, NULL) >= 0);
+ assert_se(lookup_paths_reduce(&lp_without_env) >= 0);
assert_se(!strv_isempty(lp_without_env.search_path));
systemd_unit_path = strjoina(template, "/systemd-unit-path");
@@ -45,6 +45,8 @@ static void test_paths(UnitFileScope scope) {
assert_se(lookup_paths_init(&lp_with_env, scope, NULL) == 0);
assert_se(strv_length(lp_with_env.search_path) == 1);
assert_se(streq(lp_with_env.search_path[0], systemd_unit_path));
+ assert_se(lookup_paths_reduce(&lp_with_env) >= 0);
+ assert_se(strv_length(lp_with_env.search_path) == 0);
assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}