1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "chase-symlinks.h"
6 #include "dirent-util.h"
10 #include "path-lookup.h"
13 #include "stat-util.h"
14 #include "string-util.h"
16 #include "unit-file.h"
18 bool unit_type_may_alias(UnitType type
) {
28 bool unit_type_may_template(UnitType type
) {
37 int unit_symlink_name_compatible(const char *symlink
, const char *target
, bool instance_propagation
) {
38 _cleanup_free_
char *template = NULL
;
39 int r
, un_type1
, un_type2
;
41 un_type1
= unit_name_classify(symlink
);
43 /* The straightforward case: the symlink name matches the target and we have a valid unit */
44 if (streq(symlink
, target
) &&
45 (un_type1
& (UNIT_NAME_PLAIN
| UNIT_NAME_INSTANCE
)))
48 r
= unit_name_template(symlink
, &template);
50 return 0; /* Not a template */
54 un_type2
= unit_name_classify(target
);
56 /* An instance name points to a target that is just the template name */
57 if (un_type1
== UNIT_NAME_INSTANCE
&&
58 un_type2
== UNIT_NAME_TEMPLATE
&&
59 streq(template, target
))
62 /* foo@.target.requires/bar@.service: instance will be propagated */
63 if (instance_propagation
&&
64 un_type1
== UNIT_NAME_TEMPLATE
&&
65 un_type2
== UNIT_NAME_TEMPLATE
&&
66 streq(template, target
))
72 int unit_validate_alias_symlink_and_warn(const char *filename
, const char *target
) {
73 const char *src
, *dst
;
74 _cleanup_free_
char *src_instance
= NULL
, *dst_instance
= NULL
;
75 UnitType src_unit_type
, dst_unit_type
;
76 UnitNameFlags src_name_type
, dst_name_type
;
78 /* Check if the *alias* symlink is valid. This applies to symlinks like
79 * /etc/systemd/system/dbus.service → dbus-broker.service, but not to .wants or .requires symlinks
80 * and such. Neither does this apply to symlinks which *link* units, i.e. symlinks to outside of the
83 * -EINVAL is returned if the something is wrong with the source filename or the source unit type is
84 * not allowed to symlink,
85 * -EXDEV if the target filename is not a valid unit name or doesn't match the source.
88 src
= basename(filename
);
89 dst
= basename(target
);
93 src_name_type
= unit_name_to_instance(src
, &src_instance
);
94 if (src_name_type
< 0)
95 return log_notice_errno(src_name_type
,
96 "%s: not a valid unit name \"%s\": %m", filename
, src
);
98 src_unit_type
= unit_name_to_type(src
);
99 assert(src_unit_type
>= 0); /* unit_name_to_instance() checked the suffix already */
101 if (!unit_type_may_alias(src_unit_type
))
102 return log_notice_errno(SYNTHETIC_ERRNO(EINVAL
),
103 "%s: symlinks are not allowed for units of this type, rejecting.",
106 if (src_name_type
!= UNIT_NAME_PLAIN
&&
107 !unit_type_may_template(src_unit_type
))
108 return log_notice_errno(SYNTHETIC_ERRNO(EINVAL
),
109 "%s: templates not allowed for %s units, rejecting.",
110 filename
, unit_type_to_string(src_unit_type
));
114 dst_name_type
= unit_name_to_instance(dst
, &dst_instance
);
115 if (dst_name_type
< 0)
116 return log_notice_errno(dst_name_type
== -EINVAL
? SYNTHETIC_ERRNO(EXDEV
) : dst_name_type
,
117 "%s points to \"%s\" which is not a valid unit name: %m",
120 if (!(dst_name_type
== src_name_type
||
121 (src_name_type
== UNIT_NAME_INSTANCE
&& dst_name_type
== UNIT_NAME_TEMPLATE
)))
122 return log_notice_errno(SYNTHETIC_ERRNO(EXDEV
),
123 "%s: symlink target name type \"%s\" does not match source, rejecting.",
126 if (dst_name_type
== UNIT_NAME_INSTANCE
) {
127 assert(src_instance
);
128 assert(dst_instance
);
129 if (!streq(src_instance
, dst_instance
))
130 return log_notice_errno(SYNTHETIC_ERRNO(EXDEV
),
131 "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
135 dst_unit_type
= unit_name_to_type(dst
);
136 if (dst_unit_type
!= src_unit_type
)
137 return log_notice_errno(SYNTHETIC_ERRNO(EXDEV
),
138 "%s: symlink target \"%s\" has incompatible suffix, rejecting.",
146 static int unit_ids_map_get(
147 Hashmap
*unit_ids_map
,
148 const char *unit_name
,
149 const char **ret_fragment_path
) {
151 /* Resolve recursively until we hit an absolute path, i.e. a non-aliased unit.
153 * We distinguish the case where unit_name was not found in the hashmap at all, and the case where
154 * some symlink was broken.
156 * If a symlink target points to an instance name, then we also check for the template. */
158 const char *id
= NULL
;
161 for (unsigned n
= 0; n
< FOLLOW_MAX
; n
++) {
162 const char *t
= hashmap_get(unit_ids_map
, id
?: unit_name
);
164 _cleanup_free_
char *template = NULL
;
169 r
= unit_name_template(id
, &template);
171 return -ENXIO
; /* we failed to find the symlink target */
173 return log_error_errno(r
, "Failed to determine template name for %s: %m", id
);
175 t
= hashmap_get(unit_ids_map
, template);
179 /* We successfully switched from instanced name to a template, let's continue */
182 if (path_is_absolute(t
)) {
183 if (ret_fragment_path
)
184 *ret_fragment_path
= t
;
194 static bool lookup_paths_mtime_exclude(const LookupPaths
*lp
, const char *path
) {
195 /* Paths that are under our exclusive control. Users shall not alter those directly. */
197 return streq_ptr(path
, lp
->generator
) ||
198 streq_ptr(path
, lp
->generator_early
) ||
199 streq_ptr(path
, lp
->generator_late
) ||
200 streq_ptr(path
, lp
->transient
) ||
201 streq_ptr(path
, lp
->persistent_control
) ||
202 streq_ptr(path
, lp
->runtime_control
);
205 #define HASH_KEY SD_ID128_MAKE(4e,86,1b,e3,39,b3,40,46,98,5d,b8,11,34,8f,c3,c1)
207 bool lookup_paths_timestamp_hash_same(const LookupPaths
*lp
, uint64_t timestamp_hash
, uint64_t *ret_new
) {
208 struct siphash state
;
210 siphash24_init(&state
, HASH_KEY
.bytes
);
212 STRV_FOREACH(dir
, lp
->search_path
) {
215 if (lookup_paths_mtime_exclude(lp
, *dir
))
218 /* Determine the latest lookup path modification time */
219 if (stat(*dir
, &st
) < 0) {
223 log_debug_errno(errno
, "Failed to stat %s, ignoring: %m", *dir
);
227 siphash24_compress_usec_t(timespec_load(&st
.st_mtim
), &state
);
230 uint64_t updated
= siphash24_finalize(&state
);
233 if (updated
!= timestamp_hash
)
234 log_debug("Modification times have changed, need to update cache.");
235 return updated
== timestamp_hash
;
238 static int directory_name_is_valid(const char *name
) {
241 /* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/ or .d/ */
243 FOREACH_STRING(suffix
, ".wants", ".requires", ".d") {
244 _cleanup_free_
char *chopped
= NULL
;
247 e
= endswith(name
, suffix
);
251 chopped
= strndup(name
, e
- name
);
255 if (unit_name_is_valid(chopped
, UNIT_NAME_ANY
) ||
256 unit_type_from_string(chopped
) >= 0)
263 int unit_file_build_name_map(
264 const LookupPaths
*lp
,
265 uint64_t *cache_timestamp_hash
,
266 Hashmap
**unit_ids_map
,
267 Hashmap
**unit_names_map
,
270 /* Build two mappings: any name → main unit (i.e. the end result of symlink resolution), unit name →
271 * all aliases (i.e. the entry for a given key is a list of all names which point to this key). The
272 * key is included in the value iff we saw a file or symlink with that name. In other words, if we
273 * have a key, but it is not present in the value for itself, there was an alias pointing to it, but
274 * the unit itself is not loadable.
276 * At the same, build a cache of paths where to find units. The non-const parameters are for input
277 * and output. Existing contents will be freed before the new contents are stored.
280 _cleanup_hashmap_free_ Hashmap
*ids
= NULL
, *names
= NULL
;
281 _cleanup_set_free_free_ Set
*paths
= NULL
;
282 uint64_t timestamp_hash
;
285 /* Before doing anything, check if the timestamp hash that was passed is still valid.
286 * If yes, do nothing. */
287 if (cache_timestamp_hash
&&
288 lookup_paths_timestamp_hash_same(lp
, *cache_timestamp_hash
, ×tamp_hash
))
291 /* The timestamp hash is now set based on the mtimes from before when we start reading files.
292 * If anything is modified concurrently, we'll consider the cache outdated. */
295 paths
= set_new(&path_hash_ops_free
);
300 STRV_FOREACH(dir
, lp
->search_path
) {
301 _cleanup_closedir_
DIR *d
= NULL
;
306 log_warning_errno(errno
, "Failed to open \"%s\", ignoring: %m", *dir
);
310 FOREACH_DIRENT_ALL(de
, d
, log_warning_errno(errno
, "Failed to read \"%s\", ignoring: %m", *dir
)) {
311 _unused_ _cleanup_free_
char *_filename_free
= NULL
;
312 _cleanup_free_
char *simplified
= NULL
;
313 bool symlink_to_dir
= false;
314 const char *dst
= NULL
;
317 /* We only care about valid units and dirs with certain suffixes, let's ignore the
320 if (de
->d_type
== DT_REG
) {
322 /* Accept a regular file whose name is a valid unit file name. */
323 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
326 } else if (de
->d_type
== DT_DIR
) {
328 if (!paths
) /* Skip directories early unless path_cache is requested */
331 r
= directory_name_is_valid(de
->d_name
);
337 } else if (de
->d_type
== DT_LNK
) {
339 /* Accept a symlink file whose name is a valid unit file name or
340 * ending in .wants/, .requires/ or .d/. */
342 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
)) {
343 _cleanup_free_
char *target
= NULL
;
345 if (!paths
) /* Skip symlink to a directory early unless path_cache is requested */
348 r
= directory_name_is_valid(de
->d_name
);
354 r
= readlinkat_malloc(dirfd(d
), de
->d_name
, &target
);
356 log_warning_errno(r
, "Failed to read symlink %s/%s, ignoring: %m",
361 r
= is_dir(target
, /* follow = */ true);
365 symlink_to_dir
= true;
371 filename
= path_join(*dir
, de
->d_name
);
376 r
= set_put(paths
, filename
);
380 _filename_free
= filename
; /* Make sure we free the filename. */
382 _filename_free
= filename
; /* Make sure we free the filename. */
384 if (de
->d_type
== DT_DIR
|| (de
->d_type
== DT_LNK
&& symlink_to_dir
))
387 assert(IN_SET(de
->d_type
, DT_REG
, DT_LNK
));
389 /* search_path is ordered by priority (highest first). If the name is already mapped
390 * to something (incl. itself), it means that we have already seen it, and we should
392 if (hashmap_contains(ids
, de
->d_name
))
395 if (de
->d_type
== DT_LNK
) {
396 /* We don't explicitly check for alias loops here. unit_ids_map_get() which
397 * limits the number of hops should be used to access the map. */
399 _cleanup_free_
char *target
= NULL
;
401 r
= readlinkat_malloc(dirfd(d
), de
->d_name
, &target
);
403 log_warning_errno(r
, "Failed to read symlink %s/%s, ignoring: %m",
408 const bool is_abs
= path_is_absolute(target
);
409 if (lp
->root_dir
|| !is_abs
) {
410 char *target_abs
= path_join(is_abs
? lp
->root_dir
: *dir
, target
);
414 free_and_replace(target
, target_abs
);
417 /* Get rid of "." and ".." components in target path */
418 r
= chase_symlinks(target
, lp
->root_dir
, CHASE_NOFOLLOW
| CHASE_NONEXISTENT
, &simplified
, NULL
);
420 log_warning_errno(r
, "Failed to resolve symlink %s pointing to %s, ignoring: %m",
425 /* Check if the symlink goes outside of our search path.
426 * If yes, it's a linked unit file or mask, and we don't care about the target name.
427 * Let's just store the link source directly.
428 * If not, let's verify that it's a good symlink. */
429 char *tail
= path_startswith_strv(simplified
, lp
->search_path
);
431 log_debug("%s: linked unit file: %s → %s",
432 __func__
, filename
, simplified
);
439 dst
= basename(simplified
);
440 self_alias
= streq(dst
, de
->d_name
);
443 log_full(self_alias
? LOG_DEBUG
: LOG_WARNING
,
444 "Suspicious symlink %s→%s, treating as alias.",
445 filename
, simplified
);
447 r
= unit_validate_alias_symlink_and_warn(filename
, simplified
);
452 /* A self-alias that has no effect */
453 log_debug("%s: self-alias: %s/%s → %s, ignoring.",
454 __func__
, *dir
, de
->d_name
, dst
);
458 log_debug("%s: alias: %s/%s → %s", __func__
, *dir
, de
->d_name
, dst
);
463 log_debug("%s: normal unit file: %s", __func__
, dst
);
466 r
= hashmap_put_strdup(&ids
, de
->d_name
, dst
);
468 return log_warning_errno(r
, "Failed to add entry to hashmap (%s→%s): %m",
473 /* Let's also put the names in the reverse db. */
474 const char *dummy
, *src
;
475 HASHMAP_FOREACH_KEY(dummy
, src
, ids
) {
476 _cleanup_free_
char *inst
= NULL
, *dst_inst
= NULL
;
479 r
= unit_ids_map_get(ids
, src
, &dst
);
483 if (null_or_empty_path(dst
) != 0)
488 /* If we have an symlink from an instance name to a template name, it is an alias just for
489 * this specific instance, foo@id.service ↔ template@id.service. */
490 if (unit_name_is_valid(dst
, UNIT_NAME_TEMPLATE
)) {
491 UnitNameFlags t
= unit_name_to_instance(src
, &inst
);
493 return log_error_errno(t
, "Failed to extract instance part from %s: %m", src
);
494 if (t
== UNIT_NAME_INSTANCE
) {
495 r
= unit_name_replace_instance(dst
, inst
, &dst_inst
);
497 /* This might happen e.g. if the combined length is too large.
498 * Let's not make too much of a fuss. */
499 log_debug_errno(r
, "Failed to build alias name (%s + %s), ignoring: %m",
508 r
= string_strv_hashmap_put(&names
, dst
, src
);
510 return log_warning_errno(r
, "Failed to add entry to hashmap (%s→%s): %m", dst
, src
);
513 if (cache_timestamp_hash
)
514 *cache_timestamp_hash
= timestamp_hash
;
516 hashmap_free_and_replace(*unit_ids_map
, ids
);
517 hashmap_free_and_replace(*unit_names_map
, names
);
519 set_free_and_replace(*path_cache
, paths
);
525 const char *unit_name
,
533 r
= set_put_strdup(names
, name
);
536 if (r
> 0 && !streq(unit_name
, name
))
537 log_debug("Unit %s has alias %s.", unit_name
, name
);
541 static int add_names(
542 Hashmap
*unit_ids_map
,
543 Hashmap
*unit_name_map
,
544 const char *unit_name
,
545 const char *fragment_basename
, /* Only set when adding additional names based on fragment path */
546 UnitNameFlags name_type
,
547 const char *instance
,
554 assert(name_type
== UNIT_NAME_PLAIN
|| instance
);
556 /* The unit has its own name if it's not a template. If we're looking at a fragment, the fragment
557 * name (possibly with instance inserted), is also always one of the unit names. */
558 if (name_type
!= UNIT_NAME_TEMPLATE
) {
559 r
= add_name(unit_name
, names
, name
);
564 /* Add any aliases of the name to the set of names.
566 * We don't even need to know which fragment we will use. The unit_name_map should return the same
567 * set of names for any of the aliases. */
568 aliases
= hashmap_get(unit_name_map
, name
);
569 STRV_FOREACH(alias
, aliases
) {
570 if (name_type
== UNIT_NAME_INSTANCE
&& unit_name_is_valid(*alias
, UNIT_NAME_TEMPLATE
)) {
571 _cleanup_free_
char *inst
= NULL
;
572 const char *inst_fragment
= NULL
;
574 r
= unit_name_replace_instance(*alias
, instance
, &inst
);
576 return log_debug_errno(r
, "Cannot build instance name %s + %s: %m",
579 /* Exclude any aliases that point in some other direction.
581 * See https://github.com/systemd/systemd/pull/13119#discussion_r308145418. */
582 r
= unit_ids_map_get(unit_ids_map
, inst
, &inst_fragment
);
583 if (r
< 0 && !IN_SET(r
, -ENOENT
, -ENXIO
))
584 return log_debug_errno(r
, "Cannot find instance fragment %s: %m", inst
);
587 !streq(basename(inst_fragment
), fragment_basename
)) {
588 log_debug("Instance %s has fragment %s and is not an alias of %s.",
589 inst
, inst_fragment
, unit_name
);
593 r
= set_consume(*names
, TAKE_PTR(inst
));
595 log_debug("Unit %s has alias %s.", unit_name
, inst
);
597 r
= add_name(unit_name
, names
, *alias
);
606 int unit_file_find_fragment(
607 Hashmap
*unit_ids_map
,
608 Hashmap
*unit_name_map
,
609 const char *unit_name
,
610 const char **ret_fragment_path
,
613 const char *fragment
= NULL
;
614 _cleanup_free_
char *template = NULL
, *instance
= NULL
;
615 _cleanup_set_free_ Set
*names
= NULL
;
618 /* Finds a fragment path, and returns the set of names:
619 * if we have …/foo.service and …/foo-alias.service→foo.service,
620 * and …/foo@.service and …/foo-alias@.service→foo@.service,
621 * and …/foo@inst.service,
622 * this should return:
623 * foo.service → …/foo.service, {foo.service, foo-alias.service},
624 * foo-alias.service → …/foo.service, {foo.service, foo-alias.service},
625 * foo@.service → …/foo@.service, {foo@.service, foo-alias@.service},
626 * foo-alias@.service → …/foo@.service, {foo@.service, foo-alias@.service},
627 * foo@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
628 * foo-alias@bar.service → …/foo@.service, {foo@bar.service, foo-alias@bar.service},
629 * foo-alias@inst.service → …/foo@inst.service, {foo@inst.service, foo-alias@inst.service}.
632 UnitNameFlags name_type
= unit_name_to_instance(unit_name
, &instance
);
637 r
= add_names(unit_ids_map
, unit_name_map
, unit_name
, NULL
, name_type
, instance
, &names
, unit_name
);
642 /* First try to load fragment under the original name */
643 r
= unit_ids_map_get(unit_ids_map
, unit_name
, &fragment
);
644 if (r
< 0 && !IN_SET(r
, -ENOENT
, -ENXIO
))
645 return log_debug_errno(r
, "Cannot load unit %s: %m", unit_name
);
647 if (!fragment
&& name_type
== UNIT_NAME_INSTANCE
) {
648 /* Look for a fragment under the template name */
650 r
= unit_name_template(unit_name
, &template);
652 return log_debug_errno(r
, "Failed to determine template name: %m");
654 r
= unit_ids_map_get(unit_ids_map
, template, &fragment
);
655 if (r
< 0 && !IN_SET(r
, -ENOENT
, -ENXIO
))
656 return log_debug_errno(r
, "Cannot load template %s: %m", template);
659 if (fragment
&& ret_names
) {
660 const char *fragment_basename
= basename(fragment
);
662 if (!streq(fragment_basename
, unit_name
)) {
663 /* Add names based on the fragment name to the set of names */
664 r
= add_names(unit_ids_map
, unit_name_map
, unit_name
, fragment_basename
, name_type
, instance
, &names
, fragment_basename
);
670 *ret_fragment_path
= fragment
;
672 *ret_names
= TAKE_PTR(names
);
677 static const char * const rlmap
[] = {
678 "emergency", SPECIAL_EMERGENCY_TARGET
,
679 "-b", SPECIAL_EMERGENCY_TARGET
,
680 "rescue", SPECIAL_RESCUE_TARGET
,
681 "single", SPECIAL_RESCUE_TARGET
,
682 "-s", SPECIAL_RESCUE_TARGET
,
683 "s", SPECIAL_RESCUE_TARGET
,
684 "S", SPECIAL_RESCUE_TARGET
,
685 "1", SPECIAL_RESCUE_TARGET
,
686 "2", SPECIAL_MULTI_USER_TARGET
,
687 "3", SPECIAL_MULTI_USER_TARGET
,
688 "4", SPECIAL_MULTI_USER_TARGET
,
689 "5", SPECIAL_GRAPHICAL_TARGET
,
693 static const char * const rlmap_initrd
[] = {
694 "emergency", SPECIAL_EMERGENCY_TARGET
,
695 "rescue", SPECIAL_RESCUE_TARGET
,
699 const char* runlevel_to_target(const char *word
) {
700 const char * const *rlmap_ptr
;
706 word
= startswith(word
, "rd.");
711 rlmap_ptr
= in_initrd() ? rlmap_initrd
: rlmap
;
713 for (size_t i
= 0; rlmap_ptr
[i
]; i
+= 2)
714 if (streq(word
, rlmap_ptr
[i
]))
715 return rlmap_ptr
[i
+1];