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/>.
32 #include "path-util.h"
33 #include "path-lookup.h"
35 int user_config_home(char **config_home) {
39 e = getenv("XDG_CONFIG_HOME");
41 r = strappend(e, "/systemd/user");
50 home = getenv("HOME");
52 r = strappend(home, "/.config/systemd/user");
64 int user_runtime_dir(char **runtime_dir) {
68 e = getenv("XDG_RUNTIME_DIR");
70 r = strappend(e, "/systemd/user");
81 static int user_data_home_dir(char **dir, const char *suffix) {
85 /* We don't treat /etc/xdg/systemd here as the spec
86 * suggests because we assume that that is a link to
87 * /etc/systemd/ anyway. */
89 e = getenv("XDG_DATA_HOME");
91 res = strappend(e, suffix);
95 home = getenv("HOME");
97 res = strjoin(home, "/.local/share", suffix, NULL);
108 static char** user_dirs(
109 const char *generator,
110 const char *generator_early,
111 const char *generator_late) {
113 const char * const config_unit_paths[] = {
114 USER_CONFIG_UNIT_PATH,
119 const char * const runtime_unit_path = "/run/systemd/user";
121 const char * const data_unit_paths[] = {
122 "/usr/local/lib/systemd/user",
123 "/usr/local/share/systemd/user",
125 "/usr/lib/systemd/user",
126 "/usr/share/systemd/user",
131 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
132 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
133 _cleanup_free_ char **res = NULL;
137 /* Implement the mechanisms defined in
139 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
141 * We look in both the config and the data dirs because we
142 * want to encourage that distributors ship their unit files
143 * as data, and allow overriding as configuration.
146 if (user_config_home(&config_home) < 0)
149 if (user_runtime_dir(&runtime_dir) < 0)
152 e = getenv("XDG_CONFIG_DIRS");
154 config_dirs = strv_split(e, ":");
159 r = user_data_home_dir(&data_home, "/systemd/user");
163 e = getenv("XDG_DATA_DIRS");
165 data_dirs = strv_split(e, ":");
167 data_dirs = strv_new("/usr/local/share",
173 /* Now merge everything we found. */
175 if (strv_extend(&res, generator_early) < 0)
179 if (strv_extend(&res, config_home) < 0)
182 if (!strv_isempty(config_dirs))
183 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
186 if (strv_extend_strv(&res, (char**) config_unit_paths) < 0)
190 if (strv_extend(&res, runtime_dir) < 0)
193 if (strv_extend(&res, runtime_unit_path) < 0)
197 if (strv_extend(&res, generator) < 0)
201 if (strv_extend(&res, data_home) < 0)
204 if (!strv_isempty(data_dirs))
205 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
208 if (strv_extend_strv(&res, (char**) data_unit_paths) < 0)
212 if (strv_extend(&res, generator_late) < 0)
215 if (!path_strv_make_absolute_cwd(res))
223 char **generator_paths(SystemdRunningAs running_as) {
224 if (running_as == SYSTEMD_USER)
225 return strv_new("/run/systemd/user-generators",
226 "/etc/systemd/user-generators",
227 "/usr/local/lib/systemd/user-generators",
231 return strv_new("/run/systemd/system-generators",
232 "/etc/systemd/system-generators",
233 "/usr/local/lib/systemd/system-generators",
234 SYSTEM_GENERATOR_PATH,
238 int lookup_paths_init(
240 SystemdRunningAs running_as,
242 const char *root_dir,
243 const char *generator,
244 const char *generator_early,
245 const char *generator_late) {
248 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 p->unit_path = path_split_and_make_absolute(e);
270 if (!p->unit_path || append) {
271 /* Let's figure something out. */
273 _cleanup_strv_free_ char **unit_path;
276 /* For the user units we include share/ in the search
277 * path in order to comply with the XDG basedir spec.
278 * For the system stuff we avoid such nonsense. OTOH
279 * we include /lib in the search path for the system
280 * stuff but avoid it for user stuff. */
282 if (running_as == SYSTEMD_USER) {
284 unit_path = user_dirs(generator, generator_early, generator_late);
286 unit_path = strv_new(
287 /* If you modify this you also want to modify
288 * systemduserunitpath= in systemd.pc.in, and
289 * the arrays in user_dirs() above! */
290 STRV_IFNOTNULL(generator_early),
291 USER_CONFIG_UNIT_PATH,
294 STRV_IFNOTNULL(generator),
295 "/usr/local/lib/systemd/user",
296 "/usr/local/share/systemd/user",
298 "/usr/lib/systemd/user",
299 "/usr/share/systemd/user",
300 STRV_IFNOTNULL(generator_late),
303 unit_path = strv_new(
304 /* If you modify this you also want to modify
305 * systemdsystemunitpath= in systemd.pc.in! */
306 STRV_IFNOTNULL(generator_early),
307 SYSTEM_CONFIG_UNIT_PATH,
308 "/etc/systemd/system",
309 "/run/systemd/system",
310 STRV_IFNOTNULL(generator),
311 "/usr/local/lib/systemd/system",
312 SYSTEM_DATA_UNIT_PATH,
313 "/usr/lib/systemd/system",
314 #ifdef HAVE_SPLIT_USR
315 "/lib/systemd/system",
317 STRV_IFNOTNULL(generator_late),
323 r = strv_extend_strv(&p->unit_path, unit_path);
328 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
331 if (!strv_isempty(p->unit_path)) {
332 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
335 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
337 log_debug("Ignoring unit files.");
338 strv_free(p->unit_path);
342 if (running_as == SYSTEMD_SYSTEM) {
343 #ifdef HAVE_SYSV_COMPAT
344 /* /etc/init.d/ compatibility does not matter to users */
346 e = getenv("SYSTEMD_SYSVINIT_PATH");
348 p->sysvinit_path = path_split_and_make_absolute(e);
349 if (!p->sysvinit_path)
352 p->sysvinit_path = NULL;
354 if (strv_isempty(p->sysvinit_path)) {
355 strv_free(p->sysvinit_path);
357 p->sysvinit_path = strv_new(
358 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
360 if (!p->sysvinit_path)
364 e = getenv("SYSTEMD_SYSVRCND_PATH");
366 p->sysvrcnd_path = path_split_and_make_absolute(e);
367 if (!p->sysvrcnd_path)
370 p->sysvrcnd_path = NULL;
372 if (strv_isempty(p->sysvrcnd_path)) {
373 strv_free(p->sysvrcnd_path);
375 p->sysvrcnd_path = strv_new(
376 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
378 if (!p->sysvrcnd_path)
382 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
385 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
388 if (!strv_isempty(p->sysvinit_path)) {
389 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
392 log_debug("Looking for SysV init scripts in:\n\t%s", t);
394 log_debug("Ignoring SysV init scripts.");
395 strv_free(p->sysvinit_path);
396 p->sysvinit_path = NULL;
399 if (!strv_isempty(p->sysvrcnd_path)) {
400 _cleanup_free_ char *t =
401 strv_join(p->sysvrcnd_path, "\n\t");
405 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
407 log_debug("Ignoring SysV rcN.d links.");
408 strv_free(p->sysvrcnd_path);
409 p->sysvrcnd_path = NULL;
412 log_debug("SysV init scripts and rcN.d links support disabled");
419 void lookup_paths_free(LookupPaths *p) {
422 strv_free(p->unit_path);
425 #ifdef HAVE_SYSV_COMPAT
426 strv_free(p->sysvinit_path);
427 strv_free(p->sysvrcnd_path);
428 p->sysvinit_path = p->sysvrcnd_path = NULL;
432 int lookup_paths_init_from_scope(LookupPaths *paths,
434 const char *root_dir) {
437 assert(scope < _UNIT_FILE_SCOPE_MAX);
441 return lookup_paths_init(paths,
442 scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
443 scope == UNIT_FILE_USER,