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"
30 #include "path-lookup.h"
32 int user_config_home(char **config_home) {
36 e = getenv("XDG_CONFIG_HOME");
38 r = strappend(e, "/systemd/user");
47 home = getenv("HOME");
49 r = strappend(home, "/.config/systemd/user");
61 int user_runtime_dir(char **runtime_dir) {
65 e = getenv("XDG_RUNTIME_DIR");
67 r = strappend(e, "/systemd/user");
78 static int user_data_home_dir(char **dir, const char *suffix) {
82 /* We don't treat /etc/xdg/systemd here as the spec
83 * suggests because we assume that that is a link to
84 * /etc/systemd/ anyway. */
86 e = getenv("XDG_DATA_HOME");
88 res = strappend(e, suffix);
92 home = getenv("HOME");
94 res = strjoin(home, "/.local/share", suffix, NULL);
105 static char** user_dirs(
106 const char *generator,
107 const char *generator_early,
108 const char *generator_late) {
110 const char * const config_unit_paths[] = {
111 USER_CONFIG_UNIT_PATH,
116 const char * const runtime_unit_path = "/run/systemd/user";
118 const char * const data_unit_paths[] = {
119 "/usr/local/lib/systemd/user",
120 "/usr/local/share/systemd/user",
122 "/usr/lib/systemd/user",
123 "/usr/share/systemd/user",
128 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
129 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
130 _cleanup_free_ char **res = NULL;
134 /* Implement the mechanisms defined in
136 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
138 * We look in both the config and the data dirs because we
139 * want to encourage that distributors ship their unit files
140 * as data, and allow overriding as configuration.
143 if (user_config_home(&config_home) < 0)
146 if (user_runtime_dir(&runtime_dir) < 0)
149 e = getenv("XDG_CONFIG_DIRS");
151 config_dirs = strv_split(e, ":");
156 r = user_data_home_dir(&data_home, "/systemd/user");
160 e = getenv("XDG_DATA_DIRS");
162 data_dirs = strv_split(e, ":");
164 data_dirs = strv_new("/usr/local/share",
170 /* Now merge everything we found. */
172 if (strv_extend(&res, generator_early) < 0)
176 if (strv_extend(&res, config_home) < 0)
179 if (!strv_isempty(config_dirs))
180 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
183 if (strv_extend_strv(&res, (char**) config_unit_paths) < 0)
187 if (strv_extend(&res, runtime_dir) < 0)
190 if (strv_extend(&res, runtime_unit_path) < 0)
194 if (strv_extend(&res, generator) < 0)
198 if (strv_extend(&res, data_home) < 0)
201 if (!strv_isempty(data_dirs))
202 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
205 if (strv_extend_strv(&res, (char**) data_unit_paths) < 0)
209 if (strv_extend(&res, generator_late) < 0)
212 if (!path_strv_make_absolute_cwd(res))
220 char **generator_paths(SystemdRunningAs running_as) {
221 if (running_as == SYSTEMD_USER)
222 return strv_new("/run/systemd/user-generators",
223 "/etc/systemd/user-generators",
224 "/usr/local/lib/systemd/user-generators",
228 return strv_new("/run/systemd/system-generators",
229 "/etc/systemd/system-generators",
230 "/usr/local/lib/systemd/system-generators",
231 SYSTEM_GENERATOR_PATH,
235 int lookup_paths_init(
237 SystemdRunningAs running_as,
239 const char *root_dir,
240 const char *generator,
241 const char *generator_early,
242 const char *generator_late) {
245 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
249 /* First priority is whatever has been passed to us via env
251 e = getenv("SYSTEMD_UNIT_PATH");
253 if (endswith(e, ":")) {
254 e = strndupa(e, strlen(e) - 1);
258 /* FIXME: empty components in other places should be
261 p->unit_path = path_split_and_make_absolute(e);
267 if (!p->unit_path || append) {
268 /* Let's figure something out. */
270 _cleanup_strv_free_ char **unit_path;
273 /* For the user units we include share/ in the search
274 * path in order to comply with the XDG basedir spec.
275 * For the system stuff we avoid such nonsense. OTOH
276 * we include /lib in the search path for the system
277 * stuff but avoid it for user stuff. */
279 if (running_as == SYSTEMD_USER) {
281 unit_path = user_dirs(generator, generator_early, generator_late);
283 unit_path = strv_new(
284 /* If you modify this you also want to modify
285 * systemduserunitpath= in systemd.pc.in, and
286 * the arrays in user_dirs() above! */
287 STRV_IFNOTNULL(generator_early),
288 USER_CONFIG_UNIT_PATH,
291 STRV_IFNOTNULL(generator),
292 "/usr/local/lib/systemd/user",
293 "/usr/local/share/systemd/user",
295 "/usr/lib/systemd/user",
296 "/usr/share/systemd/user",
297 STRV_IFNOTNULL(generator_late),
300 unit_path = strv_new(
301 /* If you modify this you also want to modify
302 * systemdsystemunitpath= in systemd.pc.in! */
303 STRV_IFNOTNULL(generator_early),
304 SYSTEM_CONFIG_UNIT_PATH,
305 "/etc/systemd/system",
306 "/run/systemd/system",
307 STRV_IFNOTNULL(generator),
308 "/usr/local/lib/systemd/system",
309 SYSTEM_DATA_UNIT_PATH,
310 "/usr/lib/systemd/system",
311 #ifdef HAVE_SPLIT_USR
312 "/lib/systemd/system",
314 STRV_IFNOTNULL(generator_late),
320 r = strv_extend_strv(&p->unit_path, unit_path);
325 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
328 if (!strv_isempty(p->unit_path)) {
329 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
332 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
334 log_debug("Ignoring unit files.");
335 strv_free(p->unit_path);
339 if (running_as == SYSTEMD_SYSTEM) {
340 #ifdef HAVE_SYSV_COMPAT
341 /* /etc/init.d/ compatibility does not matter to users */
343 e = getenv("SYSTEMD_SYSVINIT_PATH");
345 p->sysvinit_path = path_split_and_make_absolute(e);
346 if (!p->sysvinit_path)
349 p->sysvinit_path = NULL;
351 if (strv_isempty(p->sysvinit_path)) {
352 strv_free(p->sysvinit_path);
354 p->sysvinit_path = strv_new(
355 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
357 if (!p->sysvinit_path)
361 e = getenv("SYSTEMD_SYSVRCND_PATH");
363 p->sysvrcnd_path = path_split_and_make_absolute(e);
364 if (!p->sysvrcnd_path)
367 p->sysvrcnd_path = NULL;
369 if (strv_isempty(p->sysvrcnd_path)) {
370 strv_free(p->sysvrcnd_path);
372 p->sysvrcnd_path = strv_new(
373 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
375 if (!p->sysvrcnd_path)
379 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
382 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
385 if (!strv_isempty(p->sysvinit_path)) {
386 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
389 log_debug("Looking for SysV init scripts in:\n\t%s", t);
391 log_debug("Ignoring SysV init scripts.");
392 strv_free(p->sysvinit_path);
393 p->sysvinit_path = NULL;
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 strv_free(p->sysvrcnd_path);
406 p->sysvrcnd_path = NULL;
409 log_debug("SysV init scripts and rcN.d links support disabled");
416 void lookup_paths_free(LookupPaths *p) {
419 strv_free(p->unit_path);
422 #ifdef HAVE_SYSV_COMPAT
423 strv_free(p->sysvinit_path);
424 strv_free(p->sysvrcnd_path);
425 p->sysvinit_path = p->sysvrcnd_path = NULL;
429 int lookup_paths_init_from_scope(LookupPaths *paths,
431 const char *root_dir) {
434 assert(scope < _UNIT_FILE_SCOPE_MAX);
438 return lookup_paths_init(paths,
439 scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
440 scope == UNIT_FILE_USER,