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/>.
31 #include "path-util.h"
32 #include "path-lookup.h"
34 int user_config_home(char **config_home) {
37 e = getenv("XDG_CONFIG_HOME");
39 if (asprintf(config_home, "%s/systemd/user", e) < 0)
46 home = getenv("HOME");
48 if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
58 static char** user_dirs(
59 const char *generator,
60 const char *generator_early,
61 const char *generator_late) {
63 const char * const config_unit_paths[] = {
64 USER_CONFIG_UNIT_PATH,
70 const char * const data_unit_paths[] = {
71 "/usr/local/lib/systemd/user",
72 "/usr/local/share/systemd/user",
74 "/usr/lib/systemd/user",
75 "/usr/share/systemd/user",
80 char *config_home = NULL, *data_home = NULL;
81 char **config_dirs = NULL, **data_dirs = NULL;
84 /* Implement the mechanisms defined in
86 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
88 * We look in both the config and the data dirs because we
89 * want to encourage that distributors ship their unit files
90 * as data, and allow overriding as configuration.
93 if (user_config_home(&config_home) < 0)
96 home = getenv("HOME");
98 e = getenv("XDG_CONFIG_DIRS");
100 config_dirs = strv_split(e, ":");
105 /* We don't treat /etc/xdg/systemd here as the spec
106 * suggests because we assume that that is a link to
107 * /etc/systemd/ anyway. */
109 e = getenv("XDG_DATA_HOME");
111 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
115 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
118 /* There is really no need for two unit dirs in $HOME,
119 * except to be fully compliant with the XDG spec. We
120 * now try to link the two dirs, so that we can
121 * minimize disk seeks a little. Further down we'll
122 * then filter out this link, if it is actually is
125 mkdir_parents_label(data_home, 0777);
126 (void) symlink("../../../.config/systemd/user", data_home);
129 e = getenv("XDG_DATA_DIRS");
131 data_dirs = strv_split(e, ":");
133 data_dirs = strv_new("/usr/local/share",
139 /* Now merge everything we found. */
140 if (generator_early) {
141 t = strv_append(r, generator_early);
149 t = strv_append(r, config_home);
156 if (!strv_isempty(config_dirs)) {
157 t = strv_merge_concat(r, config_dirs, "/systemd/user");
164 t = strv_merge(r, (char**) config_unit_paths);
171 t = strv_append(r, generator);
179 t = strv_append(r, data_home);
186 if (!strv_isempty(data_dirs)) {
187 t = strv_merge_concat(r, data_dirs, "/systemd/user");
194 t = strv_merge(r, (char**) data_unit_paths);
200 if (generator_late) {
201 t = strv_append(r, generator_late);
208 if (!path_strv_make_absolute_cwd(r))
213 strv_free(config_dirs);
215 strv_free(data_dirs);
225 int lookup_paths_init(
227 ManagerRunningAs running_as,
229 const char *generator,
230 const char *generator_early,
231 const char *generator_late) {
238 /* First priority is whatever has been passed to us via env
240 e = getenv("SYSTEMD_UNIT_PATH");
242 p->unit_path = path_split_and_make_absolute(e);
248 if (strv_isempty(p->unit_path)) {
249 /* Nothing is set, so let's figure something out. */
250 strv_free(p->unit_path);
252 /* For the user units we include share/ in the search
253 * path in order to comply with the XDG basedir
254 * spec. For the system stuff we avoid such
255 * nonsense. OTOH we include /lib in the search path
256 * for the system stuff but avoid it for user
259 if (running_as == MANAGER_USER) {
262 p->unit_path = user_dirs(generator, generator_early, generator_late);
264 p->unit_path = strv_new(
265 /* If you modify this you also want to modify
266 * systemduserunitpath= in systemd.pc.in, and
267 * the arrays in user_dirs() above! */
268 STRV_IFNOTNULL(generator_early),
269 USER_CONFIG_UNIT_PATH,
272 STRV_IFNOTNULL(generator),
273 "/usr/local/lib/systemd/user",
274 "/usr/local/share/systemd/user",
276 "/usr/lib/systemd/user",
277 "/usr/share/systemd/user",
278 STRV_IFNOTNULL(generator_late),
285 p->unit_path = strv_new(
286 /* If you modify this you also want to modify
287 * systemdsystemunitpath= in systemd.pc.in! */
288 STRV_IFNOTNULL(generator_early),
289 SYSTEM_CONFIG_UNIT_PATH,
290 "/etc/systemd/system",
291 "/run/systemd/system",
292 STRV_IFNOTNULL(generator),
293 "/usr/local/lib/systemd/system",
294 SYSTEM_DATA_UNIT_PATH,
295 "/usr/lib/systemd/system",
296 #ifdef HAVE_SPLIT_USR
297 "/lib/systemd/system",
299 STRV_IFNOTNULL(generator_late),
307 if (!path_strv_canonicalize(p->unit_path))
310 strv_uniq(p->unit_path);
311 path_strv_remove_empty(p->unit_path);
313 if (!strv_isempty(p->unit_path)) {
315 t = strv_join(p->unit_path, "\n\t");
318 log_debug("Looking for unit files in:\n\t%s", t);
321 log_debug("Ignoring unit files.");
322 strv_free(p->unit_path);
326 if (running_as == MANAGER_SYSTEM) {
327 #ifdef HAVE_SYSV_COMPAT
328 /* /etc/init.d/ compatibility does not matter to users */
330 e = getenv("SYSTEMD_SYSVINIT_PATH");
332 p->sysvinit_path = path_split_and_make_absolute(e);
333 if (!p->sysvinit_path)
336 p->sysvinit_path = NULL;
338 if (strv_isempty(p->sysvinit_path)) {
339 strv_free(p->sysvinit_path);
341 p->sysvinit_path = strv_new(
342 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
344 if (!p->sysvinit_path)
348 e = getenv("SYSTEMD_SYSVRCND_PATH");
350 p->sysvrcnd_path = path_split_and_make_absolute(e);
351 if (!p->sysvrcnd_path)
354 p->sysvrcnd_path = NULL;
356 if (strv_isempty(p->sysvrcnd_path)) {
357 strv_free(p->sysvrcnd_path);
359 p->sysvrcnd_path = strv_new(
360 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
362 if (!p->sysvrcnd_path)
366 if (!path_strv_canonicalize(p->sysvinit_path))
369 if (!path_strv_canonicalize(p->sysvrcnd_path))
372 strv_uniq(p->sysvinit_path);
373 strv_uniq(p->sysvrcnd_path);
374 path_strv_remove_empty(p->sysvinit_path);
375 path_strv_remove_empty(p->sysvrcnd_path);
377 if (!strv_isempty(p->sysvinit_path)) {
379 t = strv_join(p->sysvinit_path, "\n\t");
382 log_debug("Looking for SysV init scripts in:\n\t%s", t);
385 log_debug("Ignoring SysV init scripts.");
386 strv_free(p->sysvinit_path);
387 p->sysvinit_path = NULL;
390 if (!strv_isempty(p->sysvrcnd_path)) {
392 t = strv_join(p->sysvrcnd_path, "\n\t");
396 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
399 log_debug("Ignoring SysV rcN.d links.");
400 strv_free(p->sysvrcnd_path);
401 p->sysvrcnd_path = NULL;
404 log_debug("Disabled SysV init scripts and rcN.d links support");
411 void lookup_paths_free(LookupPaths *p) {
414 strv_free(p->unit_path);
417 #ifdef HAVE_SYSV_COMPAT
418 strv_free(p->sysvinit_path);
419 strv_free(p->sysvrcnd_path);
420 p->sysvinit_path = p->sysvrcnd_path = NULL;