1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
12 #include "alloc-util.h"
13 #include "chase-symlinks.h"
14 #include "conf-files.h"
15 #include "conf-parser.h"
16 #include "constants.h"
17 #include "dirent-util.h"
18 #include "errno-list.h"
19 #include "extract-word.h"
24 #include "install-printf.h"
26 #include "locale-util.h"
29 #include "mkdir-label.h"
30 #include "path-lookup.h"
31 #include "path-util.h"
35 #include "stat-util.h"
36 #include "string-table.h"
37 #include "string-util.h"
39 #include "unit-file.h"
41 #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
43 typedef enum SearchFlags
{
45 SEARCH_FOLLOW_CONFIG_SYMLINKS
= 1 << 1,
46 SEARCH_DROPIN
= 1 << 2,
51 OrderedHashmap
*will_process
;
52 OrderedHashmap
*have_processed
;
61 struct UnitFilePresetRule
{
67 static bool install_info_has_rules(const InstallInfo
*i
) {
70 return !strv_isempty(i
->aliases
) ||
71 !strv_isempty(i
->wanted_by
) ||
72 !strv_isempty(i
->required_by
);
75 static bool install_info_has_also(const InstallInfo
*i
) {
78 return !strv_isempty(i
->also
);
81 void unit_file_presets_freep(UnitFilePresets
*p
) {
85 for (size_t i
= 0; i
< p
->n_rules
; i
++) {
86 free(p
->rules
[i
].pattern
);
87 strv_free(p
->rules
[i
].instances
);
94 static const char *const install_mode_table
[_INSTALL_MODE_MAX
] = {
95 [INSTALL_MODE_REGULAR
] = "regular",
96 [INSTALL_MODE_LINKED
] = "linked",
97 [INSTALL_MODE_ALIAS
] = "alias",
98 [INSTALL_MODE_MASKED
] = "masked",
101 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(install_mode
, InstallMode
);
103 static int in_search_path(const LookupPaths
*lp
, const char *path
) {
104 _cleanup_free_
char *parent
= NULL
;
107 /* Check if 'path' is in lp->search_path. */
109 r
= path_extract_directory(ASSERT_PTR(path
), &parent
);
113 return path_strv_contains(ASSERT_PTR(lp
)->search_path
, parent
);
116 static int underneath_search_path(const LookupPaths
*lp
, const char *path
) {
117 /* Check if 'path' is underneath lp->search_path. */
119 return !!path_startswith_strv(ASSERT_PTR(path
), ASSERT_PTR(lp
)->search_path
);
122 static const char* skip_root(const char *root_dir
, const char *path
) {
128 const char *e
= path_startswith(path
, root_dir
);
132 /* Make sure the returned path starts with a slash */
134 if (e
== path
|| e
[-1] != '/')
143 static int path_is_generator(const LookupPaths
*lp
, const char *path
) {
144 _cleanup_free_
char *parent
= NULL
;
150 r
= path_extract_directory(path
, &parent
);
154 return path_equal_ptr(parent
, lp
->generator
) ||
155 path_equal_ptr(parent
, lp
->generator_early
) ||
156 path_equal_ptr(parent
, lp
->generator_late
);
159 static int path_is_transient(const LookupPaths
*lp
, const char *path
) {
160 _cleanup_free_
char *parent
= NULL
;
166 r
= path_extract_directory(path
, &parent
);
170 return path_equal_ptr(parent
, lp
->transient
);
173 static int path_is_control(const LookupPaths
*lp
, const char *path
) {
174 _cleanup_free_
char *parent
= NULL
;
180 r
= path_extract_directory(path
, &parent
);
184 return path_equal_ptr(parent
, lp
->persistent_control
) ||
185 path_equal_ptr(parent
, lp
->runtime_control
);
188 static int path_is_config(const LookupPaths
*lp
, const char *path
, bool check_parent
) {
189 _cleanup_free_
char *parent
= NULL
;
195 /* Note that we do *not* have generic checks for /etc or /run in place, since with
196 * them we couldn't discern configuration from transient or generated units */
199 r
= path_extract_directory(path
, &parent
);
206 return path_equal_ptr(path
, lp
->persistent_config
) ||
207 path_equal_ptr(path
, lp
->runtime_config
);
210 static int path_is_runtime(const LookupPaths
*lp
, const char *path
, bool check_parent
) {
211 _cleanup_free_
char *parent
= NULL
;
218 /* Everything in /run is considered runtime. On top of that we also add
219 * explicit checks for the various runtime directories, as safety net. */
221 rpath
= skip_root(lp
->root_dir
, path
);
222 if (rpath
&& path_startswith(rpath
, "/run"))
226 r
= path_extract_directory(path
, &parent
);
233 return path_equal_ptr(path
, lp
->runtime_config
) ||
234 path_equal_ptr(path
, lp
->generator
) ||
235 path_equal_ptr(path
, lp
->generator_early
) ||
236 path_equal_ptr(path
, lp
->generator_late
) ||
237 path_equal_ptr(path
, lp
->transient
) ||
238 path_equal_ptr(path
, lp
->runtime_control
);
241 static int path_is_vendor_or_generator(const LookupPaths
*lp
, const char *path
) {
247 rpath
= skip_root(lp
->root_dir
, path
);
251 if (path_startswith(rpath
, "/usr"))
255 if (path_startswith(rpath
, "/lib"))
259 if (path_is_generator(lp
, rpath
))
262 return path_equal(rpath
, SYSTEM_DATA_UNIT_DIR
);
265 static const char* config_path_from_flags(const LookupPaths
*lp
, UnitFileFlags flags
) {
268 if (FLAGS_SET(flags
, UNIT_FILE_PORTABLE
))
269 return FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
->runtime_attached
: lp
->persistent_attached
;
271 return FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
->runtime_config
: lp
->persistent_config
;
274 InstallChangeType
install_changes_add(
275 InstallChange
**changes
,
277 InstallChangeType type
, /* INSTALL_CHANGE_SYMLINK, _UNLINK, _IS_MASKED, _IS_DANGLING, … if positive or errno if negative */
279 const char *source
) {
281 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
284 assert(!changes
== !n_changes
);
285 assert(INSTALL_CHANGE_TYPE_VALID(type
));
287 /* Register a change or error. Note that the return value may be the error
288 * that was passed in, or -ENOMEM generated internally. */
293 c
= reallocarray(*changes
, *n_changes
+ 1, sizeof(InstallChange
));
314 c
[(*n_changes
)++] = (InstallChange
) {
317 .source
= TAKE_PTR(s
),
323 void install_changes_free(InstallChange
*changes
, size_t n_changes
) {
324 assert(changes
|| n_changes
== 0);
326 for (size_t i
= 0; i
< n_changes
; i
++) {
327 free(changes
[i
].path
);
328 free(changes
[i
].source
);
334 void install_changes_dump(int r
, const char *verb
, const InstallChange
*changes
, size_t n_changes
, bool quiet
) {
337 assert(changes
|| n_changes
== 0);
338 /* If verb is not specified, errors are not allowed! */
339 assert(verb
|| r
>= 0);
341 for (size_t i
= 0; i
< n_changes
; i
++) {
342 assert(verb
|| changes
[i
].type
>= 0);
344 /* When making changes here, make sure to also change install_error() in dbus-manager.c. */
346 switch (changes
[i
].type
) {
347 case INSTALL_CHANGE_SYMLINK
:
349 log_info("Created symlink %s %s %s.",
351 special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
),
354 case INSTALL_CHANGE_UNLINK
:
356 log_info("Removed \"%s\".", changes
[i
].path
);
358 case INSTALL_CHANGE_IS_MASKED
:
360 log_info("Unit %s is masked, ignoring.", changes
[i
].path
);
362 case INSTALL_CHANGE_IS_MASKED_GENERATOR
:
364 log_info("Unit %s is masked via a generator and cannot be unmasked.",
367 case INSTALL_CHANGE_IS_DANGLING
:
369 log_info("Unit %s is an alias to a unit that is not present, ignoring.",
372 case INSTALL_CHANGE_DESTINATION_NOT_PRESENT
:
374 log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
375 changes
[i
].source
, changes
[i
].path
);
377 case INSTALL_CHANGE_AUXILIARY_FAILED
:
379 log_warning("Failed to enable auxiliary unit %s, ignoring.", changes
[i
].source
);
382 if (changes
[i
].source
)
383 err
= log_error_errno(changes
[i
].type
,
384 "Failed to %s unit, file \"%s\" already exists and is a symlink to \"%s\".",
385 verb
, changes
[i
].path
, changes
[i
].source
);
387 err
= log_error_errno(changes
[i
].type
,
388 "Failed to %s unit, file \"%s\" already exists.",
389 verb
, changes
[i
].path
);
392 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, unit %s is masked.",
393 verb
, changes
[i
].path
);
396 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, unit %s is transient or generated.",
397 verb
, changes
[i
].path
);
400 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, file %s is under the systemd unit hierarchy already.",
401 verb
, changes
[i
].path
);
404 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, invalid specifier in \"%s\".",
405 verb
, changes
[i
].path
);
408 err
= log_error_errno(changes
[i
].type
, "Failed to %s %s, destination unit %s is a non-template unit.",
409 verb
, changes
[i
].source
, changes
[i
].path
);
412 err
= log_error_errno(changes
[i
].type
,
413 "Failed to %s unit, \"%s\" is not a valid unit name.",
414 verb
, changes
[i
].path
);
417 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, refusing to operate on linked unit file %s.",
418 verb
, changes
[i
].path
);
421 if (changes
[i
].source
)
422 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, cannot alias %s as %s.",
423 verb
, changes
[i
].source
, changes
[i
].path
);
425 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, invalid unit reference \"%s\".",
426 verb
, changes
[i
].path
);
429 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, unit %s does not exist.",
430 verb
, changes
[i
].path
);
433 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, cannot resolve specifiers in \"%s\".",
434 verb
, changes
[i
].path
);
437 assert(changes
[i
].type
< 0);
438 err
= log_error_errno(changes
[i
].type
, "Failed to %s unit, file \"%s\": %m",
439 verb
, changes
[i
].path
);
443 if (r
< 0 && err
>= 0)
444 log_error_errno(r
, "Failed to %s: %m.", verb
);
448 * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is
449 * concerned. If the target is in the unit search path, then anything with the same name is equivalent.
450 * If outside the unit search path, paths must be identical.
452 static int chroot_unit_symlinks_equivalent(
453 const LookupPaths
*lp
,
455 const char *target_a
,
456 const char *target_b
) {
463 /* This will give incorrect results if the paths are relative and go outside
464 * of the chroot. False negatives are possible. */
466 const char *root
= lp
->root_dir
?: "/";
467 _cleanup_free_
char *dirname
= NULL
;
470 if (!path_is_absolute(target_a
) || !path_is_absolute(target_b
)) {
471 r
= path_extract_directory(src
, &dirname
);
476 _cleanup_free_
char *a
= path_join(path_is_absolute(target_a
) ? root
: dirname
, target_a
);
477 _cleanup_free_
char *b
= path_join(path_is_absolute(target_b
) ? root
: dirname
, target_b
);
481 r
= path_equal_or_files_same(a
, b
, 0);
485 _cleanup_free_
char *a_name
= NULL
, *b_name
= NULL
;
486 r
= path_extract_filename(a
, &a_name
);
489 r
= path_extract_filename(b
, &b_name
);
493 return streq(a_name
, b_name
) &&
494 path_startswith_strv(a
, lp
->search_path
) &&
495 path_startswith_strv(b
, lp
->search_path
);
498 static int create_symlink(
499 const LookupPaths
*lp
,
500 const char *old_path
,
501 const char *new_path
,
503 InstallChange
**changes
,
506 _cleanup_free_
char *dest
= NULL
;
513 rp
= skip_root(lp
->root_dir
, old_path
);
517 /* Actually create a symlink, and remember that we did. This function is
518 * smart enough to check if there's already a valid symlink in place.
520 * Returns 1 if a symlink was created or already exists and points to the
521 * right place, or negative on error.
524 (void) mkdir_parents_label(new_path
, 0755);
526 if (symlink(old_path
, new_path
) >= 0) {
527 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_SYMLINK
, new_path
, old_path
);
534 return install_changes_add(changes
, n_changes
, -errno
, new_path
, NULL
);
536 r
= readlink_malloc(new_path
, &dest
);
538 /* translate EINVAL (non-symlink exists) to EEXIST */
542 return install_changes_add(changes
, n_changes
, r
, new_path
, NULL
);
545 if (chroot_unit_symlinks_equivalent(lp
, new_path
, dest
, old_path
)) {
546 log_debug("Symlink %s %s %s already exists",
547 new_path
, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), dest
);
552 return install_changes_add(changes
, n_changes
, -EEXIST
, new_path
, dest
);
554 r
= symlink_atomic(old_path
, new_path
);
556 return install_changes_add(changes
, n_changes
, r
, new_path
, NULL
);
558 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, new_path
, NULL
);
561 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_SYMLINK
, new_path
, old_path
);
568 static int mark_symlink_for_removal(
569 Set
**remove_symlinks_to
,
577 r
= set_ensure_allocated(remove_symlinks_to
, &path_hash_ops
);
587 r
= set_consume(*remove_symlinks_to
, n
);
596 static int remove_marked_symlinks_fd(
597 Set
*remove_symlinks_to
,
600 const char *config_path
,
601 const LookupPaths
*lp
,
604 InstallChange
**changes
,
607 _cleanup_closedir_
DIR *d
= NULL
;
610 assert(remove_symlinks_to
);
625 FOREACH_DIRENT(de
, d
, return -errno
)
627 if (de
->d_type
== DT_DIR
) {
628 _cleanup_free_
char *p
= NULL
;
631 nfd
= openat(fd
, de
->d_name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
);
641 p
= path_make_absolute(de
->d_name
, path
);
647 /* This will close nfd, regardless whether it succeeds or not */
648 q
= remove_marked_symlinks_fd(remove_symlinks_to
, nfd
, p
, config_path
, lp
, dry_run
, restart
, changes
, n_changes
);
652 } else if (de
->d_type
== DT_LNK
) {
653 _cleanup_free_
char *p
= NULL
;
657 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
660 p
= path_make_absolute(de
->d_name
, path
);
665 /* We remove all links pointing to a file or path that is marked, as well as all
666 * files sharing the same name as a file that is marked, and files sharing the same
667 * name after the instance has been removed. Do path chasing only if we don't already
668 * know that we want to remove the symlink. */
669 found
= set_contains(remove_symlinks_to
, de
->d_name
);
672 _cleanup_free_
char *template = NULL
;
674 q
= unit_name_template(de
->d_name
, &template);
675 if (q
< 0 && q
!= -EINVAL
)
678 found
= set_contains(remove_symlinks_to
, template);
682 _cleanup_free_
char *dest
= NULL
;
684 q
= chase_symlinks(p
, lp
->root_dir
, CHASE_NONEXISTENT
, &dest
, NULL
);
688 log_debug_errno(q
, "Failed to resolve symlink \"%s\": %m", p
);
689 install_changes_add(changes
, n_changes
, q
, p
, NULL
);
696 found
= set_contains(remove_symlinks_to
, dest
) ||
697 set_contains(remove_symlinks_to
, basename(dest
));
706 if (unlinkat(fd
, de
->d_name
, 0) < 0 && errno
!= ENOENT
) {
709 install_changes_add(changes
, n_changes
, -errno
, p
, NULL
);
713 (void) rmdir_parents(p
, config_path
);
716 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, p
, NULL
);
720 /* Now, remember the full path (but with the root prefix removed) of
721 * the symlink we just removed, and remove any symlinks to it, too. */
723 const char *rp
= skip_root(lp
->root_dir
, p
);
724 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: p
);
727 if (q
> 0 && !dry_run
)
734 static int remove_marked_symlinks(
735 Set
*remove_symlinks_to
,
736 const char *config_path
,
737 const LookupPaths
*lp
,
739 InstallChange
**changes
,
742 _cleanup_close_
int fd
= -EBADF
;
749 if (set_size(remove_symlinks_to
) <= 0)
752 fd
= open(config_path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
);
754 return errno
== ENOENT
? 0 : -errno
;
760 cfd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
764 /* This takes possession of cfd and closes it */
765 q
= remove_marked_symlinks_fd(remove_symlinks_to
, cfd
, config_path
, config_path
, lp
, dry_run
, &restart
, changes
, n_changes
);
773 static int is_symlink_with_known_name(const InstallInfo
*i
, const char *name
) {
776 if (streq(name
, i
->name
))
779 if (strv_contains(i
->aliases
, name
))
782 /* Look for template symlink matching DefaultInstance */
783 if (i
->default_instance
&& unit_name_is_valid(i
->name
, UNIT_NAME_TEMPLATE
)) {
784 _cleanup_free_
char *s
= NULL
;
786 r
= unit_name_replace_instance(i
->name
, i
->default_instance
, &s
);
791 } else if (streq(name
, s
))
798 static int find_symlinks_in_directory(
800 const char *dir_path
,
801 const char *root_dir
,
802 const InstallInfo
*info
,
803 bool ignore_destination
,
805 bool ignore_same_name
,
806 const char *config_path
,
807 bool *same_name_link
) {
811 FOREACH_DIRENT(de
, dir
, return -errno
) {
812 bool found_path
= false, found_dest
= false, b
= false;
815 if (de
->d_type
!= DT_LNK
)
818 if (!ignore_destination
) {
819 _cleanup_free_
char *dest
= NULL
;
821 /* Acquire symlink destination */
822 q
= readlinkat_malloc(dirfd(dir
), de
->d_name
, &dest
);
832 if (!path_is_absolute(dest
)) {
835 x
= path_join(dir_path
, dest
);
839 free_and_replace(dest
, x
);
842 /* Check if what the symlink points to matches what we are looking for */
843 found_dest
= streq(basename(dest
), info
->name
);
846 assert(unit_name_is_valid(info
->name
, UNIT_NAME_ANY
));
848 /* Check if the symlink itself matches what we are looking for.
850 * If ignore_destination is specified, we only look at the source name.
852 * If ignore_same_name is specified, we are in one of the directories which
853 * have lower priority than the unit file, and even if a file or symlink with
854 * this name was found, we should ignore it. */
856 if (ignore_destination
|| !ignore_same_name
)
857 found_path
= streq(de
->d_name
, info
->name
);
859 if (!found_path
&& ignore_destination
) {
860 _cleanup_free_
char *template = NULL
;
862 q
= unit_name_template(de
->d_name
, &template);
863 if (q
< 0 && q
!= -EINVAL
)
866 found_dest
= streq(template, info
->name
);
869 if (found_path
&& found_dest
) {
870 _cleanup_free_
char *p
= NULL
, *t
= NULL
;
872 /* Filter out same name links in the main config path */
873 p
= path_make_absolute(de
->d_name
, dir_path
);
874 t
= path_make_absolute(info
->name
, config_path
);
879 b
= path_equal(p
, t
);
883 *same_name_link
= true;
884 else if (found_path
|| found_dest
) {
888 /* Check if symlink name is in the set of names used by [Install] */
889 q
= is_symlink_with_known_name(info
, de
->d_name
);
900 static int find_symlinks(
901 const char *root_dir
,
902 const InstallInfo
*i
,
904 bool ignore_same_name
,
905 const char *config_path
,
906 bool *same_name_link
) {
908 _cleanup_closedir_
DIR *config_dir
= NULL
;
913 assert(same_name_link
);
915 config_dir
= opendir(config_path
);
917 if (IN_SET(errno
, ENOENT
, ENOTDIR
, EACCES
))
922 FOREACH_DIRENT(de
, config_dir
, return -errno
) {
924 _cleanup_free_
const char *path
= NULL
;
925 _cleanup_closedir_
DIR *d
= NULL
;
927 if (de
->d_type
!= DT_DIR
)
930 suffix
= strrchr(de
->d_name
, '.');
931 if (!STRPTR_IN_SET(suffix
, ".wants", ".requires"))
934 path
= path_join(config_path
, de
->d_name
);
940 log_error_errno(errno
, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path
);
944 r
= find_symlinks_in_directory(d
, path
, root_dir
, i
,
945 /* ignore_destination= */ true,
946 /* match_name= */ match_name
,
947 /* ignore_same_name= */ ignore_same_name
,
953 log_debug_errno(r
, "Failed to look up symlinks in \"%s\": %m", path
);
956 /* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */
957 rewinddir(config_dir
);
958 return find_symlinks_in_directory(config_dir
, config_path
, root_dir
, i
,
959 /* ignore_destination= */ false,
960 /* match_name= */ match_name
,
961 /* ignore_same_name= */ ignore_same_name
,
966 static int find_symlinks_in_scope(
968 const LookupPaths
*lp
,
969 const InstallInfo
*info
,
971 UnitFileState
*state
) {
973 bool same_name_link_runtime
= false, same_name_link_config
= false;
974 bool enabled_in_runtime
= false, enabled_at_all
= false;
975 bool ignore_same_name
= false;
981 /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name"
982 * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
983 * effectively masked, so we should ignore them. */
985 STRV_FOREACH(p
, lp
->search_path
) {
986 bool same_name_link
= false;
988 r
= find_symlinks(lp
->root_dir
, info
, match_name
, ignore_same_name
, *p
, &same_name_link
);
992 /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
994 if (path_equal_ptr(*p
, lp
->persistent_config
)) {
995 /* This is the best outcome, let's return it immediately. */
996 *state
= UNIT_FILE_ENABLED
;
1000 /* look for global enablement of user units */
1001 if (scope
== LOOKUP_SCOPE_USER
&& path_is_user_config_dir(*p
)) {
1002 *state
= UNIT_FILE_ENABLED
;
1006 r
= path_is_runtime(lp
, *p
, false);
1010 enabled_in_runtime
= true;
1012 enabled_at_all
= true;
1014 } else if (same_name_link
) {
1015 if (path_equal_ptr(*p
, lp
->persistent_config
))
1016 same_name_link_config
= true;
1018 r
= path_is_runtime(lp
, *p
, false);
1022 same_name_link_runtime
= true;
1026 /* Check if next iteration will be "below" the unit file (either a regular file
1027 * or a symlink), and hence should be ignored */
1028 if (!ignore_same_name
&& path_startswith(info
->path
, *p
))
1029 ignore_same_name
= true;
1032 if (enabled_in_runtime
) {
1033 *state
= UNIT_FILE_ENABLED_RUNTIME
;
1037 /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
1038 * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
1039 * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
1040 * something, and hence are a much stronger concept. */
1041 if (enabled_at_all
&& unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
1042 *state
= UNIT_FILE_STATIC
;
1046 /* Hmm, we didn't find it, but maybe we found the same name
1048 if (same_name_link_config
) {
1049 *state
= UNIT_FILE_LINKED
;
1052 if (same_name_link_runtime
) {
1053 *state
= UNIT_FILE_LINKED_RUNTIME
;
1060 static void install_info_clear(InstallInfo
*i
) {
1064 i
->name
= mfree(i
->name
);
1065 i
->path
= mfree(i
->path
);
1066 i
->root
= mfree(i
->root
);
1067 i
->aliases
= strv_free(i
->aliases
);
1068 i
->wanted_by
= strv_free(i
->wanted_by
);
1069 i
->required_by
= strv_free(i
->required_by
);
1070 i
->also
= strv_free(i
->also
);
1071 i
->default_instance
= mfree(i
->default_instance
);
1072 i
->symlink_target
= mfree(i
->symlink_target
);
1075 static InstallInfo
* install_info_free(InstallInfo
*i
) {
1076 install_info_clear(i
);
1080 DEFINE_TRIVIAL_CLEANUP_FUNC(InstallInfo
*, install_info_free
);
1082 static void install_context_done(InstallContext
*ctx
) {
1085 ctx
->will_process
= ordered_hashmap_free_with_destructor(ctx
->will_process
, install_info_free
);
1086 ctx
->have_processed
= ordered_hashmap_free_with_destructor(ctx
->have_processed
, install_info_free
);
1089 static InstallInfo
*install_info_find(InstallContext
*ctx
, const char *name
) {
1092 i
= ordered_hashmap_get(ctx
->have_processed
, name
);
1096 return ordered_hashmap_get(ctx
->will_process
, name
);
1099 static int install_info_may_process(
1100 const InstallInfo
*i
,
1101 const LookupPaths
*lp
,
1102 InstallChange
**changes
,
1103 size_t *n_changes
) {
1107 /* Checks whether the loaded unit file is one we should process, or is masked,
1108 * transient or generated and thus not subject to enable/disable operations. */
1110 if (i
->install_mode
== INSTALL_MODE_MASKED
)
1111 return install_changes_add(changes
, n_changes
, -ERFKILL
, i
->path
, NULL
);
1112 if (path_is_generator(lp
, i
->path
) ||
1113 path_is_transient(lp
, i
->path
))
1114 return install_changes_add(changes
, n_changes
, -EADDRNOTAVAIL
, i
->path
, NULL
);
1120 * Adds a new InstallInfo entry under name in the InstallContext.will_process
1121 * hashmap, or retrieves the existing one if already present.
1123 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1125 static int install_info_add(
1126 InstallContext
*ctx
,
1131 InstallInfo
**ret
) {
1138 /* 'name' and 'path' must not both be null. Check here 'path' using assert_se() to
1139 * workaround a bug in gcc that generates a -Wnonnull warning when calling basename(),
1140 * but this cannot be possible in any code path (See #6119). */
1142 name
= basename(path
);
1145 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
1148 InstallInfo
*i
= install_info_find(ctx
, name
);
1150 i
->auxiliary
= i
->auxiliary
&& auxiliary
;
1157 _cleanup_(install_info_freep
) InstallInfo
*alloc
= new(InstallInfo
, 1);
1161 *alloc
= (InstallInfo
) {
1162 .install_mode
= _INSTALL_MODE_INVALID
,
1163 .auxiliary
= auxiliary
,
1166 alloc
->name
= strdup(name
);
1171 alloc
->root
= strdup(root
);
1177 alloc
->path
= strdup(path
);
1182 r
= ordered_hashmap_ensure_put(&ctx
->will_process
, &string_hash_ops
, alloc
->name
, alloc
);
1185 i
= TAKE_PTR(alloc
);
1192 static int config_parse_alias(
1194 const char *filename
,
1196 const char *section
,
1197 unsigned section_line
,
1211 type
= unit_name_to_type(unit
);
1212 if (!unit_type_may_alias(type
))
1213 return log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1214 "Alias= is not allowed for %s units, ignoring.",
1215 unit_type_to_string(type
));
1217 return config_parse_strv(unit
, filename
, line
, section
, section_line
,
1218 lvalue
, ltype
, rvalue
, data
, userdata
);
1221 static int config_parse_also(
1223 const char *filename
,
1225 const char *section
,
1226 unsigned section_line
,
1233 InstallInfo
*info
= ASSERT_PTR(userdata
);
1234 InstallContext
*ctx
= ASSERT_PTR(data
);
1243 _cleanup_free_
char *word
= NULL
, *printed
= NULL
;
1245 r
= extract_first_word(&rvalue
, &word
, NULL
, 0);
1251 r
= install_name_printf(ctx
->scope
, info
, word
, &printed
);
1253 return log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1254 "Failed to resolve unit name in Also=\"%s\": %m", word
);
1256 r
= install_info_add(ctx
, printed
, NULL
, info
->root
, /* auxiliary= */ true, NULL
);
1260 r
= strv_push(&info
->also
, printed
);
1270 static int config_parse_default_instance(
1272 const char *filename
,
1274 const char *section
,
1275 unsigned section_line
,
1282 InstallContext
*ctx
= ASSERT_PTR(data
);
1283 InstallInfo
*info
= ASSERT_PTR(userdata
);
1284 _cleanup_free_
char *printed
= NULL
;
1292 if (unit_name_is_valid(unit
, UNIT_NAME_INSTANCE
))
1293 /* When enabling an instance, we might be using a template unit file,
1294 * but we should ignore DefaultInstance silently. */
1296 if (!unit_name_is_valid(unit
, UNIT_NAME_TEMPLATE
))
1297 return log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1298 "DefaultInstance= only makes sense for template units, ignoring.");
1300 r
= install_name_printf(ctx
->scope
, info
, rvalue
, &printed
);
1302 return log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1303 "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue
);
1305 if (isempty(printed
))
1306 printed
= mfree(printed
);
1308 if (printed
&& !unit_instance_is_valid(printed
))
1309 return log_syntax(unit
, LOG_WARNING
, filename
, line
, SYNTHETIC_ERRNO(EINVAL
),
1310 "Invalid DefaultInstance= value \"%s\".", printed
);
1312 return free_and_replace(info
->default_instance
, printed
);
1315 static int unit_file_load(
1316 InstallContext
*ctx
,
1319 const char *root_dir
,
1320 SearchFlags flags
) {
1322 const ConfigTableItem items
[] = {
1323 { "Install", "Alias", config_parse_alias
, 0, &info
->aliases
},
1324 { "Install", "WantedBy", config_parse_strv
, 0, &info
->wanted_by
},
1325 { "Install", "RequiredBy", config_parse_strv
, 0, &info
->required_by
},
1326 { "Install", "DefaultInstance", config_parse_default_instance
, 0, info
},
1327 { "Install", "Also", config_parse_also
, 0, ctx
},
1332 _cleanup_fclose_
FILE *f
= NULL
;
1333 _cleanup_close_
int fd
= -EBADF
;
1340 if (!(flags
& SEARCH_DROPIN
)) {
1341 /* Loading or checking for the main unit file… */
1343 type
= unit_name_to_type(info
->name
);
1346 if (unit_name_is_valid(info
->name
, UNIT_NAME_TEMPLATE
|UNIT_NAME_INSTANCE
) && !unit_type_may_template(type
))
1347 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1348 "%s: unit type %s cannot be templated, ignoring.", path
, unit_type_to_string(type
));
1350 if (!(flags
& SEARCH_LOAD
)) {
1351 if (lstat(path
, &st
) < 0)
1354 if (null_or_empty(&st
))
1355 info
->install_mode
= INSTALL_MODE_MASKED
;
1356 else if (S_ISREG(st
.st_mode
))
1357 info
->install_mode
= INSTALL_MODE_REGULAR
;
1358 else if (S_ISLNK(st
.st_mode
))
1360 else if (S_ISDIR(st
.st_mode
))
1368 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
1372 /* Operating on a drop-in file. If we aren't supposed to load the unit file drop-ins don't matter, let's hence shortcut this. */
1374 if (!(flags
& SEARCH_LOAD
))
1377 fd
= chase_symlinks_and_open(path
, root_dir
, 0, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
, NULL
);
1382 if (fstat(fd
, &st
) < 0)
1385 if (null_or_empty(&st
)) {
1386 if ((flags
& SEARCH_DROPIN
) == 0)
1387 info
->install_mode
= INSTALL_MODE_MASKED
;
1392 r
= stat_verify_regular(&st
);
1396 f
= take_fdopen(&fd
, "r");
1400 /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1403 r
= config_parse(info
->name
, path
, f
,
1417 config_item_table_lookup
, items
,
1421 return log_debug_errno(r
, "Failed to parse \"%s\": %m", info
->name
);
1423 if ((flags
& SEARCH_DROPIN
) == 0)
1424 info
->install_mode
= INSTALL_MODE_REGULAR
;
1427 (int) strv_length(info
->aliases
) +
1428 (int) strv_length(info
->wanted_by
) +
1429 (int) strv_length(info
->required_by
);
1432 static int unit_file_load_or_readlink(
1433 InstallContext
*ctx
,
1436 const LookupPaths
*lp
,
1437 SearchFlags flags
) {
1440 r
= unit_file_load(ctx
, info
, path
, lp
->root_dir
, flags
);
1441 if (r
!= -ELOOP
|| (flags
& SEARCH_DROPIN
))
1444 /* This is a symlink, let's read and verify it. */
1445 r
= unit_file_resolve_symlink(lp
->root_dir
, lp
->search_path
,
1446 NULL
, AT_FDCWD
, path
,
1447 true, &info
->symlink_target
);
1450 bool outside_search_path
= r
> 0;
1452 r
= null_or_empty_path_with_root(info
->symlink_target
, lp
->root_dir
);
1453 if (r
< 0 && r
!= -ENOENT
)
1454 return log_debug_errno(r
, "Failed to stat %s: %m", info
->symlink_target
);
1456 info
->install_mode
= INSTALL_MODE_MASKED
;
1457 else if (outside_search_path
)
1458 info
->install_mode
= INSTALL_MODE_LINKED
;
1460 info
->install_mode
= INSTALL_MODE_ALIAS
;
1465 static int unit_file_search(
1466 InstallContext
*ctx
,
1468 const LookupPaths
*lp
,
1469 SearchFlags flags
) {
1471 const char *dropin_dir_name
= NULL
, *dropin_template_dir_name
= NULL
;
1472 _cleanup_strv_free_
char **dirs
= NULL
, **files
= NULL
;
1473 _cleanup_free_
char *template = NULL
;
1474 bool found_unit
= false;
1480 /* Was this unit already loaded? */
1481 if (info
->install_mode
!= _INSTALL_MODE_INVALID
)
1485 return unit_file_load_or_readlink(ctx
, info
, info
->path
, lp
, flags
);
1489 if (unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
1490 r
= unit_name_template(info
->name
, &template);
1495 STRV_FOREACH(p
, lp
->search_path
) {
1496 _cleanup_free_
char *path
= NULL
;
1498 path
= path_join(*p
, info
->name
);
1502 r
= unit_file_load_or_readlink(ctx
, info
, path
, lp
, flags
);
1504 info
->path
= TAKE_PTR(path
);
1508 } else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
, -EACCES
))
1512 if (!found_unit
&& template) {
1514 /* Unit file doesn't exist, however instance
1515 * enablement was requested. We will check if it is
1516 * possible to load template unit file. */
1518 STRV_FOREACH(p
, lp
->search_path
) {
1519 _cleanup_free_
char *path
= NULL
;
1521 path
= path_join(*p
, template);
1525 r
= unit_file_load_or_readlink(ctx
, info
, path
, lp
, flags
);
1527 info
->path
= TAKE_PTR(path
);
1531 } else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
, -EACCES
))
1537 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT
),
1538 "Cannot find unit %s%s%s.",
1539 info
->name
, template ? " or " : "", strempty(template));
1541 if (info
->install_mode
== INSTALL_MODE_MASKED
)
1544 /* Search for drop-in directories */
1546 dropin_dir_name
= strjoina(info
->name
, ".d");
1547 STRV_FOREACH(p
, lp
->search_path
) {
1550 path
= path_join(*p
, dropin_dir_name
);
1554 r
= strv_consume(&dirs
, path
);
1560 dropin_template_dir_name
= strjoina(template, ".d");
1561 STRV_FOREACH(p
, lp
->search_path
) {
1564 path
= path_join(*p
, dropin_template_dir_name
);
1568 r
= strv_consume(&dirs
, path
);
1574 /* Load drop-in conf files */
1576 r
= conf_files_list_strv(&files
, ".conf", NULL
, 0, (const char**) dirs
);
1578 return log_debug_errno(r
, "Failed to get list of conf files: %m");
1580 STRV_FOREACH(p
, files
) {
1581 r
= unit_file_load_or_readlink(ctx
, info
, *p
, lp
, flags
| SEARCH_DROPIN
);
1583 return log_debug_errno(r
, "Failed to load conf file \"%s\": %m", *p
);
1589 static int install_info_follow(
1590 InstallContext
*ctx
,
1592 const LookupPaths
*lp
,
1594 bool ignore_different_name
) {
1599 if (!IN_SET(info
->install_mode
, INSTALL_MODE_ALIAS
, INSTALL_MODE_LINKED
))
1601 if (!info
->symlink_target
)
1604 /* If the basename doesn't match, the caller should add a complete new entry for this. */
1606 if (!ignore_different_name
&& !streq(basename(info
->symlink_target
), info
->name
))
1609 free_and_replace(info
->path
, info
->symlink_target
);
1610 info
->install_mode
= _INSTALL_MODE_INVALID
;
1612 return unit_file_load_or_readlink(ctx
, info
, info
->path
, lp
, flags
);
1616 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1617 * target, maybe more than once. Propagate the instance name if present.
1619 static int install_info_traverse(
1620 InstallContext
*ctx
,
1621 const LookupPaths
*lp
,
1624 InstallInfo
**ret
) {
1634 r
= unit_file_search(ctx
, start
, lp
, flags
);
1639 while (IN_SET(i
->install_mode
, INSTALL_MODE_ALIAS
, INSTALL_MODE_LINKED
)) {
1640 /* Follow the symlink */
1642 if (++k
> UNIT_FILE_FOLLOW_SYMLINK_MAX
)
1645 if (!(flags
& SEARCH_FOLLOW_CONFIG_SYMLINKS
)) {
1646 r
= path_is_config(lp
, i
->path
, true);
1653 r
= install_info_follow(ctx
, i
, lp
, flags
,
1654 /* If linked, don't look at the target name */
1655 /* ignore_different_name= */ i
->install_mode
== INSTALL_MODE_LINKED
);
1657 _cleanup_free_
char *buffer
= NULL
;
1660 /* Target is an alias, create a new install info object and continue with that. */
1662 bn
= basename(i
->symlink_target
);
1664 if (unit_name_is_valid(i
->name
, UNIT_NAME_INSTANCE
) &&
1665 unit_name_is_valid(bn
, UNIT_NAME_TEMPLATE
)) {
1667 _cleanup_free_
char *instance
= NULL
;
1669 r
= unit_name_to_instance(i
->name
, &instance
);
1673 r
= unit_name_replace_instance(bn
, instance
, &buffer
);
1677 if (streq(buffer
, i
->name
)) {
1679 /* We filled in the instance, and the target stayed the same? If so,
1680 * then let's honour the link as it is. */
1682 r
= install_info_follow(ctx
, i
, lp
, flags
, true);
1692 r
= install_info_add(ctx
, bn
, NULL
, lp
->root_dir
, /* auxiliary= */ false, &i
);
1696 /* Try again, with the new target we found. */
1697 r
= unit_file_search(ctx
, i
, lp
, flags
);
1699 /* Translate error code to highlight this specific case */
1714 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1715 * or the name (otherwise). root_dir is prepended to the path.
1717 static int install_info_add_auto(
1718 InstallContext
*ctx
,
1719 const LookupPaths
*lp
,
1720 const char *name_or_path
,
1721 InstallInfo
**ret
) {
1724 assert(name_or_path
);
1726 if (path_is_absolute(name_or_path
)) {
1729 pp
= prefix_roota(lp
->root_dir
, name_or_path
);
1731 return install_info_add(ctx
, NULL
, pp
, lp
->root_dir
, /* auxiliary= */ false, ret
);
1733 return install_info_add(ctx
, name_or_path
, NULL
, lp
->root_dir
, /* auxiliary= */ false, ret
);
1736 static int install_info_discover(
1737 InstallContext
*ctx
,
1738 const LookupPaths
*lp
,
1739 const char *name_or_path
,
1742 InstallChange
**changes
,
1743 size_t *n_changes
) {
1750 assert(name_or_path
);
1752 r
= install_info_add_auto(ctx
, lp
, name_or_path
, &info
);
1754 r
= install_info_traverse(ctx
, lp
, info
, flags
, ret
);
1757 install_changes_add(changes
, n_changes
, r
, name_or_path
, NULL
);
1761 static int install_info_discover_and_check(
1762 InstallContext
*ctx
,
1763 const LookupPaths
*lp
,
1764 const char *name_or_path
,
1767 InstallChange
**changes
,
1768 size_t *n_changes
) {
1772 r
= install_info_discover(ctx
, lp
, name_or_path
, flags
, ret
, changes
, n_changes
);
1776 return install_info_may_process(ret
? *ret
: NULL
, lp
, changes
, n_changes
);
1779 int unit_file_verify_alias(
1780 const InstallInfo
*info
,
1783 InstallChange
**changes
,
1784 size_t *n_changes
) {
1786 _cleanup_free_
char *dst_updated
= NULL
;
1789 /* Verify that dst is a valid either a valid alias or a valid .wants/.requires symlink for the target
1790 * unit *i. Return negative on error or if not compatible, zero on success.
1792 * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
1793 * inserted into dst. It is not normally set, even on success, so that the caller can easily
1794 * distinguish the case where instance propagation occurred.
1797 * -EXDEV when the alias doesn't match the unit,
1798 * -EUCLEAN when the name is invalid,
1799 * -ELOOP when the alias it to the unit itself.
1802 const char *path_alias
= strrchr(dst
, '/');
1804 /* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
1805 _cleanup_free_
char *dir
= NULL
;
1808 path_alias
++; /* skip over slash */
1810 r
= path_extract_directory(dst
, &dir
);
1812 return log_error_errno(r
, "Failed to extract parent directory from '%s': %m", dst
);
1814 p
= endswith(dir
, ".wants");
1816 p
= endswith(dir
, ".requires");
1818 install_changes_add(changes
, n_changes
, -EXDEV
, dst
, NULL
);
1819 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
), "Invalid path \"%s\" in alias.", dir
);
1822 *p
= '\0'; /* dir should now be a unit name */
1824 UnitNameFlags type
= unit_name_classify(dir
);
1826 install_changes_add(changes
, n_changes
, -EXDEV
, dst
, NULL
);
1827 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
),
1828 "Invalid unit name component \"%s\" in alias.", dir
);
1831 const bool instance_propagation
= type
== UNIT_NAME_TEMPLATE
;
1833 /* That's the name we want to use for verification. */
1834 r
= unit_symlink_name_compatible(path_alias
, info
->name
, instance_propagation
);
1836 return log_error_errno(r
, "Failed to verify alias validity: %m");
1838 install_changes_add(changes
, n_changes
, -EXDEV
, dst
, info
->name
);
1839 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
),
1840 "Invalid unit \"%s\" symlink \"%s\".",
1845 /* If the symlink target has an instance set and the symlink source doesn't, we "propagate
1846 * the instance", i.e. instantiate the symlink source with the target instance. */
1847 if (unit_name_is_valid(dst
, UNIT_NAME_TEMPLATE
)) {
1848 _cleanup_free_
char *inst
= NULL
;
1850 UnitNameFlags type
= unit_name_to_instance(info
->name
, &inst
);
1852 install_changes_add(changes
, n_changes
, -EUCLEAN
, info
->name
, NULL
);
1853 return log_debug_errno(type
, "Failed to extract instance name from \"%s\": %m", info
->name
);
1856 if (type
== UNIT_NAME_INSTANCE
) {
1857 r
= unit_name_replace_instance(dst
, inst
, &dst_updated
);
1859 return log_error_errno(r
, "Failed to build unit name from %s+%s: %m",
1864 r
= unit_validate_alias_symlink_or_warn(LOG_DEBUG
, dst_updated
?: dst
, info
->name
);
1865 if (r
== -ELOOP
) /* -ELOOP means self-alias, which we (quietly) ignore */
1868 return install_changes_add(changes
, n_changes
,
1869 r
== -EINVAL
? -EXDEV
: r
,
1874 *ret_dst
= TAKE_PTR(dst_updated
);
1878 static int install_info_symlink_alias(
1881 const LookupPaths
*lp
,
1882 const char *config_path
,
1884 InstallChange
**changes
,
1885 size_t *n_changes
) {
1891 assert(config_path
);
1893 STRV_FOREACH(s
, info
->aliases
) {
1894 _cleanup_free_
char *alias_path
= NULL
, *dst
= NULL
, *dst_updated
= NULL
;
1897 q
= install_name_printf(scope
, info
, *s
, &dst
);
1899 install_changes_add(changes
, n_changes
, q
, *s
, NULL
);
1904 q
= unit_file_verify_alias(info
, dst
, &dst_updated
, changes
, n_changes
);
1912 alias_path
= path_make_absolute(dst_updated
?: dst
, config_path
);
1916 q
= chase_symlinks(alias_path
, lp
->root_dir
, CHASE_NONEXISTENT
, NULL
, NULL
);
1917 if (q
< 0 && q
!= -ENOENT
) {
1921 broken
= q
== 0; /* symlink target does not exist? */
1923 q
= create_symlink(lp
, info
->path
, alias_path
, force
|| broken
, changes
, n_changes
);
1930 static int install_info_symlink_wants(
1932 UnitFileFlags file_flags
,
1934 const LookupPaths
*lp
,
1935 const char *config_path
,
1938 InstallChange
**changes
,
1939 size_t *n_changes
) {
1941 _cleanup_(install_info_clear
) InstallInfo instance
= {
1942 .install_mode
= _INSTALL_MODE_INVALID
,
1945 UnitNameFlags valid_dst_type
= UNIT_NAME_ANY
;
1951 assert(config_path
);
1953 if (strv_isempty(list
))
1956 if (unit_name_is_valid(info
->name
, UNIT_NAME_PLAIN
| UNIT_NAME_INSTANCE
))
1957 /* Not a template unit. Use the name directly. */
1960 else if (info
->default_instance
) {
1961 /* If this is a template, and we have a default instance, use it. */
1963 r
= unit_name_replace_instance(info
->name
, info
->default_instance
, &instance
.name
);
1967 r
= unit_file_search(NULL
, &instance
, lp
, SEARCH_FOLLOW_CONFIG_SYMLINKS
);
1971 if (instance
.install_mode
== INSTALL_MODE_MASKED
)
1972 return install_changes_add(changes
, n_changes
, -ERFKILL
, instance
.path
, NULL
);
1977 /* We have a template, but no instance yet. When used with an instantiated unit, we will get
1978 * the instance from that unit. Cannot be used with non-instance units. */
1980 valid_dst_type
= UNIT_NAME_INSTANCE
| UNIT_NAME_TEMPLATE
;
1985 STRV_FOREACH(s
, list
) {
1986 _cleanup_free_
char *path
= NULL
, *dst
= NULL
;
1988 q
= install_name_printf(scope
, info
, *s
, &dst
);
1990 install_changes_add(changes
, n_changes
, q
, *s
, NULL
);
1995 if (!unit_name_is_valid(dst
, valid_dst_type
)) {
1996 /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the
1997 * template status doesn't match. If we are doing presets don't bother reporting the
1998 * error. This also covers cases like 'systemctl preset serial-getty@.service', which
1999 * has no DefaultInstance, so there is nothing we can do. At the same time,
2000 * 'systemctl enable serial-getty@.service' should fail, the user should specify an
2001 * instance like in 'systemctl enable serial-getty@ttyS0.service'.
2003 if (file_flags
& UNIT_FILE_IGNORE_AUXILIARY_FAILURE
)
2006 if (unit_name_is_valid(dst
, UNIT_NAME_ANY
))
2007 q
= install_changes_add(changes
, n_changes
, -EIDRM
, dst
, n
);
2009 q
= install_changes_add(changes
, n_changes
, -EUCLEAN
, dst
, NULL
);
2016 path
= strjoin(config_path
, "/", dst
, suffix
, n
);
2020 q
= create_symlink(lp
, info
->path
, path
, true, changes
, n_changes
);
2021 if ((q
< 0 && r
>= 0) || r
== 0)
2024 if (unit_file_exists(scope
, lp
, dst
) == 0) {
2025 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_DESTINATION_NOT_PRESENT
, dst
, info
->path
);
2034 static int install_info_symlink_link(
2036 const LookupPaths
*lp
,
2037 const char *config_path
,
2039 InstallChange
**changes
,
2040 size_t *n_changes
) {
2042 _cleanup_free_
char *path
= NULL
;
2047 assert(config_path
);
2050 r
= in_search_path(lp
, info
->path
);
2056 path
= path_join(config_path
, info
->name
);
2060 return create_symlink(lp
, info
->path
, path
, force
, changes
, n_changes
);
2063 static int install_info_apply(
2065 UnitFileFlags file_flags
,
2067 const LookupPaths
*lp
,
2068 const char *config_path
,
2069 InstallChange
**changes
,
2070 size_t *n_changes
) {
2076 assert(config_path
);
2078 if (info
->install_mode
!= INSTALL_MODE_REGULAR
)
2081 bool force
= file_flags
& UNIT_FILE_FORCE
;
2083 r
= install_info_symlink_link(info
, lp
, config_path
, force
, changes
, n_changes
);
2084 /* Do not count links to the unit file towards the "carries_install_info" count */
2086 /* If linking of the file failed, do not try to create other symlinks,
2087 * because they might would pointing to a non-existent or wrong unit. */
2090 r
= install_info_symlink_alias(scope
, info
, lp
, config_path
, force
, changes
, n_changes
);
2092 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->wanted_by
, ".wants/", changes
, n_changes
);
2096 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->required_by
, ".requires/", changes
, n_changes
);
2103 static int install_context_apply(
2104 InstallContext
*ctx
,
2105 const LookupPaths
*lp
,
2106 UnitFileFlags file_flags
,
2107 const char *config_path
,
2109 InstallChange
**changes
,
2110 size_t *n_changes
) {
2117 assert(config_path
);
2119 if (ordered_hashmap_isempty(ctx
->will_process
))
2122 r
= ordered_hashmap_ensure_allocated(&ctx
->have_processed
, &string_hash_ops
);
2127 while ((i
= ordered_hashmap_first(ctx
->will_process
))) {
2130 q
= ordered_hashmap_move_one(ctx
->have_processed
, ctx
->will_process
, i
->name
);
2134 q
= install_info_traverse(ctx
, lp
, i
, flags
, NULL
);
2137 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_AUXILIARY_FAILED
, NULL
, i
->name
);
2143 return install_changes_add(changes
, n_changes
, q
, i
->name
, NULL
);
2146 /* We can attempt to process a masked unit when a different unit
2147 * that we were processing specifies it in Also=. */
2148 if (i
->install_mode
== INSTALL_MODE_MASKED
) {
2149 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_MASKED
, i
->path
, NULL
);
2153 /* Assume that something *could* have been enabled here,
2154 * avoid "empty [Install] section" warning. */
2159 if (i
->install_mode
!= INSTALL_MODE_REGULAR
)
2162 q
= install_info_apply(ctx
->scope
, file_flags
, i
, lp
, config_path
, changes
, n_changes
);
2174 static int install_context_mark_for_removal(
2175 InstallContext
*ctx
,
2176 const LookupPaths
*lp
,
2177 Set
**remove_symlinks_to
,
2178 const char *config_path
,
2179 InstallChange
**changes
,
2180 size_t *n_changes
) {
2187 assert(config_path
);
2189 /* Marks all items for removal */
2191 if (ordered_hashmap_isempty(ctx
->will_process
))
2194 r
= ordered_hashmap_ensure_allocated(&ctx
->have_processed
, &string_hash_ops
);
2198 while ((i
= ordered_hashmap_first(ctx
->will_process
))) {
2200 r
= ordered_hashmap_move_one(ctx
->have_processed
, ctx
->will_process
, i
->name
);
2204 r
= install_info_traverse(ctx
, lp
, i
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, NULL
);
2205 if (r
== -ENOLINK
) {
2206 log_debug_errno(r
, "Name %s leads to a dangling symlink, removing name.", i
->name
);
2207 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_DANGLING
, i
->path
?: i
->name
, NULL
);
2210 } else if (r
== -ENOENT
) {
2211 if (i
->auxiliary
) /* some unit specified in Also= or similar is missing */
2212 log_debug_errno(r
, "Auxiliary unit of %s not found, removing name.", i
->name
);
2214 log_debug_errno(r
, "Unit %s not found, removing name.", i
->name
);
2215 r
= install_changes_add(changes
, n_changes
, r
, i
->path
?: i
->name
, NULL
);
2220 log_debug_errno(r
, "Failed to find unit %s, removing name: %m", i
->name
);
2221 install_changes_add(changes
, n_changes
, r
, i
->path
?: i
->name
, NULL
);
2222 } else if (i
->install_mode
== INSTALL_MODE_MASKED
) {
2223 log_debug("Unit file %s is masked, ignoring.", i
->name
);
2224 install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_MASKED
, i
->path
?: i
->name
, NULL
);
2226 } else if (i
->install_mode
!= INSTALL_MODE_REGULAR
) {
2227 log_debug("Unit %s has install mode %s, ignoring.",
2228 i
->name
, install_mode_to_string(i
->install_mode
) ?: "invalid");
2232 r
= mark_symlink_for_removal(remove_symlinks_to
, i
->name
);
2242 UnitFileFlags flags
,
2243 const char *root_dir
,
2245 InstallChange
**changes
,
2246 size_t *n_changes
) {
2248 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2249 const char *config_path
;
2253 assert(scope
< _LOOKUP_SCOPE_MAX
);
2255 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2259 config_path
= (flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2263 STRV_FOREACH(name
, names
) {
2264 _cleanup_free_
char *path
= NULL
;
2267 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
)) {
2273 path
= path_make_absolute(*name
, config_path
);
2277 q
= create_symlink(&lp
, "/dev/null", path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
);
2278 if (q
< 0 && r
>= 0)
2285 int unit_file_unmask(
2287 UnitFileFlags flags
,
2288 const char *root_dir
,
2290 InstallChange
**changes
,
2291 size_t *n_changes
) {
2293 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2294 _cleanup_set_free_free_ Set
*remove_symlinks_to
= NULL
;
2295 _cleanup_strv_free_
char **todo
= NULL
;
2296 const char *config_path
;
2301 assert(scope
< _LOOKUP_SCOPE_MAX
);
2303 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2307 config_path
= (flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2311 bool dry_run
= flags
& UNIT_FILE_DRY_RUN
;
2313 STRV_FOREACH(name
, names
) {
2314 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2317 /* If root_dir is set, we don't care about kernel commandline or generators.
2318 * But if it is not set, we need to check for interference. */
2320 _cleanup_(install_info_clear
) InstallInfo info
= {
2321 .name
= *name
, /* We borrow *name temporarily… */
2322 .install_mode
= _INSTALL_MODE_INVALID
,
2325 r
= unit_file_search(NULL
, &info
, &lp
, 0);
2328 log_debug_errno(r
, "Failed to look up unit %s, ignoring: %m", info
.name
);
2329 } else if (info
.install_mode
== INSTALL_MODE_MASKED
&&
2330 path_is_generator(&lp
, info
.path
)) {
2331 r
= install_changes_add(changes
, n_changes
,
2332 INSTALL_CHANGE_IS_MASKED_GENERATOR
, info
.name
, info
.path
);
2337 TAKE_PTR(info
.name
); /* … and give it back here */
2340 _cleanup_free_
char *path
= path_make_absolute(*name
, config_path
);
2344 r
= null_or_empty_path(path
);
2352 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2355 todo
[n_todo
] = strdup(*name
);
2365 STRV_FOREACH(i
, todo
) {
2366 _cleanup_free_
char *path
= NULL
;
2369 path
= path_make_absolute(*i
, config_path
);
2373 if (!dry_run
&& unlink(path
) < 0) {
2374 if (errno
!= ENOENT
) {
2377 install_changes_add(changes
, n_changes
, -errno
, path
, NULL
);
2383 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, path
, NULL
);
2387 rp
= skip_root(lp
.root_dir
, path
);
2388 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: path
);
2393 q
= remove_marked_symlinks(remove_symlinks_to
, config_path
, &lp
, dry_run
, changes
, n_changes
);
2402 UnitFileFlags flags
,
2403 const char *root_dir
,
2405 InstallChange
**changes
,
2406 size_t *n_changes
) {
2408 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2409 _cleanup_strv_free_
char **todo
= NULL
;
2410 const char *config_path
;
2415 assert(scope
< _LOOKUP_SCOPE_MAX
);
2417 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2421 config_path
= (flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2425 STRV_FOREACH(file
, files
) {
2426 _cleanup_free_
char *full
= NULL
;
2430 if (!path_is_absolute(*file
))
2431 return install_changes_add(changes
, n_changes
, -EINVAL
, *file
, NULL
);
2433 fn
= basename(*file
);
2434 if (!unit_name_is_valid(fn
, UNIT_NAME_ANY
))
2435 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *file
, NULL
);
2437 full
= path_join(lp
.root_dir
, *file
);
2441 if (lstat(full
, &st
) < 0)
2442 return install_changes_add(changes
, n_changes
, -errno
, *file
, NULL
);
2444 r
= stat_verify_regular(&st
);
2446 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2448 r
= in_search_path(&lp
, *file
);
2450 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2452 /* A silent noop if the file is already in the search path. */
2455 r
= underneath_search_path(&lp
, *file
);
2459 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2461 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2464 todo
[n_todo
] = strdup(*file
);
2474 STRV_FOREACH(i
, todo
) {
2475 _cleanup_free_
char *new_path
= NULL
;
2477 new_path
= path_make_absolute(basename(*i
), config_path
);
2481 q
= create_symlink(&lp
, *i
, new_path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
);
2482 if (q
< 0 && r
>= 0)
2489 static int path_shall_revert(const LookupPaths
*lp
, const char *path
) {
2495 /* Checks whether the path is one where the drop-in directories shall be removed. */
2497 r
= path_is_config(lp
, path
, true);
2501 r
= path_is_control(lp
, path
);
2505 return path_is_transient(lp
, path
);
2508 int unit_file_revert(
2510 const char *root_dir
,
2512 InstallChange
**changes
,
2513 size_t *n_changes
) {
2515 _cleanup_set_free_free_ Set
*remove_symlinks_to
= NULL
;
2516 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2517 _cleanup_strv_free_
char **todo
= NULL
;
2521 /* Puts a unit file back into vendor state. This means:
2523 * a) we remove all drop-in snippets added by the user ("config"), add to transient units
2524 * ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
2525 * generated ("generated").
2527 * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
2528 * (i.e. in "config", but not in "transient" or "control" or even "generated").
2530 * We remove all that in both the runtime and the persistent directories, if that applies.
2533 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2537 STRV_FOREACH(name
, names
) {
2538 bool has_vendor
= false;
2540 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2543 STRV_FOREACH(p
, lp
.search_path
) {
2544 _cleanup_free_
char *path
= NULL
, *dropin
= NULL
;
2547 path
= path_make_absolute(*name
, *p
);
2551 r
= RET_NERRNO(lstat(path
, &st
));
2554 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2555 } else if (S_ISREG(st
.st_mode
)) {
2556 /* Check if there's a vendor version */
2557 r
= path_is_vendor_or_generator(&lp
, path
);
2559 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2564 dropin
= strjoin(path
, ".d");
2568 r
= RET_NERRNO(lstat(dropin
, &st
));
2571 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2572 } else if (S_ISDIR(st
.st_mode
)) {
2573 /* Remove the drop-ins */
2574 r
= path_shall_revert(&lp
, dropin
);
2576 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2578 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2581 todo
[n_todo
++] = TAKE_PTR(dropin
);
2589 /* OK, there's a vendor version, hence drop all configuration versions */
2590 STRV_FOREACH(p
, lp
.search_path
) {
2591 _cleanup_free_
char *path
= NULL
;
2594 path
= path_make_absolute(*name
, *p
);
2598 r
= RET_NERRNO(lstat(path
, &st
));
2601 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2602 } else if (S_ISREG(st
.st_mode
) || S_ISLNK(st
.st_mode
)) {
2603 r
= path_is_config(&lp
, path
, true);
2605 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2607 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2610 todo
[n_todo
++] = TAKE_PTR(path
);
2619 STRV_FOREACH(i
, todo
) {
2620 _cleanup_strv_free_
char **fs
= NULL
;
2623 (void) get_files_in_directory(*i
, &fs
);
2625 q
= rm_rf(*i
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
2626 if (q
< 0 && q
!= -ENOENT
&& r
>= 0) {
2631 STRV_FOREACH(j
, fs
) {
2632 _cleanup_free_
char *t
= NULL
;
2634 t
= path_join(*i
, *j
);
2638 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, t
, NULL
);
2643 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, *i
, NULL
);
2647 rp
= skip_root(lp
.root_dir
, *i
);
2648 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: *i
);
2653 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.runtime_config
, &lp
, false, changes
, n_changes
);
2657 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.persistent_config
, &lp
, false, changes
, n_changes
);
2664 int unit_file_add_dependency(
2666 UnitFileFlags file_flags
,
2667 const char *root_dir
,
2671 InstallChange
**changes
,
2672 size_t *n_changes
) {
2674 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2675 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2676 InstallInfo
*info
, *target_info
;
2677 const char *config_path
;
2681 assert(scope
< _LOOKUP_SCOPE_MAX
);
2683 assert(IN_SET(dep
, UNIT_WANTS
, UNIT_REQUIRES
));
2685 if (!unit_name_is_valid(target
, UNIT_NAME_ANY
))
2686 return install_changes_add(changes
, n_changes
, -EUCLEAN
, target
, NULL
);
2688 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2692 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2696 r
= install_info_discover_and_check(&ctx
, &lp
, target
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2697 &target_info
, changes
, n_changes
);
2701 assert(target_info
->install_mode
== INSTALL_MODE_REGULAR
);
2703 STRV_FOREACH(name
, names
) {
2706 r
= install_info_discover_and_check(&ctx
, &lp
, *name
,
2707 SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2708 &info
, changes
, n_changes
);
2712 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2714 /* We didn't actually load anything from the unit
2715 * file, but instead just add in our new symlink to
2718 if (dep
== UNIT_WANTS
)
2719 l
= &info
->wanted_by
;
2721 l
= &info
->required_by
;
2724 *l
= strv_new(target_info
->name
);
2729 return install_context_apply(&ctx
, &lp
, file_flags
, config_path
,
2730 SEARCH_FOLLOW_CONFIG_SYMLINKS
, changes
, n_changes
);
2733 static int do_unit_file_enable(
2734 const LookupPaths
*lp
,
2736 UnitFileFlags flags
,
2737 const char *config_path
,
2738 char **names_or_paths
,
2739 InstallChange
**changes
,
2740 size_t *n_changes
) {
2742 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2746 STRV_FOREACH(name
, names_or_paths
) {
2747 r
= install_info_discover_and_check(&ctx
, lp
, *name
,
2748 SEARCH_LOAD
| SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2749 &info
, changes
, n_changes
);
2753 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2756 /* This will return the number of symlink rules that were
2757 supposed to be created, not the ones actually created. This
2758 is useful to determine whether the passed units had any
2759 installation data at all. */
2761 return install_context_apply(&ctx
, lp
, flags
, config_path
,
2762 SEARCH_LOAD
, changes
, n_changes
);
2765 int unit_file_enable(
2767 UnitFileFlags flags
,
2768 const char *root_dir
,
2769 char **names_or_paths
,
2770 InstallChange
**changes
,
2771 size_t *n_changes
) {
2773 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2777 assert(scope
< _LOOKUP_SCOPE_MAX
);
2779 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2783 const char *config_path
= config_path_from_flags(&lp
, flags
);
2787 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, names_or_paths
, changes
, n_changes
);
2790 static int do_unit_file_disable(
2791 const LookupPaths
*lp
,
2793 UnitFileFlags flags
,
2794 const char *config_path
,
2796 InstallChange
**changes
,
2797 size_t *n_changes
) {
2799 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2800 _cleanup_set_free_free_ Set
*remove_symlinks_to
= NULL
;
2802 bool has_install_info
= false;
2805 STRV_FOREACH(name
, names
) {
2806 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2807 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *name
, NULL
);
2809 r
= install_info_add(&ctx
, *name
, NULL
, lp
->root_dir
, /* auxiliary= */ false, &info
);
2811 r
= install_info_traverse(&ctx
, lp
, info
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, NULL
);
2814 return install_changes_add(changes
, n_changes
, r
, *name
, NULL
);
2816 /* If we enable multiple units, some with install info and others without,
2817 * the "empty [Install] section" warning is not shown. Let's make the behavior
2818 * of disable align with that. */
2819 has_install_info
= has_install_info
|| install_info_has_rules(info
) || install_info_has_also(info
);
2822 r
= install_context_mark_for_removal(&ctx
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
2824 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, flags
& UNIT_FILE_DRY_RUN
, changes
, n_changes
);
2829 /* The warning is shown only if it's a no-op */
2830 return install_changes_have_modification(*changes
, *n_changes
) || has_install_info
;
2833 int unit_file_disable(
2835 UnitFileFlags flags
,
2836 const char *root_dir
,
2838 InstallChange
**changes
,
2839 size_t *n_changes
) {
2841 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2845 assert(scope
< _LOOKUP_SCOPE_MAX
);
2847 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2851 const char *config_path
= config_path_from_flags(&lp
, flags
);
2855 return do_unit_file_disable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
2858 static int normalize_linked_files(
2860 const LookupPaths
*lp
,
2861 char **names_or_paths
,
2863 char ***ret_files
) {
2865 /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/,
2866 * but operates on real unit names. For each argument we we look up the actual path
2867 * where the unit is found. This way linked units can be re-enabled successfully. */
2869 _cleanup_strv_free_
char **files
= NULL
, **names
= NULL
;
2872 STRV_FOREACH(a
, names_or_paths
) {
2873 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2874 InstallInfo
*i
= NULL
;
2875 _cleanup_free_
char *n
= NULL
;
2877 r
= path_extract_filename(*a
, &n
);
2880 if (r
== O_DIRECTORY
)
2881 return log_debug_errno(SYNTHETIC_ERRNO(EISDIR
),
2882 "Unexpected path to a directory \"%s\", refusing.", *a
);
2885 r
= install_info_discover(&ctx
, lp
, n
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, &i
, NULL
, NULL
);
2887 log_debug_errno(r
, "Failed to discover unit \"%s\", operating on name: %m", n
);
2890 r
= strv_consume(&names
, TAKE_PTR(n
));
2894 const char *p
= NULL
;
2895 if (i
&& i
->path
&& i
->root
)
2896 /* Use startswith here, because we know that paths are normalized, and
2897 * path_startswith() would give us a relative path, but we need an absolute path
2898 * relative to i->root.
2900 * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
2901 * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
2902 * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
2904 p
= startswith(i
->path
, i
->root
);
2906 r
= strv_extend(&files
, p
?: *a
);
2911 *ret_names
= TAKE_PTR(names
);
2912 *ret_files
= TAKE_PTR(files
);
2916 int unit_file_reenable(
2918 UnitFileFlags flags
,
2919 const char *root_dir
,
2920 char **names_or_paths
,
2921 InstallChange
**changes
,
2922 size_t *n_changes
) {
2924 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2925 _cleanup_strv_free_
char **names
= NULL
, **files
= NULL
;
2929 assert(scope
< _LOOKUP_SCOPE_MAX
);
2931 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2935 const char *config_path
= config_path_from_flags(&lp
, flags
);
2939 r
= normalize_linked_files(scope
, &lp
, names_or_paths
, &names
, &files
);
2943 /* First, we invoke the disable command with only the basename... */
2944 r
= do_unit_file_disable(&lp
, scope
, flags
, config_path
, names
, changes
, n_changes
);
2948 /* But the enable command with the full name */
2949 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
2952 int unit_file_set_default(
2954 UnitFileFlags flags
,
2955 const char *root_dir
,
2957 InstallChange
**changes
,
2958 size_t *n_changes
) {
2960 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2961 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2963 const char *new_path
;
2967 assert(scope
< _LOOKUP_SCOPE_MAX
);
2970 if (unit_name_to_type(name
) != UNIT_TARGET
) /* this also validates the name */
2972 if (streq(name
, SPECIAL_DEFAULT_TARGET
))
2975 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2979 r
= install_info_discover_and_check(&ctx
, &lp
, name
, 0, &info
, changes
, n_changes
);
2983 new_path
= strjoina(lp
.persistent_config
, "/" SPECIAL_DEFAULT_TARGET
);
2984 return create_symlink(&lp
, info
->path
, new_path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
);
2987 int unit_file_get_default(
2989 const char *root_dir
,
2992 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
2993 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2999 assert(scope
< _LOOKUP_SCOPE_MAX
);
3002 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3006 r
= install_info_discover(&ctx
, &lp
, SPECIAL_DEFAULT_TARGET
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3010 r
= install_info_may_process(info
, &lp
, NULL
, 0);
3014 n
= strdup(info
->name
);
3022 int unit_file_lookup_state(
3024 const LookupPaths
*lp
,
3026 UnitFileState
*ret
) {
3028 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3030 UnitFileState state
;
3036 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3039 r
= install_info_discover(&ctx
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3042 return log_debug_errno(r
, "Failed to discover unit %s: %m", name
);
3044 assert(IN_SET(info
->install_mode
, INSTALL_MODE_REGULAR
, INSTALL_MODE_MASKED
));
3045 log_debug("Found unit %s at %s (%s)", name
, strna(info
->path
),
3046 info
->install_mode
== INSTALL_MODE_REGULAR
? "regular file" : "mask");
3048 /* Shortcut things, if the caller just wants to know if this unit exists. */
3052 switch (info
->install_mode
) {
3054 case INSTALL_MODE_MASKED
:
3055 r
= path_is_runtime(lp
, info
->path
, true);
3059 state
= r
> 0 ? UNIT_FILE_MASKED_RUNTIME
: UNIT_FILE_MASKED
;
3062 case INSTALL_MODE_REGULAR
:
3063 /* Check if the name we were querying is actually an alias */
3064 if (!streq(name
, basename(info
->path
)) && !unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
3065 state
= UNIT_FILE_ALIAS
;
3069 r
= path_is_generator(lp
, info
->path
);
3073 state
= UNIT_FILE_GENERATED
;
3077 r
= path_is_transient(lp
, info
->path
);
3081 state
= UNIT_FILE_TRANSIENT
;
3085 /* Check if any of the Alias= symlinks have been created.
3086 * We ignore other aliases, and only check those that would
3087 * be created by systemctl enable for this unit. */
3088 r
= find_symlinks_in_scope(scope
, lp
, info
, true, &state
);
3094 /* Check if the file is known under other names. If it is,
3095 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
3096 r
= find_symlinks_in_scope(scope
, lp
, info
, false, &state
);
3100 state
= UNIT_FILE_INDIRECT
;
3102 if (install_info_has_rules(info
))
3103 state
= UNIT_FILE_DISABLED
;
3104 else if (install_info_has_also(info
))
3105 state
= UNIT_FILE_INDIRECT
;
3107 state
= UNIT_FILE_STATIC
;
3113 assert_not_reached();
3120 int unit_file_get_state(
3122 const char *root_dir
,
3124 UnitFileState
*ret
) {
3126 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
3130 assert(scope
< _LOOKUP_SCOPE_MAX
);
3133 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3137 return unit_file_lookup_state(scope
, &lp
, name
, ret
);
3140 int unit_file_exists(LookupScope scope
, const LookupPaths
*lp
, const char *name
) {
3141 _cleanup_(install_context_done
) InstallContext c
= { .scope
= scope
};
3147 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3150 r
= install_info_discover(&c
, lp
, name
, 0, NULL
, NULL
, NULL
);
3159 static int split_pattern_into_name_and_instances(const char *pattern
, char **out_unit_name
, char ***out_instances
) {
3160 _cleanup_strv_free_
char **instances
= NULL
;
3161 _cleanup_free_
char *unit_name
= NULL
;
3165 assert(out_instances
);
3166 assert(out_unit_name
);
3168 r
= extract_first_word(&pattern
, &unit_name
, NULL
, EXTRACT_RETAIN_ESCAPE
);
3172 /* We handle the instances logic when unit name is extracted */
3174 /* We only create instances when a rule of templated unit
3175 * is seen. A rule like enable foo@.service a b c will
3176 * result in an array of (a, b, c) as instance names */
3177 if (!unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
))
3180 instances
= strv_split(pattern
, WHITESPACE
);
3184 *out_instances
= TAKE_PTR(instances
);
3187 *out_unit_name
= TAKE_PTR(unit_name
);
3192 static int presets_find_config(LookupScope scope
, const char *root_dir
, char ***files
) {
3193 static const char* const system_dirs
[] = {CONF_PATHS("systemd/system-preset"), NULL
};
3194 static const char* const user_dirs
[] = {CONF_PATHS_USR("systemd/user-preset"), NULL
};
3195 const char* const* dirs
;
3198 assert(scope
< _LOOKUP_SCOPE_MAX
);
3200 if (scope
== LOOKUP_SCOPE_SYSTEM
)
3202 else if (IN_SET(scope
, LOOKUP_SCOPE_GLOBAL
, LOOKUP_SCOPE_USER
))
3205 assert_not_reached();
3207 return conf_files_list_strv(files
, ".preset", root_dir
, 0, dirs
);
3210 static int read_presets(LookupScope scope
, const char *root_dir
, UnitFilePresets
*presets
) {
3211 _cleanup_(unit_file_presets_freep
) UnitFilePresets ps
= {};
3212 _cleanup_strv_free_
char **files
= NULL
;
3216 assert(scope
< _LOOKUP_SCOPE_MAX
);
3219 r
= presets_find_config(scope
, root_dir
, &files
);
3223 STRV_FOREACH(p
, files
) {
3224 _cleanup_fclose_
FILE *f
= NULL
;
3227 f
= fopen(*p
, "re");
3229 if (errno
== ENOENT
)
3236 _cleanup_free_
char *line
= NULL
;
3237 UnitFilePresetRule rule
= {};
3238 const char *parameter
;
3241 r
= read_line(f
, LONG_LINE_MAX
, &line
);
3252 if (strchr(COMMENTS
, *l
))
3255 parameter
= first_word(l
, "enable");
3258 char **instances
= NULL
;
3260 /* Unit_name will remain the same as parameter when no instances are specified */
3261 r
= split_pattern_into_name_and_instances(parameter
, &unit_name
, &instances
);
3263 log_syntax(NULL
, LOG_WARNING
, *p
, n
, r
, "Couldn't parse line '%s'. Ignoring.", line
);
3267 rule
= (UnitFilePresetRule
) {
3268 .pattern
= unit_name
,
3269 .action
= PRESET_ENABLE
,
3270 .instances
= instances
,
3274 parameter
= first_word(l
, "disable");
3278 pattern
= strdup(parameter
);
3282 rule
= (UnitFilePresetRule
) {
3284 .action
= PRESET_DISABLE
,
3289 if (!GREEDY_REALLOC(ps
.rules
, ps
.n_rules
+ 1))
3292 ps
.rules
[ps
.n_rules
++] = rule
;
3296 log_syntax(NULL
, LOG_WARNING
, *p
, n
, 0, "Couldn't parse line '%s'. Ignoring.", line
);
3300 ps
.initialized
= true;
3302 ps
= (UnitFilePresets
){};
3307 static int pattern_match_multiple_instances(
3308 const UnitFilePresetRule rule
,
3309 const char *unit_name
,
3312 _cleanup_free_
char *templated_name
= NULL
;
3315 /* If no ret is needed or the rule itself does not have instances
3316 * initialized, we return not matching */
3317 if (!ret
|| !rule
.instances
)
3320 r
= unit_name_template(unit_name
, &templated_name
);
3323 if (!streq(rule
.pattern
, templated_name
))
3326 /* Compose a list of specified instances when unit name is a template */
3327 if (unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
)) {
3328 _cleanup_strv_free_
char **out_strv
= NULL
;
3330 STRV_FOREACH(iter
, rule
.instances
) {
3331 _cleanup_free_
char *name
= NULL
;
3333 r
= unit_name_replace_instance(unit_name
, *iter
, &name
);
3337 r
= strv_consume(&out_strv
, TAKE_PTR(name
));
3342 *ret
= TAKE_PTR(out_strv
);
3345 /* We now know the input unit name is an instance name */
3346 _cleanup_free_
char *instance_name
= NULL
;
3348 r
= unit_name_to_instance(unit_name
, &instance_name
);
3352 if (strv_find(rule
.instances
, instance_name
))
3358 static int query_presets(const char *name
, const UnitFilePresets
*presets
, char ***instance_name_list
) {
3359 PresetAction action
= PRESET_UNKNOWN
;
3361 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3364 for (size_t i
= 0; i
< presets
->n_rules
; i
++)
3365 if (pattern_match_multiple_instances(presets
->rules
[i
], name
, instance_name_list
) > 0 ||
3366 fnmatch(presets
->rules
[i
].pattern
, name
, FNM_NOESCAPE
) == 0) {
3367 action
= presets
->rules
[i
].action
;
3372 case PRESET_UNKNOWN
:
3373 log_debug("Preset files don't specify rule for %s. Enabling.", name
);
3376 if (instance_name_list
&& *instance_name_list
)
3377 STRV_FOREACH(s
, *instance_name_list
)
3378 log_debug("Preset files say enable %s.", *s
);
3380 log_debug("Preset files say enable %s.", name
);
3382 case PRESET_DISABLE
:
3383 log_debug("Preset files say disable %s.", name
);
3386 assert_not_reached();
3390 int unit_file_query_preset(LookupScope scope
, const char *root_dir
, const char *name
, UnitFilePresets
*cached
) {
3391 _cleanup_(unit_file_presets_freep
) UnitFilePresets tmp
= {};
3396 if (!cached
->initialized
) {
3397 r
= read_presets(scope
, root_dir
, cached
);
3402 return query_presets(name
, cached
, NULL
);
3405 static int execute_preset(
3406 UnitFileFlags file_flags
,
3407 InstallContext
*plus
,
3408 InstallContext
*minus
,
3409 const LookupPaths
*lp
,
3410 const char *config_path
,
3412 UnitFilePresetMode mode
,
3413 InstallChange
**changes
,
3414 size_t *n_changes
) {
3421 assert(config_path
);
3423 if (mode
!= UNIT_FILE_PRESET_ENABLE_ONLY
) {
3424 _cleanup_set_free_free_ Set
*remove_symlinks_to
= NULL
;
3426 r
= install_context_mark_for_removal(minus
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
3430 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, false, changes
, n_changes
);
3434 if (mode
!= UNIT_FILE_PRESET_DISABLE_ONLY
) {
3437 /* Returns number of symlinks that where supposed to be installed. */
3438 q
= install_context_apply(plus
, lp
,
3439 file_flags
| UNIT_FILE_IGNORE_AUXILIARY_FAILURE
,
3441 SEARCH_LOAD
, changes
, n_changes
);
3453 static int preset_prepare_one(
3455 InstallContext
*plus
,
3456 InstallContext
*minus
,
3459 const UnitFilePresets
*presets
,
3460 InstallChange
**changes
,
3461 size_t *n_changes
) {
3463 _cleanup_(install_context_done
) InstallContext tmp
= { .scope
= scope
};
3464 _cleanup_strv_free_
char **instance_name_list
= NULL
;
3468 if (install_info_find(plus
, name
) || install_info_find(minus
, name
))
3471 r
= install_info_discover(&tmp
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3472 &info
, changes
, n_changes
);
3475 if (!streq(name
, info
->name
)) {
3476 log_debug("Skipping %s because it is an alias for %s.", name
, info
->name
);
3480 r
= query_presets(name
, presets
, &instance_name_list
);
3485 if (instance_name_list
)
3486 STRV_FOREACH(s
, instance_name_list
) {
3487 r
= install_info_discover_and_check(plus
, lp
, *s
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3488 &info
, changes
, n_changes
);
3493 r
= install_info_discover_and_check(plus
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3494 &info
, changes
, n_changes
);
3500 r
= install_info_discover(minus
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3501 &info
, changes
, n_changes
);
3506 int unit_file_preset(
3508 UnitFileFlags file_flags
,
3509 const char *root_dir
,
3511 UnitFilePresetMode mode
,
3512 InstallChange
**changes
,
3513 size_t *n_changes
) {
3515 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3516 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
3517 _cleanup_(unit_file_presets_freep
) UnitFilePresets presets
= {};
3518 const char *config_path
;
3522 assert(scope
< _LOOKUP_SCOPE_MAX
);
3523 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3525 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3529 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3533 r
= read_presets(scope
, root_dir
, &presets
);
3537 STRV_FOREACH(name
, names
) {
3538 r
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, *name
, &presets
, changes
, n_changes
);
3543 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, names
, mode
, changes
, n_changes
);
3546 int unit_file_preset_all(
3548 UnitFileFlags file_flags
,
3549 const char *root_dir
,
3550 UnitFilePresetMode mode
,
3551 InstallChange
**changes
,
3552 size_t *n_changes
) {
3554 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3555 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
3556 _cleanup_(unit_file_presets_freep
) UnitFilePresets presets
= {};
3557 const char *config_path
= NULL
;
3561 assert(scope
< _LOOKUP_SCOPE_MAX
);
3562 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3564 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3568 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3572 r
= read_presets(scope
, root_dir
, &presets
);
3576 STRV_FOREACH(i
, lp
.search_path
) {
3577 _cleanup_closedir_
DIR *d
= NULL
;
3581 if (errno
== ENOENT
)
3587 FOREACH_DIRENT(de
, d
, return -errno
) {
3589 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3592 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3595 r
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, de
->d_name
, &presets
, changes
, n_changes
);
3597 !IN_SET(r
, -EEXIST
, -ERFKILL
, -EADDRNOTAVAIL
, -EBADSLT
, -EIDRM
, -EUCLEAN
, -ELOOP
, -ENOENT
, -EUNATCH
, -EXDEV
))
3598 /* Ignore generated/transient/missing/invalid units when applying preset, propagate other errors.
3599 * Coordinate with install_changes_dump() above. */
3604 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, NULL
, mode
, changes
, n_changes
);
3607 static UnitFileList
* unit_file_list_free_one(UnitFileList
*f
) {
3615 Hashmap
* unit_file_list_free(Hashmap
*h
) {
3616 return hashmap_free_with_destructor(h
, unit_file_list_free_one
);
3619 DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList
*, unit_file_list_free_one
);
3621 int unit_file_get_list(
3623 const char *root_dir
,
3628 _cleanup_(lookup_paths_free
) LookupPaths lp
= {};
3632 assert(scope
< _LOOKUP_SCOPE_MAX
);
3635 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3639 STRV_FOREACH(dirname
, lp
.search_path
) {
3640 _cleanup_closedir_
DIR *d
= NULL
;
3642 d
= opendir(*dirname
);
3644 if (errno
== ENOENT
)
3646 if (IN_SET(errno
, ENOTDIR
, EACCES
)) {
3647 log_debug_errno(errno
, "Failed to open \"%s\": %m", *dirname
);
3654 FOREACH_DIRENT(de
, d
, return -errno
) {
3655 _cleanup_(unit_file_list_free_onep
) UnitFileList
*f
= NULL
;
3657 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3660 if (!strv_fnmatch_or_empty(patterns
, de
->d_name
, FNM_NOESCAPE
))
3663 if (hashmap_get(h
, de
->d_name
))
3666 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3669 f
= new0(UnitFileList
, 1);
3673 f
->path
= path_make_absolute(de
->d_name
, *dirname
);
3677 r
= unit_file_lookup_state(scope
, &lp
, de
->d_name
, &f
->state
);
3679 f
->state
= UNIT_FILE_BAD
;
3681 if (!strv_isempty(states
) &&
3682 !strv_contains(states
, unit_file_state_to_string(f
->state
)))
3685 r
= hashmap_put(h
, basename(f
->path
), f
);
3689 f
= NULL
; /* prevent cleanup */
3696 static const char* const unit_file_state_table
[_UNIT_FILE_STATE_MAX
] = {
3697 [UNIT_FILE_ENABLED
] = "enabled",
3698 [UNIT_FILE_ENABLED_RUNTIME
] = "enabled-runtime",
3699 [UNIT_FILE_LINKED
] = "linked",
3700 [UNIT_FILE_LINKED_RUNTIME
] = "linked-runtime",
3701 [UNIT_FILE_ALIAS
] = "alias",
3702 [UNIT_FILE_MASKED
] = "masked",
3703 [UNIT_FILE_MASKED_RUNTIME
] = "masked-runtime",
3704 [UNIT_FILE_STATIC
] = "static",
3705 [UNIT_FILE_DISABLED
] = "disabled",
3706 [UNIT_FILE_INDIRECT
] = "indirect",
3707 [UNIT_FILE_GENERATED
] = "generated",
3708 [UNIT_FILE_TRANSIENT
] = "transient",
3709 [UNIT_FILE_BAD
] = "bad",
3712 DEFINE_STRING_TABLE_LOOKUP(unit_file_state
, UnitFileState
);
3714 static const char* const install_change_type_table
[_INSTALL_CHANGE_TYPE_MAX
] = {
3715 [INSTALL_CHANGE_SYMLINK
] = "symlink",
3716 [INSTALL_CHANGE_UNLINK
] = "unlink",
3717 [INSTALL_CHANGE_IS_MASKED
] = "masked",
3718 [INSTALL_CHANGE_IS_MASKED_GENERATOR
] = "masked by generator",
3719 [INSTALL_CHANGE_IS_DANGLING
] = "dangling",
3720 [INSTALL_CHANGE_DESTINATION_NOT_PRESENT
] = "destination not present",
3721 [INSTALL_CHANGE_AUXILIARY_FAILED
] = "auxiliary unit failed",
3724 DEFINE_STRING_TABLE_LOOKUP(install_change_type
, InstallChangeType
);
3726 static const char* const unit_file_preset_mode_table
[_UNIT_FILE_PRESET_MODE_MAX
] = {
3727 [UNIT_FILE_PRESET_FULL
] = "full",
3728 [UNIT_FILE_PRESET_ENABLE_ONLY
] = "enable-only",
3729 [UNIT_FILE_PRESET_DISABLE_ONLY
] = "disable-only",
3732 DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode
, UnitFilePresetMode
);