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"
25 #include "mkdir-label.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
40 typedef enum SearchFlags
{
42 SEARCH_FOLLOW_CONFIG_SYMLINKS
= 1 << 1,
43 SEARCH_DROPIN
= 1 << 2,
48 OrderedHashmap
*will_process
;
49 OrderedHashmap
*have_processed
;
52 struct UnitFilePresetRule
{
58 /* NB! strings use past tense. */
59 static const char *const preset_action_past_tense_table
[_PRESET_ACTION_MAX
] = {
60 [PRESET_UNKNOWN
] = "unknown",
61 [PRESET_ENABLE
] = "enabled",
62 [PRESET_DISABLE
] = "disabled",
63 [PRESET_IGNORE
] = "ignored",
66 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(preset_action_past_tense
, PresetAction
);
68 static bool install_info_has_rules(const InstallInfo
*i
) {
71 return !strv_isempty(i
->aliases
) ||
72 !strv_isempty(i
->wanted_by
) ||
73 !strv_isempty(i
->required_by
) ||
74 !strv_isempty(i
->upheld_by
);
77 static bool install_info_has_also(const InstallInfo
*i
) {
80 return !strv_isempty(i
->also
);
83 static void unit_file_preset_rule_done(UnitFilePresetRule
*rule
) {
87 strv_free(rule
->instances
);
90 void unit_file_presets_done(UnitFilePresets
*p
) {
94 FOREACH_ARRAY(rule
, p
->rules
, p
->n_rules
)
95 unit_file_preset_rule_done(rule
);
101 static const char *const install_mode_table
[_INSTALL_MODE_MAX
] = {
102 [INSTALL_MODE_REGULAR
] = "regular",
103 [INSTALL_MODE_LINKED
] = "linked",
104 [INSTALL_MODE_ALIAS
] = "alias",
105 [INSTALL_MODE_MASKED
] = "masked",
108 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(install_mode
, InstallMode
);
110 static int in_search_path(const LookupPaths
*lp
, const char *path
) {
111 _cleanup_free_
char *parent
= NULL
;
114 /* Check if 'path' is in lp->search_path. */
119 r
= path_extract_directory(path
, &parent
);
123 return path_strv_contains(lp
->search_path
, parent
);
126 static bool underneath_search_path(const LookupPaths
*lp
, const char *path
) {
127 /* Check if 'path' is underneath lp->search_path. */
132 return path_startswith_strv(path
, lp
->search_path
);
135 static const char* skip_root(const char *root_dir
, const char *path
) {
141 const char *e
= path_startswith(path
, root_dir
);
145 /* Make sure the returned path starts with a slash */
147 if (e
== path
|| e
[-1] != '/')
156 static int path_is_generator(const LookupPaths
*lp
, const char *path
) {
157 _cleanup_free_
char *parent
= NULL
;
163 r
= path_extract_directory(path
, &parent
);
167 return PATH_IN_SET(parent
,
173 static int path_is_transient(const LookupPaths
*lp
, const char *path
) {
174 _cleanup_free_
char *parent
= NULL
;
180 r
= path_extract_directory(path
, &parent
);
184 return path_equal(parent
, lp
->transient
);
187 static int path_is_control(const LookupPaths
*lp
, const char *path
) {
188 _cleanup_free_
char *parent
= NULL
;
194 r
= path_extract_directory(path
, &parent
);
198 return PATH_IN_SET(parent
,
199 lp
->persistent_control
,
200 lp
->runtime_control
);
203 static int path_is_config(const LookupPaths
*lp
, const char *path
, bool check_parent
) {
204 _cleanup_free_
char *parent
= NULL
;
210 /* Note that we do *not* have generic checks for /etc or /run in place, since with
211 * them we couldn't discern configuration from transient or generated units */
214 r
= path_extract_directory(path
, &parent
);
221 return PATH_IN_SET(path
,
222 lp
->persistent_config
,
226 static int path_is_runtime(const LookupPaths
*lp
, const char *path
, bool check_parent
) {
227 _cleanup_free_
char *parent
= NULL
;
234 /* Everything in /run is considered runtime. On top of that we also add
235 * explicit checks for the various runtime directories, as safety net. */
237 rpath
= skip_root(lp
->root_dir
, path
);
238 if (rpath
&& path_startswith(rpath
, "/run"))
242 r
= path_extract_directory(path
, &parent
);
249 return PATH_IN_SET(path
,
255 lp
->runtime_control
);
258 static int path_is_vendor_or_generator(const LookupPaths
*lp
, const char *path
) {
264 rpath
= skip_root(lp
->root_dir
, path
);
268 if (path_startswith(rpath
, "/usr"))
271 if (path_is_generator(lp
, rpath
))
274 return path_equal(rpath
, SYSTEM_DATA_UNIT_DIR
);
277 static const char* config_path_from_flags(const LookupPaths
*lp
, UnitFileFlags flags
) {
280 if (FLAGS_SET(flags
, UNIT_FILE_PORTABLE
))
281 return FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
->runtime_attached
: lp
->persistent_attached
;
283 return FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
->runtime_config
: lp
->persistent_config
;
286 InstallChangeType
install_changes_add(
287 InstallChange
**changes
,
289 InstallChangeType type
, /* INSTALL_CHANGE_SYMLINK, _UNLINK, _IS_MASKED, _IS_DANGLING, … if positive or errno if negative */
291 const char *source
) {
293 _cleanup_free_
char *p
= NULL
, *s
= NULL
;
296 assert(!changes
== !n_changes
);
297 assert(INSTALL_CHANGE_TYPE_VALID(type
));
299 /* Message formatting requires <path> to be set. */
302 /* Register a change or error. Note that the return value may be the error
303 * that was passed in, or -ENOMEM generated internally. */
308 if (!GREEDY_REALLOC(*changes
, *n_changes
+ 1))
311 r
= path_simplify_alloc(path
, &p
);
315 r
= path_simplify_alloc(source
, &s
);
319 (*changes
)[(*n_changes
)++] = (InstallChange
) {
322 .source
= TAKE_PTR(s
),
328 void install_changes_free(InstallChange
*changes
, size_t n_changes
) {
329 assert(changes
|| n_changes
== 0);
331 FOREACH_ARRAY(i
, changes
, n_changes
) {
339 static void install_change_dump_success(const InstallChange
*change
) {
341 assert(change
->path
);
343 switch (change
->type
) {
345 case INSTALL_CHANGE_SYMLINK
:
346 return log_info("Created symlink '%s' %s '%s'.",
347 change
->path
, glyph(GLYPH_ARROW_RIGHT
), change
->source
);
349 case INSTALL_CHANGE_UNLINK
:
350 return log_info("Removed '%s'.", change
->path
);
352 case INSTALL_CHANGE_IS_MASKED
:
353 return log_info("Unit %s is masked, ignoring.", change
->path
);
355 case INSTALL_CHANGE_IS_MASKED_GENERATOR
:
356 return log_info("Unit %s is masked via a generator and cannot be unmasked, skipping.", change
->path
);
358 case INSTALL_CHANGE_IS_DANGLING
:
359 return log_info("Unit %s is an alias to a non-existent unit, ignoring.", change
->path
);
361 case INSTALL_CHANGE_DESTINATION_NOT_PRESENT
:
362 return log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
363 change
->source
, change
->path
);
365 case INSTALL_CHANGE_AUXILIARY_FAILED
:
366 return log_warning("Failed to enable auxiliary unit %s, ignoring.", change
->path
);
369 assert_not_reached();
373 int install_change_dump_error(const InstallChange
*change
, char **ret_errmsg
, const char **ret_bus_error
) {
375 const char *bus_error
;
377 /* Returns 0: known error and ret_errmsg formatted
378 * < 0: non-recognizable error */
381 assert(change
->path
);
382 assert(change
->type
< 0);
385 switch (change
->type
) {
388 m
= strjoin("File '", change
->path
, "' already exists",
389 change
->source
? " and is a symlink to " : NULL
, change
->source
);
390 bus_error
= BUS_ERROR_UNIT_EXISTS
;
394 m
= strjoin("Unit ", change
->path
, " is masked");
395 bus_error
= BUS_ERROR_UNIT_MASKED
;
399 m
= strjoin("Unit ", change
->path
, " is transient or generated");
400 bus_error
= BUS_ERROR_UNIT_GENERATED
;
404 m
= strjoin("File '", change
->path
, "' is under the systemd unit hierarchy already");
405 bus_error
= BUS_ERROR_UNIT_BAD_PATH
;
409 m
= strjoin("Invalid specifier in unit ", change
->path
);
410 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
414 m
= strjoin("Refusing to operate on template unit ", change
->source
,
415 " when destination unit ", change
->path
, " is a non-template unit");
416 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
420 m
= strjoin("Invalid unit name ", change
->path
);
421 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
425 m
= strjoin("Refusing to operate on linked unit file ", change
->path
);
426 bus_error
= BUS_ERROR_UNIT_LINKED
;
431 m
= strjoin("Cannot alias ", change
->source
, " as ", change
->path
);
433 m
= strjoin("Invalid unit reference ", change
->path
);
434 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
438 m
= strjoin("Unit ", change
->path
, " does not exist");
439 bus_error
= BUS_ERROR_NO_SUCH_UNIT
;
443 m
= strjoin("Unit ", change
->path
, " is an unresolvable alias");
444 bus_error
= BUS_ERROR_NO_SUCH_UNIT
;
448 m
= strjoin("Cannot resolve specifiers in unit ", change
->path
);
449 bus_error
= BUS_ERROR_BAD_UNIT_SETTING
;
460 *ret_bus_error
= bus_error
;
465 void install_changes_dump(
468 const InstallChange
*changes
,
472 bool err_logged
= false;
475 /* If verb is not specified, errors are not allowed! */
476 assert(verb
|| error
>= 0);
477 assert(changes
|| n_changes
== 0);
479 FOREACH_ARRAY(i
, changes
, n_changes
)
482 install_change_dump_success(i
);
484 _cleanup_free_
char *err_message
= NULL
;
488 r
= install_change_dump_error(i
, &err_message
, /* ret_bus_error = */ NULL
);
490 return (void) log_oom();
492 log_error_errno(r
, "Failed to %s unit %s: %m", verb
, i
->path
);
494 log_error_errno(i
->type
, "Failed to %s unit: %s", verb
, err_message
);
499 if (error
< 0 && !err_logged
)
500 log_error_errno(error
, "Failed to %s unit: %m.", verb
);
504 * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is
505 * concerned. If the target is in the unit search path, then anything with the same name is equivalent.
506 * If outside the unit search path, paths must be identical.
508 static int chroot_unit_symlinks_equivalent(
509 const LookupPaths
*lp
,
511 const char *target_a
,
512 const char *target_b
) {
519 /* This will give incorrect results if the paths are relative and go outside
520 * of the chroot. False negatives are possible. */
522 const char *root
= lp
->root_dir
?: "/";
523 _cleanup_free_
char *dirname
= NULL
;
526 if (!path_is_absolute(target_a
) || !path_is_absolute(target_b
)) {
527 r
= path_extract_directory(src
, &dirname
);
532 _cleanup_free_
char *a
= path_join(path_is_absolute(target_a
) ? root
: dirname
, target_a
);
533 _cleanup_free_
char *b
= path_join(path_is_absolute(target_b
) ? root
: dirname
, target_b
);
537 r
= path_equal_or_inode_same(a
, b
, 0);
541 _cleanup_free_
char *a_name
= NULL
, *b_name
= NULL
;
542 r
= path_extract_filename(a
, &a_name
);
545 r
= path_extract_filename(b
, &b_name
);
549 return streq(a_name
, b_name
) &&
550 path_startswith_strv(a
, lp
->search_path
) &&
551 path_startswith_strv(b
, lp
->search_path
);
554 static int create_symlink(
555 const LookupPaths
*lp
,
556 const char *old_path
,
557 const char *new_path
,
559 InstallChange
**changes
,
562 _cleanup_free_
char *dest
= NULL
;
569 rp
= skip_root(lp
->root_dir
, old_path
);
573 /* Actually create a symlink, and remember that we did. This function is
574 * smart enough to check if there's already a valid symlink in place.
576 * Returns 1 if a symlink was created or already exists and points to the
577 * right place, or negative on error.
580 (void) mkdir_parents_label(new_path
, 0755);
582 if (symlink(old_path
, new_path
) >= 0) {
583 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_SYMLINK
, new_path
, old_path
);
590 return install_changes_add(changes
, n_changes
, -errno
, new_path
, NULL
);
592 r
= readlink_malloc(new_path
, &dest
);
594 /* translate EINVAL (non-symlink exists) to EEXIST */
598 return install_changes_add(changes
, n_changes
, r
, new_path
, NULL
);
601 if (chroot_unit_symlinks_equivalent(lp
, new_path
, dest
, old_path
)) {
602 log_debug("Symlink %s %s %s already exists",
603 new_path
, glyph(GLYPH_ARROW_RIGHT
), dest
);
608 return install_changes_add(changes
, n_changes
, -EEXIST
, new_path
, dest
);
610 r
= symlink_atomic(old_path
, new_path
);
612 return install_changes_add(changes
, n_changes
, r
, new_path
, NULL
);
614 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, new_path
, NULL
);
617 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_SYMLINK
, new_path
, old_path
);
624 static int mark_symlink_for_removal(
625 Set
**remove_symlinks_to
,
633 r
= set_ensure_allocated(remove_symlinks_to
, &path_hash_ops_free
);
637 r
= path_simplify_alloc(p
, &n
);
641 r
= set_consume(*remove_symlinks_to
, n
);
650 static int remove_marked_symlinks_fd(
651 Set
*remove_symlinks_to
,
654 const char *config_path
,
655 const LookupPaths
*lp
,
658 InstallChange
**changes
,
661 _cleanup_closedir_
DIR *d
= NULL
;
664 assert(remove_symlinks_to
);
679 FOREACH_DIRENT(de
, d
, return -errno
)
680 if (de
->d_type
== DT_DIR
) {
681 _cleanup_close_
int nfd
= -EBADF
;
682 _cleanup_free_
char *p
= NULL
;
684 nfd
= RET_NERRNO(openat(fd
, de
->d_name
, O_DIRECTORY
|O_CLOEXEC
|O_NOFOLLOW
));
687 RET_GATHER(ret
, nfd
);
691 p
= path_make_absolute(de
->d_name
, path
);
695 /* This will close nfd, regardless whether it succeeds or not */
696 RET_GATHER(ret
, remove_marked_symlinks_fd(remove_symlinks_to
,
701 changes
, n_changes
));
703 } else if (de
->d_type
== DT_LNK
) {
704 _cleanup_free_
char *p
= NULL
;
707 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
710 p
= path_make_absolute(de
->d_name
, path
);
715 /* We remove all links pointing to a file or path that is marked, as well as all
716 * files sharing the same name as a file that is marked, and files sharing the same
717 * name after the instance has been removed. Do path chasing only if we don't already
718 * know that we want to remove the symlink. */
719 found
= set_contains(remove_symlinks_to
, de
->d_name
);
722 _cleanup_free_
char *template = NULL
;
724 r
= unit_name_template(de
->d_name
, &template);
725 if (r
< 0 && r
!= -EINVAL
)
728 found
= set_contains(remove_symlinks_to
, template);
732 _cleanup_free_
char *dest
= NULL
, *dest_name
= NULL
;
734 r
= chase(p
, lp
->root_dir
, CHASE_NONEXISTENT
, &dest
, NULL
);
738 log_debug_errno(r
, "Failed to resolve symlink \"%s\": %m", p
);
739 RET_GATHER(ret
, install_changes_add(changes
, n_changes
, r
, p
, NULL
));
743 r
= path_extract_filename(dest
, &dest_name
);
747 found
= set_contains(remove_symlinks_to
, dest
) ||
748 set_contains(remove_symlinks_to
, dest_name
);
755 if (unlinkat(fd
, de
->d_name
, 0) < 0 && errno
!= ENOENT
) {
756 RET_GATHER(ret
, install_changes_add(changes
, n_changes
, -errno
, p
, NULL
));
760 (void) rmdir_parents(p
, config_path
);
763 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, p
, NULL
);
767 /* Now, remember the full path (but with the root prefix removed) of
768 * the symlink we just removed, and remove any symlinks to it, too. */
770 const char *rp
= skip_root(lp
->root_dir
, p
);
771 r
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: p
);
774 if (r
> 0 && !dry_run
)
781 static int remove_marked_symlinks(
782 Set
*remove_symlinks_to
,
783 const char *config_path
,
784 const LookupPaths
*lp
,
786 InstallChange
**changes
,
789 _cleanup_close_
int fd
= -EBADF
;
796 if (set_isempty(remove_symlinks_to
))
799 fd
= open(config_path
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
);
801 return errno
== ENOENT
? 0 : -errno
;
807 cfd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
811 /* This takes possession of cfd and closes it */
812 RET_GATHER(r
, remove_marked_symlinks_fd(remove_symlinks_to
,
817 changes
, n_changes
));
823 static int is_symlink_with_known_name(const InstallInfo
*i
, const char *name
) {
829 if (streq(name
, i
->name
))
832 if (strv_contains(i
->aliases
, name
))
835 /* Look for template symlink matching DefaultInstance */
836 if (i
->default_instance
&& unit_name_is_valid(i
->name
, UNIT_NAME_TEMPLATE
)) {
837 _cleanup_free_
char *s
= NULL
;
839 r
= unit_name_replace_instance(i
->name
, i
->default_instance
, &s
);
844 } else if (streq(name
, s
))
851 static int find_symlinks_in_directory(
853 const char *dir_path
,
854 const char *root_dir
,
855 const InstallInfo
*info
,
856 bool ignore_destination
,
858 bool ignore_same_name
,
859 const char *config_path
,
860 bool *same_name_link
) {
867 assert(unit_name_is_valid(info
->name
, UNIT_NAME_ANY
));
869 assert(same_name_link
);
871 FOREACH_DIRENT(de
, dir
, return -errno
) {
872 bool found_path
= false, found_dest
= false, b
= false;
874 if (de
->d_type
!= DT_LNK
)
877 if (!ignore_destination
) {
878 _cleanup_free_
char *dest
= NULL
;
880 /* Acquire symlink destination */
881 r
= readlinkat_malloc(dirfd(dir
), de
->d_name
, &dest
);
888 /* Check if what the symlink points to matches what we are looking for */
889 found_dest
= path_equal_filename(dest
, info
->name
);
892 /* Check if the symlink itself matches what we are looking for.
894 * If ignore_destination is specified, we only look at the source name.
896 * If ignore_same_name is specified, we are in one of the directories which
897 * have lower priority than the unit file, and even if a file or symlink with
898 * this name was found, we should ignore it. */
900 if (ignore_destination
|| !ignore_same_name
)
901 found_path
= streq(de
->d_name
, info
->name
);
903 if (!found_path
&& ignore_destination
) {
904 _cleanup_free_
char *template = NULL
;
906 r
= unit_name_template(de
->d_name
, &template);
907 if (r
< 0 && r
!= -EINVAL
)
910 found_dest
= streq(template, info
->name
);
913 if (found_path
&& found_dest
) {
914 _cleanup_free_
char *p
= NULL
, *t
= NULL
;
916 /* Filter out same name links in the main config path */
917 p
= path_make_absolute(de
->d_name
, dir_path
);
918 t
= path_make_absolute(info
->name
, config_path
);
922 b
= path_equal(p
, t
);
926 *same_name_link
= true;
927 else if (found_path
|| found_dest
) {
931 /* Check if symlink name is in the set of names used by [Install] */
932 r
= is_symlink_with_known_name(info
, de
->d_name
);
941 static int find_symlinks(
942 const char *root_dir
,
943 const InstallInfo
*i
,
945 bool ignore_same_name
,
946 const char *config_path
,
947 bool *same_name_link
) {
949 _cleanup_closedir_
DIR *config_dir
= NULL
;
954 assert(same_name_link
);
956 config_dir
= opendir(config_path
);
958 if (IN_SET(errno
, ENOENT
, ENOTDIR
, EACCES
))
963 FOREACH_DIRENT(de
, config_dir
, return -errno
) {
965 _cleanup_free_
const char *path
= NULL
;
966 _cleanup_closedir_
DIR *d
= NULL
;
968 if (de
->d_type
!= DT_DIR
)
971 suffix
= strrchr(de
->d_name
, '.');
972 if (!STRPTR_IN_SET(suffix
, ".wants", ".requires", ".upholds"))
975 path
= path_join(config_path
, de
->d_name
);
981 log_error_errno(errno
, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path
);
985 r
= find_symlinks_in_directory(d
, path
, root_dir
, i
,
986 /* ignore_destination= */ true,
987 /* match_name= */ match_name
,
988 /* ignore_same_name= */ ignore_same_name
,
994 log_debug_errno(r
, "Failed to look up symlinks in \"%s\": %m", path
);
997 /* We didn't find any suitable symlinks in .wants, .requires or .upholds directories,
998 * let's look for linked unit files in this directory. */
999 rewinddir(config_dir
);
1000 return find_symlinks_in_directory(config_dir
, config_path
, root_dir
, i
,
1001 /* ignore_destination= */ false,
1002 /* match_name= */ match_name
,
1003 /* ignore_same_name= */ ignore_same_name
,
1008 static int find_symlinks_in_scope(
1010 const LookupPaths
*lp
,
1011 const InstallInfo
*info
,
1013 UnitFileState
*state
) {
1015 bool same_name_link_runtime
= false, same_name_link_config
= false;
1016 bool enabled_in_runtime
= false, enabled_at_all
= false;
1017 bool ignore_same_name
= false;
1023 /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name"
1024 * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
1025 * effectively masked, so we should ignore them. */
1027 STRV_FOREACH(p
, lp
->search_path
) {
1028 bool same_name_link
= false;
1030 r
= find_symlinks(lp
->root_dir
, info
, match_name
, ignore_same_name
, *p
, &same_name_link
);
1034 /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
1036 if (path_equal(*p
, lp
->persistent_config
)) {
1037 /* This is the best outcome, let's return it immediately. */
1038 *state
= UNIT_FILE_ENABLED
;
1042 /* look for global enablement of user units */
1043 if (scope
== RUNTIME_SCOPE_USER
&& path_is_user_config_dir(*p
)) {
1044 *state
= UNIT_FILE_ENABLED
;
1048 r
= path_is_runtime(lp
, *p
, false);
1052 enabled_in_runtime
= true;
1054 enabled_at_all
= true;
1056 } else if (same_name_link
) {
1057 if (path_equal(*p
, lp
->persistent_config
))
1058 same_name_link_config
= true;
1060 r
= path_is_runtime(lp
, *p
, false);
1064 same_name_link_runtime
= true;
1068 /* Check if next iteration will be "below" the unit file (either a regular file
1069 * or a symlink), and hence should be ignored */
1070 if (!ignore_same_name
&& path_startswith(info
->path
, *p
))
1071 ignore_same_name
= true;
1074 if (enabled_in_runtime
) {
1075 *state
= UNIT_FILE_ENABLED_RUNTIME
;
1079 /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
1080 * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
1081 * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
1082 * something, and hence are a much stronger concept. */
1083 if (enabled_at_all
&& unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
1084 *state
= UNIT_FILE_STATIC
;
1088 /* Hmm, we didn't find it, but maybe we found the same name
1090 if (same_name_link_config
) {
1091 *state
= UNIT_FILE_LINKED
;
1094 if (same_name_link_runtime
) {
1095 *state
= UNIT_FILE_LINKED_RUNTIME
;
1102 static void install_info_clear(InstallInfo
*i
) {
1106 i
->name
= mfree(i
->name
);
1107 i
->path
= mfree(i
->path
);
1108 i
->root
= mfree(i
->root
);
1109 i
->aliases
= strv_free(i
->aliases
);
1110 i
->wanted_by
= strv_free(i
->wanted_by
);
1111 i
->required_by
= strv_free(i
->required_by
);
1112 i
->upheld_by
= strv_free(i
->upheld_by
);
1113 i
->also
= strv_free(i
->also
);
1114 i
->default_instance
= mfree(i
->default_instance
);
1115 i
->symlink_target
= mfree(i
->symlink_target
);
1118 static InstallInfo
* install_info_free(InstallInfo
*i
) {
1119 install_info_clear(i
);
1123 DEFINE_TRIVIAL_CLEANUP_FUNC(InstallInfo
*, install_info_free
);
1125 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
1126 install_info_hash_ops
,
1127 char, string_hash_func
, string_compare_func
,
1128 InstallInfo
, install_info_free
);
1130 static void install_context_done(InstallContext
*ctx
) {
1133 ctx
->will_process
= ordered_hashmap_free(ctx
->will_process
);
1134 ctx
->have_processed
= ordered_hashmap_free(ctx
->have_processed
);
1137 static InstallInfo
*install_info_find(InstallContext
*ctx
, const char *name
) {
1140 i
= ordered_hashmap_get(ctx
->have_processed
, name
);
1144 return ordered_hashmap_get(ctx
->will_process
, name
);
1147 static int install_info_may_process(
1148 const InstallInfo
*i
,
1149 const LookupPaths
*lp
,
1150 InstallChange
**changes
,
1151 size_t *n_changes
) {
1155 /* Checks whether the loaded unit file is one we should process, or is masked,
1156 * transient or generated and thus not subject to enable/disable operations. */
1158 if (i
->install_mode
== INSTALL_MODE_MASKED
)
1159 return install_changes_add(changes
, n_changes
, -ERFKILL
, i
->path
, NULL
);
1160 if (path_is_generator(lp
, i
->path
) ||
1161 path_is_transient(lp
, i
->path
))
1162 return install_changes_add(changes
, n_changes
, -EADDRNOTAVAIL
, i
->path
, NULL
);
1168 * Adds a new InstallInfo entry under name in the InstallContext.will_process
1169 * hashmap, or retrieves the existing one if already present.
1171 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1173 static int install_info_add(
1174 InstallContext
*ctx
,
1179 InstallInfo
**ret
) {
1181 _cleanup_free_
char *name_alloc
= NULL
;
1189 r
= path_extract_filename(path
, &name_alloc
);
1196 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
1199 InstallInfo
*i
= install_info_find(ctx
, name
);
1201 i
->auxiliary
= i
->auxiliary
&& auxiliary
;
1208 _cleanup_(install_info_freep
) InstallInfo
*new_info
= new(InstallInfo
, 1);
1212 *new_info
= (InstallInfo
) {
1213 .install_mode
= _INSTALL_MODE_INVALID
,
1214 .auxiliary
= auxiliary
,
1218 new_info
->name
= TAKE_PTR(name_alloc
);
1220 new_info
->name
= strdup(name
);
1221 if (!new_info
->name
)
1225 r
= strdup_to(&new_info
->root
, root
);
1229 r
= strdup_to(&new_info
->path
, path
);
1233 r
= ordered_hashmap_ensure_put(&ctx
->will_process
, &install_info_hash_ops
, new_info
->name
, new_info
);
1236 i
= TAKE_PTR(new_info
);
1243 static int config_parse_alias(
1245 const char *filename
,
1247 const char *section
,
1248 unsigned section_line
,
1262 type
= unit_name_to_type(unit
);
1263 if (!unit_type_may_alias(type
))
1264 return log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1265 "Alias= is not allowed for %s units, ignoring.",
1266 unit_type_to_string(type
));
1268 return config_parse_strv(unit
, filename
, line
, section
, section_line
,
1269 lvalue
, ltype
, rvalue
, data
, userdata
);
1272 static int config_parse_also(
1274 const char *filename
,
1276 const char *section
,
1277 unsigned section_line
,
1284 InstallInfo
*info
= ASSERT_PTR(userdata
);
1285 InstallContext
*ctx
= ASSERT_PTR(data
);
1294 _cleanup_free_
char *word
= NULL
, *printed
= NULL
;
1296 r
= extract_first_word(&rvalue
, &word
, NULL
, 0);
1302 r
= install_name_printf(ctx
->scope
, info
, word
, &printed
);
1304 return log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1305 "Failed to resolve unit name in Also=\"%s\": %m", word
);
1307 r
= install_info_add(ctx
, printed
, NULL
, info
->root
, /* auxiliary= */ true, NULL
);
1311 r
= strv_push(&info
->also
, printed
);
1321 static int config_parse_default_instance(
1323 const char *filename
,
1325 const char *section
,
1326 unsigned section_line
,
1333 InstallContext
*ctx
= ASSERT_PTR(data
);
1334 InstallInfo
*info
= ASSERT_PTR(userdata
);
1335 _cleanup_free_
char *printed
= NULL
;
1343 if (unit_name_is_valid(unit
, UNIT_NAME_INSTANCE
))
1344 /* When enabling an instance, we might be using a template unit file,
1345 * but we should ignore DefaultInstance silently. */
1347 if (!unit_name_is_valid(unit
, UNIT_NAME_TEMPLATE
))
1348 return log_syntax(unit
, LOG_WARNING
, filename
, line
, 0,
1349 "DefaultInstance= only makes sense for template units, ignoring.");
1351 r
= install_name_printf(ctx
->scope
, info
, rvalue
, &printed
);
1353 return log_syntax(unit
, LOG_WARNING
, filename
, line
, r
,
1354 "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue
);
1356 if (isempty(printed
))
1357 printed
= mfree(printed
);
1359 if (printed
&& !unit_instance_is_valid(printed
))
1360 return log_syntax(unit
, LOG_WARNING
, filename
, line
, SYNTHETIC_ERRNO(EINVAL
),
1361 "Invalid DefaultInstance= value \"%s\".", printed
);
1363 return free_and_replace(info
->default_instance
, printed
);
1366 static int unit_file_load(
1367 InstallContext
*ctx
,
1370 const char *root_dir
,
1371 SearchFlags flags
) {
1373 const ConfigTableItem items
[] = {
1374 { "Install", "Alias", config_parse_alias
, 0, &info
->aliases
},
1375 { "Install", "WantedBy", config_parse_strv
, 0, &info
->wanted_by
},
1376 { "Install", "RequiredBy", config_parse_strv
, 0, &info
->required_by
},
1377 { "Install", "UpheldBy", config_parse_strv
, 0, &info
->upheld_by
},
1378 { "Install", "DefaultInstance", config_parse_default_instance
, 0, info
},
1379 { "Install", "Also", config_parse_also
, 0, ctx
},
1384 _cleanup_fclose_
FILE *f
= NULL
;
1385 _cleanup_close_
int fd
= -EBADF
;
1392 if (!(flags
& SEARCH_DROPIN
)) {
1393 /* Loading or checking for the main unit file… */
1395 type
= unit_name_to_type(info
->name
);
1398 if (unit_name_is_valid(info
->name
, UNIT_NAME_TEMPLATE
|UNIT_NAME_INSTANCE
) && !unit_type_may_template(type
))
1399 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1400 "%s: unit type %s cannot be templated, ignoring.", path
, unit_type_to_string(type
));
1402 if (!(flags
& SEARCH_LOAD
)) {
1403 if (lstat(path
, &st
) < 0)
1406 if (null_or_empty(&st
))
1407 info
->install_mode
= INSTALL_MODE_MASKED
;
1408 else if (S_ISREG(st
.st_mode
))
1409 info
->install_mode
= INSTALL_MODE_REGULAR
;
1410 else if (S_ISLNK(st
.st_mode
))
1412 else if (S_ISDIR(st
.st_mode
))
1420 fd
= open(path
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
|O_NOFOLLOW
);
1424 /* 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. */
1426 if (!(flags
& SEARCH_LOAD
))
1429 fd
= chase_and_open(path
, root_dir
, 0, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
, NULL
);
1434 if (fstat(fd
, &st
) < 0)
1437 if (null_or_empty(&st
)) {
1438 if ((flags
& SEARCH_DROPIN
) == 0)
1439 info
->install_mode
= INSTALL_MODE_MASKED
;
1444 r
= stat_verify_regular(&st
);
1448 f
= take_fdopen(&fd
, "r");
1452 /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1455 r
= config_parse(info
->name
, path
, f
,
1469 config_item_table_lookup
, items
,
1473 return log_debug_errno(r
, "Failed to parse \"%s\": %m", info
->name
);
1475 if ((flags
& SEARCH_DROPIN
) == 0)
1476 info
->install_mode
= INSTALL_MODE_REGULAR
;
1479 (int) strv_length(info
->aliases
) +
1480 (int) strv_length(info
->wanted_by
) +
1481 (int) strv_length(info
->required_by
) +
1482 (int) strv_length(info
->upheld_by
);
1485 static int unit_file_load_or_readlink(
1486 InstallContext
*ctx
,
1489 const LookupPaths
*lp
,
1490 SearchFlags flags
) {
1493 r
= unit_file_load(ctx
, info
, path
, lp
->root_dir
, flags
);
1494 if (r
!= -ELOOP
|| (flags
& SEARCH_DROPIN
))
1497 /* This is a symlink, let's read and verify it. */
1498 r
= unit_file_resolve_symlink(lp
->root_dir
, lp
->search_path
,
1499 NULL
, AT_FDCWD
, path
,
1500 true, &info
->symlink_target
);
1503 bool outside_search_path
= r
> 0;
1505 r
= null_or_empty_path_with_root(info
->symlink_target
, lp
->root_dir
);
1506 if (r
< 0 && r
!= -ENOENT
)
1507 return log_debug_errno(r
, "Failed to stat %s: %m", info
->symlink_target
);
1509 info
->install_mode
= INSTALL_MODE_MASKED
;
1510 else if (outside_search_path
)
1511 info
->install_mode
= INSTALL_MODE_LINKED
;
1513 info
->install_mode
= INSTALL_MODE_ALIAS
;
1518 static int unit_file_search(
1519 InstallContext
*ctx
,
1521 const LookupPaths
*lp
,
1522 SearchFlags flags
) {
1524 const char *dropin_dir_name
= NULL
, *dropin_template_dir_name
= NULL
;
1525 _cleanup_strv_free_
char **dirs
= NULL
, **files
= NULL
;
1526 _cleanup_free_
char *template = NULL
;
1527 bool found_unit
= false;
1533 /* Was this unit already loaded? */
1534 if (info
->install_mode
!= _INSTALL_MODE_INVALID
)
1538 return unit_file_load_or_readlink(ctx
, info
, info
->path
, lp
, flags
);
1542 if (unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
1543 r
= unit_name_template(info
->name
, &template);
1548 STRV_FOREACH(p
, lp
->search_path
) {
1549 _cleanup_free_
char *path
= NULL
;
1551 path
= path_join(*p
, info
->name
);
1555 r
= unit_file_load_or_readlink(ctx
, info
, path
, lp
, flags
);
1557 info
->path
= TAKE_PTR(path
);
1561 } else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
, -EACCES
))
1565 if (!found_unit
&& template) {
1567 /* Unit file doesn't exist, however instance
1568 * enablement was requested. We will check if it is
1569 * possible to load template unit file. */
1571 STRV_FOREACH(p
, lp
->search_path
) {
1572 _cleanup_free_
char *path
= NULL
;
1574 path
= path_join(*p
, template);
1578 r
= unit_file_load_or_readlink(ctx
, info
, path
, lp
, flags
);
1580 info
->path
= TAKE_PTR(path
);
1584 } else if (!IN_SET(r
, -ENOENT
, -ENOTDIR
, -EACCES
))
1590 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT
),
1591 "Cannot find unit %s%s%s.",
1592 info
->name
, template ? " or " : "", strempty(template));
1594 if (info
->install_mode
== INSTALL_MODE_MASKED
)
1597 /* Search for drop-in directories */
1599 dropin_dir_name
= strjoina(info
->name
, ".d");
1600 STRV_FOREACH(p
, lp
->search_path
) {
1603 path
= path_join(*p
, dropin_dir_name
);
1607 r
= strv_consume(&dirs
, path
);
1613 dropin_template_dir_name
= strjoina(template, ".d");
1614 STRV_FOREACH(p
, lp
->search_path
) {
1617 path
= path_join(*p
, dropin_template_dir_name
);
1621 r
= strv_consume(&dirs
, path
);
1627 /* Load drop-in conf files */
1629 r
= conf_files_list_strv(&files
, ".conf", NULL
, 0, (const char**) dirs
);
1631 return log_debug_errno(r
, "Failed to get list of conf files: %m");
1633 STRV_FOREACH(p
, files
) {
1634 r
= unit_file_load_or_readlink(ctx
, info
, *p
, lp
, flags
| SEARCH_DROPIN
);
1636 return log_debug_errno(r
, "Failed to load conf file \"%s\": %m", *p
);
1642 static int install_info_follow(
1643 InstallContext
*ctx
,
1645 const LookupPaths
*lp
,
1647 bool ignore_different_name
) {
1652 if (!IN_SET(info
->install_mode
, INSTALL_MODE_ALIAS
, INSTALL_MODE_LINKED
))
1654 if (!info
->symlink_target
)
1657 /* If the basename doesn't match, the caller should add a complete new entry for this. */
1659 if (!ignore_different_name
&& !path_equal_filename(info
->symlink_target
, info
->name
))
1662 free_and_replace(info
->path
, info
->symlink_target
);
1663 info
->install_mode
= _INSTALL_MODE_INVALID
;
1665 return unit_file_load_or_readlink(ctx
, info
, info
->path
, lp
, flags
);
1669 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1670 * target, maybe more than once. Propagate the instance name if present.
1672 static int install_info_traverse(
1673 InstallContext
*ctx
,
1674 const LookupPaths
*lp
,
1677 InstallInfo
**ret
) {
1687 r
= unit_file_search(ctx
, start
, lp
, flags
);
1692 while (IN_SET(i
->install_mode
, INSTALL_MODE_ALIAS
, INSTALL_MODE_LINKED
)) {
1693 /* Follow the symlink */
1695 if (++k
> UNIT_FILE_FOLLOW_SYMLINK_MAX
)
1698 if (!FLAGS_SET(flags
, SEARCH_FOLLOW_CONFIG_SYMLINKS
)) {
1699 r
= path_is_config(lp
, i
->path
, true);
1706 r
= install_info_follow(ctx
, i
, lp
, flags
,
1707 /* If linked, don't look at the target name */
1708 /* ignore_different_name= */ i
->install_mode
== INSTALL_MODE_LINKED
);
1709 if (r
== -EXDEV
&& i
->symlink_target
) {
1710 _cleanup_free_
char *target_name
= NULL
, *unit_instance
= NULL
;
1713 /* Target is an alias, create a new install info object and continue with that. */
1715 r
= path_extract_filename(i
->symlink_target
, &target_name
);
1719 if (unit_name_is_valid(i
->name
, UNIT_NAME_INSTANCE
) &&
1720 unit_name_is_valid(target_name
, UNIT_NAME_TEMPLATE
)) {
1722 _cleanup_free_
char *instance
= NULL
;
1724 r
= unit_name_to_instance(i
->name
, &instance
);
1728 r
= unit_name_replace_instance(target_name
, instance
, &unit_instance
);
1732 if (streq(unit_instance
, i
->name
)) {
1733 /* We filled in the instance, and the target stayed the same? If so,
1734 * then let's honour the link as it is. */
1736 r
= install_info_follow(ctx
, i
, lp
, flags
, true);
1747 r
= install_info_add(ctx
, bn
, NULL
, lp
->root_dir
, /* auxiliary= */ false, &i
);
1751 /* Try again, with the new target we found. */
1752 r
= unit_file_search(ctx
, i
, lp
, flags
);
1754 /* Translate error code to highlight this specific case */
1768 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1769 * or the name (otherwise). root_dir is prepended to the path.
1771 static int install_info_add_auto(
1772 InstallContext
*ctx
,
1773 const LookupPaths
*lp
,
1774 const char *name_or_path
,
1775 InstallInfo
**ret
) {
1778 assert(name_or_path
);
1780 if (path_is_absolute(name_or_path
)) {
1781 _cleanup_free_
char *pp
= path_join(lp
->root_dir
, name_or_path
);
1785 return install_info_add(ctx
, NULL
, pp
, lp
->root_dir
, /* auxiliary= */ false, ret
);
1787 return install_info_add(ctx
, name_or_path
, NULL
, lp
->root_dir
, /* auxiliary= */ false, ret
);
1790 static int install_info_discover(
1791 InstallContext
*ctx
,
1792 const LookupPaths
*lp
,
1793 const char *name_or_path
,
1796 InstallChange
**changes
,
1797 size_t *n_changes
) {
1804 assert(name_or_path
);
1806 r
= install_info_add_auto(ctx
, lp
, name_or_path
, &info
);
1808 r
= install_info_traverse(ctx
, lp
, info
, flags
, ret
);
1811 return install_changes_add(changes
, n_changes
, r
, name_or_path
, NULL
);
1816 static int install_info_discover_and_check(
1817 InstallContext
*ctx
,
1818 const LookupPaths
*lp
,
1819 const char *name_or_path
,
1822 InstallChange
**changes
,
1823 size_t *n_changes
) {
1827 r
= install_info_discover(ctx
, lp
, name_or_path
, flags
, ret
, changes
, n_changes
);
1831 return install_info_may_process(ret
? *ret
: NULL
, lp
, changes
, n_changes
);
1834 int unit_file_verify_alias(
1835 const InstallInfo
*info
,
1838 InstallChange
**changes
,
1839 size_t *n_changes
) {
1841 _cleanup_free_
char *dst_updated
= NULL
;
1844 /* Verify that dst is a valid either a valid alias or a valid .wants/.requires symlink for the target
1845 * unit *i. Return negative on error or if not compatible, zero on success.
1847 * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
1848 * inserted into dst. It is not normally set, even on success, so that the caller can easily
1849 * distinguish the case where instance propagation occurred.
1852 * -EXDEV when the alias doesn't match the unit,
1853 * -EUCLEAN when the name is invalid,
1854 * -ELOOP when the alias it to the unit itself.
1857 const char *path_alias
= strrchr(dst
, '/');
1859 /* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
1860 _cleanup_free_
char *dir
= NULL
;
1863 path_alias
++; /* skip over slash */
1865 r
= path_extract_directory(dst
, &dir
);
1867 return log_error_errno(r
, "Failed to extract parent directory from '%s': %m", dst
);
1869 p
= endswith(dir
, ".wants");
1871 p
= endswith(dir
, ".requires");
1873 r
= install_changes_add(changes
, n_changes
, -EXDEV
, dst
, NULL
);
1877 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
), "Invalid path \"%s\" in alias.", dir
);
1880 *p
= '\0'; /* dir should now be a unit name */
1882 UnitNameFlags type
= unit_name_classify(dir
);
1884 r
= install_changes_add(changes
, n_changes
, -EXDEV
, dst
, NULL
);
1887 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
),
1888 "Invalid unit name component \"%s\" in alias.", dir
);
1891 const bool instance_propagation
= type
== UNIT_NAME_TEMPLATE
;
1893 /* That's the name we want to use for verification. */
1894 r
= unit_symlink_name_compatible(path_alias
, info
->name
, instance_propagation
);
1896 return log_error_errno(r
, "Failed to verify alias validity: %m");
1898 r
= install_changes_add(changes
, n_changes
, -EXDEV
, dst
, info
->name
);
1902 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV
),
1903 "Invalid unit \"%s\" symlink \"%s\".",
1908 /* If the symlink target has an instance set and the symlink source doesn't, we "propagate
1909 * the instance", i.e. instantiate the symlink source with the target instance. */
1910 if (unit_name_is_valid(dst
, UNIT_NAME_TEMPLATE
)) {
1911 _cleanup_free_
char *inst
= NULL
;
1913 UnitNameFlags type
= unit_name_to_instance(info
->name
, &inst
);
1915 r
= install_changes_add(changes
, n_changes
, -EUCLEAN
, info
->name
, NULL
);
1918 return log_debug_errno(type
, "Failed to extract instance name from \"%s\": %m", info
->name
);
1921 if (type
== UNIT_NAME_INSTANCE
) {
1922 r
= unit_name_replace_instance(dst
, inst
, &dst_updated
);
1924 return log_error_errno(r
, "Failed to build unit name from %s+%s: %m",
1929 r
= unit_validate_alias_symlink_or_warn(LOG_DEBUG
, dst_updated
?: dst
, info
->name
);
1930 if (r
== -ELOOP
) /* -ELOOP means self-alias, which we (quietly) ignore */
1933 return install_changes_add(changes
, n_changes
,
1934 r
== -EINVAL
? -EXDEV
: r
,
1939 *ret_dst
= TAKE_PTR(dst_updated
);
1943 static int install_info_symlink_alias(
1946 const LookupPaths
*lp
,
1947 const char *config_path
,
1949 InstallChange
**changes
,
1950 size_t *n_changes
) {
1956 assert(config_path
);
1958 STRV_FOREACH(s
, info
->aliases
) {
1959 _cleanup_free_
char *alias_path
= NULL
, *alias_target
= NULL
, *dst
= NULL
, *dst_updated
= NULL
;
1961 r
= install_name_printf(scope
, info
, *s
, &dst
);
1963 RET_GATHER(ret
, install_changes_add(changes
, n_changes
, r
, *s
, NULL
));
1967 r
= unit_file_verify_alias(info
, dst
, &dst_updated
, changes
, n_changes
);
1974 alias_path
= path_make_absolute(dst_updated
?: dst
, config_path
);
1978 r
= in_search_path(lp
, info
->path
);
1982 /* The unit path itself is outside of the search path. To
1983 * correctly apply the alias, we need the alias symlink to
1984 * point to the symlink that was created in the search path. */
1985 alias_target
= path_join(config_path
, info
->name
);
1991 r
= chase(alias_path
, lp
->root_dir
, CHASE_NONEXISTENT
, /* ret_path = */ NULL
, /* ret_fd = */ NULL
);
1992 if (r
< 0 && r
!= -ENOENT
) {
1996 broken
= r
== 0; /* symlink target does not exist? */
1998 r
= create_symlink(lp
, alias_target
?: info
->path
, alias_path
, force
|| broken
, changes
, n_changes
);
1999 if (r
!= 0 && ret
>= 0)
2006 static int install_info_symlink_wants(
2008 UnitFileFlags file_flags
,
2010 const LookupPaths
*lp
,
2011 const char *config_path
,
2014 InstallChange
**changes
,
2015 size_t *n_changes
) {
2017 _cleanup_(install_info_clear
) InstallInfo instance
= {
2018 .install_mode
= _INSTALL_MODE_INVALID
,
2021 UnitNameFlags valid_dst_type
= UNIT_NAME_ANY
;
2027 assert(config_path
);
2029 if (strv_isempty(list
))
2032 if (unit_name_is_valid(info
->name
, UNIT_NAME_PLAIN
| UNIT_NAME_INSTANCE
))
2033 /* Not a template unit. Use the name directly. */
2036 else if (info
->default_instance
) {
2037 /* If this is a template, and we have a default instance, use it. */
2039 r
= unit_name_replace_instance(info
->name
, info
->default_instance
, &instance
.name
);
2043 r
= unit_file_search(NULL
, &instance
, lp
, SEARCH_FOLLOW_CONFIG_SYMLINKS
);
2047 if (instance
.install_mode
== INSTALL_MODE_MASKED
)
2048 return install_changes_add(changes
, n_changes
, -ERFKILL
, instance
.path
, NULL
);
2053 /* We have a template, but no instance yet. When used with an instantiated unit, we will get
2054 * the instance from that unit. Cannot be used with non-instance units. */
2056 valid_dst_type
= UNIT_NAME_INSTANCE
| UNIT_NAME_TEMPLATE
;
2061 STRV_FOREACH(s
, list
) {
2062 _cleanup_free_
char *path
= NULL
, *dst
= NULL
;
2064 q
= install_name_printf(scope
, info
, *s
, &dst
);
2066 RET_GATHER(r
, install_changes_add(changes
, n_changes
, q
, *s
, NULL
));
2070 if (!unit_name_is_valid(dst
, valid_dst_type
)) {
2071 /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the
2072 * template status doesn't match. If we are doing presets don't bother reporting the
2073 * error. This also covers cases like 'systemctl preset serial-getty@.service', which
2074 * has no DefaultInstance, so there is nothing we can do. At the same time,
2075 * 'systemctl enable serial-getty@.service' should fail, the user should specify an
2076 * instance like in 'systemctl enable serial-getty@ttyS0.service'.
2078 if (FLAGS_SET(file_flags
, UNIT_FILE_IGNORE_AUXILIARY_FAILURE
))
2081 if (unit_name_is_valid(dst
, UNIT_NAME_ANY
))
2082 RET_GATHER(r
, install_changes_add(changes
, n_changes
, -EIDRM
, dst
, n
));
2084 RET_GATHER(r
, install_changes_add(changes
, n_changes
, -EUCLEAN
, dst
, NULL
));
2089 path
= strjoin(config_path
, "/", dst
, suffix
, n
);
2093 q
= create_symlink(lp
, info
->path
, path
, /* force = */ true, changes
, n_changes
);
2094 if (q
!= 0 && r
>= 0)
2097 if (unit_file_exists(scope
, lp
, dst
) == 0) {
2098 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_DESTINATION_NOT_PRESENT
, dst
, info
->path
);
2107 static int install_info_symlink_link(
2109 const LookupPaths
*lp
,
2110 const char *config_path
,
2112 InstallChange
**changes
,
2113 size_t *n_changes
) {
2115 _cleanup_free_
char *path
= NULL
;
2120 assert(config_path
);
2123 r
= in_search_path(lp
, info
->path
);
2129 path
= path_join(config_path
, info
->name
);
2133 return create_symlink(lp
, info
->path
, path
, force
, changes
, n_changes
);
2136 static int install_info_apply(
2138 UnitFileFlags file_flags
,
2140 const LookupPaths
*lp
,
2141 const char *config_path
,
2142 InstallChange
**changes
,
2143 size_t *n_changes
) {
2149 assert(config_path
);
2151 if (info
->install_mode
!= INSTALL_MODE_REGULAR
)
2154 bool force
= file_flags
& UNIT_FILE_FORCE
;
2156 r
= install_info_symlink_link(info
, lp
, config_path
, force
, changes
, n_changes
);
2157 /* Do not count links to the unit file towards the "carries_install_info" count */
2159 /* If linking of the file failed, do not try to create other symlinks,
2160 * because they might would pointing to a non-existent or wrong unit. */
2163 r
= install_info_symlink_alias(scope
, info
, lp
, config_path
, force
, changes
, n_changes
);
2165 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->wanted_by
, ".wants/", changes
, n_changes
);
2166 if (q
!= 0 && r
>= 0)
2169 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->required_by
, ".requires/", changes
, n_changes
);
2170 if (q
!= 0 && r
>= 0)
2173 q
= install_info_symlink_wants(scope
, file_flags
, info
, lp
, config_path
, info
->upheld_by
, ".upholds/", changes
, n_changes
);
2174 if (q
!= 0 && r
>= 0)
2180 static int install_context_apply(
2181 InstallContext
*ctx
,
2182 const LookupPaths
*lp
,
2183 UnitFileFlags file_flags
,
2184 const char *config_path
,
2186 InstallChange
**changes
,
2187 size_t *n_changes
) {
2194 assert(config_path
);
2196 if (ordered_hashmap_isempty(ctx
->will_process
))
2199 r
= ordered_hashmap_ensure_allocated(&ctx
->have_processed
, &install_info_hash_ops
);
2204 while ((i
= ordered_hashmap_first(ctx
->will_process
))) {
2207 q
= ordered_hashmap_move_one(ctx
->have_processed
, ctx
->will_process
, i
->name
);
2211 q
= install_info_traverse(ctx
, lp
, i
, flags
, NULL
);
2214 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_AUXILIARY_FAILED
, i
->name
, NULL
);
2220 return install_changes_add(changes
, n_changes
, q
, i
->name
, NULL
);
2223 /* We can attempt to process a masked unit when a different unit
2224 * that we were processing specifies it in Also=. */
2225 if (i
->install_mode
== INSTALL_MODE_MASKED
) {
2226 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_MASKED
, i
->path
, NULL
);
2230 /* Assume that something *could* have been enabled here,
2231 * avoid "empty [Install] section" warning. */
2236 if (i
->install_mode
!= INSTALL_MODE_REGULAR
)
2239 q
= install_info_apply(ctx
->scope
, file_flags
, i
, lp
, config_path
, changes
, n_changes
);
2251 static int install_context_mark_for_removal(
2252 InstallContext
*ctx
,
2253 const LookupPaths
*lp
,
2254 Set
**remove_symlinks_to
,
2255 const char *config_path
,
2256 InstallChange
**changes
,
2257 size_t *n_changes
) {
2264 assert(config_path
);
2266 /* Marks all items for removal */
2268 if (ordered_hashmap_isempty(ctx
->will_process
))
2271 r
= ordered_hashmap_ensure_allocated(&ctx
->have_processed
, &install_info_hash_ops
);
2275 while ((i
= ordered_hashmap_first(ctx
->will_process
))) {
2277 r
= ordered_hashmap_move_one(ctx
->have_processed
, ctx
->will_process
, i
->name
);
2281 r
= install_info_traverse(ctx
, lp
, i
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, NULL
);
2282 if (r
== -ENOLINK
) {
2283 log_debug_errno(r
, "Name %s leads to a dangling symlink, removing name.", i
->name
);
2284 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_DANGLING
, i
->path
?: i
->name
, NULL
);
2287 } else if (r
== -ENOENT
) {
2288 if (i
->auxiliary
) /* some unit specified in Also= or similar is missing */
2289 log_debug_errno(r
, "Auxiliary unit of %s not found, removing name.", i
->name
);
2291 log_debug_errno(r
, "Unit %s not found, removing name.", i
->name
);
2292 r
= install_changes_add(changes
, n_changes
, r
, i
->path
?: i
->name
, NULL
);
2293 /* In case there's no unit, we still want to remove any leftover symlink, even if
2294 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2299 log_debug_errno(r
, "Failed to find unit %s, removing name: %m", i
->name
);
2300 int k
= install_changes_add(changes
, n_changes
, r
, i
->path
?: i
->name
, NULL
);
2303 } else if (i
->install_mode
== INSTALL_MODE_MASKED
) {
2304 log_debug("Unit file %s is masked, ignoring.", i
->name
);
2305 r
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_IS_MASKED
, i
->path
?: i
->name
, NULL
);
2309 } else if (i
->install_mode
!= INSTALL_MODE_REGULAR
) {
2310 log_debug("Unit %s has install mode %s, ignoring.",
2311 i
->name
, install_mode_to_string(i
->install_mode
) ?: "invalid");
2315 r
= mark_symlink_for_removal(remove_symlinks_to
, i
->name
);
2325 UnitFileFlags flags
,
2326 const char *root_dir
,
2327 char * const *names
,
2328 InstallChange
**changes
,
2329 size_t *n_changes
) {
2331 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2332 const char *config_path
;
2336 assert(scope
< _RUNTIME_SCOPE_MAX
);
2338 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2342 config_path
= (flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2348 STRV_FOREACH(name
, names
) {
2349 _cleanup_free_
char *path
= NULL
;
2351 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
)) {
2352 RET_GATHER(r
, -EINVAL
);
2356 path
= path_make_absolute(*name
, config_path
);
2360 RET_GATHER(r
, create_symlink(&lp
, "/dev/null", path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
));
2366 int unit_file_unmask(
2368 UnitFileFlags flags
,
2369 const char *root_dir
,
2370 char * const *names
,
2371 InstallChange
**changes
,
2372 size_t *n_changes
) {
2374 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2375 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2376 _cleanup_strv_free_
char **todo
= NULL
;
2377 const char *config_path
;
2382 assert(scope
< _RUNTIME_SCOPE_MAX
);
2384 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2388 config_path
= (flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2392 bool dry_run
= flags
& UNIT_FILE_DRY_RUN
;
2394 STRV_FOREACH(name
, names
) {
2395 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2398 /* If root_dir is set, we don't care about kernel command line or generators.
2399 * But if it is not set, we need to check for interference. */
2401 _cleanup_(install_info_clear
) InstallInfo info
= {
2402 .name
= *name
, /* We borrow *name temporarily… */
2403 .install_mode
= _INSTALL_MODE_INVALID
,
2406 r
= unit_file_search(NULL
, &info
, &lp
, 0);
2409 log_debug_errno(r
, "Failed to look up unit %s, ignoring: %m", info
.name
);
2410 } else if (info
.install_mode
== INSTALL_MODE_MASKED
&&
2411 path_is_generator(&lp
, info
.path
)) {
2412 r
= install_changes_add(changes
, n_changes
,
2413 INSTALL_CHANGE_IS_MASKED_GENERATOR
, info
.name
, info
.path
);
2418 TAKE_PTR(info
.name
); /* … and give it back here */
2421 _cleanup_free_
char *path
= path_make_absolute(*name
, config_path
);
2425 r
= null_or_empty_path(path
);
2433 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2436 todo
[n_todo
] = strdup(*name
);
2446 STRV_FOREACH(i
, todo
) {
2447 _cleanup_free_
char *path
= NULL
;
2450 path
= path_make_absolute(*i
, config_path
);
2454 if (!dry_run
&& unlink(path
) < 0) {
2455 if (errno
!= ENOENT
)
2456 RET_GATHER(r
, install_changes_add(changes
, n_changes
, -errno
, path
, NULL
));
2461 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, path
, NULL
);
2465 rp
= skip_root(lp
.root_dir
, path
);
2466 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: path
);
2471 RET_GATHER(r
, remove_marked_symlinks(remove_symlinks_to
, config_path
, &lp
, dry_run
, changes
, n_changes
));
2478 UnitFileFlags flags
,
2479 const char *root_dir
,
2480 char * const *files
,
2481 InstallChange
**changes
,
2482 size_t *n_changes
) {
2484 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2485 _cleanup_ordered_hashmap_free_ OrderedHashmap
*todo
= NULL
;
2486 const char *config_path
;
2490 assert(scope
< _RUNTIME_SCOPE_MAX
);
2494 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2498 config_path
= FLAGS_SET(flags
, UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2502 STRV_FOREACH(file
, files
) {
2503 _cleanup_free_
char *fn
= NULL
, *path
= NULL
, *full
= NULL
;
2505 if (ordered_hashmap_contains(todo
, *file
))
2508 if (!path_is_absolute(*file
))
2509 return install_changes_add(changes
, n_changes
, -EINVAL
, *file
, NULL
);
2511 r
= path_extract_filename(*file
, &fn
);
2513 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2515 if (!unit_name_is_valid(fn
, UNIT_NAME_ANY
))
2516 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *file
, NULL
);
2518 full
= path_join(lp
.root_dir
, *file
);
2522 r
= verify_regular_at(AT_FDCWD
, full
, /* follow = */ false);
2524 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2526 r
= in_search_path(&lp
, *file
);
2528 return install_changes_add(changes
, n_changes
, r
, *file
, NULL
);
2530 /* A silent noop if the file is already in the search path. */
2533 if (underneath_search_path(&lp
, *file
))
2534 return install_changes_add(changes
, n_changes
, -ETXTBSY
, *file
, NULL
);
2536 path
= strdup(*file
);
2540 r
= ordered_hashmap_ensure_put(&todo
, &path_hash_ops_free_free
, path
, fn
);
2551 const char *fn
, *path
;
2552 ORDERED_HASHMAP_FOREACH_KEY(fn
, path
, todo
) {
2553 _cleanup_free_
char *new_path
= NULL
;
2555 new_path
= path_make_absolute(fn
, config_path
);
2559 RET_GATHER(r
, create_symlink(&lp
, path
, new_path
, FLAGS_SET(flags
, UNIT_FILE_FORCE
), changes
, n_changes
));
2565 static int path_shall_revert(const LookupPaths
*lp
, const char *path
) {
2571 /* Checks whether the path is one where the drop-in directories shall be removed. */
2573 r
= path_is_config(lp
, path
, true);
2577 r
= path_is_control(lp
, path
);
2581 return path_is_transient(lp
, path
);
2584 int unit_file_revert(
2586 const char *root_dir
,
2587 char * const *names
,
2588 InstallChange
**changes
,
2589 size_t *n_changes
) {
2591 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2592 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2593 _cleanup_strv_free_
char **todo
= NULL
;
2597 /* Puts a unit file back into vendor state. This means:
2599 * a) we remove all drop-in snippets added by the user ("config"), add to transient units
2600 * ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
2601 * generated ("generated").
2603 * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
2604 * (i.e. in "config", but not in "transient" or "control" or even "generated").
2606 * We remove all that in both the runtime and the persistent directories, if that applies.
2609 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2613 STRV_FOREACH(name
, names
) {
2614 bool has_vendor
= false;
2616 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2619 STRV_FOREACH(p
, lp
.search_path
) {
2620 _cleanup_free_
char *path
= NULL
, *dropin
= NULL
;
2623 path
= path_make_absolute(*name
, *p
);
2627 r
= RET_NERRNO(lstat(path
, &st
));
2630 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2631 } else if (S_ISREG(st
.st_mode
)) {
2632 /* Check if there's a vendor version */
2633 r
= path_is_vendor_or_generator(&lp
, path
);
2635 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2640 dropin
= strjoin(path
, ".d");
2644 r
= RET_NERRNO(lstat(dropin
, &st
));
2647 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2648 } else if (S_ISDIR(st
.st_mode
)) {
2649 /* Remove the drop-ins */
2650 r
= path_shall_revert(&lp
, dropin
);
2652 return install_changes_add(changes
, n_changes
, r
, dropin
, NULL
);
2654 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2657 todo
[n_todo
++] = TAKE_PTR(dropin
);
2665 /* OK, there's a vendor version, hence drop all configuration versions */
2666 STRV_FOREACH(p
, lp
.search_path
) {
2667 _cleanup_free_
char *path
= NULL
;
2670 path
= path_make_absolute(*name
, *p
);
2674 r
= RET_NERRNO(lstat(path
, &st
));
2677 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2678 } else if (S_ISREG(st
.st_mode
) || S_ISLNK(st
.st_mode
)) {
2679 r
= path_is_config(&lp
, path
, true);
2681 return install_changes_add(changes
, n_changes
, r
, path
, NULL
);
2683 if (!GREEDY_REALLOC0(todo
, n_todo
+ 2))
2686 todo
[n_todo
++] = TAKE_PTR(path
);
2695 STRV_FOREACH(i
, todo
) {
2696 _cleanup_strv_free_
char **fs
= NULL
;
2699 (void) get_files_in_directory(*i
, &fs
);
2701 q
= rm_rf(*i
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
2702 if (q
< 0 && q
!= -ENOENT
&& r
>= 0) {
2707 STRV_FOREACH(j
, fs
) {
2708 _cleanup_free_
char *t
= NULL
;
2710 t
= path_join(*i
, *j
);
2714 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, t
, NULL
);
2719 q
= install_changes_add(changes
, n_changes
, INSTALL_CHANGE_UNLINK
, *i
, NULL
);
2723 rp
= skip_root(lp
.root_dir
, *i
);
2724 q
= mark_symlink_for_removal(&remove_symlinks_to
, rp
?: *i
);
2729 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.runtime_config
, &lp
, false, changes
, n_changes
);
2733 q
= remove_marked_symlinks(remove_symlinks_to
, lp
.persistent_config
, &lp
, false, changes
, n_changes
);
2740 int unit_file_add_dependency(
2742 UnitFileFlags file_flags
,
2743 const char *root_dir
,
2744 char * const *names
,
2747 InstallChange
**changes
,
2748 size_t *n_changes
) {
2750 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2751 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2752 InstallInfo
*info
, *target_info
;
2753 const char *config_path
;
2757 assert(scope
< _RUNTIME_SCOPE_MAX
);
2759 assert(IN_SET(dep
, UNIT_WANTS
, UNIT_REQUIRES
));
2761 if (!unit_name_is_valid(target
, UNIT_NAME_ANY
))
2762 return install_changes_add(changes
, n_changes
, -EUCLEAN
, target
, NULL
);
2764 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2768 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
2772 r
= install_info_discover_and_check(&ctx
, &lp
, target
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2773 &target_info
, changes
, n_changes
);
2777 assert(target_info
->install_mode
== INSTALL_MODE_REGULAR
);
2779 STRV_FOREACH(name
, names
) {
2782 r
= install_info_discover_and_check(&ctx
, &lp
, *name
,
2783 SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2784 &info
, changes
, n_changes
);
2788 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2790 /* We didn't actually load anything from the unit
2791 * file, but instead just add in our new symlink to
2794 if (dep
== UNIT_WANTS
)
2795 l
= &info
->wanted_by
;
2796 else if (dep
== UNIT_REQUIRES
)
2797 l
= &info
->required_by
;
2799 l
= &info
->upheld_by
;
2802 *l
= strv_new(target_info
->name
);
2807 return install_context_apply(&ctx
, &lp
, file_flags
, config_path
,
2808 SEARCH_FOLLOW_CONFIG_SYMLINKS
, changes
, n_changes
);
2811 static int do_unit_file_enable(
2812 const LookupPaths
*lp
,
2814 UnitFileFlags flags
,
2815 const char *config_path
,
2816 char * const *names_or_paths
,
2817 InstallChange
**changes
,
2818 size_t *n_changes
) {
2820 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2824 STRV_FOREACH(name
, names_or_paths
) {
2825 r
= install_info_discover_and_check(&ctx
, lp
, *name
,
2826 SEARCH_LOAD
| SEARCH_FOLLOW_CONFIG_SYMLINKS
,
2827 &info
, changes
, n_changes
);
2831 assert(info
->install_mode
== INSTALL_MODE_REGULAR
);
2834 /* This will return the number of symlink rules that were
2835 supposed to be created, not the ones actually created. This
2836 is useful to determine whether the passed units had any
2837 installation data at all. */
2839 return install_context_apply(&ctx
, lp
, flags
, config_path
,
2840 SEARCH_LOAD
, changes
, n_changes
);
2843 int unit_file_enable(
2845 UnitFileFlags flags
,
2846 const char *root_dir
,
2847 char * const *names_or_paths
,
2848 InstallChange
**changes
,
2849 size_t *n_changes
) {
2851 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2855 assert(scope
< _RUNTIME_SCOPE_MAX
);
2857 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2861 const char *config_path
= config_path_from_flags(&lp
, flags
);
2865 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, names_or_paths
, changes
, n_changes
);
2868 static int do_unit_file_disable(
2869 const LookupPaths
*lp
,
2871 UnitFileFlags flags
,
2872 const char *config_path
,
2873 char * const *names
,
2874 InstallChange
**changes
,
2875 size_t *n_changes
) {
2877 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2878 bool has_install_info
= false;
2881 STRV_FOREACH(name
, names
) {
2884 if (!unit_name_is_valid(*name
, UNIT_NAME_ANY
))
2885 return install_changes_add(changes
, n_changes
, -EUCLEAN
, *name
, NULL
);
2887 r
= install_info_add(&ctx
, *name
, NULL
, lp
->root_dir
, /* auxiliary= */ false, &info
);
2889 r
= install_info_traverse(&ctx
, lp
, info
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, NULL
);
2891 r
= install_changes_add(changes
, n_changes
, r
, *name
, NULL
);
2892 /* In case there's no unit, we still want to remove any leftover symlink, even if
2893 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2898 /* If we enable multiple units, some with install info and others without,
2899 * the "empty [Install] section" warning is not shown. Let's make the behavior
2900 * of disable align with that. */
2901 has_install_info
= has_install_info
|| install_info_has_rules(info
) || install_info_has_also(info
);
2904 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
2905 r
= install_context_mark_for_removal(&ctx
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
2907 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, flags
& UNIT_FILE_DRY_RUN
, changes
, n_changes
);
2911 /* The warning is shown only if it's a no-op */
2912 return install_changes_have_modification(*changes
, *n_changes
) || has_install_info
;
2915 int unit_file_disable(
2917 UnitFileFlags flags
,
2918 const char *root_dir
,
2919 char * const *files
,
2920 InstallChange
**changes
,
2921 size_t *n_changes
) {
2923 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
2927 assert(scope
< _RUNTIME_SCOPE_MAX
);
2929 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
2933 const char *config_path
= config_path_from_flags(&lp
, flags
);
2937 return do_unit_file_disable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
2940 static int normalize_linked_files(
2942 const LookupPaths
*lp
,
2943 char * const *names_or_paths
,
2945 char ***ret_files
) {
2947 /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/,
2948 * but operates on real unit names. For each argument we look up the actual path
2949 * where the unit is found. This way linked units can be re-enabled successfully. */
2951 _cleanup_strv_free_
char **files
= NULL
, **names
= NULL
;
2954 STRV_FOREACH(a
, names_or_paths
) {
2955 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
2956 InstallInfo
*i
= NULL
;
2957 _cleanup_free_
char *n
= NULL
;
2959 r
= path_extract_filename(*a
, &n
);
2962 if (r
== O_DIRECTORY
)
2963 return log_debug_errno(SYNTHETIC_ERRNO(EISDIR
),
2964 "Unexpected path to a directory \"%s\", refusing.", *a
);
2966 if (!is_path(*a
) && !unit_name_is_valid(*a
, UNIT_NAME_INSTANCE
)) {
2967 r
= install_info_discover(&ctx
, lp
, n
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
, &i
, NULL
, NULL
);
2969 log_debug_errno(r
, "Failed to discover unit \"%s\", operating on name: %m", n
);
2972 r
= strv_consume(&names
, TAKE_PTR(n
));
2976 const char *p
= NULL
;
2977 if (i
&& i
->path
&& i
->root
)
2978 /* Use startswith here, because we know that paths are normalized, and
2979 * path_startswith() would give us a relative path, but we need an absolute path
2980 * relative to i->root.
2982 * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
2983 * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
2984 * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
2986 p
= startswith(i
->path
, i
->root
);
2988 r
= strv_extend(&files
, p
?: *a
);
2993 *ret_names
= TAKE_PTR(names
);
2994 *ret_files
= TAKE_PTR(files
);
2998 int unit_file_reenable(
3000 UnitFileFlags flags
,
3001 const char *root_dir
,
3002 char * const *names_or_paths
,
3003 InstallChange
**changes
,
3004 size_t *n_changes
) {
3006 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3007 _cleanup_strv_free_
char **names
= NULL
, **files
= NULL
;
3011 assert(scope
< _RUNTIME_SCOPE_MAX
);
3013 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3017 const char *config_path
= config_path_from_flags(&lp
, flags
);
3021 r
= normalize_linked_files(scope
, &lp
, names_or_paths
, &names
, &files
);
3025 /* First, we invoke the disable command with only the basename... */
3026 r
= do_unit_file_disable(&lp
, scope
, flags
, config_path
, names
, changes
, n_changes
);
3030 /* But the enable command with the full name */
3031 return do_unit_file_enable(&lp
, scope
, flags
, config_path
, files
, changes
, n_changes
);
3034 int unit_file_set_default(
3036 UnitFileFlags flags
,
3037 const char *root_dir
,
3039 InstallChange
**changes
,
3040 size_t *n_changes
) {
3042 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3043 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3045 const char *new_path
;
3049 assert(scope
< _RUNTIME_SCOPE_MAX
);
3052 if (unit_name_to_type(name
) != UNIT_TARGET
) /* this also validates the name */
3054 if (streq(name
, SPECIAL_DEFAULT_TARGET
))
3057 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3061 r
= install_info_discover_and_check(&ctx
, &lp
, name
, 0, &info
, changes
, n_changes
);
3065 new_path
= strjoina(lp
.persistent_config
, "/" SPECIAL_DEFAULT_TARGET
);
3066 return create_symlink(&lp
, info
->path
, new_path
, flags
& UNIT_FILE_FORCE
, changes
, n_changes
);
3069 int unit_file_get_default(
3071 const char *root_dir
,
3074 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3075 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3080 assert(scope
< _RUNTIME_SCOPE_MAX
);
3083 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3087 r
= install_info_discover(&ctx
, &lp
, SPECIAL_DEFAULT_TARGET
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3092 return strdup_to(ret
, info
->name
);
3095 int unit_file_lookup_state(
3097 const LookupPaths
*lp
,
3099 UnitFileState
*ret
) {
3101 _cleanup_(install_context_done
) InstallContext ctx
= { .scope
= scope
};
3103 UnitFileState state
;
3109 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3112 r
= install_info_discover(&ctx
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3115 return log_debug_errno(r
, "Failed to discover unit %s: %m", name
);
3117 assert(IN_SET(info
->install_mode
, INSTALL_MODE_REGULAR
, INSTALL_MODE_MASKED
));
3118 log_debug("Found unit %s at %s (%s)", name
, strna(info
->path
),
3119 info
->install_mode
== INSTALL_MODE_REGULAR
? "regular file" : "mask");
3121 /* Shortcut things, if the caller just wants to know if this unit exists. */
3125 switch (info
->install_mode
) {
3127 case INSTALL_MODE_MASKED
:
3128 r
= path_is_runtime(lp
, info
->path
, true);
3132 state
= r
> 0 ? UNIT_FILE_MASKED_RUNTIME
: UNIT_FILE_MASKED
;
3135 case INSTALL_MODE_REGULAR
:
3136 /* Check if the name we were querying is actually an alias */
3137 if (!path_equal_filename(name
, info
->path
) && !unit_name_is_valid(info
->name
, UNIT_NAME_INSTANCE
)) {
3138 state
= UNIT_FILE_ALIAS
;
3142 r
= path_is_generator(lp
, info
->path
);
3146 state
= UNIT_FILE_GENERATED
;
3150 r
= path_is_transient(lp
, info
->path
);
3154 state
= UNIT_FILE_TRANSIENT
;
3158 /* Check if any of the Alias= symlinks have been created.
3159 * We ignore other aliases, and only check those that would
3160 * be created by systemctl enable for this unit. */
3161 r
= find_symlinks_in_scope(scope
, lp
, info
, true, &state
);
3167 /* Check if the file is known under other names. If it is,
3168 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
3169 r
= find_symlinks_in_scope(scope
, lp
, info
, false, &state
);
3173 state
= UNIT_FILE_INDIRECT
;
3175 if (install_info_has_rules(info
))
3176 state
= UNIT_FILE_DISABLED
;
3177 else if (install_info_has_also(info
))
3178 state
= UNIT_FILE_INDIRECT
;
3180 state
= UNIT_FILE_STATIC
;
3186 assert_not_reached();
3193 int unit_file_get_state(
3195 const char *root_dir
,
3197 UnitFileState
*ret
) {
3199 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3203 assert(scope
< _RUNTIME_SCOPE_MAX
);
3206 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3210 return unit_file_lookup_state(scope
, &lp
, name
, ret
);
3213 int unit_file_exists_full(RuntimeScope scope
, const LookupPaths
*lp
, const char *name
, char **ret_path
) {
3214 _cleanup_(install_context_done
) InstallContext c
= {
3222 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3225 InstallInfo
*info
= NULL
;
3226 r
= install_info_discover(
3231 ret_path
? &info
: NULL
,
3232 /* changes= */ NULL
,
3233 /* n_changes= */ NULL
);
3245 r
= strdup_to(ret_path
, info
->path
);
3253 static int split_pattern_into_name_and_instances(const char *pattern
, char **out_unit_name
, char ***out_instances
) {
3254 _cleanup_strv_free_
char **instances
= NULL
;
3255 _cleanup_free_
char *unit_name
= NULL
;
3259 assert(out_instances
);
3260 assert(out_unit_name
);
3262 r
= extract_first_word(&pattern
, &unit_name
, NULL
, EXTRACT_RETAIN_ESCAPE
);
3266 /* We handle the instances logic when unit name is extracted */
3268 /* We only create instances when a rule of templated unit
3269 * is seen. A rule like enable foo@.service a b c will
3270 * result in an array of (a, b, c) as instance names */
3271 if (!unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
))
3274 instances
= strv_split(pattern
, WHITESPACE
);
3278 *out_instances
= TAKE_PTR(instances
);
3281 *out_unit_name
= TAKE_PTR(unit_name
);
3286 static int presets_find_config(RuntimeScope scope
, const char *root_dir
, char ***files
) {
3287 static const char* const initrd_dirs
[] = { CONF_PATHS("systemd/initrd-preset"), NULL
};
3288 static const char* const system_dirs
[] = { CONF_PATHS("systemd/system-preset"), NULL
};
3289 static const char* const user_dirs
[] = { CONF_PATHS("systemd/user-preset"), NULL
};
3290 const char* const* dirs
;
3294 assert(scope
< _RUNTIME_SCOPE_MAX
);
3296 if (scope
== RUNTIME_SCOPE_SYSTEM
) {
3297 r
= chase_and_access("/etc/initrd-release", root_dir
, CHASE_PREFIX_ROOT
, F_OK
, /* ret_path= */ NULL
);
3298 if (r
< 0 && r
!= -ENOENT
)
3301 dirs
= r
>= 0 ? initrd_dirs
: system_dirs
;
3302 } else if (IN_SET(scope
, RUNTIME_SCOPE_GLOBAL
, RUNTIME_SCOPE_USER
))
3305 assert_not_reached();
3307 return conf_files_list_strv(files
, ".preset", root_dir
, 0, dirs
);
3310 static int read_presets(RuntimeScope scope
, const char *root_dir
, UnitFilePresets
*presets
) {
3311 _cleanup_(unit_file_presets_done
) UnitFilePresets ps
= {};
3312 _cleanup_strv_free_
char **files
= NULL
;
3316 assert(scope
< _RUNTIME_SCOPE_MAX
);
3319 r
= presets_find_config(scope
, root_dir
, &files
);
3323 STRV_FOREACH(p
, files
) {
3324 _cleanup_fclose_
FILE *f
= NULL
;
3327 f
= fopen(*p
, "re");
3329 if (errno
== ENOENT
)
3336 _cleanup_free_
char *line
= NULL
;
3337 _cleanup_(unit_file_preset_rule_done
) UnitFilePresetRule rule
= {};
3338 const char *parameter
;
3340 r
= read_stripped_line(f
, LONG_LINE_MAX
, &line
);
3350 if (strchr(COMMENTS
, line
[0]))
3353 parameter
= first_word(line
, "enable");
3356 char **instances
= NULL
;
3358 /* Unit_name will remain the same as parameter when no instances are specified */
3359 r
= split_pattern_into_name_and_instances(parameter
, &unit_name
, &instances
);
3361 log_syntax(NULL
, LOG_WARNING
, *p
, n
, r
, "Couldn't parse line '%s'. Ignoring.", line
);
3365 rule
= (UnitFilePresetRule
) {
3366 .pattern
= unit_name
,
3367 .action
= PRESET_ENABLE
,
3368 .instances
= instances
,
3372 parameter
= first_word(line
, "disable");
3376 pattern
= strdup(parameter
);
3380 rule
= (UnitFilePresetRule
) {
3382 .action
= PRESET_DISABLE
,
3386 parameter
= first_word(line
, "ignore");
3390 pattern
= strdup(parameter
);
3394 rule
= (UnitFilePresetRule
) {
3396 .action
= PRESET_IGNORE
,
3401 if (!GREEDY_REALLOC(ps
.rules
, ps
.n_rules
+ 1))
3404 ps
.rules
[ps
.n_rules
++] = TAKE_STRUCT(rule
);
3408 log_syntax(NULL
, LOG_WARNING
, *p
, n
, 0, "Couldn't parse line '%s'. Ignoring.", line
);
3412 ps
.initialized
= true;
3413 *presets
= TAKE_STRUCT(ps
);
3418 static int pattern_match_multiple_instances(
3419 const UnitFilePresetRule rule
,
3420 const char *unit_name
,
3423 _cleanup_free_
char *templated_name
= NULL
;
3428 /* If no ret is needed or the rule itself does not have instances
3429 * initialized, we return not matching */
3430 if (!ret
|| !rule
.instances
)
3433 r
= unit_name_template(unit_name
, &templated_name
);
3436 if (!streq(rule
.pattern
, templated_name
))
3439 /* Compose a list of specified instances when unit name is a template */
3440 if (unit_name_is_valid(unit_name
, UNIT_NAME_TEMPLATE
)) {
3441 _cleanup_strv_free_
char **out_strv
= NULL
;
3443 STRV_FOREACH(iter
, rule
.instances
) {
3444 _cleanup_free_
char *name
= NULL
;
3446 r
= unit_name_replace_instance(unit_name
, *iter
, &name
);
3450 r
= strv_consume(&out_strv
, TAKE_PTR(name
));
3455 *ret
= TAKE_PTR(out_strv
);
3458 /* We now know the input unit name is an instance name */
3459 _cleanup_free_
char *instance_name
= NULL
;
3461 r
= unit_name_to_instance(unit_name
, &instance_name
);
3465 if (strv_find(rule
.instances
, instance_name
))
3471 static int query_presets(const char *name
, const UnitFilePresets
*presets
, char ***instance_name_list
) {
3472 PresetAction action
= PRESET_UNKNOWN
;
3477 if (!unit_name_is_valid(name
, UNIT_NAME_ANY
))
3480 FOREACH_ARRAY(i
, presets
->rules
, presets
->n_rules
)
3481 if (pattern_match_multiple_instances(*i
, name
, instance_name_list
) > 0 ||
3482 fnmatch(i
->pattern
, name
, FNM_NOESCAPE
) == 0) {
3489 case PRESET_UNKNOWN
:
3490 log_debug("Preset files don't specify rule for %s. Enabling.", name
);
3491 return PRESET_ENABLE
;
3494 if (instance_name_list
&& *instance_name_list
)
3495 STRV_FOREACH(s
, *instance_name_list
)
3496 log_debug("Preset files say enable %s.", *s
);
3498 log_debug("Preset files say enable %s.", name
);
3499 return PRESET_ENABLE
;
3501 case PRESET_DISABLE
:
3502 log_debug("Preset files say disable %s.", name
);
3503 return PRESET_DISABLE
;
3506 log_debug("Preset files say ignore %s.", name
);
3507 return PRESET_IGNORE
;
3510 assert_not_reached();
3514 PresetAction
unit_file_query_preset(RuntimeScope scope
, const char *root_dir
, const char *name
, UnitFilePresets
*cached
) {
3515 _cleanup_(unit_file_presets_done
) UnitFilePresets tmp
= {};
3520 if (!cached
->initialized
) {
3521 r
= read_presets(scope
, root_dir
, cached
);
3526 return query_presets(name
, cached
, NULL
);
3529 static int execute_preset(
3530 UnitFileFlags file_flags
,
3531 InstallContext
*plus
,
3532 InstallContext
*minus
,
3533 const LookupPaths
*lp
,
3534 const char *config_path
,
3535 char * const *files
,
3536 UnitFilePresetMode mode
,
3537 InstallChange
**changes
,
3538 size_t *n_changes
) {
3545 assert(config_path
);
3547 if (mode
!= UNIT_FILE_PRESET_ENABLE_ONLY
) {
3548 _cleanup_set_free_ Set
*remove_symlinks_to
= NULL
;
3550 r
= install_context_mark_for_removal(minus
, lp
, &remove_symlinks_to
, config_path
, changes
, n_changes
);
3554 r
= remove_marked_symlinks(remove_symlinks_to
, config_path
, lp
, false, changes
, n_changes
);
3558 if (mode
!= UNIT_FILE_PRESET_DISABLE_ONLY
) {
3561 /* Returns number of symlinks that where supposed to be installed. */
3562 q
= install_context_apply(plus
, lp
,
3563 file_flags
| UNIT_FILE_IGNORE_AUXILIARY_FAILURE
,
3565 SEARCH_LOAD
, changes
, n_changes
);
3577 static int preset_prepare_one(
3579 InstallContext
*plus
,
3580 InstallContext
*minus
,
3583 const UnitFilePresets
*presets
,
3584 InstallChange
**changes
,
3585 size_t *n_changes
) {
3587 _cleanup_(install_context_done
) InstallContext tmp
= { .scope
= scope
};
3588 _cleanup_strv_free_
char **instance_name_list
= NULL
;
3592 if (install_info_find(plus
, name
) || install_info_find(minus
, name
))
3595 r
= install_info_discover(&tmp
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3596 &info
, changes
, n_changes
);
3599 if (!streq(name
, info
->name
)) {
3600 log_debug("Skipping %s because it is an alias for %s.", name
, info
->name
);
3604 r
= query_presets(name
, presets
, &instance_name_list
);
3608 if (r
== PRESET_ENABLE
) {
3609 if (instance_name_list
)
3610 STRV_FOREACH(s
, instance_name_list
) {
3611 r
= install_info_discover_and_check(plus
, lp
, *s
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3612 &info
, changes
, n_changes
);
3617 r
= install_info_discover_and_check(plus
, lp
, name
, SEARCH_LOAD
|SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3618 &info
, changes
, n_changes
);
3623 } else if (r
== PRESET_DISABLE
)
3624 r
= install_info_discover(minus
, lp
, name
, SEARCH_FOLLOW_CONFIG_SYMLINKS
,
3625 &info
, changes
, n_changes
);
3630 int unit_file_preset(
3632 UnitFileFlags file_flags
,
3633 const char *root_dir
,
3634 char * const *names
,
3635 UnitFilePresetMode mode
,
3636 InstallChange
**changes
,
3637 size_t *n_changes
) {
3639 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3640 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3641 _cleanup_(unit_file_presets_done
) UnitFilePresets presets
= {};
3642 const char *config_path
;
3646 assert(scope
< _RUNTIME_SCOPE_MAX
);
3647 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3649 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3653 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3657 r
= read_presets(scope
, root_dir
, &presets
);
3661 STRV_FOREACH(name
, names
) {
3662 r
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, *name
, &presets
, changes
, n_changes
);
3667 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, names
, mode
, changes
, n_changes
);
3670 int unit_file_preset_all(
3672 UnitFileFlags file_flags
,
3673 const char *root_dir
,
3674 UnitFilePresetMode mode
,
3675 InstallChange
**changes
,
3676 size_t *n_changes
) {
3678 _cleanup_(install_context_done
) InstallContext plus
= {}, minus
= {};
3679 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3680 _cleanup_(unit_file_presets_done
) UnitFilePresets presets
= {};
3681 const char *config_path
= NULL
;
3685 assert(scope
< _RUNTIME_SCOPE_MAX
);
3686 assert(mode
< _UNIT_FILE_PRESET_MODE_MAX
);
3688 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3692 config_path
= (file_flags
& UNIT_FILE_RUNTIME
) ? lp
.runtime_config
: lp
.persistent_config
;
3696 r
= read_presets(scope
, root_dir
, &presets
);
3701 STRV_FOREACH(i
, lp
.search_path
) {
3702 _cleanup_closedir_
DIR *d
= NULL
;
3706 if (errno
!= ENOENT
)
3707 RET_GATHER(r
, -errno
);
3711 FOREACH_DIRENT(de
, d
, RET_GATHER(r
, -errno
)) {
3714 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3717 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3720 k
= preset_prepare_one(scope
, &plus
, &minus
, &lp
, de
->d_name
, &presets
, changes
, n_changes
);
3734 /* Ignore generated/transient/missing/invalid units when applying preset, propagate other errors.
3735 * Coordinate with install_change_dump_error() above. */
3740 return execute_preset(file_flags
, &plus
, &minus
, &lp
, config_path
, NULL
, mode
, changes
, n_changes
);
3743 static UnitFileList
* unit_file_list_free(UnitFileList
*f
) {
3751 DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList
*, unit_file_list_free
);
3753 DEFINE_PRIVATE_HASH_OPS_FULL(unit_file_list_hash_ops_free_free
,
3754 char, string_hash_func
, string_compare_func
, free
,
3755 UnitFileList
, unit_file_list_free
);
3757 int unit_file_get_list(
3759 const char *root_dir
,
3760 char * const *states
,
3761 char * const *patterns
,
3764 _cleanup_(lookup_paths_done
) LookupPaths lp
= {};
3765 _cleanup_hashmap_free_ Hashmap
*h
= NULL
;
3769 assert(scope
< _RUNTIME_SCOPE_MAX
);
3772 r
= lookup_paths_init(&lp
, scope
, 0, root_dir
);
3776 STRV_FOREACH(dirname
, lp
.search_path
) {
3777 _cleanup_closedir_
DIR *d
= NULL
;
3779 d
= opendir(*dirname
);
3781 if (errno
== ENOENT
)
3783 if (IN_SET(errno
, ENOTDIR
, EACCES
)) {
3784 log_debug_errno(errno
, "Failed to open \"%s\": %m", *dirname
);
3791 FOREACH_DIRENT(de
, d
, return -errno
) {
3792 if (!IN_SET(de
->d_type
, DT_LNK
, DT_REG
))
3795 if (hashmap_contains(h
, de
->d_name
))
3798 if (!unit_name_is_valid(de
->d_name
, UNIT_NAME_ANY
))
3801 if (!strv_fnmatch_or_empty(patterns
, de
->d_name
, FNM_NOESCAPE
))
3804 UnitFileState state
;
3806 r
= unit_file_lookup_state(scope
, &lp
, de
->d_name
, &state
);
3808 state
= UNIT_FILE_BAD
;
3810 if (!strv_isempty(states
) &&
3811 !strv_contains(states
, unit_file_state_to_string(state
)))
3814 _cleanup_(unit_file_list_freep
) UnitFileList
*f
= new(UnitFileList
, 1);
3818 *f
= (UnitFileList
) {
3819 .path
= path_make_absolute(de
->d_name
, *dirname
),
3825 _cleanup_free_
char *unit_name
= strdup(de
->d_name
);
3829 r
= hashmap_ensure_put(&h
, &unit_file_list_hash_ops_free_free
, unit_name
, f
);
3834 TAKE_PTR(unit_name
);
3843 static const char* const unit_file_state_table
[_UNIT_FILE_STATE_MAX
] = {
3844 [UNIT_FILE_ENABLED
] = "enabled",
3845 [UNIT_FILE_ENABLED_RUNTIME
] = "enabled-runtime",
3846 [UNIT_FILE_LINKED
] = "linked",
3847 [UNIT_FILE_LINKED_RUNTIME
] = "linked-runtime",
3848 [UNIT_FILE_ALIAS
] = "alias",
3849 [UNIT_FILE_MASKED
] = "masked",
3850 [UNIT_FILE_MASKED_RUNTIME
] = "masked-runtime",
3851 [UNIT_FILE_STATIC
] = "static",
3852 [UNIT_FILE_DISABLED
] = "disabled",
3853 [UNIT_FILE_INDIRECT
] = "indirect",
3854 [UNIT_FILE_GENERATED
] = "generated",
3855 [UNIT_FILE_TRANSIENT
] = "transient",
3856 [UNIT_FILE_BAD
] = "bad",
3859 DEFINE_STRING_TABLE_LOOKUP(unit_file_state
, UnitFileState
);
3861 static const char* const install_change_type_table
[_INSTALL_CHANGE_TYPE_MAX
] = {
3862 [INSTALL_CHANGE_SYMLINK
] = "symlink",
3863 [INSTALL_CHANGE_UNLINK
] = "unlink",
3864 [INSTALL_CHANGE_IS_MASKED
] = "masked",
3865 [INSTALL_CHANGE_IS_MASKED_GENERATOR
] = "masked by generator",
3866 [INSTALL_CHANGE_IS_DANGLING
] = "dangling",
3867 [INSTALL_CHANGE_DESTINATION_NOT_PRESENT
] = "destination not present",
3868 [INSTALL_CHANGE_AUXILIARY_FAILED
] = "auxiliary unit failed",
3871 DEFINE_STRING_TABLE_LOOKUP(install_change_type
, InstallChangeType
);
3873 static const char* const unit_file_preset_mode_table
[_UNIT_FILE_PRESET_MODE_MAX
] = {
3874 [UNIT_FILE_PRESET_FULL
] = "full",
3875 [UNIT_FILE_PRESET_ENABLE_ONLY
] = "enable-only",
3876 [UNIT_FILE_PRESET_DISABLE_ONLY
] = "disable-only",
3879 DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode
, UnitFilePresetMode
);