1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
8 #include "alloc-util.h"
9 #include "bus-common-errors.h"
11 #include "conf-files.h"
12 #include "conf-parser.h"
13 #include "constants.h"
14 #include "dirent-util.h"
15 #include "errno-util.h"
16 #include "extract-word.h"
20 #include "glyph-util.h"
23 #include "install-printf.h"
26 #include "path-lookup.h"
27 #include "path-util.h"
31 #include "stat-util.h"
32 #include "string-table.h"
33 #include "string-util.h"
35 #include "unit-file.h"
36 #include "unit-name.h"
38 #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
42 OrderedHashmap
*will_process
;
43 OrderedHashmap
*have_processed
;
46 struct UnitFilePresetRule
{
52 /* NB! strings use past tense. */
53 static const char *const preset_action_past_tense_table
[_PRESET_ACTION_MAX
] = {
54 [PRESET_UNKNOWN
] = "unknown",
55 [PRESET_ENABLE
] = "enabled",
56 [PRESET_DISABLE
] = "disabled",
57 [PRESET_IGNORE
] = "ignored",
60 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(preset_action_past_tense
, PresetAction
);
62 static bool install_info_has_rules(const InstallInfo
*i
) {
65 return !strv_isempty(i
->aliases
) ||
66 !strv_isempty(i
->wanted_by
) ||
67 !strv_isempty(i
->required_by
) ||
68 !strv_isempty(i
->upheld_by
);
71 static bool install_info_has_also(const InstallInfo
*i
) {
74 return !strv_isempty(i
->also
);
77 static void unit_file_preset_rule_done(UnitFilePresetRule
*rule
) {
81 strv_free(rule
->instances
);
84 void unit_file_presets_done(UnitFilePresets
*p
) {
88 FOREACH_ARRAY(rule
, p
->rules
, p
->n_rules
)
89 unit_file_preset_rule_done(rule
);
95 static const char *const install_mode_table
[_INSTALL_MODE_MAX
] = {
96 [INSTALL_MODE_REGULAR
] = "regular",
97 [INSTALL_MODE_LINKED
] = "linked",
98 [INSTALL_MODE_ALIAS
] = "alias",
99 [INSTALL_MODE_MASKED
] = "masked",
102 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(install_mode
, InstallMode
);
104 static int in_search_path(const LookupPaths
*lp
, const char *path
) {
105 _cleanup_free_
char *parent
= NULL
;
108 /* Check if 'path' is in lp->search_path. */
113 r
= path_extract_directory(path
, &parent
);
117 return path_strv_contains(lp
->search_path
, parent
);
120 static bool underneath_search_path(const LookupPaths
*lp
, const char *path
) {
121 /* Check if 'path' is underneath lp->search_path. */
126 return path_startswith_strv(path
, lp
->search_path
);
129 static const char* skip_root(const char *root_dir
, const char *path
) {
135 const char *e
= path_startswith(path
, root_dir
);
139 /* Make sure the returned path starts with a slash */
141 if (e
== path
|| e
[-1] != '/')
150 static int path_is_generator(const LookupPaths
*lp
, const char *path
) {
151 _cleanup_free_
char *parent
= NULL
;
157 r
= path_extract_directory(path
, &parent
);
161 return PATH_IN_SET(parent
,
167 static int path_is_transient(const LookupPaths
*lp
, const char *path
) {
168 _cleanup_free_
char *parent
= NULL
;
174 r
= path_extract_directory(path
, &parent
);
178 return path_equal(parent
, lp
->transient
);
181 static int path_is_control(const LookupPaths
*lp
, const char *path
) {
182 _cleanup_free_
char *parent
= NULL
;
188 r
= path_extract_directory(path
, &parent
);
192 return PATH_IN_SET(parent
,
193 lp
->persistent_control
,
194 lp
->runtime_control
);
197 static int path_is_config(const LookupPaths
*lp
, const char *path
, bool check_parent
) {
198 _cleanup_free_
char *parent
= NULL
;
204 /* Note that we do *not* have generic checks for /etc or /run in place, since with
205 * them we couldn't discern configuration from transient or generated units */
208 r
= path_extract_directory(path
, &parent
);
215 return PATH_IN_SET(path
,
216 lp
->persistent_config
,
220 static int path_is_runtime(const LookupPaths
*lp
, const char *path
, bool check_parent
) {
221 _cleanup_free_
char *parent
= NULL
;
228 /* Everything in /run is considered runtime. On top of that we also add
229 * explicit checks for the various runtime directories, as safety net. */
231 rpath
= skip_root(lp
->root_dir
, path
);
232 if (rpath
&& path_startswith(rpath
, "/run"))
236 r
= path_extract_directory(path
, &parent
);
243 return PATH_IN_SET(path
,
249 lp
->runtime_control
);
252 static int path_is_vendor_or_generator(const LookupPaths
*lp
, const char *path
) {
258 rpath
= skip_root(lp
->root_dir
, path
);
262 if (path_startswith(rpath
, "/usr"))
265 if (path_is_generator(lp
, rpath
))
268 return path_equal(rpath
, SYSTEM_DATA_UNIT_DIR
);
271 static const char* config_path_from_flags(const LookupPaths
*lp
, UnitFileFlags flags
) {
274 if (FLAGS_SET(flags
, UNIT_FILE_PORTABLE
))
275 return FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
->runtime_attached
: lp
->persistent_attached
;
277 return FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
->runtime_config
: lp
->persistent_config
;
280 InstallChangeType
install_changes_add(
281 InstallChange
**changes
,
283 InstallChangeType type
, /* INSTALL_CHANGE_SYMLINK, _UNLINK, _IS_MASKED, _IS_DANGLING, … if positive or errno if negative */
285 const char *source
) {
287 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
290 assert(!changes
== !n_changes
);
291 assert(INSTALL_CHANGE_TYPE_VALID(type
));
293 /* Message formatting requires <path> to be set. */
296 /* Register a change or error. Note that the return value may be the error
297 * that was passed in, or -ENOMEM generated internally. */
302 if (!GREEDY_REALLOC(*changes
, *n_changes
+ 1))
305 r
= path_simplify_alloc(path
, &p
);
309 r
= path_simplify_alloc(source
, &s
);
313 (*changes
)[(*n_changes
)++] = (InstallChange
) {
316 .source
= TAKE_PTR(s
),
322 static void install_change_done(InstallChange
*change
) {
325 change
->path
= mfree(change
->path
);
326 change
->source
= mfree(change
->source
);
329 DEFINE_ARRAY_FREE_FUNC(install_changes_free
, InstallChange
, install_change_done
);
331 static void install_change_dump_success(const InstallChange
*change
) {
333 assert(change
->path
);
335 switch (change
->type
) {
337 case INSTALL_CHANGE_SYMLINK
:
338 return log_info("Created symlink '%s' %s '%s'.",
339 change
->path
, glyph(GLYPH_ARROW_RIGHT
), change
->source
);
341 case INSTALL_CHANGE_UNLINK
:
342 return log_info("Removed '%s'.", change
->path
);
344 case INSTALL_CHANGE_IS_MASKED
:
345 return log_info("Unit %s is masked, ignoring.", change
->path
);
347 case INSTALL_CHANGE_IS_MASKED_GENERATOR
:
348 return log_info("Unit %s is masked via a generator and cannot be unmasked, skipping.", change
->path
);
350 case INSTALL_CHANGE_IS_DANGLING
:
351 return log_info("Unit %s is an alias to a non-existent unit, ignoring.", change
->path
);
353 case INSTALL_CHANGE_DESTINATION_NOT_PRESENT
:
354 return log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
355 change
->source
, change
->path
);
357 case INSTALL_CHANGE_AUXILIARY_FAILED
:
358 return log_warning("Failed to enable auxiliary unit %s, ignoring.", change
->path
);
361 assert_not_reached();
365 /* Generated/transient/missing/invalid units when applying presets.
366 * Coordinate with install_change_dump_error() below. */
367 static bool ERRNO_IS_NEG_UNIT_ISSUE(intmax_t r
) {
383 int install_change_dump_error(const InstallChange
*change
, char **ret_errmsg
, const char **ret_bus_error
) {
385 const char *bus_error
;
387 /* Returns 0: known error and ret_errmsg formatted
388 * < 0: non-recognizable error */
391 assert(change
->path
);
392 assert(change
->type
< 0);
395 switch (change
->type
) {
398 m
= strjoin("File '", change
->path
, "' already exists",
399 change
->source
? " and is a symlink to " : NULL
, change
->source
);
400 bus_error
= BUS_ERROR_UNIT_EXISTS
;
404 m
= strjoin("Unit ", change
->path
, " is masked");
405 bus_error
= BUS_ERROR_UNIT_MASKED
;
409 m
= strjoin("Unit ", change
->path
, " is transient or generated");
410 bus_error
= BUS_ERROR_UNIT_GENERATED
;
414 m
= strjoin("File '", change
->path
, "' is under the systemd unit hierarchy already");
415 bus_error
= BUS_ERROR_UNIT_BAD_PATH
;
419 m
= strjoin("Invalid specifier in unit ", change
->path
);
420 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
424 m
= strjoin("Refusing to operate on template unit ", change
->source
,
425 " when destination unit ", change
->path
, " is a non-template unit");
426 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
430 m
= strjoin("Invalid unit name ", change
->path
);
431 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
435 m
= strjoin("Refusing to operate on linked unit file ", change
->path
);
436 bus_error
= BUS_ERROR_UNIT_LINKED
;
441 m
= strjoin("Cannot alias ", change
->source
, " as ", change
->path
);
443 m
= strjoin("Invalid unit reference ", change
->path
);
444 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
448 m
= strjoin("Unit ", change
->path
, " does not exist");
449 bus_error
= BUS_ERROR_NO_SUCH_UNIT
;
453 m
= strjoin("Unit ", change
->path
, " is an unresolvable alias");
454 bus_error
= BUS_ERROR_NO_SUCH_UNIT
;
458 m
= strjoin("Cannot resolve specifiers in unit ", change
->path
);
459 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
470 *ret_bus_error
= bus_error
;
475 int install_changes_dump(
478 const InstallChange
*changes
,
482 bool err_logged
= false;
485 /* If verb is not specified, errors are not allowed! */
486 assert(verb
|| error
>= 0);
487 assert(changes
|| n_changes
== 0);
489 /* An error is returned if 'error' contains an error or if any of the changes failed. */
491 FOREACH_ARRAY(i
, changes
, n_changes
)
494 install_change_dump_success(i
);
496 _cleanup_free_
char *err_message
= NULL
;
500 r
= install_change_dump_error(i
, &err_message
, /* ret_bus_error= */ NULL
);
505 log_error_errno(r
, "Failed to %s unit %s: %m", verb
, i
->path
));
508 log_error_errno(i
->type
, "Failed to %s unit: %s", verb
, err_message
));
513 if (error
< 0 && !err_logged
)
514 log_error_errno(error
, "Failed to %s units: %m.", verb
);
520 * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is
521 * concerned. If the target is in the unit search path, then anything with the same name is equivalent.
522 * If outside the unit search path, paths must be identical.
524 static int chroot_unit_symlinks_equivalent(
525 const LookupPaths
*lp
,
527 const char *target_a
,
528 const char *target_b
) {
535 /* This will give incorrect results if the paths are relative and go outside
536 * of the chroot. False negatives are possible. */
538 const char *root
= lp
->root_dir
?: "/";
539 _cleanup_free_
char *dirname
= NULL
;
542 if (!path_is_absolute(target_a
) || !path_is_absolute(target_b
)) {
543 r
= path_extract_directory(src
, &dirname
);
548 _cleanup_free_
char *a
= path_join(path_is_absolute(target_a
) ? root
: dirname
, target_a
);
549 _cleanup_free_
char *b
= path_join(path_is_absolute(target_b
) ? root
: dirname
, target_b
);
553 r
= path_equal_or_inode_same(a
, b
, 0);
557 _cleanup_free_
char *a_name
= NULL
, *b_name
= NULL
;
558 r
= path_extract_filename(a
, &a_name
);
561 r
= path_extract_filename(b
, &b_name
);
565 return streq(a_name
, b_name
) &&
566 path_startswith_strv(a
, lp
->search_path
) &&
567 path_startswith_strv(b
, lp
->search_path
);
570 static int create_symlink(
571 const LookupPaths
*lp
,
572 const char *old_path
,
573 const char *new_path
,
575 InstallChange
**changes
,
578 _cleanup_free_
char *dest
= NULL
;
585 rp
= skip_root(lp
->root_dir
, old_path
);
589 /* Actually create a symlink, and remember that we did. This function is
590 * smart enough to check if there's already a valid symlink in place.
592 * Returns 1 if a symlink was created or already exists and points to the
593 * right place, or negative on error.
596 (void) mkdir_parents_label(new_path
, 0755);
598 if (symlink(old_path
, new_path
) >= 0) {
599 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_SYMLINK
, new_path
, old_path
);
606 return install_changes_add(changes
, n_changes
, -errno
, new_path
, NULL
);
608 r
= readlink_malloc(new_path
, &dest
);
610 /* translate EINVAL (non-symlink exists) to EEXIST */
614 return install_changes_add(changes
, n_changes
, r
, new_path
, NULL
);
617 if (chroot_unit_symlinks_equivalent(lp
, new_path
, dest
, old_path
)) {
618 log_debug("Symlink %s %s %s already exists",
619 new_path
, glyph(GLYPH_ARROW_RIGHT
), dest
);
624 return install_changes_add(changes
, n_changes
, -EEXIST
, new_path
, dest
);
626 r
= symlink_atomic(old_path
, new_path
);
628 return install_changes_add(changes
, n_changes
, r
, new_path
, NULL
);
630 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, new_path
, NULL
);
633 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_SYMLINK
, new_path
, old_path
);
640 static int mark_symlink_for_removal(
641 Set
**remove_symlinks_to
,
647 assert(remove_symlinks_to
);
650 r
= set_ensure_allocated(remove_symlinks_to
, &path_hash_ops_free
);
654 r
= path_simplify_alloc(p
, &n
);
658 r
= set_consume(*remove_symlinks_to
, n
);
667 static int remove_marked_symlinks_fd(
668 Set
*remove_symlinks_to
,
671 const char *config_path
,
672 const LookupPaths
*lp
,
675 InstallChange
**changes
,
678 _cleanup_closedir_
DIR *d
= NULL
;
681 assert(remove_symlinks_to
);
696 FOREACH_DIRENT(de
, d
, return -errno
)
697 if (de
->d_type
== DT_DIR
) {
698 _cleanup_close_
int nfd
= -EBADF
;
699 _cleanup_free_
char *p
= NULL
;
701 nfd
= RET_NERRNO(openat(fd
, de
->d_name
, O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
));
704 RET_GATHER(ret
, nfd
);
708 p
= path_make_absolute(de
->d_name
, path
);
712 /* This will close nfd, regardless whether it succeeds or not */
713 RET_GATHER(ret
, remove_marked_symlinks_fd(remove_symlinks_to
,
718 changes
, n_changes
));
720 } else if (de
->d_type
== DT_LNK
) {
721 _cleanup_free_
char *p
= NULL
;
724 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
727 p
= path_make_absolute(de
->d_name
, path
);
732 /* We remove all links pointing to a file or path that is marked, as well as all
733 * files sharing the same name as a file that is marked, and files sharing the same
734 * name after the instance has been removed. Do path chasing only if we don't already
735 * know that we want to remove the symlink. */
736 found
= set_contains(remove_symlinks_to
, de
->d_name
);
739 _cleanup_free_
char *template = NULL
;
741 r
= unit_name_template(de
->d_name
, &template);
742 if (r
< 0 && r
!= -EINVAL
)
745 found
= set_contains(remove_symlinks_to
, template);
749 _cleanup_free_
char *dest
= NULL
, *dest_name
= NULL
;
751 r
= chase(p
, lp
->root_dir
, CHASE_NONEXISTENT
, &dest
, NULL
);
755 log_debug_errno(r
, "Failed to resolve symlink \"%s\": %m", p
);
756 RET_GATHER(ret
, install_changes_add(changes
, n_changes
, r
, p
, NULL
));
760 r
= path_extract_filename(dest
, &dest_name
);
764 found
= set_contains(remove_symlinks_to
, dest
) ||
765 set_contains(remove_symlinks_to
, dest_name
);
772 if (unlinkat(fd
, de
->d_name
, 0) < 0 && errno
!= ENOENT
) {
773 RET_GATHER(ret
, install_changes_add(changes
, n_changes
, -errno
, p
, NULL
));
777 (void) rmdir_parents(p
, config_path
);
780 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, p
, NULL
);
784 /* Now, remember the full path (but with the root prefix removed) of
785 * the symlink we just removed, and remove any symlinks to it, too. */
787 const char *rp
= skip_root(lp
->root_dir
, p
);
788 r
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: p
);
791 if (r
> 0 && !dry_run
)
798 static int remove_marked_symlinks(
799 Set
*remove_symlinks_to
,
800 const char *config_path
,
801 const LookupPaths
*lp
,
803 InstallChange
**changes
,
806 _cleanup_close_
int fd
= -EBADF
;
813 if (set_isempty(remove_symlinks_to
))
816 fd
= open(config_path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
);
818 return errno
== ENOENT
? 0 : -errno
;
824 cfd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
828 /* This takes possession of cfd and closes it */
829 RET_GATHER(r
, remove_marked_symlinks_fd(remove_symlinks_to
,
834 changes
, n_changes
));
840 static int is_symlink_with_known_name(const InstallInfo
*i
, const char *name
) {
846 if (streq(name
, i
->name
))
849 if (strv_contains(i
->aliases
, name
))
852 /* Look for template symlink matching DefaultInstance */
853 if (i
->default_instance
&& unit_name_is_valid(i
->name
, UNIT_NAME_TEMPLATE
)) {
854 _cleanup_free_
char *s
= NULL
;
856 r
= unit_name_replace_instance(i
->name
, i
->default_instance
, &s
);
861 } else if (streq(name
, s
))
868 static int find_symlinks_in_directory(
870 const char *dir_path
,
871 const char *root_dir
,
872 const InstallInfo
*info
,
873 bool ignore_destination
,
875 bool ignore_same_name
,
876 const char *config_path
,
877 bool *same_name_link
) {
884 assert(unit_name_is_valid(info
->name
, UNIT_NAME_ANY
));
886 assert(same_name_link
);
888 FOREACH_DIRENT(de
, dir
, return -errno
) {
889 bool found_path
= false, found_dest
= false, b
= false;
891 if (de
->d_type
!= DT_LNK
)
894 if (!ignore_destination
) {
895 _cleanup_free_
char *dest
= NULL
;
897 /* Acquire symlink destination */
898 r
= readlinkat_malloc(dirfd(dir
), de
->d_name
, &dest
);
905 /* Check if what the symlink points to matches what we are looking for */
906 found_dest
= path_equal_filename(dest
, info
->name
);
909 /* Check if the symlink itself matches what we are looking for.
911 * If ignore_destination is specified, we only look at the source name.
913 * If ignore_same_name is specified, we are in one of the directories which
914 * have lower priority than the unit file, and even if a file or symlink with
915 * this name was found, we should ignore it. */
917 if (ignore_destination
|| !ignore_same_name
)
918 found_path
= streq(de
->d_name
, info
->name
);
920 if (!found_path
&& ignore_destination
) {
921 _cleanup_free_
char *template = NULL
;
923 r
= unit_name_template(de
->d_name
, &template);
924 if (r
< 0 && r
!= -EINVAL
)
927 found_dest
= streq(template, info
->name
);
930 if (found_path
&& found_dest
) {
931 _cleanup_free_
char *p
= NULL
, *t
= NULL
;
933 /* Filter out same name links in the main config path */
934 p
= path_make_absolute(de
->d_name
, dir_path
);
935 t
= path_make_absolute(info
->name
, config_path
);
939 b
= path_equal(p
, t
);
943 *same_name_link
= true;
944 else if (found_path
|| found_dest
) {
948 /* Check if symlink name is in the set of names used by [Install] */
949 r
= is_symlink_with_known_name(info
, de
->d_name
);
958 static int find_symlinks(
959 const char *root_dir
,
960 const InstallInfo
*i
,
962 bool ignore_same_name
,
963 const char *config_path
,
964 bool *same_name_link
) {
966 _cleanup_closedir_
DIR *config_dir
= NULL
;
971 assert(same_name_link
);
973 config_dir
= opendir(config_path
);
975 if (IN_SET(errno
, ENOENT
, ENOTDIR
, EACCES
))
980 FOREACH_DIRENT(de
, config_dir
, return -errno
) {
982 _cleanup_free_
const char *path
= NULL
;
983 _cleanup_closedir_
DIR *d
= NULL
;
985 if (de
->d_type
!= DT_DIR
)
988 suffix
= strrchr(de
->d_name
, '.');
989 if (!STRPTR_IN_SET(suffix
, ".wants", ".requires", ".upholds"))
992 path
= path_join(config_path
, de
->d_name
);
998 log_error_errno(errno
, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path
);
1002 r
= find_symlinks_in_directory(d
, path
, root_dir
, i
,
1003 /* ignore_destination= */ true,
1004 /* match_name= */ match_name
,
1005 /* ignore_same_name= */ ignore_same_name
,
1011 log_debug_errno(r
, "Failed to look up symlinks in \"%s\": %m", path
);
1014 /* We didn't find any suitable symlinks in .wants, .requires or .upholds directories,
1015 * let's look for linked unit files in this directory. */
1016 rewinddir(config_dir
);
1017 return find_symlinks_in_directory(config_dir
, config_path
, root_dir
, i
,
1018 /* ignore_destination= */ false,
1019 /* match_name= */ match_name
,
1020 /* ignore_same_name= */ ignore_same_name
,
1025 static int find_symlinks_in_scope(
1027 const LookupPaths
*lp
,
1028 const InstallInfo
*info
,
1030 UnitFileState
*state
) {
1032 bool same_name_link_runtime
= false, same_name_link_config
= false;
1033 bool enabled_in_runtime
= false, enabled_at_all
= false;
1034 bool ignore_same_name
= false;
1041 /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name"
1042 * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
1043 * effectively masked, so we should ignore them. */
1045 STRV_FOREACH(p
, lp
->search_path
) {
1046 bool same_name_link
= false;
1048 r
= find_symlinks(lp
->root_dir
, info
, match_name
, ignore_same_name
, *p
, &same_name_link
);
1052 /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
1054 if (path_equal(*p
, lp
->persistent_config
)) {
1055 /* This is the best outcome, let's return it immediately. */
1056 *state
= UNIT_FILE_ENABLED
;
1060 /* look for global enablement of user units */
1061 if (scope
== RUNTIME_SCOPE_USER
&& path_is_user_config_dir(*p
)) {
1062 *state
= UNIT_FILE_ENABLED
;
1066 r
= path_is_runtime(lp
, *p
, false);
1070 enabled_in_runtime
= true;
1072 enabled_at_all
= true;
1074 } else if (same_name_link
) {
1075 if (path_equal(*p
, lp
->persistent_config
))
1076 same_name_link_config
= true;
1078 r
= path_is_runtime(lp
, *p
, false);
1082 same_name_link_runtime
= true;
1086 /* Check if next iteration will be "below" the unit file (either a regular file
1087 * or a symlink), and hence should be ignored */
1088 if (!ignore_same_name
&& path_startswith(info
->path
, *p
))
1089 ignore_same_name
= true;
1092 if (enabled_in_runtime
) {
1093 *state
= UNIT_FILE_ENABLED_RUNTIME
;
1097 /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
1098 * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
1099 * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
1100 * something, and hence are a much stronger concept. */
1101 if (enabled_at_all
&& unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
1102 *state
= UNIT_FILE_STATIC
;
1106 /* Hmm, we didn't find it, but maybe we found the same name
1108 if (same_name_link_config
) {
1109 *state
= UNIT_FILE_LINKED
;
1112 if (same_name_link_runtime
) {
1113 *state
= UNIT_FILE_LINKED_RUNTIME
;
1120 static void install_info_clear(InstallInfo
*i
) {
1124 i
->name
= mfree(i
->name
);
1125 i
->path
= mfree(i
->path
);
1126 i
->root
= mfree(i
->root
);
1127 i
->aliases
= strv_free(i
->aliases
);
1128 i
->wanted_by
= strv_free(i
->wanted_by
);
1129 i
->required_by
= strv_free(i
->required_by
);
1130 i
->upheld_by
= strv_free(i
->upheld_by
);
1131 i
->also
= strv_free(i
->also
);
1132 i
->default_instance
= mfree(i
->default_instance
);
1133 i
->symlink_target
= mfree(i
->symlink_target
);
1136 static InstallInfo
* install_info_free(InstallInfo
*i
) {
1137 install_info_clear(i
);
1141 DEFINE_TRIVIAL_CLEANUP_FUNC(InstallInfo
*, install_info_free
);
1143 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
1144 install_info_hash_ops
,
1145 char, string_hash_func
, string_compare_func
,
1146 InstallInfo
, install_info_free
);
1148 static void install_context_done(InstallContext
*ctx
) {
1151 ctx
->will_process
= ordered_hashmap_free(ctx
->will_process
);
1152 ctx
->have_processed
= ordered_hashmap_free(ctx
->have_processed
);
1155 static InstallInfo
*install_info_find(InstallContext
*ctx
, const char *name
) {
1158 i
= ordered_hashmap_get(ctx
->have_processed
, name
);
1162 return ordered_hashmap_get(ctx
->will_process
, name
);
1165 static int install_info_may_process(
1166 const InstallInfo
*i
,
1167 const LookupPaths
*lp
,
1168 InstallChange
**changes
,
1169 size_t *n_changes
) {
1173 /* Checks whether the loaded unit file is one we should process, or is masked,
1174 * transient or generated and thus not subject to enable/disable operations. */
1176 if (i
->install_mode
== INSTALL_MODE_MASKED
)
1177 return install_changes_add(changes
, n_changes
, -ERFKILL
, i
->path
, NULL
);
1178 if (path_is_generator(lp
, i
->path
) ||
1179 path_is_transient(lp
, i
->path
))
1180 return install_changes_add(changes
, n_changes
, -EADDRNOTAVAIL
, i
->path
, NULL
);
1186 * Adds a new InstallInfo entry under name in the InstallContext.will_process
1187 * hashmap, or retrieves the existing one if already present.
1189 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1191 static int install_info_add(
1192 InstallContext
*ctx
,
1197 InstallInfo
**ret
) {
1199 _cleanup_free_
char *name_alloc
= NULL
;
1207 r
= path_extract_filename(path
, &name_alloc
);
1214 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
1217 InstallInfo
*i
= install_info_find(ctx
, name
);
1219 i
->auxiliary
= i
->auxiliary
&& auxiliary
;
1226 _cleanup_(install_info_freep
) InstallInfo
*new_info
= new(InstallInfo
, 1);
1230 *new_info
= (InstallInfo
) {
1231 .install_mode
= _INSTALL_MODE_INVALID
,
1232 .auxiliary
= auxiliary
,
1236 new_info
->name
= TAKE_PTR(name_alloc
);
1238 new_info
->name
= strdup(name
);
1239 if (!new_info
->name
)
1243 r
= strdup_to(&new_info
->root
, root
);
1247 r
= strdup_to(&new_info
->path
, path
);
1251 r
= ordered_hashmap_ensure_put(&ctx
->will_process
, &install_info_hash_ops
, new_info
->name
, new_info
);
1254 i
= TAKE_PTR(new_info
);
1261 static int config_parse_alias(
1263 const char *filename
,
1265 const char *section
,
1266 unsigned section_line
,
1280 type
= unit_name_to_type(unit
);
1281 if (!unit_type_may_alias(type
))
1282 return log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1283 "Alias= is not allowed for %s units, ignoring.",
1284 unit_type_to_string(type
));
1286 return config_parse_strv(unit
, filename
, line
, section
, section_line
,
1287 lvalue
, ltype
, rvalue
, data
, userdata
);
1290 static int config_parse_also(
1292 const char *filename
,
1294 const char *section
,
1295 unsigned section_line
,
1302 InstallInfo
*info
= ASSERT_PTR(userdata
);
1303 InstallContext
*ctx
= ASSERT_PTR(data
);
1312 _cleanup_free_
char *word
= NULL
, *printed
= NULL
;
1314 r
= extract_first_word(&rvalue
, &word
, NULL
, 0);
1320 r
= install_name_printf(ctx
->scope
, info
, word
, &printed
);
1322 return log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1323 "Failed to resolve unit name in Also=\"%s\": %m", word
);
1325 r
= install_info_add(ctx
, printed
, NULL
, info
->root
, /* auxiliary= */ true, NULL
);
1329 r
= strv_push(&info
->also
, printed
);
1339 static int config_parse_default_instance(
1341 const char *filename
,
1343 const char *section
,
1344 unsigned section_line
,
1351 InstallContext
*ctx
= ASSERT_PTR(data
);
1352 InstallInfo
*info
= ASSERT_PTR(userdata
);
1353 _cleanup_free_
char *printed
= NULL
;
1361 if (unit_name_is_valid(unit
, UNIT_NAME_INSTANCE
))
1362 /* When enabling an instance, we might be using a template unit file,
1363 * but we should ignore DefaultInstance silently. */
1365 if (!unit_name_is_valid(unit
, UNIT_NAME_TEMPLATE
))
1366 return log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1367 "DefaultInstance= only makes sense for template units, ignoring.");
1369 r
= install_name_printf(ctx
->scope
, info
, rvalue
, &printed
);
1371 return log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1372 "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue
);
1374 if (isempty(printed
))
1375 printed
= mfree(printed
);
1377 if (printed
&& !unit_instance_is_valid(printed
))
1378 return log_syntax(unit
, LOG_WARNING
, filename
, line
, SYNTHETIC_ERRNO(EINVAL
),
1379 "Invalid DefaultInstance= value \"%s\".", printed
);
1381 return free_and_replace(info
->default_instance
, printed
);
1384 static int unit_file_load(
1385 InstallContext
*ctx
,
1388 const char *root_dir
,
1389 SearchFlags flags
) {
1391 const ConfigTableItem items
[] = {
1392 { "Install", "Alias", config_parse_alias
, 0, &info
->aliases
},
1393 { "Install", "WantedBy", config_parse_strv
, 0, &info
->wanted_by
},
1394 { "Install", "RequiredBy", config_parse_strv
, 0, &info
->required_by
},
1395 { "Install", "UpheldBy", config_parse_strv
, 0, &info
->upheld_by
},
1396 { "Install", "DefaultInstance", config_parse_default_instance
, 0, info
},
1397 { "Install", "Also", config_parse_also
, 0, ctx
},
1402 _cleanup_fclose_
FILE *f
= NULL
;
1403 _cleanup_close_
int fd
= -EBADF
;
1410 if (!(flags
& SEARCH_DROPIN
)) {
1411 /* Loading or checking for the main unit file… */
1413 type
= unit_name_to_type(info
->name
);
1416 if (unit_name_is_valid(info
->name
, UNIT_NAME_TEMPLATE
|UNIT_NAME_INSTANCE
) && !unit_type_may_template(type
))
1417 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1418 "%s: unit type %s cannot be templated, ignoring.", path
, unit_type_to_string(type
));
1420 if (!(flags
& SEARCH_LOAD
)) {
1421 if (lstat(path
, &st
) < 0)
1424 if (null_or_empty(&st
))
1425 info
->install_mode
= INSTALL_MODE_MASKED
;
1426 else if (S_ISREG(st
.st_mode
))
1427 info
->install_mode
= INSTALL_MODE_REGULAR
;
1428 else if (S_ISLNK(st
.st_mode
))
1430 else if (S_ISDIR(st
.st_mode
))
1438 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
1442 /* 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. */
1444 if (!(flags
& SEARCH_LOAD
))
1447 fd
= chase_and_open(path
, root_dir
, 0, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
, NULL
);
1452 if (fstat(fd
, &st
) < 0)
1455 if (null_or_empty(&st
)) {
1456 if ((flags
& SEARCH_DROPIN
) == 0)
1457 info
->install_mode
= INSTALL_MODE_MASKED
;
1462 r
= stat_verify_regular(&st
);
1466 f
= take_fdopen(&fd
, "r");
1470 /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1473 r
= config_parse(info
->name
, path
, f
,
1487 config_item_table_lookup
, items
,
1491 return log_debug_errno(r
, "Failed to parse \"%s\": %m", info
->name
);
1493 if ((flags
& SEARCH_DROPIN
) == 0)
1494 info
->install_mode
= INSTALL_MODE_REGULAR
;
1497 (int) strv_length(info
->aliases
) +
1498 (int) strv_length(info
->wanted_by
) +
1499 (int) strv_length(info
->required_by
) +
1500 (int) strv_length(info
->upheld_by
);
1503 static int unit_file_load_or_readlink(
1504 InstallContext
*ctx
,
1507 const LookupPaths
*lp
,
1508 SearchFlags flags
) {
1511 r
= unit_file_load(ctx
, info
, path
, lp
->root_dir
, flags
);
1512 if (r
!= -ELOOP
|| (flags
& SEARCH_DROPIN
))
1515 /* This is a symlink, let's read and verify it. */
1516 r
= unit_file_resolve_symlink(lp
->root_dir
, lp
->search_path
,
1517 NULL
, AT_FDCWD
, path
,
1518 true, &info
->symlink_target
);
1521 bool outside_search_path
= r
> 0;
1523 r
= null_or_empty_path_with_root(info
->symlink_target
, lp
->root_dir
);
1524 if (r
< 0 && r
!= -ENOENT
)
1525 return log_debug_errno(r
, "Failed to stat %s: %m", info
->symlink_target
);
1527 info
->install_mode
= INSTALL_MODE_MASKED
;
1528 else if (outside_search_path
)
1529 info
->install_mode
= INSTALL_MODE_LINKED
;
1531 info
->install_mode
= INSTALL_MODE_ALIAS
;
1536 static int unit_file_search(
1537 InstallContext
*ctx
,
1539 const LookupPaths
*lp
,
1540 SearchFlags flags
) {
1542 const char *dropin_dir_name
= NULL
, *dropin_template_dir_name
= NULL
;
1543 _cleanup_strv_free_
char **dirs
= NULL
, **files
= NULL
;
1544 _cleanup_free_
char *template = NULL
;
1545 bool found_unit
= false;
1551 /* Was this unit already loaded? */
1552 if (info
->install_mode
!= _INSTALL_MODE_INVALID
)
1556 return unit_file_load_or_readlink(ctx
, info
, info
->path
, lp
, flags
);
1560 if (!FLAGS_SET(flags
, SEARCH_IGNORE_TEMPLATE
) && unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
1561 r
= unit_name_template(info
->name
, &template);
1566 STRV_FOREACH(p
, lp
->search_path
) {
1567 _cleanup_free_
char *path
= NULL
;
1569 path
= path_join(*p
, info
->name
);
1573 r
= unit_file_load_or_readlink(ctx
, info
, path
, lp
, flags
);
1575 info
->path
= TAKE_PTR(path
);
1579 } else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
, -EACCES
))
1583 if (!found_unit
&& template) {
1585 /* Unit file doesn't exist, however instance
1586 * enablement was requested. We will check if it is
1587 * possible to load template unit file. */
1589 STRV_FOREACH(p
, lp
->search_path
) {
1590 _cleanup_free_
char *path
= NULL
;
1592 path
= path_join(*p
, template);
1596 r
= unit_file_load_or_readlink(ctx
, info
, path
, lp
, flags
);
1598 info
->path
= TAKE_PTR(path
);
1602 } else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
, -EACCES
))
1608 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT
),
1609 "Cannot find unit %s%s%s.",
1610 info
->name
, template ? " or " : "", strempty(template));
1612 if (info
->install_mode
== INSTALL_MODE_MASKED
)
1615 /* Search for drop-in directories */
1617 dropin_dir_name
= strjoina(info
->name
, ".d");
1618 STRV_FOREACH(p
, lp
->search_path
) {
1621 path
= path_join(*p
, dropin_dir_name
);
1625 r
= strv_consume(&dirs
, path
);
1631 dropin_template_dir_name
= strjoina(template, ".d");
1632 STRV_FOREACH(p
, lp
->search_path
) {
1635 path
= path_join(*p
, dropin_template_dir_name
);
1639 r
= strv_consume(&dirs
, path
);
1645 /* Load drop-in conf files */
1647 r
= conf_files_list_strv(&files
, ".conf", NULL
, 0, (const char**) dirs
);
1649 return log_debug_errno(r
, "Failed to get list of conf files: %m");
1651 STRV_FOREACH(p
, files
) {
1652 r
= unit_file_load_or_readlink(ctx
, info
, *p
, lp
, flags
| SEARCH_DROPIN
);
1654 return log_debug_errno(r
, "Failed to load conf file \"%s\": %m", *p
);
1660 static int install_info_follow(
1661 InstallContext
*ctx
,
1663 const LookupPaths
*lp
,
1665 bool ignore_different_name
) {
1670 if (!IN_SET(info
->install_mode
, INSTALL_MODE_ALIAS
, INSTALL_MODE_LINKED
))
1672 if (!info
->symlink_target
)
1675 /* If the basename doesn't match, the caller should add a complete new entry for this. */
1677 if (!ignore_different_name
&& !path_equal_filename(info
->symlink_target
, info
->name
))
1680 free_and_replace(info
->path
, info
->symlink_target
);
1681 info
->install_mode
= _INSTALL_MODE_INVALID
;
1683 return unit_file_load_or_readlink(ctx
, info
, info
->path
, lp
, flags
);
1687 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1688 * target, maybe more than once. Propagate the instance name if present.
1690 static int install_info_traverse(
1691 InstallContext
*ctx
,
1692 const LookupPaths
*lp
,
1695 InstallInfo
**ret
) {
1705 r
= unit_file_search(ctx
, start
, lp
, flags
);
1710 while (IN_SET(i
->install_mode
, INSTALL_MODE_ALIAS
, INSTALL_MODE_LINKED
)) {
1711 /* Follow the symlink */
1713 if (++k
> UNIT_FILE_FOLLOW_SYMLINK_MAX
)
1716 if (!FLAGS_SET(flags
, SEARCH_FOLLOW_CONFIG_SYMLINKS
)) {
1717 r
= path_is_config(lp
, i
->path
, true);
1724 r
= install_info_follow(ctx
, i
, lp
, flags
,
1725 /* If linked, don't look at the target name */
1726 /* ignore_different_name= */ i
->install_mode
== INSTALL_MODE_LINKED
);
1727 if (r
== -EXDEV
&& i
->symlink_target
) {
1728 _cleanup_free_
char *target_name
= NULL
, *unit_instance
= NULL
;
1731 /* Target is an alias, create a new install info object and continue with that. */
1733 r
= path_extract_filename(i
->symlink_target
, &target_name
);
1737 if (unit_name_is_valid(i
->name
, UNIT_NAME_INSTANCE
) &&
1738 unit_name_is_valid(target_name
, UNIT_NAME_TEMPLATE
)) {
1740 _cleanup_free_
char *instance
= NULL
;
1742 r
= unit_name_to_instance(i
->name
, &instance
);
1746 r
= unit_name_replace_instance(target_name
, instance
, &unit_instance
);
1750 if (streq(unit_instance
, i
->name
)) {
1751 /* We filled in the instance, and the target stayed the same? If so,
1752 * then let's honour the link as it is. */
1754 r
= install_info_follow(ctx
, i
, lp
, flags
, true);
1765 r
= install_info_add(ctx
, bn
, NULL
, lp
->root_dir
, /* auxiliary= */ false, &i
);
1769 /* Try again, with the new target we found. */
1770 r
= unit_file_search(ctx
, i
, lp
, flags
);
1772 /* Translate error code to highlight this specific case */
1786 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1787 * or the name (otherwise). root_dir is prepended to the path.
1789 static int install_info_add_auto(
1790 InstallContext
*ctx
,
1791 const LookupPaths
*lp
,
1792 const char *name_or_path
,
1793 InstallInfo
**ret
) {
1796 assert(name_or_path
);
1798 if (path_is_absolute(name_or_path
)) {
1799 _cleanup_free_
char *pp
= path_join(lp
->root_dir
, name_or_path
);
1803 return install_info_add(ctx
, NULL
, pp
, lp
->root_dir
, /* auxiliary= */ false, ret
);
1805 return install_info_add(ctx
, name_or_path
, NULL
, lp
->root_dir
, /* auxiliary= */ false, ret
);
1808 static int install_info_discover(
1809 InstallContext
*ctx
,
1810 const LookupPaths
*lp
,
1811 const char *name_or_path
,
1814 InstallChange
**changes
,
1815 size_t *n_changes
) {
1822 assert(name_or_path
);
1824 r
= install_info_add_auto(ctx
, lp
, name_or_path
, &info
);
1826 r
= install_info_traverse(ctx
, lp
, info
, flags
, ret
);
1829 return install_changes_add(changes
, n_changes
, r
, name_or_path
, NULL
);
1834 static int install_info_discover_and_check(
1835 InstallContext
*ctx
,
1836 const LookupPaths
*lp
,
1837 const char *name_or_path
,
1840 InstallChange
**changes
,
1841 size_t *n_changes
) {
1845 POINTER_MAY_BE_NULL(ret
);
1847 r
= install_info_discover(ctx
, lp
, name_or_path
, flags
, ret
, changes
, n_changes
);
1851 return install_info_may_process(ret
? *ret
: NULL
, lp
, changes
, n_changes
);
1854 int unit_file_verify_alias(
1855 const InstallInfo
*info
,
1858 InstallChange
**changes
,
1859 size_t *n_changes
) {
1861 _cleanup_free_
char *dst_updated
= NULL
;
1866 /* Verify that dst is a valid either a valid alias or a valid .wants/.requires symlink for the target
1867 * unit *i. Return negative on error or if not compatible, zero on success.
1869 * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
1870 * inserted into dst. It is not normally set, even on success, so that the caller can easily
1871 * distinguish the case where instance propagation occurred.
1874 * -EXDEV when the alias doesn't match the unit,
1875 * -EUCLEAN when the name is invalid,
1876 * -ELOOP when the alias it to the unit itself.
1879 const char *path_alias
= strrchr(dst
, '/');
1881 /* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
1882 _cleanup_free_
char *dir
= NULL
;
1885 path_alias
++; /* skip over slash */
1887 r
= path_extract_directory(dst
, &dir
);
1889 return log_error_errno(r
, "Failed to extract parent directory from '%s': %m", dst
);
1891 p
= endswith(dir
, ".wants");
1893 p
= endswith(dir
, ".requires");
1895 r
= install_changes_add(changes
, n_changes
, -EXDEV
, dst
, NULL
);
1899 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
), "Invalid path \"%s\" in alias.", dir
);
1902 *p
= '\0'; /* dir should now be a unit name */
1904 UnitNameFlags type
= unit_name_classify(dir
);
1906 r
= install_changes_add(changes
, n_changes
, -EXDEV
, dst
, NULL
);
1909 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
),
1910 "Invalid unit name component \"%s\" in alias.", dir
);
1913 const bool instance_propagation
= type
== UNIT_NAME_TEMPLATE
;
1915 /* That's the name we want to use for verification. */
1916 r
= unit_symlink_name_compatible(path_alias
, info
->name
, instance_propagation
);
1918 return log_error_errno(r
, "Failed to verify alias validity: %m");
1920 r
= install_changes_add(changes
, n_changes
, -EXDEV
, dst
, info
->name
);
1924 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
),
1925 "Invalid unit \"%s\" symlink \"%s\".",
1930 /* If the symlink target has an instance set and the symlink source doesn't, we "propagate
1931 * the instance", i.e. instantiate the symlink source with the target instance. */
1932 if (unit_name_is_valid(dst
, UNIT_NAME_TEMPLATE
)) {
1933 _cleanup_free_
char *inst
= NULL
;
1935 UnitNameFlags type
= unit_name_to_instance(info
->name
, &inst
);
1937 r
= install_changes_add(changes
, n_changes
, -EUCLEAN
, info
->name
, NULL
);
1940 return log_debug_errno(type
, "Failed to extract instance name from \"%s\": %m", info
->name
);
1943 if (type
== UNIT_NAME_INSTANCE
) {
1944 r
= unit_name_replace_instance(dst
, inst
, &dst_updated
);
1946 return log_error_errno(r
, "Failed to build unit name from %s+%s: %m",
1951 r
= unit_validate_alias_symlink_or_warn(LOG_DEBUG
, dst_updated
?: dst
, info
->name
);
1952 if (r
== -ELOOP
) /* -ELOOP means self-alias, which we (quietly) ignore */
1955 return install_changes_add(changes
, n_changes
,
1956 r
== -EINVAL
? -EXDEV
: r
,
1961 *ret_dst
= TAKE_PTR(dst_updated
);
1965 static int install_info_symlink_alias(
1967 UnitFileFlags file_flags
,
1969 const LookupPaths
*lp
,
1970 const char *config_path
,
1972 InstallChange
**changes
,
1973 size_t *n_changes
) {
1979 assert(config_path
);
1981 STRV_FOREACH(s
, info
->aliases
) {
1982 _cleanup_free_
char *alias_path
= NULL
, *alias_target
= NULL
, *dst
= NULL
, *dst_updated
= NULL
;
1984 r
= install_name_printf(scope
, info
, *s
, &dst
);
1986 RET_GATHER(ret
, install_changes_add(changes
, n_changes
, r
, *s
, NULL
));
1990 r
= unit_file_verify_alias(info
, dst
, &dst_updated
, changes
, n_changes
);
1997 alias_path
= path_make_absolute(dst_updated
?: dst
, config_path
);
2001 r
= in_search_path(lp
, info
->path
);
2005 /* The unit path itself is outside of the search path. To
2006 * correctly apply the alias, we need the alias symlink to
2007 * point to the symlink that was created in the search path. */
2008 alias_target
= path_join(config_path
, info
->name
);
2014 r
= chase(alias_path
, lp
->root_dir
, CHASE_NONEXISTENT
, /* ret_path= */ NULL
, /* ret_fd= */ NULL
);
2015 if (r
< 0 && r
!= -ENOENT
) {
2019 broken
= r
== 0; /* symlink target does not exist? */
2021 r
= create_symlink(lp
, alias_target
?: info
->path
, alias_path
, force
|| broken
, changes
, n_changes
);
2022 if (r
== -EEXIST
&& FLAGS_SET(file_flags
, UNIT_FILE_IGNORE_AUXILIARY_FAILURE
))
2023 /* We cannot realize the alias because a conflicting alias exists.
2024 * Do not propagate this as error. */
2026 if (r
!= 0 && ret
>= 0)
2033 static int install_info_symlink_wants(
2035 UnitFileFlags file_flags
,
2037 const LookupPaths
*lp
,
2038 const char *config_path
,
2041 InstallChange
**changes
,
2042 size_t *n_changes
) {
2044 _cleanup_(install_info_clear
) InstallInfo instance
= {
2045 .install_mode
= _INSTALL_MODE_INVALID
,
2048 UnitNameFlags valid_dst_type
= UNIT_NAME_ANY
;
2054 assert(config_path
);
2056 if (strv_isempty(list
))
2059 if (unit_name_is_valid(info
->name
, UNIT_NAME_PLAIN
| UNIT_NAME_INSTANCE
))
2060 /* Not a template unit. Use the name directly. */
2063 else if (info
->default_instance
) {
2064 /* If this is a template, and we have a default instance, use it. */
2066 r
= unit_name_replace_instance(info
->name
, info
->default_instance
, &instance
.name
);
2070 r
= unit_file_search(NULL
, &instance
, lp
, SEARCH_FOLLOW_CONFIG_SYMLINKS
);
2074 if (instance
.install_mode
== INSTALL_MODE_MASKED
)
2075 return install_changes_add(changes
, n_changes
, -ERFKILL
, instance
.path
, NULL
);
2080 /* We have a template, but no instance yet. When used with an instantiated unit, we will get
2081 * the instance from that unit. Cannot be used with non-instance units. */
2083 valid_dst_type
= UNIT_NAME_INSTANCE
| UNIT_NAME_TEMPLATE
;
2088 STRV_FOREACH(s
, list
) {
2089 _cleanup_free_
char *path
= NULL
, *dst
= NULL
;
2091 q
= install_name_printf(scope
, info
, *s
, &dst
);
2093 RET_GATHER(r
, install_changes_add(changes
, n_changes
, q
, *s
, NULL
));
2097 if (!unit_name_is_valid(dst
, valid_dst_type
)) {
2098 /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the
2099 * template status doesn't match. If we are doing presets don't bother reporting the
2100 * error. This also covers cases like 'systemctl preset serial-getty@.service', which
2101 * has no DefaultInstance, so there is nothing we can do. At the same time,
2102 * 'systemctl enable serial-getty@.service' should fail, the user should specify an
2103 * instance like in 'systemctl enable serial-getty@ttyS0.service'.
2105 if (FLAGS_SET(file_flags
, UNIT_FILE_IGNORE_AUXILIARY_FAILURE
))
2108 if (unit_name_is_valid(dst
, UNIT_NAME_ANY
))
2109 RET_GATHER(r
, install_changes_add(changes
, n_changes
, -EIDRM
, dst
, n
));
2111 RET_GATHER(r
, install_changes_add(changes
, n_changes
, -EUCLEAN
, dst
, NULL
));
2116 path
= strjoin(config_path
, "/", dst
, suffix
, n
);
2120 q
= create_symlink(lp
, info
->path
, path
, /* force= */ true, changes
, n_changes
);
2121 if (q
!= 0 && r
>= 0)
2124 if (unit_file_exists(scope
, lp
, dst
) == 0) {
2125 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_DESTINATION_NOT_PRESENT
, dst
, info
->path
);
2134 static int install_info_symlink_link(
2136 const LookupPaths
*lp
,
2137 const char *config_path
,
2139 InstallChange
**changes
,
2140 size_t *n_changes
) {
2142 _cleanup_free_
char *path
= NULL
;
2147 assert(config_path
);
2150 r
= in_search_path(lp
, info
->path
);
2156 path
= path_join(config_path
, info
->name
);
2160 return create_symlink(lp
, info
->path
, path
, force
, changes
, n_changes
);
2163 static int install_info_apply(
2165 UnitFileFlags file_flags
,
2167 const LookupPaths
*lp
,
2168 const char *config_path
,
2169 InstallChange
**changes
,
2170 size_t *n_changes
) {
2176 assert(config_path
);
2178 if (info
->install_mode
!= INSTALL_MODE_REGULAR
)
2181 bool force
= file_flags
& UNIT_FILE_FORCE
;
2183 r
= install_info_symlink_link(info
, lp
, config_path
, force
, changes
, n_changes
);
2184 /* Do not count links to the unit file towards the "carries_install_info" count */
2186 /* If linking of the file failed, do not try to create other symlinks,
2187 * because they might would pointing to a non-existent or wrong unit. */
2190 r
= install_info_symlink_alias(scope
, file_flags
, info
, lp
, config_path
, force
, changes
, n_changes
);
2192 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->wanted_by
, ".wants/", changes
, n_changes
);
2193 if (q
!= 0 && r
>= 0)
2196 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->required_by
, ".requires/", changes
, n_changes
);
2197 if (q
!= 0 && r
>= 0)
2200 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->upheld_by
, ".upholds/", changes
, n_changes
);
2201 if (q
!= 0 && r
>= 0)
2207 static int install_context_apply(
2208 InstallContext
*ctx
,
2209 const LookupPaths
*lp
,
2210 UnitFileFlags file_flags
,
2211 const char *config_path
,
2213 InstallChange
**changes
,
2214 size_t *n_changes
) {
2221 assert(config_path
);
2223 if (ordered_hashmap_isempty(ctx
->will_process
))
2226 r
= ordered_hashmap_ensure_allocated(&ctx
->have_processed
, &install_info_hash_ops
);
2231 while ((i
= ordered_hashmap_first(ctx
->will_process
))) {
2234 q
= ordered_hashmap_move_one(ctx
->have_processed
, ctx
->will_process
, i
->name
);
2238 q
= install_info_traverse(ctx
, lp
, i
, flags
, NULL
);
2241 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_AUXILIARY_FAILED
, i
->name
, NULL
);
2247 return install_changes_add(changes
, n_changes
, q
, i
->name
, NULL
);
2250 /* We can attempt to process a masked unit when a different unit
2251 * that we were processing specifies it in Also=. */
2252 if (i
->install_mode
== INSTALL_MODE_MASKED
) {
2253 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_MASKED
, i
->path
, NULL
);
2257 /* Assume that something *could* have been enabled here,
2258 * avoid "empty [Install] section" warning. */
2263 if (i
->install_mode
!= INSTALL_MODE_REGULAR
)
2266 q
= install_info_apply(ctx
->scope
, file_flags
, i
, lp
, config_path
, changes
, n_changes
);
2278 static int install_context_mark_for_removal(
2279 InstallContext
*ctx
,
2280 const LookupPaths
*lp
,
2281 Set
**remove_symlinks_to
,
2282 const char *config_path
,
2283 InstallChange
**changes
,
2284 size_t *n_changes
) {
2291 assert(config_path
);
2293 /* Marks all items for removal */
2295 if (ordered_hashmap_isempty(ctx
->will_process
))
2298 r
= ordered_hashmap_ensure_allocated(&ctx
->have_processed
, &install_info_hash_ops
);
2302 while ((i
= ordered_hashmap_first(ctx
->will_process
))) {
2304 r
= ordered_hashmap_move_one(ctx
->have_processed
, ctx
->will_process
, i
->name
);
2308 r
= install_info_traverse(ctx
, lp
, i
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, NULL
);
2309 if (r
== -ENOLINK
) {
2310 log_debug_errno(r
, "Name %s leads to a dangling symlink, removing name.", i
->name
);
2311 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_DANGLING
, i
->path
?: i
->name
, NULL
);
2314 } else if (r
== -ENOENT
) {
2315 if (i
->auxiliary
) /* some unit specified in Also= or similar is missing */
2316 log_debug_errno(r
, "Auxiliary unit of %s not found, removing name.", i
->name
);
2318 log_debug_errno(r
, "Unit %s not found, removing name.", i
->name
);
2319 r
= install_changes_add(changes
, n_changes
, r
, i
->path
?: i
->name
, NULL
);
2320 /* In case there's no unit, we still want to remove any leftover symlink, even if
2321 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2326 log_debug_errno(r
, "Failed to find unit %s, removing name: %m", i
->name
);
2327 int k
= install_changes_add(changes
, n_changes
, r
, i
->path
?: i
->name
, NULL
);
2330 } else if (i
->install_mode
== INSTALL_MODE_MASKED
) {
2331 log_debug("Unit file %s is masked, ignoring.", i
->name
);
2332 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_MASKED
, i
->path
?: i
->name
, NULL
);
2336 } else if (i
->install_mode
!= INSTALL_MODE_REGULAR
) {
2337 log_debug("Unit %s has install mode %s, ignoring.",
2338 i
->name
, install_mode_to_string(i
->install_mode
) ?: "invalid");
2342 r
= mark_symlink_for_removal(remove_symlinks_to
, i
->name
);
2352 UnitFileFlags flags
,
2353 const char *root_dir
,
2354 char * const *names
,
2355 InstallChange
**changes
,
2356 size_t *n_changes
) {
2358 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2359 const char *config_path
;
2363 assert(scope
< _RUNTIME_SCOPE_MAX
);
2365 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2369 config_path
= (flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2375 STRV_FOREACH(name
, names
) {
2376 _cleanup_free_
char *path
= NULL
;
2378 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
)) {
2379 RET_GATHER(r
, -EINVAL
);
2383 path
= path_make_absolute(*name
, config_path
);
2387 RET_GATHER(r
, create_symlink(&lp
, "/dev/null", path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
));
2393 int unit_file_unmask(
2395 UnitFileFlags flags
,
2396 const char *root_dir
,
2397 char * const *names
,
2398 InstallChange
**changes
,
2399 size_t *n_changes
) {
2401 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2402 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2403 _cleanup_strv_free_
char **todo
= NULL
;
2404 const char *config_path
;
2409 assert(scope
< _RUNTIME_SCOPE_MAX
);
2411 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2415 config_path
= (flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2419 bool dry_run
= flags
& UNIT_FILE_DRY_RUN
;
2421 STRV_FOREACH(name
, names
) {
2422 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2425 /* If root_dir is set, we don't care about kernel command line or generators.
2426 * But if it is not set, we need to check for interference. */
2428 _cleanup_(install_info_clear
) InstallInfo info
= {
2429 .name
= *name
, /* We borrow *name temporarily… */
2430 .install_mode
= _INSTALL_MODE_INVALID
,
2433 r
= unit_file_search(NULL
, &info
, &lp
, 0);
2436 log_debug_errno(r
, "Failed to look up unit %s, ignoring: %m", info
.name
);
2437 } else if (info
.install_mode
== INSTALL_MODE_MASKED
&&
2438 path_is_generator(&lp
, info
.path
)) {
2439 r
= install_changes_add(changes
, n_changes
,
2440 INSTALL_CHANGE_IS_MASKED_GENERATOR
, info
.name
, info
.path
);
2442 TAKE_PTR(info
.name
); /* Return the borrowed name before bailing */
2447 TAKE_PTR(info
.name
); /* … and give it back here */
2450 _cleanup_free_
char *path
= path_make_absolute(*name
, config_path
);
2454 r
= null_or_empty_path(path
);
2462 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2465 todo
[n_todo
] = strdup(*name
);
2475 STRV_FOREACH(i
, todo
) {
2476 _cleanup_free_
char *path
= NULL
;
2479 path
= path_make_absolute(*i
, config_path
);
2483 if (!dry_run
&& unlink(path
) < 0) {
2484 if (errno
!= ENOENT
)
2485 RET_GATHER(r
, install_changes_add(changes
, n_changes
, -errno
, path
, NULL
));
2490 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, path
, NULL
);
2494 rp
= skip_root(lp
.root_dir
, path
);
2495 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: path
);
2500 RET_GATHER(r
, remove_marked_symlinks(remove_symlinks_to
, config_path
, &lp
, dry_run
, changes
, n_changes
));
2507 UnitFileFlags flags
,
2508 const char *root_dir
,
2509 char * const *files
,
2510 InstallChange
**changes
,
2511 size_t *n_changes
) {
2513 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2514 _cleanup_ordered_hashmap_free_ OrderedHashmap
*todo
= NULL
;
2515 const char *config_path
;
2519 assert(scope
< _RUNTIME_SCOPE_MAX
);
2523 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2527 config_path
= FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2531 STRV_FOREACH(file
, files
) {
2532 _cleanup_free_
char *fn
= NULL
, *path
= NULL
, *full
= NULL
;
2534 if (ordered_hashmap_contains(todo
, *file
))
2537 if (!path_is_absolute(*file
))
2538 return install_changes_add(changes
, n_changes
, -EINVAL
, *file
, NULL
);
2540 r
= path_extract_filename(*file
, &fn
);
2542 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2544 if (!unit_name_is_valid(fn
, UNIT_NAME_ANY
))
2545 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *file
, NULL
);
2547 full
= path_join(lp
.root_dir
, *file
);
2551 r
= verify_regular_at(AT_FDCWD
, full
, /* follow= */ false);
2553 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2555 r
= in_search_path(&lp
, *file
);
2557 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2559 /* A silent noop if the file is already in the search path. */
2562 if (underneath_search_path(&lp
, *file
))
2563 return install_changes_add(changes
, n_changes
, -ETXTBSY
, *file
, NULL
);
2565 path
= strdup(*file
);
2569 r
= ordered_hashmap_ensure_put(&todo
, &path_hash_ops_free_free
, path
, fn
);
2580 const char *fn
, *path
;
2581 ORDERED_HASHMAP_FOREACH_KEY(fn
, path
, todo
) {
2582 _cleanup_free_
char *new_path
= NULL
;
2584 new_path
= path_make_absolute(fn
, config_path
);
2588 RET_GATHER(r
, create_symlink(&lp
, path
, new_path
, FLAGS_SET(flags
, UNIT_FILE_FORCE
), changes
, n_changes
));
2594 static int path_shall_revert(const LookupPaths
*lp
, const char *path
) {
2600 /* Checks whether the path is one where the drop-in directories shall be removed. */
2602 r
= path_is_config(lp
, path
, true);
2606 r
= path_is_control(lp
, path
);
2610 return path_is_transient(lp
, path
);
2613 int unit_file_revert(
2615 const char *root_dir
,
2616 char * const *names
,
2617 InstallChange
**changes
,
2618 size_t *n_changes
) {
2620 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2621 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2622 _cleanup_strv_free_
char **todo
= NULL
;
2626 /* Puts a unit file back into vendor state. This means:
2628 * a) we remove all drop-in snippets added by the user ("config"), add to transient units
2629 * ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
2630 * generated ("generated").
2632 * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
2633 * (i.e. in "config", but not in "transient" or "control" or even "generated").
2635 * We remove all that in both the runtime and the persistent directories, if that applies.
2638 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2642 STRV_FOREACH(name
, names
) {
2643 bool has_vendor
= false;
2645 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2648 STRV_FOREACH(p
, lp
.search_path
) {
2649 _cleanup_free_
char *path
= NULL
, *dropin
= NULL
;
2652 path
= path_make_absolute(*name
, *p
);
2656 r
= RET_NERRNO(lstat(path
, &st
));
2659 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2660 } else if (S_ISREG(st
.st_mode
)) {
2661 /* Check if there's a vendor version */
2662 r
= path_is_vendor_or_generator(&lp
, path
);
2664 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2669 dropin
= strjoin(path
, ".d");
2673 r
= RET_NERRNO(lstat(dropin
, &st
));
2676 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2677 } else if (S_ISDIR(st
.st_mode
)) {
2678 /* Remove the drop-ins */
2679 r
= path_shall_revert(&lp
, dropin
);
2681 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2683 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2686 todo
[n_todo
++] = TAKE_PTR(dropin
);
2694 /* OK, there's a vendor version, hence drop all configuration versions */
2695 STRV_FOREACH(p
, lp
.search_path
) {
2696 _cleanup_free_
char *path
= NULL
;
2699 path
= path_make_absolute(*name
, *p
);
2703 r
= RET_NERRNO(lstat(path
, &st
));
2706 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2707 } else if (S_ISREG(st
.st_mode
) || S_ISLNK(st
.st_mode
)) {
2708 r
= path_is_config(&lp
, path
, true);
2710 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2712 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2715 todo
[n_todo
++] = TAKE_PTR(path
);
2724 STRV_FOREACH(i
, todo
) {
2725 _cleanup_strv_free_
char **fs
= NULL
;
2728 (void) get_files_in_directory(*i
, &fs
);
2730 q
= rm_rf(*i
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
2731 if (q
< 0 && q
!= -ENOENT
&& r
>= 0) {
2736 STRV_FOREACH(j
, fs
) {
2737 _cleanup_free_
char *t
= NULL
;
2739 t
= path_join(*i
, *j
);
2743 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, t
, NULL
);
2748 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, *i
, NULL
);
2752 rp
= skip_root(lp
.root_dir
, *i
);
2753 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: *i
);
2758 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.runtime_config
, &lp
, false, changes
, n_changes
);
2762 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.persistent_config
, &lp
, false, changes
, n_changes
);
2769 int unit_file_add_dependency(
2771 UnitFileFlags file_flags
,
2772 const char *root_dir
,
2773 char * const *names
,
2776 InstallChange
**changes
,
2777 size_t *n_changes
) {
2779 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2780 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2781 InstallInfo
*info
, *target_info
;
2782 const char *config_path
;
2786 assert(scope
< _RUNTIME_SCOPE_MAX
);
2788 assert(IN_SET(dep
, UNIT_WANTS
, UNIT_REQUIRES
));
2790 if (!unit_name_is_valid(target
, UNIT_NAME_ANY
))
2791 return install_changes_add(changes
, n_changes
, -EUCLEAN
, target
, NULL
);
2793 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2797 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2801 r
= install_info_discover_and_check(&ctx
, &lp
, target
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2802 &target_info
, changes
, n_changes
);
2806 assert(target_info
->install_mode
== INSTALL_MODE_REGULAR
);
2808 STRV_FOREACH(name
, names
) {
2811 r
= install_info_discover_and_check(&ctx
, &lp
, *name
,
2812 SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2813 &info
, changes
, n_changes
);
2817 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2819 /* We didn't actually load anything from the unit
2820 * file, but instead just add in our new symlink to
2823 if (dep
== UNIT_WANTS
)
2824 l
= &info
->wanted_by
;
2825 else if (dep
== UNIT_REQUIRES
)
2826 l
= &info
->required_by
;
2828 l
= &info
->upheld_by
;
2831 *l
= strv_new(target_info
->name
);
2836 return install_context_apply(&ctx
, &lp
, file_flags
, config_path
,
2837 SEARCH_FOLLOW_CONFIG_SYMLINKS
, changes
, n_changes
);
2840 static int do_unit_file_enable(
2841 const LookupPaths
*lp
,
2843 UnitFileFlags flags
,
2844 const char *config_path
,
2845 char * const *names_or_paths
,
2846 InstallChange
**changes
,
2847 size_t *n_changes
) {
2849 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2853 STRV_FOREACH(name
, names_or_paths
) {
2854 r
= install_info_discover_and_check(&ctx
, lp
, *name
,
2855 SEARCH_LOAD
| SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2856 &info
, changes
, n_changes
);
2860 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2863 /* This will return the number of symlink rules that were
2864 supposed to be created, not the ones actually created. This
2865 is useful to determine whether the passed units had any
2866 installation data at all. */
2868 return install_context_apply(&ctx
, lp
, flags
, config_path
,
2869 SEARCH_LOAD
, changes
, n_changes
);
2872 int unit_file_enable(
2874 UnitFileFlags flags
,
2875 const char *root_dir
,
2876 char * const *names_or_paths
,
2877 InstallChange
**changes
,
2878 size_t *n_changes
) {
2880 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2884 assert(scope
< _RUNTIME_SCOPE_MAX
);
2886 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2890 const char *config_path
= config_path_from_flags(&lp
, flags
);
2894 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, names_or_paths
, changes
, n_changes
);
2897 static int do_unit_file_disable(
2898 const LookupPaths
*lp
,
2900 UnitFileFlags flags
,
2901 const char *config_path
,
2902 char * const *names
,
2903 InstallChange
**changes
,
2904 size_t *n_changes
) {
2906 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2907 bool has_install_info
= false;
2913 STRV_FOREACH(name
, names
) {
2916 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2917 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *name
, NULL
);
2919 r
= install_info_add(&ctx
, *name
, NULL
, lp
->root_dir
, /* auxiliary= */ false, &info
);
2921 r
= install_info_traverse(&ctx
, lp
, info
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, NULL
);
2923 r
= install_changes_add(changes
, n_changes
, r
, *name
, NULL
);
2924 /* In case there's no unit, we still want to remove any leftover symlink, even if
2925 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2930 /* If we enable multiple units, some with install info and others without,
2931 * the "empty [Install] section" warning is not shown. Let's make the behavior
2932 * of disable align with that. */
2933 has_install_info
= has_install_info
|| install_info_has_rules(info
) || install_info_has_also(info
);
2936 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2937 r
= install_context_mark_for_removal(&ctx
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
2939 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, flags
& UNIT_FILE_DRY_RUN
, changes
, n_changes
);
2943 /* The warning is shown only if it's a no-op */
2944 return install_changes_have_modification(*changes
, *n_changes
) || has_install_info
;
2947 int unit_file_disable(
2949 UnitFileFlags flags
,
2950 const char *root_dir
,
2951 char * const *files
,
2952 InstallChange
**changes
,
2953 size_t *n_changes
) {
2955 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2959 assert(scope
< _RUNTIME_SCOPE_MAX
);
2961 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2965 const char *config_path
= config_path_from_flags(&lp
, flags
);
2969 return do_unit_file_disable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
2972 static int normalize_linked_files(
2974 const LookupPaths
*lp
,
2975 char * const *names_or_paths
,
2977 char ***ret_files
) {
2979 /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/,
2980 * but operates on real unit names. For each argument we look up the actual path
2981 * where the unit is found. This way linked units can be re-enabled successfully. */
2983 _cleanup_strv_free_
char **files
= NULL
, **names
= NULL
;
2990 STRV_FOREACH(a
, names_or_paths
) {
2991 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2992 InstallInfo
*i
= NULL
;
2993 _cleanup_free_
char *n
= NULL
;
2995 r
= path_extract_filename(*a
, &n
);
2998 if (r
== O_DIRECTORY
)
2999 return log_debug_errno(SYNTHETIC_ERRNO(EISDIR
),
3000 "Unexpected path to a directory \"%s\", refusing.", *a
);
3002 if (!is_path(*a
) && !unit_name_is_valid(*a
, UNIT_NAME_INSTANCE
)) {
3003 r
= install_info_discover(&ctx
, lp
, n
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, &i
, NULL
, NULL
);
3005 log_debug_errno(r
, "Failed to discover unit \"%s\", operating on name: %m", n
);
3008 r
= strv_consume(&names
, TAKE_PTR(n
));
3012 const char *p
= NULL
;
3013 if (i
&& i
->path
&& i
->root
)
3014 /* Use startswith here, because we know that paths are normalized, and
3015 * path_startswith() would give us a relative path, but we need an absolute path
3016 * relative to i->root.
3018 * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
3019 * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
3020 * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
3022 p
= startswith(i
->path
, i
->root
);
3024 r
= strv_extend(&files
, p
?: *a
);
3029 *ret_names
= TAKE_PTR(names
);
3030 *ret_files
= TAKE_PTR(files
);
3034 int unit_file_reenable(
3036 UnitFileFlags flags
,
3037 const char *root_dir
,
3038 char * const *names_or_paths
,
3039 InstallChange
**changes
,
3040 size_t *n_changes
) {
3042 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3043 _cleanup_strv_free_
char **names
= NULL
, **files
= NULL
;
3047 assert(scope
< _RUNTIME_SCOPE_MAX
);
3049 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3053 const char *config_path
= config_path_from_flags(&lp
, flags
);
3057 r
= normalize_linked_files(scope
, &lp
, names_or_paths
, &names
, &files
);
3061 /* First, we invoke the disable command with only the basename... */
3062 r
= do_unit_file_disable(&lp
, scope
, flags
, config_path
, names
, changes
, n_changes
);
3066 /* But the enable command with the full name */
3067 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
3070 int unit_file_set_default(
3072 UnitFileFlags flags
,
3073 const char *root_dir
,
3075 InstallChange
**changes
,
3076 size_t *n_changes
) {
3078 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3079 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3081 const char *new_path
;
3085 assert(scope
< _RUNTIME_SCOPE_MAX
);
3088 if (unit_name_to_type(name
) != UNIT_TARGET
) /* this also validates the name */
3090 if (streq(name
, SPECIAL_DEFAULT_TARGET
))
3093 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3097 r
= install_info_discover_and_check(&ctx
, &lp
, name
, 0, &info
, changes
, n_changes
);
3101 new_path
= strjoina(lp
.persistent_config
, "/" SPECIAL_DEFAULT_TARGET
);
3102 return create_symlink(&lp
, info
->path
, new_path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
);
3105 int unit_file_get_default(
3107 const char *root_dir
,
3110 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3111 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3116 assert(scope
< _RUNTIME_SCOPE_MAX
);
3119 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3123 r
= install_info_discover(&ctx
, &lp
, SPECIAL_DEFAULT_TARGET
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3128 return strdup_to(ret
, info
->name
);
3131 int unit_file_lookup_state(
3133 const LookupPaths
*lp
,
3135 UnitFileState
*ret
) {
3137 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3139 UnitFileState state
;
3145 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3148 r
= install_info_discover(&ctx
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3151 return log_debug_errno(r
, "Failed to discover unit %s: %m", name
);
3153 assert(IN_SET(info
->install_mode
, INSTALL_MODE_REGULAR
, INSTALL_MODE_MASKED
));
3154 log_debug("Found unit %s at %s (%s)", name
, strna(info
->path
),
3155 info
->install_mode
== INSTALL_MODE_REGULAR
? "regular file" : "mask");
3157 /* Shortcut things, if the caller just wants to know if this unit exists. */
3161 switch (info
->install_mode
) {
3163 case INSTALL_MODE_MASKED
:
3164 r
= path_is_runtime(lp
, info
->path
, true);
3168 state
= r
> 0 ? UNIT_FILE_MASKED_RUNTIME
: UNIT_FILE_MASKED
;
3171 case INSTALL_MODE_REGULAR
:
3172 /* Check if the name we were querying is actually an alias */
3173 if (!path_equal_filename(name
, info
->path
) && !unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
3174 state
= UNIT_FILE_ALIAS
;
3178 r
= path_is_generator(lp
, info
->path
);
3182 state
= UNIT_FILE_GENERATED
;
3186 r
= path_is_transient(lp
, info
->path
);
3190 state
= UNIT_FILE_TRANSIENT
;
3194 /* Check if any of the Alias= symlinks have been created.
3195 * We ignore other aliases, and only check those that would
3196 * be created by systemctl enable for this unit. */
3197 r
= find_symlinks_in_scope(scope
, lp
, info
, true, &state
);
3203 /* Check if the file is known under other names. If it is,
3204 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
3205 r
= find_symlinks_in_scope(scope
, lp
, info
, false, &state
);
3209 state
= UNIT_FILE_INDIRECT
;
3211 if (install_info_has_rules(info
))
3212 state
= UNIT_FILE_DISABLED
;
3213 else if (install_info_has_also(info
))
3214 state
= UNIT_FILE_INDIRECT
;
3216 state
= UNIT_FILE_STATIC
;
3222 assert_not_reached();
3229 int unit_file_get_state(
3231 const char *root_dir
,
3233 UnitFileState
*ret
) {
3235 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3239 assert(scope
< _RUNTIME_SCOPE_MAX
);
3242 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3246 return unit_file_lookup_state(scope
, &lp
, name
, ret
);
3249 int unit_file_exists_full(
3251 const LookupPaths
*lp
,
3256 _cleanup_(install_context_done
) InstallContext c
= {
3264 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3267 InstallInfo
*info
= NULL
;
3268 r
= install_info_discover(
3273 ret_path
? &info
: NULL
,
3274 /* changes= */ NULL
,
3275 /* n_changes= */ NULL
);
3287 r
= strdup_to(ret_path
, info
->path
);
3295 static int split_pattern_into_name_and_instances(const char *pattern
, char **out_unit_name
, char ***out_instances
) {
3296 _cleanup_strv_free_
char **instances
= NULL
;
3297 _cleanup_free_
char *unit_name
= NULL
;
3301 assert(out_instances
);
3302 assert(out_unit_name
);
3304 r
= extract_first_word(&pattern
, &unit_name
, NULL
, EXTRACT_RETAIN_ESCAPE
);
3308 /* We handle the instances logic when unit name is extracted */
3310 /* We only create instances when a rule of templated unit
3311 * is seen. A rule like enable foo@.service a b c will
3312 * result in an array of (a, b, c) as instance names */
3313 if (!unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
))
3316 instances
= strv_split(pattern
, WHITESPACE
);
3320 *out_instances
= TAKE_PTR(instances
);
3323 *out_unit_name
= TAKE_PTR(unit_name
);
3328 static int presets_find_config(RuntimeScope scope
, const char *root_dir
, char ***ret
) {
3329 static const char* const initrd_dirs
[] = { CONF_PATHS("systemd/initrd-preset"), NULL
};
3330 static const char* const system_dirs
[] = { CONF_PATHS("systemd/system-preset"), NULL
};
3331 static const char* const user_dirs
[] = { CONF_PATHS("systemd/user-preset"), NULL
};
3332 const char* const* dirs
;
3336 assert(scope
< _RUNTIME_SCOPE_MAX
);
3338 if (scope
== RUNTIME_SCOPE_SYSTEM
) {
3339 r
= chase_and_access("/etc/initrd-release", root_dir
, CHASE_PREFIX_ROOT
, F_OK
, /* ret_path= */ NULL
);
3340 if (r
< 0 && r
!= -ENOENT
)
3343 /* Make sure that we fall back to the system preset directories if we're operating on a root
3344 * directory without initrd preset directories. This makes sure that we don't regress when
3345 * using a newer systemctl to operate on a root directory with an older version of systemd
3346 * installed that doesn't yet known about initrd preset directories. */
3348 STRV_FOREACH(d
, initrd_dirs
) {
3349 r
= chase_and_access(*d
, root_dir
, CHASE_PREFIX_ROOT
, F_OK
, /* ret_path= */ NULL
);
3351 return conf_files_list_strv(ret
, ".preset", root_dir
, 0, initrd_dirs
);
3357 } else if (IN_SET(scope
, RUNTIME_SCOPE_GLOBAL
, RUNTIME_SCOPE_USER
))
3360 assert_not_reached();
3362 return conf_files_list_strv(ret
, ".preset", root_dir
, 0, dirs
);
3365 static int read_presets(RuntimeScope scope
, const char *root_dir
, UnitFilePresets
*presets
) {
3366 _cleanup_(unit_file_presets_done
) UnitFilePresets ps
= {};
3367 _cleanup_strv_free_
char **files
= NULL
;
3371 assert(scope
< _RUNTIME_SCOPE_MAX
);
3374 r
= presets_find_config(scope
, root_dir
, &files
);
3378 STRV_FOREACH(p
, files
) {
3379 _cleanup_fclose_
FILE *f
= NULL
;
3382 f
= fopen(*p
, "re");
3384 if (errno
== ENOENT
)
3391 _cleanup_free_
char *line
= NULL
;
3392 _cleanup_(unit_file_preset_rule_done
) UnitFilePresetRule rule
= {};
3393 const char *parameter
;
3395 r
= read_stripped_line(f
, LONG_LINE_MAX
, &line
);
3405 if (strchr(COMMENTS
, line
[0]))
3408 if ((parameter
= first_word(line
, "enable"))) {
3410 char **instances
= NULL
;
3412 /* Unit_name will remain the same as parameter when no instances are specified */
3413 r
= split_pattern_into_name_and_instances(parameter
, &unit_name
, &instances
);
3415 log_syntax(NULL
, LOG_WARNING
, *p
, n
, r
, "Couldn't parse line '%s'. Ignoring.", line
);
3419 rule
= (UnitFilePresetRule
) {
3420 .pattern
= unit_name
,
3421 .action
= PRESET_ENABLE
,
3422 .instances
= instances
,
3425 } else if ((parameter
= first_word(line
, "disable"))) {
3428 pattern
= strdup(parameter
);
3432 rule
= (UnitFilePresetRule
) {
3434 .action
= PRESET_DISABLE
,
3437 } else if ((parameter
= first_word(line
, "ignore"))) {
3440 pattern
= strdup(parameter
);
3444 rule
= (UnitFilePresetRule
) {
3446 .action
= PRESET_IGNORE
,
3450 if (rule
.action
!= 0) {
3451 if (!GREEDY_REALLOC(ps
.rules
, ps
.n_rules
+ 1))
3454 ps
.rules
[ps
.n_rules
++] = TAKE_STRUCT(rule
);
3458 log_syntax(NULL
, LOG_WARNING
, *p
, n
, 0, "Couldn't parse line '%s'. Ignoring.", line
);
3462 ps
.initialized
= true;
3463 *presets
= TAKE_STRUCT(ps
);
3468 static int pattern_match_multiple_instances(
3469 const UnitFilePresetRule rule
,
3470 const char *unit_name
,
3473 _cleanup_free_
char *templated_name
= NULL
;
3478 /* If no ret is needed or the rule itself does not have instances
3479 * initialized, we return not matching */
3480 if (!ret
|| !rule
.instances
)
3483 r
= unit_name_template(unit_name
, &templated_name
);
3486 if (!streq(rule
.pattern
, templated_name
))
3489 /* Compose a list of specified instances when unit name is a template */
3490 if (unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
)) {
3491 _cleanup_strv_free_
char **out_strv
= NULL
;
3493 STRV_FOREACH(iter
, rule
.instances
) {
3494 _cleanup_free_
char *name
= NULL
;
3496 r
= unit_name_replace_instance(unit_name
, *iter
, &name
);
3500 r
= strv_consume(&out_strv
, TAKE_PTR(name
));
3505 *ret
= TAKE_PTR(out_strv
);
3508 /* We now know the input unit name is an instance name */
3509 _cleanup_free_
char *instance_name
= NULL
;
3511 r
= unit_name_to_instance(unit_name
, &instance_name
);
3515 if (strv_contains(rule
.instances
, instance_name
))
3521 static int query_presets(const char *name
, const UnitFilePresets
*presets
, char ***instance_name_list
) {
3522 PresetAction action
= PRESET_UNKNOWN
;
3526 POINTER_MAY_BE_NULL(instance_name_list
);
3528 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3531 FOREACH_ARRAY(i
, presets
->rules
, presets
->n_rules
)
3532 if (pattern_match_multiple_instances(*i
, name
, instance_name_list
) > 0 ||
3533 fnmatch(i
->pattern
, name
, FNM_NOESCAPE
) == 0) {
3540 case PRESET_UNKNOWN
:
3541 log_debug("Preset files don't specify rule for %s. Enabling.", name
);
3542 return PRESET_ENABLE
;
3545 if (instance_name_list
&& *instance_name_list
)
3546 STRV_FOREACH(s
, *instance_name_list
)
3547 log_debug("Preset files say enable %s.", *s
);
3549 log_debug("Preset files say enable %s.", name
);
3550 return PRESET_ENABLE
;
3552 case PRESET_DISABLE
:
3553 log_debug("Preset files say disable %s.", name
);
3554 return PRESET_DISABLE
;
3557 log_debug("Preset files say ignore %s.", name
);
3558 return PRESET_IGNORE
;
3561 assert_not_reached();
3565 PresetAction
unit_file_query_preset(RuntimeScope scope
, const char *root_dir
, const char *name
, UnitFilePresets
*cached
) {
3566 _cleanup_(unit_file_presets_done
) UnitFilePresets tmp
= {};
3571 if (!cached
->initialized
) {
3572 r
= read_presets(scope
, root_dir
, cached
);
3577 return query_presets(name
, cached
, NULL
);
3580 static int execute_preset(
3581 UnitFileFlags file_flags
,
3582 InstallContext
*plus
,
3583 InstallContext
*minus
,
3584 const LookupPaths
*lp
,
3585 const char *config_path
,
3586 char * const *files
,
3587 UnitFilePresetMode mode
,
3588 InstallChange
**changes
,
3589 size_t *n_changes
) {
3596 assert(config_path
);
3598 if (mode
!= UNIT_FILE_PRESET_ENABLE_ONLY
) {
3599 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
3601 r
= install_context_mark_for_removal(minus
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
3605 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, false, changes
, n_changes
);
3609 if (mode
!= UNIT_FILE_PRESET_DISABLE_ONLY
) {
3612 /* Returns number of symlinks that where supposed to be installed. */
3613 q
= install_context_apply(plus
, lp
,
3614 file_flags
| UNIT_FILE_IGNORE_AUXILIARY_FAILURE
,
3616 SEARCH_LOAD
, changes
, n_changes
);
3628 static int preset_prepare_one(
3630 InstallContext
*plus
,
3631 InstallContext
*minus
,
3634 const UnitFilePresets
*presets
,
3635 InstallChange
**changes
,
3636 size_t *n_changes
) {
3638 _cleanup_(install_context_done
) InstallContext tmp
= { .scope
= scope
};
3639 _cleanup_strv_free_
char **instance_name_list
= NULL
;
3643 if (install_info_find(plus
, name
) || install_info_find(minus
, name
))
3646 r
= install_info_discover(&tmp
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3647 &info
, changes
, n_changes
);
3651 if (!streq(name
, info
->name
)) {
3652 log_debug("Skipping %s because it is an alias for %s.", name
, info
->name
);
3656 r
= query_presets(name
, presets
, &instance_name_list
);
3660 if (r
== PRESET_ENABLE
) {
3661 if (instance_name_list
)
3662 STRV_FOREACH(s
, instance_name_list
) {
3663 r
= install_info_discover_and_check(plus
, lp
, *s
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3664 &info
, changes
, n_changes
);
3669 r
= install_info_discover_and_check(plus
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3670 &info
, changes
, n_changes
);
3675 } else if (r
== PRESET_DISABLE
)
3676 r
= install_info_discover(minus
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3677 &info
, changes
, n_changes
);
3682 int unit_file_preset(
3684 UnitFileFlags file_flags
,
3685 const char *root_dir
,
3686 char * const *names
,
3687 UnitFilePresetMode mode
,
3688 InstallChange
**changes
,
3689 size_t *n_changes
) {
3691 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3692 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3693 _cleanup_(unit_file_presets_done
) UnitFilePresets presets
= {};
3694 const char *config_path
;
3698 assert(scope
< _RUNTIME_SCOPE_MAX
);
3699 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3701 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3705 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3709 r
= read_presets(scope
, root_dir
, &presets
);
3713 STRV_FOREACH(name
, names
) {
3714 r
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, *name
, &presets
, changes
, n_changes
);
3715 if (r
< 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r
))
3719 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, names
, mode
, changes
, n_changes
);
3722 int unit_file_preset_all(
3724 UnitFileFlags file_flags
,
3725 const char *root_dir
,
3726 UnitFilePresetMode mode
,
3727 InstallChange
**changes
,
3728 size_t *n_changes
) {
3730 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3731 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3732 _cleanup_(unit_file_presets_done
) UnitFilePresets presets
= {};
3733 const char *config_path
= NULL
;
3737 assert(scope
< _RUNTIME_SCOPE_MAX
);
3738 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3740 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3744 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3748 r
= read_presets(scope
, root_dir
, &presets
);
3752 STRV_FOREACH(i
, lp
.search_path
) {
3753 _cleanup_closedir_
DIR *d
= NULL
;
3757 if (errno
== ENOENT
)
3760 return log_debug_errno(errno
, "Failed to opendir %s: %m", *i
);
3763 FOREACH_DIRENT(de
, d
,
3764 return log_debug_errno(errno
, "Failed to read directory %s: %m", *i
)) {
3766 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3769 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3772 r
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, de
->d_name
, &presets
, changes
, n_changes
);
3773 if (r
< 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r
))
3778 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, NULL
, mode
, changes
, n_changes
);
3781 static UnitFileList
* unit_file_list_free(UnitFileList
*f
) {
3789 DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList
*, unit_file_list_free
);
3791 DEFINE_PRIVATE_HASH_OPS_FULL(unit_file_list_hash_ops_free_free
,
3792 char, string_hash_func
, string_compare_func
, free
,
3793 UnitFileList
, unit_file_list_free
);
3795 int unit_file_get_list(
3797 const char *root_dir
,
3798 char * const *states
,
3799 char * const *patterns
,
3802 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3803 _cleanup_hashmap_free_ Hashmap
*h
= NULL
;
3807 assert(scope
< _RUNTIME_SCOPE_MAX
);
3810 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3814 STRV_FOREACH(dirname
, lp
.search_path
) {
3815 _cleanup_closedir_
DIR *d
= NULL
;
3817 d
= opendir(*dirname
);
3819 if (errno
== ENOENT
)
3821 if (IN_SET(errno
, ENOTDIR
, EACCES
)) {
3822 log_debug_errno(errno
, "Failed to open \"%s\": %m", *dirname
);
3829 FOREACH_DIRENT(de
, d
, return -errno
) {
3830 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3833 if (hashmap_contains(h
, de
->d_name
))
3836 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3839 if (!strv_fnmatch_or_empty(patterns
, de
->d_name
, FNM_NOESCAPE
))
3842 UnitFileState state
;
3844 r
= unit_file_lookup_state(scope
, &lp
, de
->d_name
, &state
);
3846 state
= UNIT_FILE_BAD
;
3848 if (!strv_isempty(states
) &&
3849 !strv_contains(states
, unit_file_state_to_string(state
)))
3852 _cleanup_(unit_file_list_freep
) UnitFileList
*f
= new(UnitFileList
, 1);
3856 *f
= (UnitFileList
) {
3857 .path
= path_make_absolute(de
->d_name
, *dirname
),
3863 _cleanup_free_
char *unit_name
= strdup(de
->d_name
);
3867 r
= hashmap_ensure_put(&h
, &unit_file_list_hash_ops_free_free
, unit_name
, f
);
3872 TAKE_PTR(unit_name
);
3881 static const char* const unit_file_state_table
[_UNIT_FILE_STATE_MAX
] = {
3882 [UNIT_FILE_ENABLED
] = "enabled",
3883 [UNIT_FILE_ENABLED_RUNTIME
] = "enabled-runtime",
3884 [UNIT_FILE_LINKED
] = "linked",
3885 [UNIT_FILE_LINKED_RUNTIME
] = "linked-runtime",
3886 [UNIT_FILE_ALIAS
] = "alias",
3887 [UNIT_FILE_MASKED
] = "masked",
3888 [UNIT_FILE_MASKED_RUNTIME
] = "masked-runtime",
3889 [UNIT_FILE_STATIC
] = "static",
3890 [UNIT_FILE_DISABLED
] = "disabled",
3891 [UNIT_FILE_INDIRECT
] = "indirect",
3892 [UNIT_FILE_GENERATED
] = "generated",
3893 [UNIT_FILE_TRANSIENT
] = "transient",
3894 [UNIT_FILE_BAD
] = "bad",
3897 DEFINE_STRING_TABLE_LOOKUP(unit_file_state
, UnitFileState
);
3899 static const char* const install_change_type_table
[_INSTALL_CHANGE_TYPE_MAX
] = {
3900 [INSTALL_CHANGE_SYMLINK
] = "symlink",
3901 [INSTALL_CHANGE_UNLINK
] = "unlink",
3902 [INSTALL_CHANGE_IS_MASKED
] = "masked",
3903 [INSTALL_CHANGE_IS_MASKED_GENERATOR
] = "masked by generator",
3904 [INSTALL_CHANGE_IS_DANGLING
] = "dangling",
3905 [INSTALL_CHANGE_DESTINATION_NOT_PRESENT
] = "destination not present",
3906 [INSTALL_CHANGE_AUXILIARY_FAILED
] = "auxiliary unit failed",
3909 DEFINE_STRING_TABLE_LOOKUP(install_change_type
, InstallChangeType
);
3911 static const char* const unit_file_preset_mode_table
[_UNIT_FILE_PRESET_MODE_MAX
] = {
3912 [UNIT_FILE_PRESET_FULL
] = "full",
3913 [UNIT_FILE_PRESET_ENABLE_ONLY
] = "enable-only",
3914 [UNIT_FILE_PRESET_DISABLE_ONLY
] = "disable-only",
3917 DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode
, UnitFilePresetMode
);