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 static const char* const systemd_running_as_table[_SYSTEMD_RUNNING_AS_MAX] = {
36 [SYSTEMD_SYSTEM] = "system",
37 [SYSTEMD_USER] = "user"
40 DEFINE_STRING_TABLE_LOOKUP(systemd_running_as, SystemdRunningAs);
42 int user_config_home(char **config_home) {
45 e = getenv("XDG_CONFIG_HOME");
47 if (asprintf(config_home, "%s/systemd/user", e) < 0)
54 home = getenv("HOME");
56 if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
66 static char** user_dirs(
67 const char *generator,
68 const char *generator_early,
69 const char *generator_late) {
71 const char * const config_unit_paths[] = {
72 USER_CONFIG_UNIT_PATH,
78 const char * const data_unit_paths[] = {
79 "/usr/local/lib/systemd/user",
80 "/usr/local/share/systemd/user",
82 "/usr/lib/systemd/user",
83 "/usr/share/systemd/user",
88 char *config_home = NULL, *data_home = NULL;
89 char **config_dirs = NULL, **data_dirs = NULL;
92 /* Implement the mechanisms defined in
94 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
96 * We look in both the config and the data dirs because we
97 * want to encourage that distributors ship their unit files
98 * as data, and allow overriding as configuration.
101 if (user_config_home(&config_home) < 0)
104 home = getenv("HOME");
106 e = getenv("XDG_CONFIG_DIRS");
108 config_dirs = strv_split(e, ":");
113 /* We don't treat /etc/xdg/systemd here as the spec
114 * suggests because we assume that that is a link to
115 * /etc/systemd/ anyway. */
117 e = getenv("XDG_DATA_HOME");
119 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
123 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
126 /* There is really no need for two unit dirs in $HOME,
127 * except to be fully compliant with the XDG spec. We
128 * now try to link the two dirs, so that we can
129 * minimize disk seeks a little. Further down we'll
130 * then filter out this link, if it is actually is
133 mkdir_parents_label(data_home, 0777);
134 (void) symlink("../../../.config/systemd/user", data_home);
137 e = getenv("XDG_DATA_DIRS");
139 data_dirs = strv_split(e, ":");
141 data_dirs = strv_new("/usr/local/share",
147 /* Now merge everything we found. */
148 if (generator_early) {
149 t = strv_append(r, generator_early);
157 t = strv_append(r, config_home);
164 if (!strv_isempty(config_dirs)) {
165 t = strv_merge_concat(r, config_dirs, "/systemd/user");
172 t = strv_merge(r, (char**) config_unit_paths);
179 t = strv_append(r, generator);
187 t = strv_append(r, data_home);
194 if (!strv_isempty(data_dirs)) {
195 t = strv_merge_concat(r, data_dirs, "/systemd/user");
202 t = strv_merge(r, (char**) data_unit_paths);
208 if (generator_late) {
209 t = strv_append(r, generator_late);
216 if (!path_strv_make_absolute_cwd(r))
221 strv_free(config_dirs);
223 strv_free(data_dirs);
233 int lookup_paths_init(
235 SystemdRunningAs running_as,
237 const char *generator,
238 const char *generator_early,
239 const char *generator_late) {
246 /* First priority is whatever has been passed to us via env
248 e = getenv("SYSTEMD_UNIT_PATH");
250 p->unit_path = path_split_and_make_absolute(e);
256 if (strv_isempty(p->unit_path)) {
257 /* Nothing is set, so let's figure something out. */
258 strv_free(p->unit_path);
260 /* For the user units we include share/ in the search
261 * path in order to comply with the XDG basedir
262 * spec. For the system stuff we avoid such
263 * nonsense. OTOH we include /lib in the search path
264 * for the system stuff but avoid it for user
267 if (running_as == SYSTEMD_USER) {
270 p->unit_path = user_dirs(generator, generator_early, generator_late);
272 p->unit_path = strv_new(
273 /* If you modify this you also want to modify
274 * systemduserunitpath= in systemd.pc.in, and
275 * the arrays in user_dirs() above! */
276 STRV_IFNOTNULL(generator_early),
277 USER_CONFIG_UNIT_PATH,
280 STRV_IFNOTNULL(generator),
281 "/usr/local/lib/systemd/user",
282 "/usr/local/share/systemd/user",
284 "/usr/lib/systemd/user",
285 "/usr/share/systemd/user",
286 STRV_IFNOTNULL(generator_late),
293 p->unit_path = strv_new(
294 /* If you modify this you also want to modify
295 * systemdsystemunitpath= in systemd.pc.in! */
296 STRV_IFNOTNULL(generator_early),
297 SYSTEM_CONFIG_UNIT_PATH,
298 "/etc/systemd/system",
299 "/run/systemd/system",
300 STRV_IFNOTNULL(generator),
301 "/usr/local/lib/systemd/system",
302 SYSTEM_DATA_UNIT_PATH,
303 "/usr/lib/systemd/system",
304 #ifdef HAVE_SPLIT_USR
305 "/lib/systemd/system",
307 STRV_IFNOTNULL(generator_late),
315 if (!path_strv_canonicalize(p->unit_path))
318 strv_uniq(p->unit_path);
319 path_strv_remove_empty(p->unit_path);
321 if (!strv_isempty(p->unit_path)) {
323 t = strv_join(p->unit_path, "\n\t");
326 log_debug("Looking for unit files in:\n\t%s", t);
329 log_debug("Ignoring unit files.");
330 strv_free(p->unit_path);
334 if (running_as == SYSTEMD_SYSTEM) {
335 #ifdef HAVE_SYSV_COMPAT
336 /* /etc/init.d/ compatibility does not matter to users */
338 e = getenv("SYSTEMD_SYSVINIT_PATH");
340 p->sysvinit_path = path_split_and_make_absolute(e);
341 if (!p->sysvinit_path)
344 p->sysvinit_path = NULL;
346 if (strv_isempty(p->sysvinit_path)) {
347 strv_free(p->sysvinit_path);
349 p->sysvinit_path = strv_new(
350 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
352 if (!p->sysvinit_path)
356 e = getenv("SYSTEMD_SYSVRCND_PATH");
358 p->sysvrcnd_path = path_split_and_make_absolute(e);
359 if (!p->sysvrcnd_path)
362 p->sysvrcnd_path = NULL;
364 if (strv_isempty(p->sysvrcnd_path)) {
365 strv_free(p->sysvrcnd_path);
367 p->sysvrcnd_path = strv_new(
368 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
370 if (!p->sysvrcnd_path)
374 if (!path_strv_canonicalize(p->sysvinit_path))
377 if (!path_strv_canonicalize(p->sysvrcnd_path))
380 strv_uniq(p->sysvinit_path);
381 strv_uniq(p->sysvrcnd_path);
382 path_strv_remove_empty(p->sysvinit_path);
383 path_strv_remove_empty(p->sysvrcnd_path);
385 if (!strv_isempty(p->sysvinit_path)) {
387 t = strv_join(p->sysvinit_path, "\n\t");
390 log_debug("Looking for SysV init scripts in:\n\t%s", t);
393 log_debug("Ignoring SysV init scripts.");
394 strv_free(p->sysvinit_path);
395 p->sysvinit_path = NULL;
398 if (!strv_isempty(p->sysvrcnd_path)) {
400 t = strv_join(p->sysvrcnd_path, "\n\t");
404 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("Disabled SysV init scripts and rcN.d links support");
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;