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
);
2445 TAKE_PTR(info
.name
); /* … and give it back here */
2448 _cleanup_free_
char *path
= path_make_absolute(*name
, config_path
);
2452 r
= null_or_empty_path(path
);
2460 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2463 todo
[n_todo
] = strdup(*name
);
2473 STRV_FOREACH(i
, todo
) {
2474 _cleanup_free_
char *path
= NULL
;
2477 path
= path_make_absolute(*i
, config_path
);
2481 if (!dry_run
&& unlink(path
) < 0) {
2482 if (errno
!= ENOENT
)
2483 RET_GATHER(r
, install_changes_add(changes
, n_changes
, -errno
, path
, NULL
));
2488 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, path
, NULL
);
2492 rp
= skip_root(lp
.root_dir
, path
);
2493 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: path
);
2498 RET_GATHER(r
, remove_marked_symlinks(remove_symlinks_to
, config_path
, &lp
, dry_run
, changes
, n_changes
));
2505 UnitFileFlags flags
,
2506 const char *root_dir
,
2507 char * const *files
,
2508 InstallChange
**changes
,
2509 size_t *n_changes
) {
2511 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2512 _cleanup_ordered_hashmap_free_ OrderedHashmap
*todo
= NULL
;
2513 const char *config_path
;
2517 assert(scope
< _RUNTIME_SCOPE_MAX
);
2521 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2525 config_path
= FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2529 STRV_FOREACH(file
, files
) {
2530 _cleanup_free_
char *fn
= NULL
, *path
= NULL
, *full
= NULL
;
2532 if (ordered_hashmap_contains(todo
, *file
))
2535 if (!path_is_absolute(*file
))
2536 return install_changes_add(changes
, n_changes
, -EINVAL
, *file
, NULL
);
2538 r
= path_extract_filename(*file
, &fn
);
2540 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2542 if (!unit_name_is_valid(fn
, UNIT_NAME_ANY
))
2543 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *file
, NULL
);
2545 full
= path_join(lp
.root_dir
, *file
);
2549 r
= verify_regular_at(AT_FDCWD
, full
, /* follow= */ false);
2551 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2553 r
= in_search_path(&lp
, *file
);
2555 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2557 /* A silent noop if the file is already in the search path. */
2560 if (underneath_search_path(&lp
, *file
))
2561 return install_changes_add(changes
, n_changes
, -ETXTBSY
, *file
, NULL
);
2563 path
= strdup(*file
);
2567 r
= ordered_hashmap_ensure_put(&todo
, &path_hash_ops_free_free
, path
, fn
);
2578 const char *fn
, *path
;
2579 ORDERED_HASHMAP_FOREACH_KEY(fn
, path
, todo
) {
2580 _cleanup_free_
char *new_path
= NULL
;
2582 new_path
= path_make_absolute(fn
, config_path
);
2586 RET_GATHER(r
, create_symlink(&lp
, path
, new_path
, FLAGS_SET(flags
, UNIT_FILE_FORCE
), changes
, n_changes
));
2592 static int path_shall_revert(const LookupPaths
*lp
, const char *path
) {
2598 /* Checks whether the path is one where the drop-in directories shall be removed. */
2600 r
= path_is_config(lp
, path
, true);
2604 r
= path_is_control(lp
, path
);
2608 return path_is_transient(lp
, path
);
2611 int unit_file_revert(
2613 const char *root_dir
,
2614 char * const *names
,
2615 InstallChange
**changes
,
2616 size_t *n_changes
) {
2618 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2619 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2620 _cleanup_strv_free_
char **todo
= NULL
;
2624 /* Puts a unit file back into vendor state. This means:
2626 * a) we remove all drop-in snippets added by the user ("config"), add to transient units
2627 * ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
2628 * generated ("generated").
2630 * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
2631 * (i.e. in "config", but not in "transient" or "control" or even "generated").
2633 * We remove all that in both the runtime and the persistent directories, if that applies.
2636 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2640 STRV_FOREACH(name
, names
) {
2641 bool has_vendor
= false;
2643 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2646 STRV_FOREACH(p
, lp
.search_path
) {
2647 _cleanup_free_
char *path
= NULL
, *dropin
= NULL
;
2650 path
= path_make_absolute(*name
, *p
);
2654 r
= RET_NERRNO(lstat(path
, &st
));
2657 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2658 } else if (S_ISREG(st
.st_mode
)) {
2659 /* Check if there's a vendor version */
2660 r
= path_is_vendor_or_generator(&lp
, path
);
2662 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2667 dropin
= strjoin(path
, ".d");
2671 r
= RET_NERRNO(lstat(dropin
, &st
));
2674 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2675 } else if (S_ISDIR(st
.st_mode
)) {
2676 /* Remove the drop-ins */
2677 r
= path_shall_revert(&lp
, dropin
);
2679 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2681 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2684 todo
[n_todo
++] = TAKE_PTR(dropin
);
2692 /* OK, there's a vendor version, hence drop all configuration versions */
2693 STRV_FOREACH(p
, lp
.search_path
) {
2694 _cleanup_free_
char *path
= NULL
;
2697 path
= path_make_absolute(*name
, *p
);
2701 r
= RET_NERRNO(lstat(path
, &st
));
2704 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2705 } else if (S_ISREG(st
.st_mode
) || S_ISLNK(st
.st_mode
)) {
2706 r
= path_is_config(&lp
, path
, true);
2708 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2710 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2713 todo
[n_todo
++] = TAKE_PTR(path
);
2722 STRV_FOREACH(i
, todo
) {
2723 _cleanup_strv_free_
char **fs
= NULL
;
2726 (void) get_files_in_directory(*i
, &fs
);
2728 q
= rm_rf(*i
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
2729 if (q
< 0 && q
!= -ENOENT
&& r
>= 0) {
2734 STRV_FOREACH(j
, fs
) {
2735 _cleanup_free_
char *t
= NULL
;
2737 t
= path_join(*i
, *j
);
2741 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, t
, NULL
);
2746 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, *i
, NULL
);
2750 rp
= skip_root(lp
.root_dir
, *i
);
2751 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: *i
);
2756 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.runtime_config
, &lp
, false, changes
, n_changes
);
2760 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.persistent_config
, &lp
, false, changes
, n_changes
);
2767 int unit_file_add_dependency(
2769 UnitFileFlags file_flags
,
2770 const char *root_dir
,
2771 char * const *names
,
2774 InstallChange
**changes
,
2775 size_t *n_changes
) {
2777 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2778 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2779 InstallInfo
*info
, *target_info
;
2780 const char *config_path
;
2784 assert(scope
< _RUNTIME_SCOPE_MAX
);
2786 assert(IN_SET(dep
, UNIT_WANTS
, UNIT_REQUIRES
));
2788 if (!unit_name_is_valid(target
, UNIT_NAME_ANY
))
2789 return install_changes_add(changes
, n_changes
, -EUCLEAN
, target
, NULL
);
2791 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2795 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2799 r
= install_info_discover_and_check(&ctx
, &lp
, target
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2800 &target_info
, changes
, n_changes
);
2804 assert(target_info
->install_mode
== INSTALL_MODE_REGULAR
);
2806 STRV_FOREACH(name
, names
) {
2809 r
= install_info_discover_and_check(&ctx
, &lp
, *name
,
2810 SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2811 &info
, changes
, n_changes
);
2815 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2817 /* We didn't actually load anything from the unit
2818 * file, but instead just add in our new symlink to
2821 if (dep
== UNIT_WANTS
)
2822 l
= &info
->wanted_by
;
2823 else if (dep
== UNIT_REQUIRES
)
2824 l
= &info
->required_by
;
2826 l
= &info
->upheld_by
;
2829 *l
= strv_new(target_info
->name
);
2834 return install_context_apply(&ctx
, &lp
, file_flags
, config_path
,
2835 SEARCH_FOLLOW_CONFIG_SYMLINKS
, changes
, n_changes
);
2838 static int do_unit_file_enable(
2839 const LookupPaths
*lp
,
2841 UnitFileFlags flags
,
2842 const char *config_path
,
2843 char * const *names_or_paths
,
2844 InstallChange
**changes
,
2845 size_t *n_changes
) {
2847 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2851 STRV_FOREACH(name
, names_or_paths
) {
2852 r
= install_info_discover_and_check(&ctx
, lp
, *name
,
2853 SEARCH_LOAD
| SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2854 &info
, changes
, n_changes
);
2858 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2861 /* This will return the number of symlink rules that were
2862 supposed to be created, not the ones actually created. This
2863 is useful to determine whether the passed units had any
2864 installation data at all. */
2866 return install_context_apply(&ctx
, lp
, flags
, config_path
,
2867 SEARCH_LOAD
, changes
, n_changes
);
2870 int unit_file_enable(
2872 UnitFileFlags flags
,
2873 const char *root_dir
,
2874 char * const *names_or_paths
,
2875 InstallChange
**changes
,
2876 size_t *n_changes
) {
2878 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2882 assert(scope
< _RUNTIME_SCOPE_MAX
);
2884 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2888 const char *config_path
= config_path_from_flags(&lp
, flags
);
2892 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, names_or_paths
, changes
, n_changes
);
2895 static int do_unit_file_disable(
2896 const LookupPaths
*lp
,
2898 UnitFileFlags flags
,
2899 const char *config_path
,
2900 char * const *names
,
2901 InstallChange
**changes
,
2902 size_t *n_changes
) {
2904 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2905 bool has_install_info
= false;
2911 STRV_FOREACH(name
, names
) {
2914 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2915 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *name
, NULL
);
2917 r
= install_info_add(&ctx
, *name
, NULL
, lp
->root_dir
, /* auxiliary= */ false, &info
);
2919 r
= install_info_traverse(&ctx
, lp
, info
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, NULL
);
2921 r
= install_changes_add(changes
, n_changes
, r
, *name
, NULL
);
2922 /* In case there's no unit, we still want to remove any leftover symlink, even if
2923 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2928 /* If we enable multiple units, some with install info and others without,
2929 * the "empty [Install] section" warning is not shown. Let's make the behavior
2930 * of disable align with that. */
2931 has_install_info
= has_install_info
|| install_info_has_rules(info
) || install_info_has_also(info
);
2934 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2935 r
= install_context_mark_for_removal(&ctx
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
2937 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, flags
& UNIT_FILE_DRY_RUN
, changes
, n_changes
);
2941 /* The warning is shown only if it's a no-op */
2942 return install_changes_have_modification(*changes
, *n_changes
) || has_install_info
;
2945 int unit_file_disable(
2947 UnitFileFlags flags
,
2948 const char *root_dir
,
2949 char * const *files
,
2950 InstallChange
**changes
,
2951 size_t *n_changes
) {
2953 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2957 assert(scope
< _RUNTIME_SCOPE_MAX
);
2959 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2963 const char *config_path
= config_path_from_flags(&lp
, flags
);
2967 return do_unit_file_disable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
2970 static int normalize_linked_files(
2972 const LookupPaths
*lp
,
2973 char * const *names_or_paths
,
2975 char ***ret_files
) {
2977 /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/,
2978 * but operates on real unit names. For each argument we look up the actual path
2979 * where the unit is found. This way linked units can be re-enabled successfully. */
2981 _cleanup_strv_free_
char **files
= NULL
, **names
= NULL
;
2984 STRV_FOREACH(a
, names_or_paths
) {
2985 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2986 InstallInfo
*i
= NULL
;
2987 _cleanup_free_
char *n
= NULL
;
2989 r
= path_extract_filename(*a
, &n
);
2992 if (r
== O_DIRECTORY
)
2993 return log_debug_errno(SYNTHETIC_ERRNO(EISDIR
),
2994 "Unexpected path to a directory \"%s\", refusing.", *a
);
2996 if (!is_path(*a
) && !unit_name_is_valid(*a
, UNIT_NAME_INSTANCE
)) {
2997 r
= install_info_discover(&ctx
, lp
, n
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, &i
, NULL
, NULL
);
2999 log_debug_errno(r
, "Failed to discover unit \"%s\", operating on name: %m", n
);
3002 r
= strv_consume(&names
, TAKE_PTR(n
));
3006 const char *p
= NULL
;
3007 if (i
&& i
->path
&& i
->root
)
3008 /* Use startswith here, because we know that paths are normalized, and
3009 * path_startswith() would give us a relative path, but we need an absolute path
3010 * relative to i->root.
3012 * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
3013 * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
3014 * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
3016 p
= startswith(i
->path
, i
->root
);
3018 r
= strv_extend(&files
, p
?: *a
);
3023 *ret_names
= TAKE_PTR(names
);
3024 *ret_files
= TAKE_PTR(files
);
3028 int unit_file_reenable(
3030 UnitFileFlags flags
,
3031 const char *root_dir
,
3032 char * const *names_or_paths
,
3033 InstallChange
**changes
,
3034 size_t *n_changes
) {
3036 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3037 _cleanup_strv_free_
char **names
= NULL
, **files
= NULL
;
3041 assert(scope
< _RUNTIME_SCOPE_MAX
);
3043 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3047 const char *config_path
= config_path_from_flags(&lp
, flags
);
3051 r
= normalize_linked_files(scope
, &lp
, names_or_paths
, &names
, &files
);
3055 /* First, we invoke the disable command with only the basename... */
3056 r
= do_unit_file_disable(&lp
, scope
, flags
, config_path
, names
, changes
, n_changes
);
3060 /* But the enable command with the full name */
3061 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
3064 int unit_file_set_default(
3066 UnitFileFlags flags
,
3067 const char *root_dir
,
3069 InstallChange
**changes
,
3070 size_t *n_changes
) {
3072 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3073 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3075 const char *new_path
;
3079 assert(scope
< _RUNTIME_SCOPE_MAX
);
3082 if (unit_name_to_type(name
) != UNIT_TARGET
) /* this also validates the name */
3084 if (streq(name
, SPECIAL_DEFAULT_TARGET
))
3087 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3091 r
= install_info_discover_and_check(&ctx
, &lp
, name
, 0, &info
, changes
, n_changes
);
3095 new_path
= strjoina(lp
.persistent_config
, "/" SPECIAL_DEFAULT_TARGET
);
3096 return create_symlink(&lp
, info
->path
, new_path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
);
3099 int unit_file_get_default(
3101 const char *root_dir
,
3104 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3105 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3110 assert(scope
< _RUNTIME_SCOPE_MAX
);
3113 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3117 r
= install_info_discover(&ctx
, &lp
, SPECIAL_DEFAULT_TARGET
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3122 return strdup_to(ret
, info
->name
);
3125 int unit_file_lookup_state(
3127 const LookupPaths
*lp
,
3129 UnitFileState
*ret
) {
3131 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3133 UnitFileState state
;
3139 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3142 r
= install_info_discover(&ctx
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3145 return log_debug_errno(r
, "Failed to discover unit %s: %m", name
);
3147 assert(IN_SET(info
->install_mode
, INSTALL_MODE_REGULAR
, INSTALL_MODE_MASKED
));
3148 log_debug("Found unit %s at %s (%s)", name
, strna(info
->path
),
3149 info
->install_mode
== INSTALL_MODE_REGULAR
? "regular file" : "mask");
3151 /* Shortcut things, if the caller just wants to know if this unit exists. */
3155 switch (info
->install_mode
) {
3157 case INSTALL_MODE_MASKED
:
3158 r
= path_is_runtime(lp
, info
->path
, true);
3162 state
= r
> 0 ? UNIT_FILE_MASKED_RUNTIME
: UNIT_FILE_MASKED
;
3165 case INSTALL_MODE_REGULAR
:
3166 /* Check if the name we were querying is actually an alias */
3167 if (!path_equal_filename(name
, info
->path
) && !unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
3168 state
= UNIT_FILE_ALIAS
;
3172 r
= path_is_generator(lp
, info
->path
);
3176 state
= UNIT_FILE_GENERATED
;
3180 r
= path_is_transient(lp
, info
->path
);
3184 state
= UNIT_FILE_TRANSIENT
;
3188 /* Check if any of the Alias= symlinks have been created.
3189 * We ignore other aliases, and only check those that would
3190 * be created by systemctl enable for this unit. */
3191 r
= find_symlinks_in_scope(scope
, lp
, info
, true, &state
);
3197 /* Check if the file is known under other names. If it is,
3198 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
3199 r
= find_symlinks_in_scope(scope
, lp
, info
, false, &state
);
3203 state
= UNIT_FILE_INDIRECT
;
3205 if (install_info_has_rules(info
))
3206 state
= UNIT_FILE_DISABLED
;
3207 else if (install_info_has_also(info
))
3208 state
= UNIT_FILE_INDIRECT
;
3210 state
= UNIT_FILE_STATIC
;
3216 assert_not_reached();
3223 int unit_file_get_state(
3225 const char *root_dir
,
3227 UnitFileState
*ret
) {
3229 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3233 assert(scope
< _RUNTIME_SCOPE_MAX
);
3236 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3240 return unit_file_lookup_state(scope
, &lp
, name
, ret
);
3243 int unit_file_exists_full(
3245 const LookupPaths
*lp
,
3250 _cleanup_(install_context_done
) InstallContext c
= {
3258 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3261 InstallInfo
*info
= NULL
;
3262 r
= install_info_discover(
3267 ret_path
? &info
: NULL
,
3268 /* changes= */ NULL
,
3269 /* n_changes= */ NULL
);
3281 r
= strdup_to(ret_path
, info
->path
);
3289 static int split_pattern_into_name_and_instances(const char *pattern
, char **out_unit_name
, char ***out_instances
) {
3290 _cleanup_strv_free_
char **instances
= NULL
;
3291 _cleanup_free_
char *unit_name
= NULL
;
3295 assert(out_instances
);
3296 assert(out_unit_name
);
3298 r
= extract_first_word(&pattern
, &unit_name
, NULL
, EXTRACT_RETAIN_ESCAPE
);
3302 /* We handle the instances logic when unit name is extracted */
3304 /* We only create instances when a rule of templated unit
3305 * is seen. A rule like enable foo@.service a b c will
3306 * result in an array of (a, b, c) as instance names */
3307 if (!unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
))
3310 instances
= strv_split(pattern
, WHITESPACE
);
3314 *out_instances
= TAKE_PTR(instances
);
3317 *out_unit_name
= TAKE_PTR(unit_name
);
3322 static int presets_find_config(RuntimeScope scope
, const char *root_dir
, char ***ret
) {
3323 static const char* const initrd_dirs
[] = { CONF_PATHS("systemd/initrd-preset"), NULL
};
3324 static const char* const system_dirs
[] = { CONF_PATHS("systemd/system-preset"), NULL
};
3325 static const char* const user_dirs
[] = { CONF_PATHS("systemd/user-preset"), NULL
};
3326 const char* const* dirs
;
3330 assert(scope
< _RUNTIME_SCOPE_MAX
);
3332 if (scope
== RUNTIME_SCOPE_SYSTEM
) {
3333 r
= chase_and_access("/etc/initrd-release", root_dir
, CHASE_PREFIX_ROOT
, F_OK
, /* ret_path= */ NULL
);
3334 if (r
< 0 && r
!= -ENOENT
)
3337 /* Make sure that we fall back to the system preset directories if we're operating on a root
3338 * directory without initrd preset directories. This makes sure that we don't regress when
3339 * using a newer systemctl to operate on a root directory with an older version of systemd
3340 * installed that doesn't yet known about initrd preset directories. */
3342 STRV_FOREACH(d
, initrd_dirs
) {
3343 r
= chase_and_access(*d
, root_dir
, CHASE_PREFIX_ROOT
, F_OK
, /* ret_path= */ NULL
);
3345 return conf_files_list_strv(ret
, ".preset", root_dir
, 0, initrd_dirs
);
3351 } else if (IN_SET(scope
, RUNTIME_SCOPE_GLOBAL
, RUNTIME_SCOPE_USER
))
3354 assert_not_reached();
3356 return conf_files_list_strv(ret
, ".preset", root_dir
, 0, dirs
);
3359 static int read_presets(RuntimeScope scope
, const char *root_dir
, UnitFilePresets
*presets
) {
3360 _cleanup_(unit_file_presets_done
) UnitFilePresets ps
= {};
3361 _cleanup_strv_free_
char **files
= NULL
;
3365 assert(scope
< _RUNTIME_SCOPE_MAX
);
3368 r
= presets_find_config(scope
, root_dir
, &files
);
3372 STRV_FOREACH(p
, files
) {
3373 _cleanup_fclose_
FILE *f
= NULL
;
3376 f
= fopen(*p
, "re");
3378 if (errno
== ENOENT
)
3385 _cleanup_free_
char *line
= NULL
;
3386 _cleanup_(unit_file_preset_rule_done
) UnitFilePresetRule rule
= {};
3387 const char *parameter
;
3389 r
= read_stripped_line(f
, LONG_LINE_MAX
, &line
);
3399 if (strchr(COMMENTS
, line
[0]))
3402 if ((parameter
= first_word(line
, "enable"))) {
3404 char **instances
= NULL
;
3406 /* Unit_name will remain the same as parameter when no instances are specified */
3407 r
= split_pattern_into_name_and_instances(parameter
, &unit_name
, &instances
);
3409 log_syntax(NULL
, LOG_WARNING
, *p
, n
, r
, "Couldn't parse line '%s'. Ignoring.", line
);
3413 rule
= (UnitFilePresetRule
) {
3414 .pattern
= unit_name
,
3415 .action
= PRESET_ENABLE
,
3416 .instances
= instances
,
3419 } else if ((parameter
= first_word(line
, "disable"))) {
3422 pattern
= strdup(parameter
);
3426 rule
= (UnitFilePresetRule
) {
3428 .action
= PRESET_DISABLE
,
3431 } else if ((parameter
= first_word(line
, "ignore"))) {
3434 pattern
= strdup(parameter
);
3438 rule
= (UnitFilePresetRule
) {
3440 .action
= PRESET_IGNORE
,
3444 if (rule
.action
!= 0) {
3445 if (!GREEDY_REALLOC(ps
.rules
, ps
.n_rules
+ 1))
3448 ps
.rules
[ps
.n_rules
++] = TAKE_STRUCT(rule
);
3452 log_syntax(NULL
, LOG_WARNING
, *p
, n
, 0, "Couldn't parse line '%s'. Ignoring.", line
);
3456 ps
.initialized
= true;
3457 *presets
= TAKE_STRUCT(ps
);
3462 static int pattern_match_multiple_instances(
3463 const UnitFilePresetRule rule
,
3464 const char *unit_name
,
3467 _cleanup_free_
char *templated_name
= NULL
;
3472 /* If no ret is needed or the rule itself does not have instances
3473 * initialized, we return not matching */
3474 if (!ret
|| !rule
.instances
)
3477 r
= unit_name_template(unit_name
, &templated_name
);
3480 if (!streq(rule
.pattern
, templated_name
))
3483 /* Compose a list of specified instances when unit name is a template */
3484 if (unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
)) {
3485 _cleanup_strv_free_
char **out_strv
= NULL
;
3487 STRV_FOREACH(iter
, rule
.instances
) {
3488 _cleanup_free_
char *name
= NULL
;
3490 r
= unit_name_replace_instance(unit_name
, *iter
, &name
);
3494 r
= strv_consume(&out_strv
, TAKE_PTR(name
));
3499 *ret
= TAKE_PTR(out_strv
);
3502 /* We now know the input unit name is an instance name */
3503 _cleanup_free_
char *instance_name
= NULL
;
3505 r
= unit_name_to_instance(unit_name
, &instance_name
);
3509 if (strv_contains(rule
.instances
, instance_name
))
3515 static int query_presets(const char *name
, const UnitFilePresets
*presets
, char ***instance_name_list
) {
3516 PresetAction action
= PRESET_UNKNOWN
;
3520 POINTER_MAY_BE_NULL(instance_name_list
);
3522 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3525 FOREACH_ARRAY(i
, presets
->rules
, presets
->n_rules
)
3526 if (pattern_match_multiple_instances(*i
, name
, instance_name_list
) > 0 ||
3527 fnmatch(i
->pattern
, name
, FNM_NOESCAPE
) == 0) {
3534 case PRESET_UNKNOWN
:
3535 log_debug("Preset files don't specify rule for %s. Enabling.", name
);
3536 return PRESET_ENABLE
;
3539 if (instance_name_list
&& *instance_name_list
)
3540 STRV_FOREACH(s
, *instance_name_list
)
3541 log_debug("Preset files say enable %s.", *s
);
3543 log_debug("Preset files say enable %s.", name
);
3544 return PRESET_ENABLE
;
3546 case PRESET_DISABLE
:
3547 log_debug("Preset files say disable %s.", name
);
3548 return PRESET_DISABLE
;
3551 log_debug("Preset files say ignore %s.", name
);
3552 return PRESET_IGNORE
;
3555 assert_not_reached();
3559 PresetAction
unit_file_query_preset(RuntimeScope scope
, const char *root_dir
, const char *name
, UnitFilePresets
*cached
) {
3560 _cleanup_(unit_file_presets_done
) UnitFilePresets tmp
= {};
3565 if (!cached
->initialized
) {
3566 r
= read_presets(scope
, root_dir
, cached
);
3571 return query_presets(name
, cached
, NULL
);
3574 static int execute_preset(
3575 UnitFileFlags file_flags
,
3576 InstallContext
*plus
,
3577 InstallContext
*minus
,
3578 const LookupPaths
*lp
,
3579 const char *config_path
,
3580 char * const *files
,
3581 UnitFilePresetMode mode
,
3582 InstallChange
**changes
,
3583 size_t *n_changes
) {
3590 assert(config_path
);
3592 if (mode
!= UNIT_FILE_PRESET_ENABLE_ONLY
) {
3593 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
3595 r
= install_context_mark_for_removal(minus
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
3599 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, false, changes
, n_changes
);
3603 if (mode
!= UNIT_FILE_PRESET_DISABLE_ONLY
) {
3606 /* Returns number of symlinks that where supposed to be installed. */
3607 q
= install_context_apply(plus
, lp
,
3608 file_flags
| UNIT_FILE_IGNORE_AUXILIARY_FAILURE
,
3610 SEARCH_LOAD
, changes
, n_changes
);
3622 static int preset_prepare_one(
3624 InstallContext
*plus
,
3625 InstallContext
*minus
,
3628 const UnitFilePresets
*presets
,
3629 InstallChange
**changes
,
3630 size_t *n_changes
) {
3632 _cleanup_(install_context_done
) InstallContext tmp
= { .scope
= scope
};
3633 _cleanup_strv_free_
char **instance_name_list
= NULL
;
3637 if (install_info_find(plus
, name
) || install_info_find(minus
, name
))
3640 r
= install_info_discover(&tmp
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3641 &info
, changes
, n_changes
);
3645 if (!streq(name
, info
->name
)) {
3646 log_debug("Skipping %s because it is an alias for %s.", name
, info
->name
);
3650 r
= query_presets(name
, presets
, &instance_name_list
);
3654 if (r
== PRESET_ENABLE
) {
3655 if (instance_name_list
)
3656 STRV_FOREACH(s
, instance_name_list
) {
3657 r
= install_info_discover_and_check(plus
, lp
, *s
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3658 &info
, changes
, n_changes
);
3663 r
= install_info_discover_and_check(plus
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3664 &info
, changes
, n_changes
);
3669 } else if (r
== PRESET_DISABLE
)
3670 r
= install_info_discover(minus
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3671 &info
, changes
, n_changes
);
3676 int unit_file_preset(
3678 UnitFileFlags file_flags
,
3679 const char *root_dir
,
3680 char * const *names
,
3681 UnitFilePresetMode mode
,
3682 InstallChange
**changes
,
3683 size_t *n_changes
) {
3685 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3686 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3687 _cleanup_(unit_file_presets_done
) UnitFilePresets presets
= {};
3688 const char *config_path
;
3692 assert(scope
< _RUNTIME_SCOPE_MAX
);
3693 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3695 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3699 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3703 r
= read_presets(scope
, root_dir
, &presets
);
3707 STRV_FOREACH(name
, names
) {
3708 r
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, *name
, &presets
, changes
, n_changes
);
3709 if (r
< 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r
))
3713 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, names
, mode
, changes
, n_changes
);
3716 int unit_file_preset_all(
3718 UnitFileFlags file_flags
,
3719 const char *root_dir
,
3720 UnitFilePresetMode mode
,
3721 InstallChange
**changes
,
3722 size_t *n_changes
) {
3724 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3725 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3726 _cleanup_(unit_file_presets_done
) UnitFilePresets presets
= {};
3727 const char *config_path
= NULL
;
3731 assert(scope
< _RUNTIME_SCOPE_MAX
);
3732 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3734 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3738 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3742 r
= read_presets(scope
, root_dir
, &presets
);
3746 STRV_FOREACH(i
, lp
.search_path
) {
3747 _cleanup_closedir_
DIR *d
= NULL
;
3751 if (errno
== ENOENT
)
3754 return log_debug_errno(errno
, "Failed to opendir %s: %m", *i
);
3757 FOREACH_DIRENT(de
, d
,
3758 return log_debug_errno(errno
, "Failed to read directory %s: %m", *i
)) {
3760 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3763 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3766 r
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, de
->d_name
, &presets
, changes
, n_changes
);
3767 if (r
< 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r
))
3772 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, NULL
, mode
, changes
, n_changes
);
3775 static UnitFileList
* unit_file_list_free(UnitFileList
*f
) {
3783 DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList
*, unit_file_list_free
);
3785 DEFINE_PRIVATE_HASH_OPS_FULL(unit_file_list_hash_ops_free_free
,
3786 char, string_hash_func
, string_compare_func
, free
,
3787 UnitFileList
, unit_file_list_free
);
3789 int unit_file_get_list(
3791 const char *root_dir
,
3792 char * const *states
,
3793 char * const *patterns
,
3796 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3797 _cleanup_hashmap_free_ Hashmap
*h
= NULL
;
3801 assert(scope
< _RUNTIME_SCOPE_MAX
);
3804 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3808 STRV_FOREACH(dirname
, lp
.search_path
) {
3809 _cleanup_closedir_
DIR *d
= NULL
;
3811 d
= opendir(*dirname
);
3813 if (errno
== ENOENT
)
3815 if (IN_SET(errno
, ENOTDIR
, EACCES
)) {
3816 log_debug_errno(errno
, "Failed to open \"%s\": %m", *dirname
);
3823 FOREACH_DIRENT(de
, d
, return -errno
) {
3824 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3827 if (hashmap_contains(h
, de
->d_name
))
3830 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3833 if (!strv_fnmatch_or_empty(patterns
, de
->d_name
, FNM_NOESCAPE
))
3836 UnitFileState state
;
3838 r
= unit_file_lookup_state(scope
, &lp
, de
->d_name
, &state
);
3840 state
= UNIT_FILE_BAD
;
3842 if (!strv_isempty(states
) &&
3843 !strv_contains(states
, unit_file_state_to_string(state
)))
3846 _cleanup_(unit_file_list_freep
) UnitFileList
*f
= new(UnitFileList
, 1);
3850 *f
= (UnitFileList
) {
3851 .path
= path_make_absolute(de
->d_name
, *dirname
),
3857 _cleanup_free_
char *unit_name
= strdup(de
->d_name
);
3861 r
= hashmap_ensure_put(&h
, &unit_file_list_hash_ops_free_free
, unit_name
, f
);
3866 TAKE_PTR(unit_name
);
3875 static const char* const unit_file_state_table
[_UNIT_FILE_STATE_MAX
] = {
3876 [UNIT_FILE_ENABLED
] = "enabled",
3877 [UNIT_FILE_ENABLED_RUNTIME
] = "enabled-runtime",
3878 [UNIT_FILE_LINKED
] = "linked",
3879 [UNIT_FILE_LINKED_RUNTIME
] = "linked-runtime",
3880 [UNIT_FILE_ALIAS
] = "alias",
3881 [UNIT_FILE_MASKED
] = "masked",
3882 [UNIT_FILE_MASKED_RUNTIME
] = "masked-runtime",
3883 [UNIT_FILE_STATIC
] = "static",
3884 [UNIT_FILE_DISABLED
] = "disabled",
3885 [UNIT_FILE_INDIRECT
] = "indirect",
3886 [UNIT_FILE_GENERATED
] = "generated",
3887 [UNIT_FILE_TRANSIENT
] = "transient",
3888 [UNIT_FILE_BAD
] = "bad",
3891 DEFINE_STRING_TABLE_LOOKUP(unit_file_state
, UnitFileState
);
3893 static const char* const install_change_type_table
[_INSTALL_CHANGE_TYPE_MAX
] = {
3894 [INSTALL_CHANGE_SYMLINK
] = "symlink",
3895 [INSTALL_CHANGE_UNLINK
] = "unlink",
3896 [INSTALL_CHANGE_IS_MASKED
] = "masked",
3897 [INSTALL_CHANGE_IS_MASKED_GENERATOR
] = "masked by generator",
3898 [INSTALL_CHANGE_IS_DANGLING
] = "dangling",
3899 [INSTALL_CHANGE_DESTINATION_NOT_PRESENT
] = "destination not present",
3900 [INSTALL_CHANGE_AUXILIARY_FAILED
] = "auxiliary unit failed",
3903 DEFINE_STRING_TABLE_LOOKUP(install_change_type
, InstallChangeType
);
3905 static const char* const unit_file_preset_mode_table
[_UNIT_FILE_PRESET_MODE_MAX
] = {
3906 [UNIT_FILE_PRESET_FULL
] = "full",
3907 [UNIT_FILE_PRESET_ENABLE_ONLY
] = "enable-only",
3908 [UNIT_FILE_PRESET_DISABLE_ONLY
] = "disable-only",
3911 DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode
, UnitFilePresetMode
);