summaryrefslogtreecommitdiff
path: root/src/shared/path-lookup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/path-lookup.c')
-rw-r--r--src/shared/path-lookup.c107
1 files changed, 92 insertions, 15 deletions
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;