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) {
46 e = getenv("XDG_CONFIG_HOME");
48 r = strappend(e, "/systemd/user");
57 home = getenv("HOME");
59 r = strappend(home, "/.config/systemd/user");
71 static char** user_dirs(
72 const char *generator,
73 const char *generator_early,
74 const char *generator_late) {
76 const char * const config_unit_paths[] = {
77 USER_CONFIG_UNIT_PATH,
83 const char * const data_unit_paths[] = {
84 "/usr/local/lib/systemd/user",
85 "/usr/local/share/systemd/user",
87 "/usr/lib/systemd/user",
88 "/usr/share/systemd/user",
93 _cleanup_free_ char *config_home = NULL, *data_home = NULL;
94 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
97 /* Implement the mechanisms defined in
99 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
101 * We look in both the config and the data dirs because we
102 * want to encourage that distributors ship their unit files
103 * as data, and allow overriding as configuration.
106 if (user_config_home(&config_home) < 0)
109 home = getenv("HOME");
111 e = getenv("XDG_CONFIG_DIRS");
113 config_dirs = strv_split(e, ":");
118 /* We don't treat /etc/xdg/systemd here as the spec
119 * suggests because we assume that that is a link to
120 * /etc/systemd/ anyway. */
122 e = getenv("XDG_DATA_HOME");
124 if (asprintf(&data_home, "%s/systemd/user", e) < 0)
128 _cleanup_free_ char *data_home_parent = NULL;
130 if (asprintf(&data_home, "%s/.local/share/systemd/user", home) < 0)
133 /* There is really no need for two unit dirs in $HOME,
134 * except to be fully compliant with the XDG spec. We
135 * now try to link the two dirs, so that we can
136 * minimize disk seeks a little. Further down we'll
137 * then filter out this link, if it is actually is
140 if (path_get_parent(data_home, &data_home_parent) >= 0) {
141 _cleanup_free_ char *config_home_relative = NULL;
143 if (path_make_relative(data_home_parent, config_home, &config_home_relative) >= 0) {
144 mkdir_parents_label(data_home, 0777);
145 (void) symlink(config_home_relative, data_home);
150 e = getenv("XDG_DATA_DIRS");
152 data_dirs = strv_split(e, ":");
154 data_dirs = strv_new("/usr/local/share",
160 /* Now merge everything we found. */
162 if (strv_extend(&r, generator_early) < 0)
166 if (strv_extend(&r, config_home) < 0)
169 if (!strv_isempty(config_dirs))
170 if (strv_extend_strv_concat(&r, config_dirs, "/systemd/user") < 0)
173 if (strv_extend_strv(&r, (char**) config_unit_paths) < 0)
177 if (strv_extend(&r, generator) < 0)
181 if (strv_extend(&r, data_home) < 0)
184 if (!strv_isempty(data_dirs))
185 if (strv_extend_strv_concat(&r, data_dirs, "/systemd/user") < 0)
188 if (strv_extend_strv(&r, (char**) data_unit_paths) < 0)
192 if (strv_extend(&r, generator_late) < 0)
195 if (!path_strv_make_absolute_cwd(r))
205 int lookup_paths_init(
207 SystemdRunningAs running_as,
209 const char *root_dir,
210 const char *generator,
211 const char *generator_early,
212 const char *generator_late) {
218 /* First priority is whatever has been passed to us via env
220 e = getenv("SYSTEMD_UNIT_PATH");
222 p->unit_path = path_split_and_make_absolute(e);
228 if (strv_isempty(p->unit_path)) {
229 /* Nothing is set, so let's figure something out. */
230 strv_free(p->unit_path);
232 /* For the user units we include share/ in the search
233 * path in order to comply with the XDG basedir
234 * spec. For the system stuff we avoid such
235 * nonsense. OTOH we include /lib in the search path
236 * for the system stuff but avoid it for user
239 if (running_as == SYSTEMD_USER) {
242 p->unit_path = user_dirs(generator, generator_early, generator_late);
244 p->unit_path = strv_new(
245 /* If you modify this you also want to modify
246 * systemduserunitpath= in systemd.pc.in, and
247 * the arrays in user_dirs() above! */
248 STRV_IFNOTNULL(generator_early),
249 USER_CONFIG_UNIT_PATH,
252 STRV_IFNOTNULL(generator),
253 "/usr/local/lib/systemd/user",
254 "/usr/local/share/systemd/user",
256 "/usr/lib/systemd/user",
257 "/usr/share/systemd/user",
258 STRV_IFNOTNULL(generator_late),
265 p->unit_path = strv_new(
266 /* If you modify this you also want to modify
267 * systemdsystemunitpath= in systemd.pc.in! */
268 STRV_IFNOTNULL(generator_early),
269 SYSTEM_CONFIG_UNIT_PATH,
270 "/etc/systemd/system",
271 "/run/systemd/system",
272 STRV_IFNOTNULL(generator),
273 "/usr/local/lib/systemd/system",
274 SYSTEM_DATA_UNIT_PATH,
275 "/usr/lib/systemd/system",
276 #ifdef HAVE_SPLIT_USR
277 "/lib/systemd/system",
279 STRV_IFNOTNULL(generator_late),
287 if (!path_strv_canonicalize_absolute_uniq(p->unit_path, root_dir))
290 if (!strv_isempty(p->unit_path)) {
291 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
294 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
296 log_debug("Ignoring unit files.");
297 strv_free(p->unit_path);
301 if (running_as == SYSTEMD_SYSTEM) {
302 #ifdef HAVE_SYSV_COMPAT
303 /* /etc/init.d/ compatibility does not matter to users */
305 e = getenv("SYSTEMD_SYSVINIT_PATH");
307 p->sysvinit_path = path_split_and_make_absolute(e);
308 if (!p->sysvinit_path)
311 p->sysvinit_path = NULL;
313 if (strv_isempty(p->sysvinit_path)) {
314 strv_free(p->sysvinit_path);
316 p->sysvinit_path = strv_new(
317 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
319 if (!p->sysvinit_path)
323 e = getenv("SYSTEMD_SYSVRCND_PATH");
325 p->sysvrcnd_path = path_split_and_make_absolute(e);
326 if (!p->sysvrcnd_path)
329 p->sysvrcnd_path = NULL;
331 if (strv_isempty(p->sysvrcnd_path)) {
332 strv_free(p->sysvrcnd_path);
334 p->sysvrcnd_path = strv_new(
335 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
337 if (!p->sysvrcnd_path)
341 if (!path_strv_canonicalize_absolute_uniq(p->sysvinit_path, root_dir))
344 if (!path_strv_canonicalize_absolute_uniq(p->sysvrcnd_path, root_dir))
347 if (!strv_isempty(p->sysvinit_path)) {
348 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
351 log_debug("Looking for SysV init scripts in:\n\t%s", t);
353 log_debug("Ignoring SysV init scripts.");
354 strv_free(p->sysvinit_path);
355 p->sysvinit_path = NULL;
358 if (!strv_isempty(p->sysvrcnd_path)) {
359 _cleanup_free_ char *t =
360 strv_join(p->sysvrcnd_path, "\n\t");
364 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
366 log_debug("Ignoring SysV rcN.d links.");
367 strv_free(p->sysvrcnd_path);
368 p->sysvrcnd_path = NULL;
371 log_debug("SysV init scripts and rcN.d links support disabled");
378 void lookup_paths_free(LookupPaths *p) {
381 strv_free(p->unit_path);
384 #ifdef HAVE_SYSV_COMPAT
385 strv_free(p->sysvinit_path);
386 strv_free(p->sysvrcnd_path);
387 p->sysvinit_path = p->sysvrcnd_path = NULL;