shared/install: ignore symlinks which have lower priority than the unit file

In #10583, a unit file lives in ~/.config/systemd/user, and
'systemctl --runtime --user mask' is used to create a symlink in /run.
This symlink has lower priority than the config file, so
'systemctl --user' will happily load the unit file, and does't care about
the symlink at all.

But when asked if the unit is enabled, we'd look for all symlinks, find the
symlink in the runtime directory, and report that the unit is runtime-enabled.
In this particular case the fact that the symlink points at /dev/null, creates
additional confusion, but it doesn't really matter: *any* symlink (or regular
file) that is lower in the priority order is "covered" by the unit fragment,
and should be ignored.

Fixes #10583.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-12-13 10:46:27 +01:00
parent 3e8d06d951
commit 4b37c89f06

View file

@ -715,6 +715,7 @@ static int find_symlinks_fd(
const char *root_dir,
const UnitFileInstallInfo *i,
bool match_aliases,
bool ignore_same_name,
int fd,
const char *path,
const char *config_path,
@ -761,7 +762,7 @@ static int find_symlinks_fd(
}
/* This will close nfd, regardless whether it succeeds or not */
q = find_symlinks_fd(root_dir, i, match_aliases, nfd,
q = find_symlinks_fd(root_dir, i, match_aliases, ignore_same_name, nfd,
p, config_path, same_name_link);
if (q > 0)
return 1;
@ -770,7 +771,7 @@ static int find_symlinks_fd(
} else if (de->d_type == DT_LNK) {
_cleanup_free_ char *p = NULL, *dest = NULL;
bool found_path, found_dest, b = false;
bool found_path = false, found_dest, b = false;
int q;
/* Acquire symlink name */
@ -799,9 +800,14 @@ static int find_symlinks_fd(
free_and_replace(dest, x);
}
/* Check if the symlink itself matches what we are looking for */
assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
found_path = streq(de->d_name, i->name);
if (!ignore_same_name)
/* Check if the symlink itself matches what we are looking for.
*
* If ignore_same_name is specified, we are in one of the directories which
* have lower priority than the unit file, and even if a file or symlink with
* this name was found, we should ignore it. */
found_path = streq(de->d_name, i->name);
/* Check if what the symlink points to matches what we are looking for */
found_dest = streq(basename(dest), i->name);
@ -841,6 +847,7 @@ static int find_symlinks(
const char *root_dir,
const UnitFileInstallInfo *i,
bool match_name,
bool ignore_same_name,
const char *config_path,
bool *same_name_link) {
@ -858,7 +865,7 @@ static int find_symlinks(
}
/* This takes possession of fd and closes it */
return find_symlinks_fd(root_dir, i, match_name, fd,
return find_symlinks_fd(root_dir, i, match_name, ignore_same_name, fd,
config_path, config_path, same_name_link);
}
@ -871,16 +878,21 @@ static int find_symlinks_in_scope(
bool same_name_link_runtime = false, same_name_link_config = false;
bool enabled_in_runtime = false, enabled_at_all = false;
bool ignore_same_name = false;
char **p;
int r;
assert(paths);
assert(i);
/* As we iterate over the list of search paths in paths->search_path, we may encounter "same name"
* symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
* efectively masked, so we should ignore them. */
STRV_FOREACH(p, paths->search_path) {
bool same_name_link = false;
r = find_symlinks(paths->root_dir, i, match_name, *p, &same_name_link);
r = find_symlinks(paths->root_dir, i, match_name, ignore_same_name, *p, &same_name_link);
if (r < 0)
return r;
if (r > 0) {
@ -917,6 +929,11 @@ static int find_symlinks_in_scope(
same_name_link_runtime = true;
}
}
/* Check if next iteration will be "below" the unit file (either a regular file
* or a symlink), and hence should be ignored */
if (!ignore_same_name && path_startswith(i->path, *p))
ignore_same_name = true;
}
if (enabled_in_runtime) {