mirror of
https://github.com/systemd/systemd.git
synced 2025-03-19 22:50:17 +03:00
install: refactor find_symlinks() and don't search for symlinks recursively
After all we are only interested in symlinks either in top-level config directory or in .wants and .requires sub-directories. As a bonus this should speed up ListUnitFiles() roughly 3-4x on systems with a lot of units that use drop-ins (e.g. SSH jump hosts with a lot of user session scopes).
This commit is contained in:
parent
170c659338
commit
43b4e3058c
@ -705,132 +705,88 @@ static int is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *
|
||||
return false;
|
||||
}
|
||||
|
||||
static int find_symlinks_fd(
|
||||
static int find_symlinks_in_directory(
|
||||
DIR *dir,
|
||||
const char *dir_path,
|
||||
const char *root_dir,
|
||||
const UnitFileInstallInfo *i,
|
||||
bool match_aliases,
|
||||
bool ignore_same_name,
|
||||
int fd,
|
||||
const char *path,
|
||||
const char *config_path,
|
||||
bool *same_name_link) {
|
||||
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
struct dirent *de;
|
||||
int r = 0;
|
||||
|
||||
assert(i);
|
||||
assert(fd >= 0);
|
||||
assert(path);
|
||||
assert(config_path);
|
||||
assert(same_name_link);
|
||||
FOREACH_DIRENT(de, dir, return -errno) {
|
||||
_cleanup_free_ char *dest = NULL;
|
||||
bool found_path = false, found_dest, b = false;
|
||||
int q;
|
||||
|
||||
d = fdopendir(fd);
|
||||
if (!d) {
|
||||
safe_close(fd);
|
||||
return -errno;
|
||||
}
|
||||
dirent_ensure_type(dir, de);
|
||||
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
if (de->d_type != DT_LNK)
|
||||
continue;
|
||||
|
||||
dirent_ensure_type(d, de);
|
||||
|
||||
if (de->d_type == DT_DIR) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int nfd, q;
|
||||
|
||||
nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
|
||||
if (nfd < 0) {
|
||||
if (errno == ENOENT)
|
||||
continue;
|
||||
|
||||
if (r == 0)
|
||||
r = -errno;
|
||||
continue;
|
||||
}
|
||||
|
||||
p = path_make_absolute(de->d_name, path);
|
||||
if (!p) {
|
||||
safe_close(nfd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* This will close nfd, regardless whether it succeeds or not */
|
||||
q = find_symlinks_fd(root_dir, i, match_aliases, ignore_same_name, nfd,
|
||||
p, config_path, same_name_link);
|
||||
if (q > 0)
|
||||
return 1;
|
||||
/* Acquire symlink destination */
|
||||
q = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
|
||||
if (q == -ENOENT)
|
||||
continue;
|
||||
if (q < 0) {
|
||||
if (r == 0)
|
||||
r = q;
|
||||
continue;
|
||||
}
|
||||
|
||||
} else if (de->d_type == DT_LNK) {
|
||||
_cleanup_free_ char *p = NULL, *dest = NULL;
|
||||
bool found_path = false, found_dest, b = false;
|
||||
int q;
|
||||
/* Make absolute */
|
||||
if (!path_is_absolute(dest)) {
|
||||
char *x;
|
||||
|
||||
/* Acquire symlink name */
|
||||
p = path_make_absolute(de->d_name, path);
|
||||
if (!p)
|
||||
x = path_join(dir_path, dest);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Acquire symlink destination */
|
||||
q = readlink_malloc(p, &dest);
|
||||
if (q == -ENOENT)
|
||||
continue;
|
||||
if (q < 0) {
|
||||
if (r == 0)
|
||||
r = q;
|
||||
continue;
|
||||
}
|
||||
free_and_replace(dest, x);
|
||||
}
|
||||
|
||||
/* Make absolute */
|
||||
if (!path_is_absolute(dest)) {
|
||||
char *x;
|
||||
assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
|
||||
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);
|
||||
|
||||
x = path_join(root_dir, dest);
|
||||
if (!x)
|
||||
return -ENOMEM;
|
||||
/* Check if what the symlink points to matches what we are looking for */
|
||||
found_dest = streq(basename(dest), i->name);
|
||||
|
||||
free_and_replace(dest, x);
|
||||
}
|
||||
if (found_path && found_dest) {
|
||||
_cleanup_free_ char *p = NULL, *t = NULL;
|
||||
|
||||
assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
|
||||
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);
|
||||
/* Filter out same name links in the main
|
||||
* config path */
|
||||
p = path_make_absolute(de->d_name, dir_path);
|
||||
t = path_make_absolute(i->name, config_path);
|
||||
|
||||
/* Check if what the symlink points to matches what we are looking for */
|
||||
found_dest = streq(basename(dest), i->name);
|
||||
if (!p || !t)
|
||||
return -ENOMEM;
|
||||
|
||||
if (found_path && found_dest) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
b = path_equal(p, t);
|
||||
}
|
||||
|
||||
/* Filter out same name links in the main
|
||||
* config path */
|
||||
t = path_make_absolute(i->name, config_path);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
if (b)
|
||||
*same_name_link = true;
|
||||
else if (found_path || found_dest) {
|
||||
if (!match_aliases)
|
||||
return 1;
|
||||
|
||||
b = path_equal(t, p);
|
||||
}
|
||||
|
||||
if (b)
|
||||
*same_name_link = true;
|
||||
else if (found_path || found_dest) {
|
||||
if (!match_aliases)
|
||||
return 1;
|
||||
|
||||
/* Check if symlink name is in the set of names used by [Install] */
|
||||
q = is_symlink_with_known_name(i, de->d_name);
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0)
|
||||
return 1;
|
||||
}
|
||||
/* Check if symlink name is in the set of names used by [Install] */
|
||||
q = is_symlink_with_known_name(i, de->d_name);
|
||||
if (q < 0)
|
||||
return q;
|
||||
if (q > 0)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -845,22 +801,55 @@ static int find_symlinks(
|
||||
const char *config_path,
|
||||
bool *same_name_link) {
|
||||
|
||||
int fd;
|
||||
_cleanup_closedir_ DIR *config_dir = NULL;
|
||||
struct dirent *de;
|
||||
int r = 0;
|
||||
|
||||
assert(i);
|
||||
assert(config_path);
|
||||
assert(same_name_link);
|
||||
|
||||
fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
config_dir = opendir(config_path);
|
||||
if (!config_dir) {
|
||||
if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
|
||||
return 0;
|
||||
return -errno;
|
||||
}
|
||||
|
||||
/* This takes possession of fd and closes it */
|
||||
return find_symlinks_fd(root_dir, i, match_name, ignore_same_name, fd,
|
||||
config_path, config_path, same_name_link);
|
||||
FOREACH_DIRENT(de, config_dir, return -errno) {
|
||||
const char *suffix;
|
||||
_cleanup_free_ const char *path = NULL;
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
|
||||
dirent_ensure_type(config_dir, de);
|
||||
|
||||
if (de->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
suffix = strrchr(de->d_name, '.');
|
||||
if (!STRPTR_IN_SET(suffix, ".wants", ".requires"))
|
||||
continue;
|
||||
|
||||
path = path_join(config_path, de->d_name);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
d = opendir(path);
|
||||
if (!d) {
|
||||
log_error_errno(errno, "Failed to open directory '%s' while scanning for symlinks, ignoring: %m", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = find_symlinks_in_directory(d, path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link);
|
||||
if (r > 0)
|
||||
return 1;
|
||||
else if (r < 0)
|
||||
log_debug_errno(r, "Failed to lookup for symlinks in '%s': %m", path);
|
||||
}
|
||||
|
||||
/* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */
|
||||
rewinddir(config_dir);
|
||||
return find_symlinks_in_directory(config_dir, config_path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link);
|
||||
}
|
||||
|
||||
static int find_symlinks_in_scope(
|
||||
|
Loading…
x
Reference in New Issue
Block a user