1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
29 #include "path-util.h"
31 #include "string-util.h"
32 #include "path-lookup.h"
34 int user_config_home(char **config_home
) {
38 e
= getenv("XDG_CONFIG_HOME");
40 r
= strappend(e
, "/systemd/user");
49 home
= getenv("HOME");
51 r
= strappend(home
, "/.config/systemd/user");
63 int user_runtime_dir(char **runtime_dir
) {
67 e
= getenv("XDG_RUNTIME_DIR");
69 r
= strappend(e
, "/systemd/user");
80 static int user_data_home_dir(char **dir
, const char *suffix
) {
84 /* We don't treat /etc/xdg/systemd here as the spec
85 * suggests because we assume that that is a link to
86 * /etc/systemd/ anyway. */
88 e
= getenv("XDG_DATA_HOME");
90 res
= strappend(e
, suffix
);
94 home
= getenv("HOME");
96 res
= strjoin(home
, "/.local/share", suffix
, NULL
);
107 static char** user_dirs(
108 const char *generator
,
109 const char *generator_early
,
110 const char *generator_late
) {
112 const char * const config_unit_paths
[] = {
113 USER_CONFIG_UNIT_PATH
,
118 const char * const runtime_unit_path
= "/run/systemd/user";
120 const char * const data_unit_paths
[] = {
121 "/usr/local/lib/systemd/user",
122 "/usr/local/share/systemd/user",
124 "/usr/lib/systemd/user",
125 "/usr/share/systemd/user",
130 _cleanup_free_
char *config_home
= NULL
, *runtime_dir
= NULL
, *data_home
= NULL
;
131 _cleanup_strv_free_
char **config_dirs
= NULL
, **data_dirs
= NULL
;
132 _cleanup_free_
char **res
= NULL
;
136 /* Implement the mechanisms defined in
138 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
140 * We look in both the config and the data dirs because we
141 * want to encourage that distributors ship their unit files
142 * as data, and allow overriding as configuration.
145 if (user_config_home(&config_home
) < 0)
148 if (user_runtime_dir(&runtime_dir
) < 0)
151 e
= getenv("XDG_CONFIG_DIRS");
153 config_dirs
= strv_split(e
, ":");
158 r
= user_data_home_dir(&data_home
, "/systemd/user");
162 e
= getenv("XDG_DATA_DIRS");
164 data_dirs
= strv_split(e
, ":");
166 data_dirs
= strv_new("/usr/local/share",
172 /* Now merge everything we found. */
174 if (strv_extend(&res
, generator_early
) < 0)
178 if (strv_extend(&res
, config_home
) < 0)
181 if (!strv_isempty(config_dirs
))
182 if (strv_extend_strv_concat(&res
, config_dirs
, "/systemd/user") < 0)
185 if (strv_extend_strv(&res
, (char**) config_unit_paths
, false) < 0)
189 if (strv_extend(&res
, runtime_dir
) < 0)
192 if (strv_extend(&res
, runtime_unit_path
) < 0)
196 if (strv_extend(&res
, generator
) < 0)
200 if (strv_extend(&res
, data_home
) < 0)
203 if (!strv_isempty(data_dirs
))
204 if (strv_extend_strv_concat(&res
, data_dirs
, "/systemd/user") < 0)
207 if (strv_extend_strv(&res
, (char**) data_unit_paths
, false) < 0)
211 if (strv_extend(&res
, generator_late
) < 0)
214 if (path_strv_make_absolute_cwd(res
) < 0)
222 char **generator_paths(ManagerRunningAs running_as
) {
223 if (running_as
== MANAGER_USER
)
224 return strv_new("/run/systemd/user-generators",
225 "/etc/systemd/user-generators",
226 "/usr/local/lib/systemd/user-generators",
230 return strv_new("/run/systemd/system-generators",
231 "/etc/systemd/system-generators",
232 "/usr/local/lib/systemd/system-generators",
233 SYSTEM_GENERATOR_PATH
,
237 int lookup_paths_init(
239 ManagerRunningAs running_as
,
241 const char *root_dir
,
242 const char *generator
,
243 const char *generator_early
,
244 const char *generator_late
) {
247 bool append
= false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
252 /* First priority is whatever has been passed to us via env
254 e
= getenv("SYSTEMD_UNIT_PATH");
256 if (endswith(e
, ":")) {
257 e
= strndupa(e
, strlen(e
) - 1);
261 /* FIXME: empty components in other places should be
264 r
= path_split_and_make_absolute(e
, &p
->unit_path
);
270 if (!p
->unit_path
|| append
) {
271 /* Let's figure something out. */
273 _cleanup_strv_free_
char **unit_path
;
275 /* For the user units we include share/ in the search
276 * path in order to comply with the XDG basedir spec.
277 * For the system stuff we avoid such nonsense. OTOH
278 * we include /lib in the search path for the system
279 * stuff but avoid it for user stuff. */
281 if (running_as
== MANAGER_USER
) {
283 unit_path
= user_dirs(generator
, generator_early
, generator_late
);
285 unit_path
= strv_new(
286 /* If you modify this you also want to modify
287 * systemduserunitpath= in systemd.pc.in, and
288 * the arrays in user_dirs() above! */
289 STRV_IFNOTNULL(generator_early
),
290 USER_CONFIG_UNIT_PATH
,
293 STRV_IFNOTNULL(generator
),
294 "/usr/local/lib/systemd/user",
295 "/usr/local/share/systemd/user",
297 "/usr/lib/systemd/user",
298 "/usr/share/systemd/user",
299 STRV_IFNOTNULL(generator_late
),
302 unit_path
= strv_new(
303 /* If you modify this you also want to modify
304 * systemdsystemunitpath= in systemd.pc.in! */
305 STRV_IFNOTNULL(generator_early
),
306 SYSTEM_CONFIG_UNIT_PATH
,
307 "/etc/systemd/system",
308 "/run/systemd/system",
309 STRV_IFNOTNULL(generator
),
310 "/usr/local/lib/systemd/system",
311 SYSTEM_DATA_UNIT_PATH
,
312 "/usr/lib/systemd/system",
313 #ifdef HAVE_SPLIT_USR
314 "/lib/systemd/system",
316 STRV_IFNOTNULL(generator_late
),
322 r
= strv_extend_strv(&p
->unit_path
, unit_path
, false);
327 if (!path_strv_resolve_uniq(p
->unit_path
, root_dir
))
330 if (!strv_isempty(p
->unit_path
)) {
331 _cleanup_free_
char *t
= strv_join(p
->unit_path
, "\n\t");
334 log_debug("Looking for unit files in (higher priority first):\n\t%s", t
);
336 log_debug("Ignoring unit files.");
337 p
->unit_path
= strv_free(p
->unit_path
);
340 if (running_as
== MANAGER_SYSTEM
) {
341 #ifdef HAVE_SYSV_COMPAT
342 /* /etc/init.d/ compatibility does not matter to users */
344 e
= getenv("SYSTEMD_SYSVINIT_PATH");
346 r
= path_split_and_make_absolute(e
, &p
->sysvinit_path
);
350 p
->sysvinit_path
= NULL
;
352 if (strv_isempty(p
->sysvinit_path
)) {
353 strv_free(p
->sysvinit_path
);
355 p
->sysvinit_path
= strv_new(
356 SYSTEM_SYSVINIT_PATH
, /* /etc/init.d/ */
358 if (!p
->sysvinit_path
)
362 e
= getenv("SYSTEMD_SYSVRCND_PATH");
364 r
= path_split_and_make_absolute(e
, &p
->sysvrcnd_path
);
368 p
->sysvrcnd_path
= NULL
;
370 if (strv_isempty(p
->sysvrcnd_path
)) {
371 strv_free(p
->sysvrcnd_path
);
373 p
->sysvrcnd_path
= strv_new(
374 SYSTEM_SYSVRCND_PATH
, /* /etc/rcN.d/ */
376 if (!p
->sysvrcnd_path
)
380 if (!path_strv_resolve_uniq(p
->sysvinit_path
, root_dir
))
383 if (!path_strv_resolve_uniq(p
->sysvrcnd_path
, root_dir
))
386 if (!strv_isempty(p
->sysvinit_path
)) {
387 _cleanup_free_
char *t
= strv_join(p
->sysvinit_path
, "\n\t");
390 log_debug("Looking for SysV init scripts in:\n\t%s", t
);
392 log_debug("Ignoring SysV init scripts.");
393 p
->sysvinit_path
= strv_free(p
->sysvinit_path
);
396 if (!strv_isempty(p
->sysvrcnd_path
)) {
397 _cleanup_free_
char *t
=
398 strv_join(p
->sysvrcnd_path
, "\n\t");
402 log_debug("Looking for SysV rcN.d links in:\n\t%s", t
);
404 log_debug("Ignoring SysV rcN.d links.");
405 p
->sysvrcnd_path
= strv_free(p
->sysvrcnd_path
);
408 log_debug("SysV init scripts and rcN.d links support disabled");
415 void lookup_paths_free(LookupPaths
*p
) {
418 p
->unit_path
= strv_free(p
->unit_path
);
420 #ifdef HAVE_SYSV_COMPAT
421 p
->sysvinit_path
= strv_free(p
->sysvinit_path
);
422 p
->sysvrcnd_path
= strv_free(p
->sysvrcnd_path
);
426 int lookup_paths_init_from_scope(LookupPaths
*paths
,
428 const char *root_dir
) {
431 assert(scope
< _UNIT_FILE_SCOPE_MAX
);
435 return lookup_paths_init(paths
,
436 scope
== UNIT_FILE_SYSTEM
? MANAGER_SYSTEM
: MANAGER_USER
,
437 scope
== UNIT_FILE_USER
,