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 static char** user_dirs(
65 const char *generator,
66 const char *generator_early,
67 const char *generator_late) {
69 const char * const config_unit_paths[] = {
70 USER_CONFIG_UNIT_PATH,
76 const char * const data_unit_paths[] = {
77 "/usr/local/lib/systemd/user",
78 "/usr/local/share/systemd/user",
80 "/usr/lib/systemd/user",
81 "/usr/share/systemd/user",
86 _cleanup_free_ char *config_home = NULL, *data_home = NULL;
87 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
90 /* Implement the mechanisms defined in
92 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
94 * We look in both the config and the data dirs because we
95 * want to encourage that distributors ship their unit files
96 * as data, and allow overriding as configuration.
99 if (user_config_home(&config_home) < 0)
102 home = getenv("HOME");
104 e = getenv("XDG_CONFIG_DIRS");
106 config_dirs = strv_split(e, ":");
111 /* We don't treat /etc/xdg/systemd here as the spec
112 * suggests because we assume that that is a link to
113 * /etc/systemd/ anyway. */
115 e = getenv("XDG_DATA_HOME");
117 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
121 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
125 e = getenv("XDG_DATA_DIRS");
127 data_dirs = strv_split(e, ":");
129 data_dirs = strv_new("/usr/local/share",
135 /* Now merge everything we found. */
137 if (strv_extend(&r, generator_early) < 0)
141 if (strv_extend(&r, config_home) < 0)
144 if (!strv_isempty(config_dirs))
145 if (strv_extend_strv_concat(&r, config_dirs, "/systemd/user") < 0)
148 if (strv_extend_strv(&r, (char**) config_unit_paths) < 0)
152 if (strv_extend(&r, generator) < 0)
156 if (strv_extend(&r, data_home) < 0)
159 if (!strv_isempty(data_dirs))
160 if (strv_extend_strv_concat(&r, data_dirs, "/systemd/user") < 0)
163 if (strv_extend_strv(&r, (char**) data_unit_paths) < 0)
167 if (strv_extend(&r, generator_late) < 0)
170 if (!path_strv_make_absolute_cwd(r))
180 int lookup_paths_init(
182 SystemdRunningAs running_as,
184 const char *root_dir,
185 const char *generator,
186 const char *generator_early,
187 const char *generator_late) {
190 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
194 /* First priority is whatever has been passed to us via env
196 e = getenv("SYSTEMD_UNIT_PATH");
198 if (endswith(e, ":")) {
199 e = strndupa(e, strlen(e) - 1);
203 /* FIXME: empty components in other places should be
206 p->unit_path = path_split_and_make_absolute(e);
212 if (!p->unit_path || append) {
213 /* Let's figure something out. */
215 _cleanup_strv_free_ char **unit_path;
218 /* For the user units we include share/ in the search
219 * path in order to comply with the XDG basedir spec.
220 * For the system stuff we avoid such nonsense. OTOH
221 * we include /lib in the search path for the system
222 * stuff but avoid it for user stuff. */
224 if (running_as == SYSTEMD_USER) {
226 unit_path = user_dirs(generator, generator_early, generator_late);
228 unit_path = strv_new(
229 /* If you modify this you also want to modify
230 * systemduserunitpath= in systemd.pc.in, and
231 * the arrays in user_dirs() above! */
232 STRV_IFNOTNULL(generator_early),
233 USER_CONFIG_UNIT_PATH,
236 STRV_IFNOTNULL(generator),
237 "/usr/local/lib/systemd/user",
238 "/usr/local/share/systemd/user",
240 "/usr/lib/systemd/user",
241 "/usr/share/systemd/user",
242 STRV_IFNOTNULL(generator_late),
245 unit_path = strv_new(
246 /* If you modify this you also want to modify
247 * systemdsystemunitpath= in systemd.pc.in! */
248 STRV_IFNOTNULL(generator_early),
249 SYSTEM_CONFIG_UNIT_PATH,
250 "/etc/systemd/system",
251 "/run/systemd/system",
252 STRV_IFNOTNULL(generator),
253 "/usr/local/lib/systemd/system",
254 SYSTEM_DATA_UNIT_PATH,
255 "/usr/lib/systemd/system",
256 #ifdef HAVE_SPLIT_USR
257 "/lib/systemd/system",
259 STRV_IFNOTNULL(generator_late),
265 r = strv_extend_strv(&p->unit_path, unit_path);
270 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
273 if (!strv_isempty(p->unit_path)) {
274 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
277 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
279 log_debug("Ignoring unit files.");
280 strv_free(p->unit_path);
284 if (running_as == SYSTEMD_SYSTEM) {
285 #ifdef HAVE_SYSV_COMPAT
286 /* /etc/init.d/ compatibility does not matter to users */
288 e = getenv("SYSTEMD_SYSVINIT_PATH");
290 p->sysvinit_path = path_split_and_make_absolute(e);
291 if (!p->sysvinit_path)
294 p->sysvinit_path = NULL;
296 if (strv_isempty(p->sysvinit_path)) {
297 strv_free(p->sysvinit_path);
299 p->sysvinit_path = strv_new(
300 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
302 if (!p->sysvinit_path)
306 e = getenv("SYSTEMD_SYSVRCND_PATH");
308 p->sysvrcnd_path = path_split_and_make_absolute(e);
309 if (!p->sysvrcnd_path)
312 p->sysvrcnd_path = NULL;
314 if (strv_isempty(p->sysvrcnd_path)) {
315 strv_free(p->sysvrcnd_path);
317 p->sysvrcnd_path = strv_new(
318 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
320 if (!p->sysvrcnd_path)
324 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
327 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
330 if (!strv_isempty(p->sysvinit_path)) {
331 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
334 log_debug("Looking for SysV init scripts in:\n\t%s", t);
336 log_debug("Ignoring SysV init scripts.");
337 strv_free(p->sysvinit_path);
338 p->sysvinit_path = NULL;
341 if (!strv_isempty(p->sysvrcnd_path)) {
342 _cleanup_free_ char *t =
343 strv_join(p->sysvrcnd_path, "\n\t");
347 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
349 log_debug("Ignoring SysV rcN.d links.");
350 strv_free(p->sysvrcnd_path);
351 p->sysvrcnd_path = NULL;
354 log_debug("SysV init scripts and rcN.d links support disabled");
361 void lookup_paths_free(LookupPaths *p) {
364 strv_free(p->unit_path);
367 #ifdef HAVE_SYSV_COMPAT
368 strv_free(p->sysvinit_path);
369 strv_free(p->sysvrcnd_path);
370 p->sysvinit_path = p->sysvrcnd_path = NULL;