summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2017-02-07 20:16:12 +0100
committerLennart Poettering <lennart@poettering.net>2017-02-07 20:16:12 +0100
commitdfead90d936d4c4cbe4f164f35def7399d80817c (patch)
treed3c8805a28d325c8791ea05bccfdb02fb8a12306
parent9f6cbcf53cd3abe416b9808bbfb5648c6b808400 (diff)
install: when a template unit is instantiated via a /usr symlink, consider it enabled
If a unit foobar@.service stored below /usr is instantiated via a symlink foobar@quux.service also below /usr, then we should consider the instance statically enabled, while the template itself should continue to be considered enabled/disabled/static depending on its [Install] section. In order to implement this we'll now look for enablement symlinks in all unit search paths, not just in the config and runtime dirs. Fixes: #5136
-rw-r--r--src/shared/install.c119
-rw-r--r--src/test/test-install-root.c23
2 files changed, 107 insertions, 35 deletions
diff --git a/src/shared/install.c b/src/shared/install.c
index 975a789ebe..b938507587 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -208,7 +208,7 @@ static int path_is_control(const LookupPaths *p, const char *path) {
path_equal_ptr(parent, p->runtime_control);
}
-static int path_is_config(const LookupPaths *p, const char *path) {
+static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
assert(p);
@@ -217,15 +217,19 @@ static int path_is_config(const LookupPaths *p, const char *path) {
/* Note that we do *not* have generic checks for /etc or /run in place, since with
* them we couldn't discern configuration from transient or generated units */
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ if (check_parent) {
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- return path_equal_ptr(parent, p->persistent_config) ||
- path_equal_ptr(parent, p->runtime_config);
+ path = parent;
+ }
+
+ return path_equal_ptr(path, p->persistent_config) ||
+ path_equal_ptr(path, p->runtime_config);
}
-static int path_is_runtime(const LookupPaths *p, const char *path) {
+static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) {
_cleanup_free_ char *parent = NULL;
const char *rpath;
@@ -239,16 +243,20 @@ static int path_is_runtime(const LookupPaths *p, const char *path) {
if (rpath && path_startswith(rpath, "/run"))
return true;
- parent = dirname_malloc(path);
- if (!parent)
- return -ENOMEM;
+ if (check_parent) {
+ parent = dirname_malloc(path);
+ if (!parent)
+ return -ENOMEM;
- return path_equal_ptr(parent, p->runtime_config) ||
- path_equal_ptr(parent, p->generator) ||
- path_equal_ptr(parent, p->generator_early) ||
- path_equal_ptr(parent, p->generator_late) ||
- path_equal_ptr(parent, p->transient) ||
- path_equal_ptr(parent, p->runtime_control);
+ path = parent;
+ }
+
+ return path_equal_ptr(path, p->runtime_config) ||
+ path_equal_ptr(path, p->generator) ||
+ path_equal_ptr(path, p->generator_early) ||
+ path_equal_ptr(path, p->generator_late) ||
+ path_equal_ptr(path, p->transient) ||
+ path_equal_ptr(path, p->runtime_control);
}
static int path_is_vendor(const LookupPaths *p, const char *path) {
@@ -826,7 +834,9 @@ static int find_symlinks_in_scope(
const char *name,
UnitFileState *state) {
- bool same_name_link_runtime = false, same_name_link = false;
+ bool same_name_link_runtime = false, same_name_link_config = false;
+ bool enabled_in_runtime = false, enabled_at_all = false;
+ char **p;
int r;
assert(scope >= 0);
@@ -834,27 +844,66 @@ static int find_symlinks_in_scope(
assert(paths);
assert(name);
- /* First look in the persistent config path */
- r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link);
- if (r < 0)
- return r;
- if (r > 0) {
- *state = UNIT_FILE_ENABLED;
- return r;
+ STRV_FOREACH(p, paths->search_path) {
+ bool same_name_link = false;
+
+ r = find_symlinks(paths->root_dir, name, *p, paths, &same_name_link);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
+
+ r = path_is_config(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* This is the best outcome, let's return it immediately. */
+ *state = UNIT_FILE_ENABLED;
+ return 1;
+ }
+
+ r = path_is_runtime(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ enabled_in_runtime = true;
+ else
+ enabled_at_all = true;
+
+ } else if (same_name_link) {
+
+ r = path_is_config(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ same_name_link_config = true;
+ else {
+ r = path_is_runtime(paths, *p, false);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ same_name_link_runtime = true;
+ }
+ }
}
- /* Then look in runtime config path */
- r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime);
- if (r < 0)
- return r;
- if (r > 0) {
+ if (enabled_in_runtime) {
*state = UNIT_FILE_ENABLED_RUNTIME;
- return r;
+ return 1;
+ }
+
+ /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
+ * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
+ * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
+ * something, and hence are a much stronger concept. */
+ if (enabled_at_all && unit_name_is_valid(name, UNIT_NAME_INSTANCE)) {
+ *state = UNIT_FILE_STATIC;
+ return 1;
}
/* Hmm, we didn't find it, but maybe we found the same name
* link? */
- if (same_name_link) {
+ if (same_name_link_config) {
*state = UNIT_FILE_LINKED;
return 1;
}
@@ -1409,7 +1458,7 @@ static int install_info_traverse(
return -ELOOP;
if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
- r = path_is_config(paths, i->path);
+ r = path_is_config(paths, i->path, true);
if (r < 0)
return r;
if (r > 0)
@@ -2040,7 +2089,7 @@ static int path_shall_revert(const LookupPaths *paths, const char *path) {
/* Checks whether the path is one where the drop-in directories shall be removed. */
- r = path_is_config(paths, path);
+ r = path_is_config(paths, path, true);
if (r != 0)
return r;
@@ -2148,7 +2197,7 @@ int unit_file_revert(
if (errno != ENOENT)
return -errno;
} else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
- r = path_is_config(&paths, path);
+ r = path_is_config(&paths, path, true);
if (r < 0)
return r;
if (r > 0) {
@@ -2494,7 +2543,7 @@ static int unit_file_lookup_state(
switch (i->type) {
case UNIT_FILE_TYPE_MASKED:
- r = path_is_runtime(paths, i->path);
+ r = path_is_runtime(paths, i->path, true);
if (r < 0)
return r;
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index d0bc8004f3..575401cb91 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -736,6 +736,28 @@ static void test_preset_order(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
}
+static void test_static_instance(const char *root) {
+ UnitFileState state;
+ const char *p;
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT);
+
+ p = strjoina(root, "/usr/lib/systemd/system/static-instance@.service");
+ assert_se(write_string_file(p,
+ "[Install]\n"
+ "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+
+ p = strjoina(root, "/usr/lib/systemd/system/static-instance@foo.service");
+ assert_se(symlink("static-instance@.service", p) >= 0);
+
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
+ assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC);
+}
+
int main(int argc, char *argv[]) {
char root[] = "/tmp/rootXXXXXX";
const char *p;
@@ -766,6 +788,7 @@ int main(int argc, char *argv[]) {
test_preset_and_list(root);
test_preset_order(root);
test_revert(root);
+ test_static_instance(root);
assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);