1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
11 #include "nulstr-util.h"
12 #include "path-lookup.h"
13 #include "path-util.h"
14 #include "stat-util.h"
15 #include "string-util.h"
17 #include "tmpfile-util.h"
18 #include "user-util.h"
20 int xdg_user_runtime_dir(char **ret
, const char *suffix
) {
27 e
= getenv("XDG_RUNTIME_DIR");
31 j
= path_join(e
, suffix
);
39 int xdg_user_config_dir(char **ret
, const char *suffix
) {
40 _cleanup_free_
char *j
= NULL
;
46 e
= getenv("XDG_CONFIG_HOME");
48 j
= path_join(e
, suffix
);
56 if (!path_extend(&j
, "/.config", suffix
))
64 int xdg_user_data_dir(char **ret
, const char *suffix
) {
65 _cleanup_free_
char *j
= NULL
;
72 /* We don't treat /etc/xdg/systemd here as the spec
73 * suggests because we assume that is a link to
74 * /etc/systemd/ anyway. */
76 e
= getenv("XDG_DATA_HOME");
78 j
= path_join(e
, suffix
);
86 if (!path_extend(&j
, "/.local/share", suffix
))
94 static const char* const user_data_unit_paths
[] = {
95 "/usr/local/lib/systemd/user",
96 "/usr/local/share/systemd/user",
98 "/usr/lib/systemd/user",
99 "/usr/share/systemd/user",
103 static const char* const user_config_unit_paths
[] = {
104 USER_CONFIG_UNIT_DIR
,
109 int xdg_user_dirs(char ***ret_config_dirs
, char ***ret_data_dirs
) {
110 /* Implement the mechanisms defined in
112 * https://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
114 * We look in both the config and the data dirs because we
115 * want to encourage that distributors ship their unit files
116 * as data, and allow overriding as configuration.
119 _cleanup_strv_free_
char **config_dirs
= NULL
, **data_dirs
= NULL
;
121 e
= getenv("XDG_CONFIG_DIRS");
123 config_dirs
= strv_split(e
, ":");
125 config_dirs
= strv_new("/etc/xdg");
129 e
= getenv("XDG_DATA_DIRS");
131 data_dirs
= strv_split(e
, ":");
133 data_dirs
= strv_new("/usr/local/share",
138 *ret_config_dirs
= TAKE_PTR(config_dirs
);
139 *ret_data_dirs
= TAKE_PTR(data_dirs
);
144 static char** user_dirs(
145 const char *persistent_config
,
146 const char *runtime_config
,
147 const char *global_persistent_config
,
148 const char *global_runtime_config
,
149 const char *generator
,
150 const char *generator_early
,
151 const char *generator_late
,
152 const char *transient
,
153 const char *persistent_control
,
154 const char *runtime_control
) {
156 _cleanup_strv_free_
char **config_dirs
= NULL
, **data_dirs
= NULL
;
157 _cleanup_free_
char *data_home
= NULL
;
158 _cleanup_strv_free_
char **res
= NULL
;
161 r
= xdg_user_dirs(&config_dirs
, &data_dirs
);
165 r
= xdg_user_data_dir(&data_home
, "/systemd/user");
166 if (r
< 0 && r
!= -ENXIO
)
169 /* Now merge everything we found. */
170 if (strv_extend(&res
, persistent_control
) < 0)
173 if (strv_extend(&res
, runtime_control
) < 0)
176 if (strv_extend(&res
, transient
) < 0)
179 if (strv_extend(&res
, generator_early
) < 0)
182 if (strv_extend(&res
, persistent_config
) < 0)
185 if (strv_extend_strv_concat(&res
, config_dirs
, "/systemd/user") < 0)
188 /* global config has lower priority than the user config of the same type */
189 if (strv_extend(&res
, global_persistent_config
) < 0)
192 if (strv_extend_strv(&res
, (char**) user_config_unit_paths
, false) < 0)
195 if (strv_extend(&res
, runtime_config
) < 0)
198 if (strv_extend(&res
, global_runtime_config
) < 0)
201 if (strv_extend(&res
, generator
) < 0)
204 if (strv_extend(&res
, data_home
) < 0)
207 if (strv_extend_strv_concat(&res
, data_dirs
, "/systemd/user") < 0)
210 if (strv_extend_strv(&res
, (char**) user_data_unit_paths
, false) < 0)
213 if (strv_extend(&res
, generator_late
) < 0)
216 if (path_strv_make_absolute_cwd(res
) < 0)
219 return TAKE_PTR(res
);
222 bool path_is_user_data_dir(const char *path
) {
225 return strv_contains((char**) user_data_unit_paths
, path
);
228 bool path_is_user_config_dir(const char *path
) {
231 return strv_contains((char**) user_config_unit_paths
, path
);
234 static int acquire_generator_dirs(
238 char **generator_early
,
239 char **generator_late
) {
241 _cleanup_free_
char *x
= NULL
, *y
= NULL
, *z
= NULL
, *p
= NULL
;
245 assert(generator_early
);
246 assert(generator_late
);
247 assert(IN_SET(scope
, RUNTIME_SCOPE_SYSTEM
, RUNTIME_SCOPE_USER
, RUNTIME_SCOPE_GLOBAL
));
249 if (scope
== RUNTIME_SCOPE_GLOBAL
)
254 else if (scope
== RUNTIME_SCOPE_SYSTEM
)
255 prefix
= "/run/systemd";
257 /* RUNTIME_SCOPE_USER */
260 e
= getenv("XDG_RUNTIME_DIR");
264 p
= path_join(e
, "/systemd");
271 x
= path_join(prefix
, "generator");
275 y
= path_join(prefix
, "generator.early");
279 z
= path_join(prefix
, "generator.late");
283 *generator
= TAKE_PTR(x
);
284 *generator_early
= TAKE_PTR(y
);
285 *generator_late
= TAKE_PTR(z
);
290 static int acquire_transient_dir(
298 assert(IN_SET(scope
, RUNTIME_SCOPE_SYSTEM
, RUNTIME_SCOPE_USER
, RUNTIME_SCOPE_GLOBAL
));
300 if (scope
== RUNTIME_SCOPE_GLOBAL
)
304 transient
= path_join(tempdir
, "transient");
305 else if (scope
== RUNTIME_SCOPE_SYSTEM
)
306 transient
= strdup("/run/systemd/transient");
308 return xdg_user_runtime_dir(ret
, "/systemd/transient");
316 static int acquire_config_dirs(RuntimeScope scope
, char **persistent
, char **runtime
) {
317 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
325 case RUNTIME_SCOPE_SYSTEM
:
326 a
= strdup(SYSTEM_CONFIG_UNIT_DIR
);
327 b
= strdup("/run/systemd/system");
330 case RUNTIME_SCOPE_GLOBAL
:
331 a
= strdup(USER_CONFIG_UNIT_DIR
);
332 b
= strdup("/run/systemd/user");
335 case RUNTIME_SCOPE_USER
:
336 r
= xdg_user_config_dir(&a
, "/systemd/user");
337 if (r
< 0 && r
!= -ENXIO
)
340 r
= xdg_user_runtime_dir(runtime
, "/systemd/user");
345 /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
346 * directory to NULL */
350 *persistent
= TAKE_PTR(a
);
355 assert_not_reached();
361 *persistent
= TAKE_PTR(a
);
362 *runtime
= TAKE_PTR(b
);
367 static int acquire_control_dirs(RuntimeScope scope
, char **persistent
, char **runtime
) {
368 _cleanup_free_
char *a
= NULL
;
376 case RUNTIME_SCOPE_SYSTEM
: {
377 _cleanup_free_
char *b
= NULL
;
379 a
= strdup("/etc/systemd/system.control");
383 b
= strdup("/run/systemd/system.control");
387 *runtime
= TAKE_PTR(b
);
392 case RUNTIME_SCOPE_USER
:
393 r
= xdg_user_config_dir(&a
, "/systemd/user.control");
394 if (r
< 0 && r
!= -ENXIO
)
397 r
= xdg_user_runtime_dir(runtime
, "/systemd/user.control");
402 /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
409 case RUNTIME_SCOPE_GLOBAL
:
413 assert_not_reached();
416 *persistent
= TAKE_PTR(a
);
421 static int acquire_attached_dirs(
423 char **ret_persistent
,
424 char **ret_runtime
) {
426 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
428 assert(ret_persistent
);
431 /* Portable services are not available to regular users for now. */
432 if (scope
!= RUNTIME_SCOPE_SYSTEM
)
435 a
= strdup("/etc/systemd/system.attached");
439 b
= strdup("/run/systemd/system.attached");
443 *ret_persistent
= TAKE_PTR(a
);
444 *ret_runtime
= TAKE_PTR(b
);
449 static int patch_root_prefix(char **p
, const char *root_dir
) {
457 c
= path_join(root_dir
, *p
);
461 free_and_replace(*p
, c
);
465 static int patch_root_prefix_strv(char **l
, const char *root_dir
) {
472 r
= patch_root_prefix(i
, root_dir
);
480 static int get_paths_from_environ(const char *var
, char ***paths
, bool *append
) {
494 k
= endswith(e
, ":");
496 e
= strndupa_safe(e
, k
- e
);
500 /* FIXME: empty components in other places should be rejected. */
502 r
= path_split_and_make_absolute(e
, paths
);
510 int lookup_paths_init(
513 LookupPathsFlags flags
,
514 const char *root_dir
) {
516 _cleanup_(rmdir_and_freep
) char *tempdir
= NULL
;
519 *persistent_config
= NULL
, *runtime_config
= NULL
,
520 *global_persistent_config
= NULL
, *global_runtime_config
= NULL
,
521 *generator
= NULL
, *generator_early
= NULL
, *generator_late
= NULL
,
523 *persistent_control
= NULL
, *runtime_control
= NULL
,
524 *persistent_attached
= NULL
, *runtime_attached
= NULL
;
525 bool append
= false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
526 _cleanup_strv_free_
char **paths
= NULL
;
531 assert(scope
< _RUNTIME_SCOPE_MAX
);
534 flags
|= LOOKUP_PATHS_SPLIT_USR
;
537 if (!empty_or_root(root_dir
)) {
538 if (scope
== RUNTIME_SCOPE_USER
)
541 r
= is_dir(root_dir
, true);
547 root
= strdup(root_dir
);
552 if (flags
& LOOKUP_PATHS_TEMPORARY_GENERATED
) {
553 r
= mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir
);
555 return log_debug_errno(r
, "Failed to create temporary directory: %m");
558 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
559 r
= acquire_config_dirs(scope
, &persistent_config
, &runtime_config
);
563 if (scope
== RUNTIME_SCOPE_USER
) {
564 r
= acquire_config_dirs(RUNTIME_SCOPE_GLOBAL
, &global_persistent_config
, &global_runtime_config
);
569 if ((flags
& LOOKUP_PATHS_EXCLUDE_GENERATED
) == 0) {
570 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
571 r
= acquire_generator_dirs(scope
, tempdir
,
572 &generator
, &generator_early
, &generator_late
);
573 if (r
< 0 && !IN_SET(r
, -EOPNOTSUPP
, -ENXIO
))
577 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
578 r
= acquire_transient_dir(scope
, tempdir
, &transient
);
579 if (r
< 0 && !IN_SET(r
, -EOPNOTSUPP
, -ENXIO
))
582 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
583 r
= acquire_control_dirs(scope
, &persistent_control
, &runtime_control
);
584 if (r
< 0 && r
!= -EOPNOTSUPP
)
587 r
= acquire_attached_dirs(scope
, &persistent_attached
, &runtime_attached
);
588 if (r
< 0 && r
!= -EOPNOTSUPP
)
591 /* First priority is whatever has been passed to us via env vars */
592 r
= get_paths_from_environ("SYSTEMD_UNIT_PATH", &paths
, &append
);
596 if (!paths
|| append
) {
597 /* Let's figure something out. */
599 _cleanup_strv_free_
char **add
= NULL
;
601 /* For the user units we include share/ in the search
602 * path in order to comply with the XDG basedir spec.
603 * For the system stuff we avoid such nonsense. OTOH
604 * we include /lib in the search path for the system
605 * stuff but avoid it for user stuff. */
609 case RUNTIME_SCOPE_SYSTEM
:
611 /* If you modify this you also want to modify
612 * systemdsystemunitpath= in systemd.pc.in! */
613 STRV_IFNOTNULL(persistent_control
),
614 STRV_IFNOTNULL(runtime_control
),
615 STRV_IFNOTNULL(transient
),
616 STRV_IFNOTNULL(generator_early
),
618 SYSTEM_CONFIG_UNIT_DIR
,
619 "/etc/systemd/system",
620 STRV_IFNOTNULL(persistent_attached
),
622 "/run/systemd/system",
623 STRV_IFNOTNULL(runtime_attached
),
624 STRV_IFNOTNULL(generator
),
625 "/usr/local/lib/systemd/system",
626 SYSTEM_DATA_UNIT_DIR
,
627 "/usr/lib/systemd/system",
628 STRV_IFNOTNULL(flags
& LOOKUP_PATHS_SPLIT_USR
? "/lib/systemd/system" : NULL
),
629 STRV_IFNOTNULL(generator_late
));
632 case RUNTIME_SCOPE_GLOBAL
:
634 /* If you modify this you also want to modify
635 * systemduserunitpath= in systemd.pc.in, and
636 * the arrays in user_dirs() above! */
637 STRV_IFNOTNULL(persistent_control
),
638 STRV_IFNOTNULL(runtime_control
),
639 STRV_IFNOTNULL(transient
),
640 STRV_IFNOTNULL(generator_early
),
642 USER_CONFIG_UNIT_DIR
,
646 STRV_IFNOTNULL(generator
),
647 "/usr/local/share/systemd/user",
648 "/usr/share/systemd/user",
649 "/usr/local/lib/systemd/user",
651 "/usr/lib/systemd/user",
652 STRV_IFNOTNULL(generator_late
));
655 case RUNTIME_SCOPE_USER
:
656 add
= user_dirs(persistent_config
, runtime_config
,
657 global_persistent_config
, global_runtime_config
,
658 generator
, generator_early
, generator_late
,
660 persistent_control
, runtime_control
);
664 assert_not_reached();
671 r
= strv_extend_strv(&paths
, add
, true);
675 /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
676 * and don't have to copy anything */
677 paths
= TAKE_PTR(add
);
680 r
= patch_root_prefix(&persistent_config
, root
);
683 r
= patch_root_prefix(&runtime_config
, root
);
687 r
= patch_root_prefix(&generator
, root
);
690 r
= patch_root_prefix(&generator_early
, root
);
693 r
= patch_root_prefix(&generator_late
, root
);
697 r
= patch_root_prefix(&transient
, root
);
701 r
= patch_root_prefix(&persistent_control
, root
);
704 r
= patch_root_prefix(&runtime_control
, root
);
708 r
= patch_root_prefix(&persistent_attached
, root
);
711 r
= patch_root_prefix(&runtime_attached
, root
);
715 r
= patch_root_prefix_strv(paths
, root
);
719 *lp
= (LookupPaths
) {
720 .search_path
= strv_uniq(TAKE_PTR(paths
)),
722 .persistent_config
= TAKE_PTR(persistent_config
),
723 .runtime_config
= TAKE_PTR(runtime_config
),
725 .generator
= TAKE_PTR(generator
),
726 .generator_early
= TAKE_PTR(generator_early
),
727 .generator_late
= TAKE_PTR(generator_late
),
729 .transient
= TAKE_PTR(transient
),
731 .persistent_control
= TAKE_PTR(persistent_control
),
732 .runtime_control
= TAKE_PTR(runtime_control
),
734 .persistent_attached
= TAKE_PTR(persistent_attached
),
735 .runtime_attached
= TAKE_PTR(runtime_attached
),
737 .root_dir
= TAKE_PTR(root
),
738 .temporary_dir
= TAKE_PTR(tempdir
),
744 int lookup_paths_init_or_warn(LookupPaths
*lp
, RuntimeScope scope
, LookupPathsFlags flags
, const char *root_dir
) {
747 r
= lookup_paths_init(lp
, scope
, flags
, root_dir
);
749 return log_error_errno(r
, "Failed to initialize unit search paths%s%s: %m",
750 isempty(root_dir
) ? "" : " for root directory ", strempty(root_dir
));
754 void lookup_paths_free(LookupPaths
*lp
) {
758 lp
->search_path
= strv_free(lp
->search_path
);
760 lp
->persistent_config
= mfree(lp
->persistent_config
);
761 lp
->runtime_config
= mfree(lp
->runtime_config
);
763 lp
->persistent_attached
= mfree(lp
->persistent_attached
);
764 lp
->runtime_attached
= mfree(lp
->runtime_attached
);
766 lp
->generator
= mfree(lp
->generator
);
767 lp
->generator_early
= mfree(lp
->generator_early
);
768 lp
->generator_late
= mfree(lp
->generator_late
);
770 lp
->transient
= mfree(lp
->transient
);
772 lp
->persistent_control
= mfree(lp
->persistent_control
);
773 lp
->runtime_control
= mfree(lp
->runtime_control
);
775 lp
->root_dir
= mfree(lp
->root_dir
);
776 lp
->temporary_dir
= mfree(lp
->temporary_dir
);
779 void lookup_paths_log(LookupPaths
*lp
) {
782 if (strv_isempty(lp
->search_path
)) {
783 log_debug("Ignoring unit files.");
784 lp
->search_path
= strv_free(lp
->search_path
);
786 _cleanup_free_
char *t
= NULL
;
788 t
= strv_join(lp
->search_path
, "\n\t");
789 log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t
));
793 char **generator_binary_paths(RuntimeScope scope
) {
794 bool append
= false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
795 _cleanup_strv_free_
char **paths
= NULL
;
798 /* First priority is whatever has been passed to us via env vars */
799 r
= get_paths_from_environ("SYSTEMD_GENERATOR_PATH", &paths
, &append
);
803 if (!paths
|| append
) {
804 _cleanup_strv_free_
char **add
= NULL
;
808 case RUNTIME_SCOPE_SYSTEM
:
809 add
= strv_new("/run/systemd/system-generators",
810 "/etc/systemd/system-generators",
811 "/usr/local/lib/systemd/system-generators",
812 SYSTEM_GENERATOR_DIR
);
815 case RUNTIME_SCOPE_GLOBAL
:
816 case RUNTIME_SCOPE_USER
:
817 add
= strv_new("/run/systemd/user-generators",
818 "/etc/systemd/user-generators",
819 "/usr/local/lib/systemd/user-generators",
824 assert_not_reached();
830 r
= strv_extend_strv(&paths
, add
, true);
834 /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
835 * and don't have to copy anything */
836 paths
= TAKE_PTR(add
);
839 return TAKE_PTR(paths
);
842 char **env_generator_binary_paths(RuntimeScope runtime_scope
) {
843 _cleanup_strv_free_
char **paths
= NULL
, **add
= NULL
;
844 bool append
= false; /* Add items from SYSTEMD_ENVIRONMENT_GENERATOR_PATH before normal directories */
847 /* First priority is whatever has been passed to us via env vars */
848 r
= get_paths_from_environ("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", &paths
, &append
);
852 if (!paths
|| append
) {
853 switch (runtime_scope
) {
855 case RUNTIME_SCOPE_SYSTEM
:
856 add
= strv_new("/run/systemd/system-environment-generators",
857 "/etc/systemd/system-environment-generators",
858 "/usr/local/lib/systemd/system-environment-generators",
859 SYSTEM_ENV_GENERATOR_DIR
);
862 case RUNTIME_SCOPE_USER
:
863 add
= strv_new("/run/systemd/user-environment-generators",
864 "/etc/systemd/user-environment-generators",
865 "/usr/local/lib/systemd/user-environment-generators",
866 USER_ENV_GENERATOR_DIR
);
870 assert_not_reached();
877 r
= strv_extend_strv(&paths
, add
, true);
881 /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
882 * and don't have to copy anything */
883 paths
= TAKE_PTR(add
);
885 return TAKE_PTR(paths
);
888 int find_portable_profile(const char *name
, const char *unit
, char **ret_path
) {
894 assert_se(dot
= strrchr(unit
, '.'));
896 NULSTR_FOREACH(p
, PORTABLE_PROFILE_DIRS
) {
897 _cleanup_free_
char *joined
= NULL
;
899 joined
= strjoin(p
, "/", name
, "/", dot
+ 1, ".conf");
903 if (laccess(joined
, F_OK
) >= 0) {
904 *ret_path
= TAKE_PTR(joined
);