1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "alloc-util.h"
33 #include "path-lookup.h"
34 #include "path-util.h"
36 #include "stat-util.h"
37 #include "string-util.h"
39 #include "user-util.h"
42 int xdg_user_runtime_dir(char **ret
, const char *suffix
) {
49 e
= getenv("XDG_RUNTIME_DIR");
53 j
= strappend(e
, suffix
);
61 int xdg_user_config_dir(char **ret
, const char *suffix
) {
68 e
= getenv("XDG_CONFIG_HOME");
70 j
= strappend(e
, suffix
);
72 _cleanup_free_
char *home
= NULL
;
74 r
= get_home_dir(&home
);
78 j
= strjoin(home
, "/.config", suffix
);
88 int xdg_user_data_dir(char **ret
, const char *suffix
) {
96 /* We don't treat /etc/xdg/systemd here as the spec
97 * suggests because we assume that is a link to
98 * /etc/systemd/ anyway. */
100 e
= getenv("XDG_DATA_HOME");
102 j
= strappend(e
, suffix
);
104 _cleanup_free_
char *home
= NULL
;
106 r
= get_home_dir(&home
);
110 j
= strjoin(home
, "/.local/share", suffix
);
119 static const char* const user_data_unit_paths
[] = {
120 "/usr/local/lib/systemd/user",
121 "/usr/local/share/systemd/user",
123 "/usr/lib/systemd/user",
124 "/usr/share/systemd/user",
128 static const char* const user_config_unit_paths
[] = {
129 USER_CONFIG_UNIT_PATH
,
134 int xdg_user_dirs(char ***ret_config_dirs
, char ***ret_data_dirs
) {
135 /* Implement the mechanisms defined in
137 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
139 * We look in both the config and the data dirs because we
140 * want to encourage that distributors ship their unit files
141 * as data, and allow overriding as configuration.
144 _cleanup_strv_free_
char **config_dirs
= NULL
, **data_dirs
= NULL
;
146 e
= getenv("XDG_CONFIG_DIRS");
148 config_dirs
= strv_split(e
, ":");
153 e
= getenv("XDG_DATA_DIRS");
155 data_dirs
= strv_split(e
, ":");
157 data_dirs
= strv_new("/usr/local/share",
163 *ret_config_dirs
= config_dirs
;
164 *ret_data_dirs
= data_dirs
;
165 config_dirs
= data_dirs
= NULL
;
169 static char** user_dirs(
170 const char *persistent_config
,
171 const char *runtime_config
,
172 const char *global_persistent_config
,
173 const char *global_runtime_config
,
174 const char *generator
,
175 const char *generator_early
,
176 const char *generator_late
,
177 const char *transient
,
178 const char *persistent_control
,
179 const char *runtime_control
) {
181 _cleanup_strv_free_
char **config_dirs
= NULL
, **data_dirs
= NULL
;
182 _cleanup_free_
char *data_home
= NULL
;
183 _cleanup_strv_free_
char **res
= NULL
;
186 r
= xdg_user_dirs(&config_dirs
, &data_dirs
);
190 r
= xdg_user_data_dir(&data_home
, "/systemd/user");
191 if (r
< 0 && r
!= -ENXIO
)
194 /* Now merge everything we found. */
195 if (strv_extend(&res
, persistent_control
) < 0)
198 if (strv_extend(&res
, runtime_control
) < 0)
201 if (strv_extend(&res
, transient
) < 0)
204 if (strv_extend(&res
, generator_early
) < 0)
207 if (strv_extend_strv_concat(&res
, config_dirs
, "/systemd/user") < 0)
210 if (strv_extend(&res
, persistent_config
) < 0)
213 /* global config has lower priority than the user config of the same type */
214 if (strv_extend(&res
, global_persistent_config
) < 0)
217 if (strv_extend_strv(&res
, (char**) user_config_unit_paths
, false) < 0)
220 if (strv_extend(&res
, runtime_config
) < 0)
223 if (strv_extend(&res
, global_runtime_config
) < 0)
226 if (strv_extend(&res
, generator
) < 0)
229 if (strv_extend(&res
, data_home
) < 0)
232 if (strv_extend_strv_concat(&res
, data_dirs
, "/systemd/user") < 0)
235 if (strv_extend_strv(&res
, (char**) user_data_unit_paths
, false) < 0)
238 if (strv_extend(&res
, generator_late
) < 0)
241 if (path_strv_make_absolute_cwd(res
) < 0)
244 return TAKE_PTR(res
);
247 bool path_is_user_data_dir(const char *path
) {
250 return strv_contains((char**) user_data_unit_paths
, path
);
253 bool path_is_user_config_dir(const char *path
) {
256 return strv_contains((char**) user_config_unit_paths
, path
);
259 static int acquire_generator_dirs(
263 char **generator_early
,
264 char **generator_late
) {
266 _cleanup_free_
char *x
= NULL
, *y
= NULL
, *z
= NULL
;
270 assert(generator_early
);
271 assert(generator_late
);
272 assert(IN_SET(scope
, UNIT_FILE_SYSTEM
, UNIT_FILE_USER
, UNIT_FILE_GLOBAL
));
274 if (scope
== UNIT_FILE_GLOBAL
)
280 else if (scope
== UNIT_FILE_SYSTEM
)
281 prefix
= "/run/systemd";
283 else if (scope
== UNIT_FILE_USER
) {
286 e
= getenv("XDG_RUNTIME_DIR");
290 prefix
= strjoina(e
, "/systemd");
293 x
= strappend(prefix
, "/generator");
297 y
= strappend(prefix
, "/generator.early");
301 z
= strappend(prefix
, "/generator.late");
306 *generator_early
= y
;
313 static int acquire_transient_dir(
321 assert(IN_SET(scope
, UNIT_FILE_SYSTEM
, UNIT_FILE_USER
, UNIT_FILE_GLOBAL
));
323 if (scope
== UNIT_FILE_GLOBAL
)
327 transient
= strjoin(tempdir
, "/transient");
328 else if (scope
== UNIT_FILE_SYSTEM
)
329 transient
= strdup("/run/systemd/transient");
331 return xdg_user_runtime_dir(ret
, "/systemd/transient");
339 static int acquire_config_dirs(UnitFileScope scope
, char **persistent
, char **runtime
) {
340 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
348 case UNIT_FILE_SYSTEM
:
349 a
= strdup(SYSTEM_CONFIG_UNIT_PATH
);
350 b
= strdup("/run/systemd/system");
353 case UNIT_FILE_GLOBAL
:
354 a
= strdup(USER_CONFIG_UNIT_PATH
);
355 b
= strdup("/run/systemd/user");
359 r
= xdg_user_config_dir(&a
, "/systemd/user");
360 if (r
< 0 && r
!= -ENXIO
)
363 r
= xdg_user_runtime_dir(runtime
, "/systemd/user");
368 /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
369 * directory to NULL */
373 *persistent
= TAKE_PTR(a
);
378 assert_not_reached("Hmm, unexpected scope value.");
391 static int acquire_control_dirs(UnitFileScope scope
, char **persistent
, char **runtime
) {
392 _cleanup_free_
char *a
= NULL
;
400 case UNIT_FILE_SYSTEM
: {
401 _cleanup_free_
char *b
= NULL
;
403 a
= strdup("/etc/systemd/system.control");
407 b
= strdup("/run/systemd/system.control");
411 *runtime
= TAKE_PTR(b
);
417 r
= xdg_user_config_dir(&a
, "/systemd/user.control");
418 if (r
< 0 && r
!= -ENXIO
)
421 r
= xdg_user_runtime_dir(runtime
, "/systemd/user.control");
426 /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
433 case UNIT_FILE_GLOBAL
:
437 assert_not_reached("Hmm, unexpected scope value.");
440 *persistent
= TAKE_PTR(a
);
445 static int patch_root_prefix(char **p
, const char *root_dir
) {
453 c
= prefix_root(root_dir
, *p
);
463 static int patch_root_prefix_strv(char **l
, const char *root_dir
) {
471 r
= patch_root_prefix(i
, root_dir
);
479 int lookup_paths_init(
482 LookupPathsFlags flags
,
483 const char *root_dir
) {
485 _cleanup_(rmdir_and_freep
) char *tempdir
= NULL
;
488 *persistent_config
= NULL
, *runtime_config
= NULL
,
489 *global_persistent_config
= NULL
, *global_runtime_config
= NULL
,
490 *generator
= NULL
, *generator_early
= NULL
, *generator_late
= NULL
,
492 *persistent_control
= NULL
, *runtime_control
= NULL
;
493 bool append
= false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
494 _cleanup_strv_free_
char **paths
= NULL
;
500 assert(scope
< _UNIT_FILE_SCOPE_MAX
);
502 if (!isempty(root_dir
) && !path_equal(root_dir
, "/")) {
503 if (scope
== UNIT_FILE_USER
)
506 r
= is_dir(root_dir
, true);
512 root
= strdup(root_dir
);
517 if (flags
& LOOKUP_PATHS_TEMPORARY_GENERATED
) {
518 r
= mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir
);
520 return log_error_errno(r
, "Failed to create temporary directory: %m");
523 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
524 r
= acquire_config_dirs(scope
, &persistent_config
, &runtime_config
);
528 if (scope
== UNIT_FILE_USER
) {
529 r
= acquire_config_dirs(UNIT_FILE_GLOBAL
, &global_persistent_config
, &global_runtime_config
);
534 if ((flags
& LOOKUP_PATHS_EXCLUDE_GENERATED
) == 0) {
535 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
536 r
= acquire_generator_dirs(scope
, tempdir
,
537 &generator
, &generator_early
, &generator_late
);
538 if (r
< 0 && !IN_SET(r
, -EOPNOTSUPP
, -ENXIO
))
542 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
543 r
= acquire_transient_dir(scope
, tempdir
, &transient
);
544 if (r
< 0 && !IN_SET(r
, -EOPNOTSUPP
, -ENXIO
))
547 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
548 r
= acquire_control_dirs(scope
, &persistent_control
, &runtime_control
);
549 if (r
< 0 && r
!= -EOPNOTSUPP
)
552 /* First priority is whatever has been passed to us via env vars */
553 e
= getenv("SYSTEMD_UNIT_PATH");
557 k
= endswith(e
, ":");
559 e
= strndupa(e
, k
- e
);
563 /* FIXME: empty components in other places should be rejected. */
565 r
= path_split_and_make_absolute(e
, &paths
);
570 if (!paths
|| append
) {
571 /* Let's figure something out. */
573 _cleanup_strv_free_
char **add
= NULL
;
575 /* For the user units we include share/ in the search
576 * path in order to comply with the XDG basedir spec.
577 * For the system stuff we avoid such nonsense. OTOH
578 * we include /lib in the search path for the system
579 * stuff but avoid it for user stuff. */
583 case UNIT_FILE_SYSTEM
:
585 /* If you modify this you also want to modify
586 * systemdsystemunitpath= in systemd.pc.in! */
587 STRV_IFNOTNULL(persistent_control
),
588 STRV_IFNOTNULL(runtime_control
),
589 STRV_IFNOTNULL(transient
),
590 STRV_IFNOTNULL(generator_early
),
592 SYSTEM_CONFIG_UNIT_PATH
,
593 "/etc/systemd/system",
595 "/run/systemd/system",
596 STRV_IFNOTNULL(generator
),
597 "/usr/local/lib/systemd/system",
598 SYSTEM_DATA_UNIT_PATH
,
599 "/usr/lib/systemd/system",
601 "/lib/systemd/system",
603 STRV_IFNOTNULL(generator_late
),
607 case UNIT_FILE_GLOBAL
:
609 /* If you modify this you also want to modify
610 * systemduserunitpath= in systemd.pc.in, and
611 * the arrays in user_dirs() above! */
612 STRV_IFNOTNULL(persistent_control
),
613 STRV_IFNOTNULL(runtime_control
),
614 STRV_IFNOTNULL(transient
),
615 STRV_IFNOTNULL(generator_early
),
617 USER_CONFIG_UNIT_PATH
,
621 STRV_IFNOTNULL(generator
),
622 "/usr/local/share/systemd/user",
623 "/usr/share/systemd/user",
624 "/usr/local/lib/systemd/user",
626 "/usr/lib/systemd/user",
627 STRV_IFNOTNULL(generator_late
),
632 add
= user_dirs(persistent_config
, runtime_config
,
633 global_persistent_config
, global_runtime_config
,
634 generator
, generator_early
, generator_late
,
636 persistent_control
, runtime_control
);
640 assert_not_reached("Hmm, unexpected scope?");
647 r
= strv_extend_strv(&paths
, add
, true);
651 /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
652 * and don't have to copy anything */
658 r
= patch_root_prefix(&persistent_config
, root
);
661 r
= patch_root_prefix(&runtime_config
, root
);
665 r
= patch_root_prefix(&generator
, root
);
668 r
= patch_root_prefix(&generator_early
, root
);
671 r
= patch_root_prefix(&generator_late
, root
);
675 r
= patch_root_prefix(&transient
, root
);
679 r
= patch_root_prefix(&persistent_control
, root
);
683 r
= patch_root_prefix(&runtime_control
, root
);
687 r
= patch_root_prefix_strv(paths
, root
);
691 p
->search_path
= strv_uniq(paths
);
694 p
->persistent_config
= persistent_config
;
695 p
->runtime_config
= runtime_config
;
696 persistent_config
= runtime_config
= NULL
;
698 p
->generator
= generator
;
699 p
->generator_early
= generator_early
;
700 p
->generator_late
= generator_late
;
701 generator
= generator_early
= generator_late
= NULL
;
703 p
->transient
= transient
;
706 p
->persistent_control
= persistent_control
;
707 p
->runtime_control
= runtime_control
;
708 persistent_control
= runtime_control
= NULL
;
713 p
->temporary_dir
= tempdir
;
719 void lookup_paths_free(LookupPaths
*p
) {
723 p
->search_path
= strv_free(p
->search_path
);
725 p
->persistent_config
= mfree(p
->persistent_config
);
726 p
->runtime_config
= mfree(p
->runtime_config
);
728 p
->generator
= mfree(p
->generator
);
729 p
->generator_early
= mfree(p
->generator_early
);
730 p
->generator_late
= mfree(p
->generator_late
);
732 p
->transient
= mfree(p
->transient
);
734 p
->persistent_control
= mfree(p
->persistent_control
);
735 p
->runtime_control
= mfree(p
->runtime_control
);
737 p
->root_dir
= mfree(p
->root_dir
);
738 p
->temporary_dir
= mfree(p
->temporary_dir
);
741 int lookup_paths_reduce(LookupPaths
*p
) {
742 _cleanup_free_
struct stat
*stats
= NULL
;
743 size_t n_stats
= 0, allocated
= 0;
749 /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
750 * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
751 * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
752 * account when following symlinks. When we have no root path set this restriction does not apply however. */
757 while (p
->search_path
[c
]) {
761 /* Never strip the transient and control directories from the path */
762 if (path_equal_ptr(p
->search_path
[c
], p
->transient
) ||
763 path_equal_ptr(p
->search_path
[c
], p
->persistent_control
) ||
764 path_equal_ptr(p
->search_path
[c
], p
->runtime_control
)) {
770 r
= lstat(p
->search_path
[c
], &st
);
772 r
= stat(p
->search_path
[c
], &st
);
777 /* If something we don't grok happened, let's better leave it in. */
778 log_debug_errno(errno
, "Failed to stat %s: %m", p
->search_path
[c
]);
783 for (k
= 0; k
< n_stats
; k
++) {
784 if (stats
[k
].st_dev
== st
.st_dev
&&
785 stats
[k
].st_ino
== st
.st_ino
)
789 if (k
< n_stats
) /* Is there already an entry with the same device/inode? */
792 if (!GREEDY_REALLOC(stats
, allocated
, n_stats
+1))
795 stats
[n_stats
++] = st
;
800 free(p
->search_path
[c
]);
801 memmove(p
->search_path
+ c
,
802 p
->search_path
+ c
+ 1,
803 (strv_length(p
->search_path
+ c
+ 1) + 1) * sizeof(char*));
806 if (strv_isempty(p
->search_path
)) {
807 log_debug("Ignoring unit files.");
808 p
->search_path
= strv_free(p
->search_path
);
810 _cleanup_free_
char *t
;
812 t
= strv_join(p
->search_path
, "\n\t");
816 log_debug("Looking for unit files in (higher priority first):\n\t%s", t
);
822 int lookup_paths_mkdir_generator(LookupPaths
*p
) {
827 if (!p
->generator
|| !p
->generator_early
|| !p
->generator_late
)
830 r
= mkdir_p_label(p
->generator
, 0755);
832 q
= mkdir_p_label(p
->generator_early
, 0755);
836 q
= mkdir_p_label(p
->generator_late
, 0755);
843 void lookup_paths_trim_generator(LookupPaths
*p
) {
846 /* Trim empty dirs */
849 (void) rmdir(p
->generator
);
850 if (p
->generator_early
)
851 (void) rmdir(p
->generator_early
);
852 if (p
->generator_late
)
853 (void) rmdir(p
->generator_late
);
856 void lookup_paths_flush_generator(LookupPaths
*p
) {
859 /* Flush the generated unit files in full */
862 (void) rm_rf(p
->generator
, REMOVE_ROOT
);
863 if (p
->generator_early
)
864 (void) rm_rf(p
->generator_early
, REMOVE_ROOT
);
865 if (p
->generator_late
)
866 (void) rm_rf(p
->generator_late
, REMOVE_ROOT
);
868 if (p
->temporary_dir
)
869 (void) rm_rf(p
->temporary_dir
, REMOVE_ROOT
);
872 char **generator_binary_paths(UnitFileScope scope
) {
876 case UNIT_FILE_SYSTEM
:
877 return strv_new("/run/systemd/system-generators",
878 "/etc/systemd/system-generators",
879 "/usr/local/lib/systemd/system-generators",
880 SYSTEM_GENERATOR_PATH
,
883 case UNIT_FILE_GLOBAL
:
885 return strv_new("/run/systemd/user-generators",
886 "/etc/systemd/user-generators",
887 "/usr/local/lib/systemd/user-generators",
892 assert_not_reached("Hmm, unexpected scope.");