core: rework logic to drop duplicate and non-existing items from search path

Move this into a function of its own, so that we can run it after we ran the
generators, so that it takes into account removed generator dirs.
This commit is contained in:
Lennart Poettering 2016-02-25 02:32:19 +01:00
parent cd64fd5613
commit a145334304
4 changed files with 100 additions and 17 deletions

View file

@ -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 */

View file

@ -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;

View file

@ -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);

View file

@ -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);
}