1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "dirent-util.h"
7 #include "path-lookup.h"
10 #include "string-util.h"
12 #include "unit-file.h"
14 bool unit_type_may_alias(UnitType type
) {
24 bool unit_type_may_template(UnitType type
) {
33 int unit_validate_alias_symlink_and_warn(const char *filename
, const char *target
) {
34 const char *src
, *dst
;
35 _cleanup_free_
char *src_instance
= NULL
, *dst_instance
= NULL
;
36 UnitType src_unit_type
, dst_unit_type
;
37 int src_name_type
, dst_name_type
;
39 /* Check if the *alias* symlink is valid. This applies to symlinks like
40 * /etc/systemd/system/dbus.service → dbus-broker.service, but not to .wants or .requires symlinks
41 * and such. Neither does this apply to symlinks which *link* units, i.e. symlinks to outside of the
44 * -EINVAL is returned if the something is wrong with the source filename or the source unit type is
45 * not allowed to symlink,
46 * -EXDEV if the target filename is not a valid unit name or doesn't match the source.
49 src
= basename(filename
);
50 dst
= basename(target
);
54 src_name_type
= unit_name_to_instance(src
, &src_instance
);
55 if (src_name_type
< 0)
56 return log_notice_errno(src_name_type
,
57 "%s: not a valid unit name \"%s\": %m", filename
, src
);
59 src_unit_type
= unit_name_to_type(src
);
60 assert(src_unit_type
>= 0); /* unit_name_to_instance() checked the suffix already */
62 if (!unit_type_may_alias(src_unit_type
))
63 return log_notice_errno(SYNTHETIC_ERRNO(EINVAL
),
64 "%s: symlinks are not allowed for units of this type, rejecting.",
67 if (src_name_type
!= UNIT_NAME_PLAIN
&&
68 !unit_type_may_template(src_unit_type
))
69 return log_notice_errno(SYNTHETIC_ERRNO(EINVAL
),
70 "%s: templates not allowed for %s units, rejecting.",
71 filename
, unit_type_to_string(src_unit_type
));
75 dst_name_type
= unit_name_to_instance(dst
, &dst_instance
);
76 if (dst_name_type
< 0)
77 return log_notice_errno(dst_name_type
== -EINVAL
? SYNTHETIC_ERRNO(EXDEV
) : dst_name_type
,
78 "%s points to \"%s\" which is not a valid unit name: %m",
81 if (!(dst_name_type
== src_name_type
||
82 (src_name_type
== UNIT_NAME_INSTANCE
&& dst_name_type
== UNIT_NAME_TEMPLATE
)))
83 return log_notice_errno(SYNTHETIC_ERRNO(EXDEV
),
84 "%s: symlink target name type \"%s\" does not match source, rejecting.",
87 if (dst_name_type
== UNIT_NAME_INSTANCE
) {
90 if (!streq(src_instance
, dst_instance
))
91 return log_notice_errno(SYNTHETIC_ERRNO(EXDEV
),
92 "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
96 dst_unit_type
= unit_name_to_type(dst
);
97 if (dst_unit_type
!= src_unit_type
)
98 return log_notice_errno(SYNTHETIC_ERRNO(EXDEV
),
99 "%s: symlink target \"%s\" has incompatible suffix, rejecting.",
107 static int unit_ids_map_get(
108 Hashmap
*unit_ids_map
,
109 const char *unit_name
,
110 const char **ret_fragment_path
) {
112 /* Resolve recursively until we hit an absolute path, i.e. a non-aliased unit.
114 * We distinguish the case where unit_name was not found in the hashmap at all, and the case where
115 * some symlink was broken.
117 * If a symlink target points to an instance name, then we also check for the template. */
119 const char *id
= NULL
;
122 for (unsigned n
= 0; n
< FOLLOW_MAX
; n
++) {
123 const char *t
= hashmap_get(unit_ids_map
, id
?: unit_name
);
125 _cleanup_free_
char *template = NULL
;
130 r
= unit_name_template(id
, &template);
132 return -ENXIO
; /* we failed to find the symlink target */
134 return log_error_errno(r
, "Failed to determine template name for %s: %m", id
);
136 t
= hashmap_get(unit_ids_map
, template);
140 /* We successfully switched from instanced name to a template, let's continue */
143 if (path_is_absolute(t
)) {
144 if (ret_fragment_path
)
145 *ret_fragment_path
= t
;
155 static bool lookup_paths_mtime_exclude(const LookupPaths
*lp
, const char *path
) {
156 /* Paths that are under our exclusive control. Users shall not alter those directly. */
158 return streq_ptr(path
, lp
->generator
) ||
159 streq_ptr(path
, lp
->generator_early
) ||
160 streq_ptr(path
, lp
->generator_late
) ||
161 streq_ptr(path
, lp
->transient
) ||
162 streq_ptr(path
, lp
->persistent_control
) ||
163 streq_ptr(path
, lp
->runtime_control
);
166 static bool lookup_paths_mtime_good(const LookupPaths
*lp
, usec_t mtime
) {
169 STRV_FOREACH(dir
, (char**) lp
->search_path
) {
172 if (lookup_paths_mtime_exclude(lp
, *dir
))
175 /* Determine the latest lookup path modification time */
176 if (stat(*dir
, &st
) < 0) {
180 log_debug_errno(errno
, "Failed to stat %s, ignoring: %m", *dir
);
184 if (timespec_load(&st
.st_mtim
) > mtime
) {
185 log_debug_errno(errno
, "Unit dir %s has changed, need to update cache.", *dir
);
193 int unit_file_build_name_map(
194 const LookupPaths
*lp
,
196 Hashmap
**ret_unit_ids_map
,
197 Hashmap
**ret_unit_names_map
,
198 Set
**ret_path_cache
) {
200 /* Build two mappings: any name → main unit (i.e. the end result of symlink resolution), unit name →
201 * all aliases (i.e. the entry for a given key is a a list of all names which point to this key). The
202 * key is included in the value iff we saw a file or symlink with that name. In other words, if we
203 * have a key, but it is not present in the value for itself, there was an alias pointing to it, but
204 * the unit itself is not loadable.
206 * At the same, build a cache of paths where to find units.
209 _cleanup_hashmap_free_ Hashmap
*ids
= NULL
, *names
= NULL
;
210 _cleanup_set_free_free_ Set
*paths
= NULL
;
215 /* Before doing anything, check if the mtime that was passed is still valid. If
216 * yes, do nothing. If *cache_time == 0, always build the cache. */
217 if (cache_mtime
&& *cache_mtime
> 0 && lookup_paths_mtime_good(lp
, *cache_mtime
))
220 if (ret_path_cache
) {
221 paths
= set_new(&path_hash_ops
);
226 STRV_FOREACH(dir
, (char**) lp
->search_path
) {
228 _cleanup_closedir_
DIR *d
= NULL
;
234 log_warning_errno(errno
, "Failed to open \"%s\", ignoring: %m", *dir
);
238 /* Determine the latest lookup path modification time */
239 if (fstat(dirfd(d
), &st
) < 0)
240 return log_error_errno(errno
, "Failed to fstat %s: %m", *dir
);
242 if (!lookup_paths_mtime_exclude(lp
, *dir
))
243 mtime
= MAX(mtime
, timespec_load(&st
.st_mtim
));
245 FOREACH_DIRENT(de
, d
, log_warning_errno(errno
, "Failed to read \"%s\", ignoring: %m", *dir
)) {
247 _cleanup_free_
char *_filename_free
= NULL
, *simplified
= NULL
;
248 const char *suffix
, *dst
= NULL
;
250 filename
= path_join(*dir
, de
->d_name
);
254 if (ret_path_cache
) {
255 r
= set_consume(paths
, filename
);
258 /* We will still use filename below. This is safe because we know the set
259 * holds a reference. */
261 _filename_free
= filename
; /* Make sure we free the filename. */
263 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
265 assert_se(suffix
= strrchr(de
->d_name
, '.'));
267 /* search_path is ordered by priority (highest first). If the name is already mapped
268 * to something (incl. itself), it means that we have already seen it, and we should
270 if (hashmap_contains(ids
, de
->d_name
))
273 if (de
->d_type
== DT_LNK
) {
274 /* We don't explicitly check for alias loops here. unit_ids_map_get() which
275 * limits the number of hops should be used to access the map. */
277 _cleanup_free_
char *target
= NULL
, *target_abs
= NULL
;
279 r
= readlinkat_malloc(dirfd(d
), de
->d_name
, &target
);
281 log_warning_errno(r
, "Failed to read symlink %s/%s, ignoring: %m",
286 if (!path_is_absolute(target
)) {
287 target_abs
= path_join(*dir
, target
);
291 free_and_replace(target
, target_abs
);
294 /* Get rid of "." and ".." components in target path */
295 r
= chase_symlinks(target
, lp
->root_dir
, CHASE_NOFOLLOW
| CHASE_NONEXISTENT
, &simplified
);
297 log_warning_errno(r
, "Failed to resolve symlink %s pointing to %s, ignoring: %m",
302 /* Check if the symlink goes outside of our search path.
303 * If yes, it's a linked unit file or mask, and we don't care about the target name.
304 * Let's just store the link destination directly.
305 * If not, let's verify that it's a good symlink. */
306 char *tail
= path_startswith_strv(simplified
, lp
->search_path
);
310 dst
= basename(simplified
);
311 self_alias
= streq(dst
, de
->d_name
);
314 log_full(self_alias
? LOG_DEBUG
: LOG_WARNING
,
315 "Suspicious symlink %s→%s, treating as alias.",
316 filename
, simplified
);
318 r
= unit_validate_alias_symlink_and_warn(filename
, simplified
);
323 /* A self-alias that has no effect */
324 log_debug("%s: self-alias: %s/%s → %s, ignoring.",
325 __func__
, *dir
, de
->d_name
, dst
);
329 log_debug("%s: alias: %s/%s → %s", __func__
, *dir
, de
->d_name
, dst
);
333 log_debug("%s: linked unit file: %s/%s → %s", __func__
, *dir
, de
->d_name
, dst
);
338 log_debug("%s: normal unit file: %s", __func__
, dst
);
341 r
= hashmap_put_strdup(&ids
, de
->d_name
, dst
);
343 return log_warning_errno(r
, "Failed to add entry to hashmap (%s→%s): %m",
348 /* Let's also put the names in the reverse db. */
350 const char *dummy
, *src
;
351 HASHMAP_FOREACH_KEY(dummy
, src
, ids
, it
) {
354 r
= unit_ids_map_get(ids
, src
, &dst
);
358 if (null_or_empty_path(dst
) != 0)
361 /* Do not treat instance symlinks that point to the template as aliases */
362 if (unit_name_is_valid(basename(dst
), UNIT_NAME_TEMPLATE
) &&
363 unit_name_is_valid(src
, UNIT_NAME_INSTANCE
))
366 r
= string_strv_hashmap_put(&names
, basename(dst
), src
);
368 return log_warning_errno(r
, "Failed to add entry to hashmap (%s→%s): %m",
373 *cache_mtime
= mtime
;
374 *ret_unit_ids_map
= TAKE_PTR(ids
);
375 *ret_unit_names_map
= TAKE_PTR(names
);
377 *ret_path_cache
= TAKE_PTR(paths
);
382 int unit_file_find_fragment(
383 Hashmap
*unit_ids_map
,
384 Hashmap
*unit_name_map
,
385 const char *unit_name
,
386 const char **ret_fragment_path
,
389 const char *fragment
= NULL
;
390 _cleanup_free_
char *template = NULL
, *instance
= NULL
;
391 _cleanup_set_free_free_ Set
*names
= NULL
;
395 /* Finds a fragment path, and returns the set of names:
396 * if we have …/foo.service and …/foo-alias.service→foo.service,
397 * and …/foo@.service and …/foo-alias@.service→foo@.service,
398 * and …/foo@inst.service,
399 * this should return:
400 * foo.service → …/foo.service, {foo.service, foo-alias.service},
401 * foo-alias.service → …/foo.service, {foo.service, foo-alias.service},
402 * foo@.service → …/foo@.service, {foo@.service, foo-alias@.service},
403 * foo-alias@.service → …/foo@.service, {foo@.service, foo-alias@.service},
404 * foo@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
405 * foo-alias@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
406 * foo-alias@inst.service → …/foo@inst.service, {foo@inst.service, foo-alias@inst.service}.
409 name_type
= unit_name_to_instance(unit_name
, &instance
);
413 names
= set_new(&string_hash_ops
);
417 /* The unit always has its own name if it's not a template. */
418 if (unit_name_is_valid(unit_name
, UNIT_NAME_PLAIN
| UNIT_NAME_INSTANCE
)) {
419 r
= set_put_strdup(names
, unit_name
);
424 /* First try to load fragment under the original name */
425 r
= unit_ids_map_get(unit_ids_map
, unit_name
, &fragment
);
426 if (r
< 0 && !IN_SET(r
, -ENOENT
, -ENXIO
))
427 return log_debug_errno(r
, "Cannot load unit %s: %m", unit_name
);
430 /* Add any aliases of the original name to the set of names */
431 nnn
= hashmap_get(unit_name_map
, basename(fragment
));
432 STRV_FOREACH(t
, nnn
) {
433 if (instance
&& unit_name_is_valid(*t
, UNIT_NAME_TEMPLATE
)) {
436 r
= unit_name_replace_instance(*t
, instance
, &inst
);
438 return log_debug_errno(r
, "Cannot build instance name %s+%s: %m", *t
, instance
);
440 if (!streq(unit_name
, inst
))
441 log_debug("%s: %s has alias %s", __func__
, unit_name
, inst
);
443 log_info("%s: %s+%s → %s", __func__
, *t
, instance
, inst
);
444 r
= set_consume(names
, inst
);
446 if (!streq(unit_name
, *t
))
447 log_debug("%s: %s has alias %s", __func__
, unit_name
, *t
);
449 r
= set_put_strdup(names
, *t
);
456 if (!fragment
&& name_type
== UNIT_NAME_INSTANCE
) {
457 /* Look for a fragment under the template name */
459 r
= unit_name_template(unit_name
, &template);
461 return log_error_errno(r
, "Failed to determine template name: %m");
463 r
= unit_ids_map_get(unit_ids_map
, template, &fragment
);
464 if (r
< 0 && !IN_SET(r
, -ENOENT
, -ENXIO
))
465 return log_debug_errno(r
, "Cannot load template %s: %m", *t
);
468 /* Add any aliases of the original name to the set of names */
469 nnn
= hashmap_get(unit_name_map
, basename(fragment
));
470 STRV_FOREACH(t
, nnn
) {
471 _cleanup_free_
char *inst
= NULL
;
472 const char *inst_fragment
= NULL
;
474 r
= unit_name_replace_instance(*t
, instance
, &inst
);
476 return log_debug_errno(r
, "Cannot build instance name %s+%s: %m", template, instance
);
478 /* Exclude any aliases that point in some other direction. */
479 r
= unit_ids_map_get(unit_ids_map
, inst
, &inst_fragment
);
480 if (r
< 0 && !IN_SET(r
, -ENOENT
, -ENXIO
))
481 return log_debug_errno(r
, "Cannot find instance fragment %s: %m", inst
);
484 !streq(basename(inst_fragment
), basename(fragment
))) {
485 log_debug("Instance %s has fragment %s and is not an alias of %s.",
486 inst
, inst_fragment
, unit_name
);
490 if (!streq(unit_name
, inst
))
491 log_info("%s: %s has alias %s", __func__
, unit_name
, inst
);
492 r
= set_consume(names
, TAKE_PTR(inst
));
499 *ret_fragment_path
= fragment
;
500 *ret_names
= TAKE_PTR(names
);
502 // FIXME: if instance, consider any unit names with different template name