]> git.proxmox.com Git - systemd.git/blame - src/shared/path-lookup.c
Imported Upstream version 220
[systemd.git] / src / shared / path-lookup.c
CommitLineData
663996b3
MS
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
663996b3
MS
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
663996b3
MS
25#include <errno.h>
26
27#include "util.h"
663996b3
MS
28#include "strv.h"
29#include "path-util.h"
30#include "path-lookup.h"
e3bff60a 31#include "install.h"
663996b3 32
663996b3
MS
33int user_config_home(char **config_home) {
34 const char *e;
35 char *r;
36
37 e = getenv("XDG_CONFIG_HOME");
38 if (e) {
39 r = strappend(e, "/systemd/user");
40 if (!r)
41 return -ENOMEM;
42
43 *config_home = r;
44 return 1;
45 } else {
46 const char *home;
47
48 home = getenv("HOME");
49 if (home) {
50 r = strappend(home, "/.config/systemd/user");
51 if (!r)
52 return -ENOMEM;
53
54 *config_home = r;
55 return 1;
56 }
57 }
58
59 return 0;
60}
61
5eef597e
MP
62int user_runtime_dir(char **runtime_dir) {
63 const char *e;
64 char *r;
65
66 e = getenv("XDG_RUNTIME_DIR");
67 if (e) {
68 r = strappend(e, "/systemd/user");
69 if (!r)
70 return -ENOMEM;
71
72 *runtime_dir = r;
73 return 1;
74 }
75
76 return 0;
77}
78
e735f4d4
MP
79static int user_data_home_dir(char **dir, const char *suffix) {
80 const char *e;
81 char *res;
82
83 /* We don't treat /etc/xdg/systemd here as the spec
84 * suggests because we assume that that is a link to
85 * /etc/systemd/ anyway. */
86
87 e = getenv("XDG_DATA_HOME");
88 if (e)
89 res = strappend(e, suffix);
90 else {
91 const char *home;
92
93 home = getenv("HOME");
94 if (home)
95 res = strjoin(home, "/.local/share", suffix, NULL);
96 else
97 return 0;
98 }
99 if (!res)
100 return -ENOMEM;
101
102 *dir = res;
103 return 0;
104}
105
663996b3
MS
106static char** user_dirs(
107 const char *generator,
108 const char *generator_early,
109 const char *generator_late) {
110
111 const char * const config_unit_paths[] = {
112 USER_CONFIG_UNIT_PATH,
113 "/etc/systemd/user",
663996b3
MS
114 NULL
115 };
116
5eef597e
MP
117 const char * const runtime_unit_path = "/run/systemd/user";
118
663996b3
MS
119 const char * const data_unit_paths[] = {
120 "/usr/local/lib/systemd/user",
121 "/usr/local/share/systemd/user",
122 USER_DATA_UNIT_PATH,
123 "/usr/lib/systemd/user",
124 "/usr/share/systemd/user",
125 NULL
126 };
127
e735f4d4 128 const char *e;
5eef597e 129 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
60f067b4 130 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
e735f4d4
MP
131 _cleanup_free_ char **res = NULL;
132 char **tmp;
133 int r;
663996b3
MS
134
135 /* Implement the mechanisms defined in
136 *
137 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
138 *
139 * We look in both the config and the data dirs because we
140 * want to encourage that distributors ship their unit files
141 * as data, and allow overriding as configuration.
142 */
143
144 if (user_config_home(&config_home) < 0)
e735f4d4 145 return NULL;
663996b3 146
5eef597e 147 if (user_runtime_dir(&runtime_dir) < 0)
e735f4d4 148 return NULL;
663996b3
MS
149
150 e = getenv("XDG_CONFIG_DIRS");
151 if (e) {
152 config_dirs = strv_split(e, ":");
153 if (!config_dirs)
e735f4d4 154 return NULL;
663996b3
MS
155 }
156
e735f4d4
MP
157 r = user_data_home_dir(&data_home, "/systemd/user");
158 if (r < 0)
159 return NULL;
663996b3
MS
160
161 e = getenv("XDG_DATA_DIRS");
162 if (e)
163 data_dirs = strv_split(e, ":");
164 else
165 data_dirs = strv_new("/usr/local/share",
166 "/usr/share",
167 NULL);
168 if (!data_dirs)
e735f4d4 169 return NULL;
663996b3
MS
170
171 /* Now merge everything we found. */
60f067b4 172 if (generator_early)
e735f4d4
MP
173 if (strv_extend(&res, generator_early) < 0)
174 return NULL;
663996b3 175
60f067b4 176 if (config_home)
e735f4d4
MP
177 if (strv_extend(&res, config_home) < 0)
178 return NULL;
663996b3 179
60f067b4 180 if (!strv_isempty(config_dirs))
e735f4d4
MP
181 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
182 return NULL;
663996b3 183
e735f4d4
MP
184 if (strv_extend_strv(&res, (char**) config_unit_paths) < 0)
185 return NULL;
663996b3 186
5eef597e 187 if (runtime_dir)
e735f4d4
MP
188 if (strv_extend(&res, runtime_dir) < 0)
189 return NULL;
5eef597e 190
e735f4d4
MP
191 if (strv_extend(&res, runtime_unit_path) < 0)
192 return NULL;
5eef597e 193
60f067b4 194 if (generator)
e735f4d4
MP
195 if (strv_extend(&res, generator) < 0)
196 return NULL;
663996b3 197
60f067b4 198 if (data_home)
e735f4d4
MP
199 if (strv_extend(&res, data_home) < 0)
200 return NULL;
663996b3 201
60f067b4 202 if (!strv_isempty(data_dirs))
e735f4d4
MP
203 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
204 return NULL;
663996b3 205
e735f4d4
MP
206 if (strv_extend_strv(&res, (char**) data_unit_paths) < 0)
207 return NULL;
663996b3 208
60f067b4 209 if (generator_late)
e735f4d4
MP
210 if (strv_extend(&res, generator_late) < 0)
211 return NULL;
663996b3 212
e735f4d4
MP
213 if (!path_strv_make_absolute_cwd(res))
214 return NULL;
663996b3 215
e735f4d4
MP
216 tmp = res;
217 res = NULL;
218 return tmp;
219}
663996b3 220
e3bff60a
MP
221char **generator_paths(ManagerRunningAs running_as) {
222 if (running_as == MANAGER_USER)
e735f4d4
MP
223 return strv_new("/run/systemd/user-generators",
224 "/etc/systemd/user-generators",
225 "/usr/local/lib/systemd/user-generators",
226 USER_GENERATOR_PATH,
227 NULL);
228 else
229 return strv_new("/run/systemd/system-generators",
230 "/etc/systemd/system-generators",
231 "/usr/local/lib/systemd/system-generators",
232 SYSTEM_GENERATOR_PATH,
233 NULL);
663996b3
MS
234}
235
236int lookup_paths_init(
237 LookupPaths *p,
e3bff60a 238 ManagerRunningAs running_as,
663996b3 239 bool personal,
60f067b4 240 const char *root_dir,
663996b3
MS
241 const char *generator,
242 const char *generator_early,
243 const char *generator_late) {
244
245 const char *e;
5eef597e 246 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
663996b3
MS
247
248 assert(p);
249
250 /* First priority is whatever has been passed to us via env
251 * vars */
252 e = getenv("SYSTEMD_UNIT_PATH");
253 if (e) {
5eef597e
MP
254 if (endswith(e, ":")) {
255 e = strndupa(e, strlen(e) - 1);
256 append = true;
257 }
258
259 /* FIXME: empty components in other places should be
260 * rejected. */
261
663996b3
MS
262 p->unit_path = path_split_and_make_absolute(e);
263 if (!p->unit_path)
264 return -ENOMEM;
265 } else
266 p->unit_path = NULL;
267
5eef597e
MP
268 if (!p->unit_path || append) {
269 /* Let's figure something out. */
270
271 _cleanup_strv_free_ char **unit_path;
272 int r;
663996b3
MS
273
274 /* For the user units we include share/ in the search
5eef597e
MP
275 * path in order to comply with the XDG basedir spec.
276 * For the system stuff we avoid such nonsense. OTOH
277 * we include /lib in the search path for the system
278 * stuff but avoid it for user stuff. */
663996b3 279
e3bff60a 280 if (running_as == MANAGER_USER) {
663996b3 281 if (personal)
5eef597e 282 unit_path = user_dirs(generator, generator_early, generator_late);
663996b3 283 else
5eef597e 284 unit_path = strv_new(
663996b3 285 /* If you modify this you also want to modify
5eef597e
MP
286 * systemduserunitpath= in systemd.pc.in, and
287 * the arrays in user_dirs() above! */
663996b3 288 STRV_IFNOTNULL(generator_early),
5eef597e
MP
289 USER_CONFIG_UNIT_PATH,
290 "/etc/systemd/user",
291 "/run/systemd/user",
663996b3 292 STRV_IFNOTNULL(generator),
5eef597e
MP
293 "/usr/local/lib/systemd/user",
294 "/usr/local/share/systemd/user",
295 USER_DATA_UNIT_PATH,
296 "/usr/lib/systemd/user",
297 "/usr/share/systemd/user",
663996b3
MS
298 STRV_IFNOTNULL(generator_late),
299 NULL);
5eef597e
MP
300 } else
301 unit_path = strv_new(
302 /* If you modify this you also want to modify
303 * systemdsystemunitpath= in systemd.pc.in! */
304 STRV_IFNOTNULL(generator_early),
305 SYSTEM_CONFIG_UNIT_PATH,
306 "/etc/systemd/system",
307 "/run/systemd/system",
308 STRV_IFNOTNULL(generator),
309 "/usr/local/lib/systemd/system",
310 SYSTEM_DATA_UNIT_PATH,
311 "/usr/lib/systemd/system",
312#ifdef HAVE_SPLIT_USR
313 "/lib/systemd/system",
314#endif
315 STRV_IFNOTNULL(generator_late),
316 NULL);
663996b3 317
5eef597e
MP
318 if (!unit_path)
319 return -ENOMEM;
320
321 r = strv_extend_strv(&p->unit_path, unit_path);
322 if (r < 0)
323 return r;
663996b3
MS
324 }
325
e842803a 326 if (!path_strv_resolve_uniq(p->unit_path, root_dir))
663996b3
MS
327 return -ENOMEM;
328
663996b3
MS
329 if (!strv_isempty(p->unit_path)) {
330 _cleanup_free_ char *t = strv_join(p->unit_path, "\n\t");
331 if (!t)
332 return -ENOMEM;
333 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
334 } else {
335 log_debug("Ignoring unit files.");
336 strv_free(p->unit_path);
337 p->unit_path = NULL;
338 }
339
e3bff60a 340 if (running_as == MANAGER_SYSTEM) {
663996b3
MS
341#ifdef HAVE_SYSV_COMPAT
342 /* /etc/init.d/ compatibility does not matter to users */
343
344 e = getenv("SYSTEMD_SYSVINIT_PATH");
345 if (e) {
346 p->sysvinit_path = path_split_and_make_absolute(e);
347 if (!p->sysvinit_path)
348 return -ENOMEM;
349 } else
350 p->sysvinit_path = NULL;
351
352 if (strv_isempty(p->sysvinit_path)) {
353 strv_free(p->sysvinit_path);
354
355 p->sysvinit_path = strv_new(
356 SYSTEM_SYSVINIT_PATH, /* /etc/init.d/ */
357 NULL);
358 if (!p->sysvinit_path)
359 return -ENOMEM;
360 }
361
362 e = getenv("SYSTEMD_SYSVRCND_PATH");
363 if (e) {
364 p->sysvrcnd_path = path_split_and_make_absolute(e);
365 if (!p->sysvrcnd_path)
366 return -ENOMEM;
367 } else
368 p->sysvrcnd_path = NULL;
369
370 if (strv_isempty(p->sysvrcnd_path)) {
371 strv_free(p->sysvrcnd_path);
372
373 p->sysvrcnd_path = strv_new(
374 SYSTEM_SYSVRCND_PATH, /* /etc/rcN.d/ */
375 NULL);
376 if (!p->sysvrcnd_path)
377 return -ENOMEM;
378 }
379
e842803a 380 if (!path_strv_resolve_uniq(p->sysvinit_path, root_dir))
663996b3
MS
381 return -ENOMEM;
382
e842803a 383 if (!path_strv_resolve_uniq(p->sysvrcnd_path, root_dir))
663996b3
MS
384 return -ENOMEM;
385
663996b3
MS
386 if (!strv_isempty(p->sysvinit_path)) {
387 _cleanup_free_ char *t = strv_join(p->sysvinit_path, "\n\t");
388 if (!t)
389 return -ENOMEM;
390 log_debug("Looking for SysV init scripts in:\n\t%s", t);
391 } else {
392 log_debug("Ignoring SysV init scripts.");
393 strv_free(p->sysvinit_path);
394 p->sysvinit_path = NULL;
395 }
396
397 if (!strv_isempty(p->sysvrcnd_path)) {
398 _cleanup_free_ char *t =
399 strv_join(p->sysvrcnd_path, "\n\t");
400 if (!t)
401 return -ENOMEM;
402
403 log_debug("Looking for SysV rcN.d links in:\n\t%s", t);
404 } else {
405 log_debug("Ignoring SysV rcN.d links.");
406 strv_free(p->sysvrcnd_path);
407 p->sysvrcnd_path = NULL;
408 }
409#else
410 log_debug("SysV init scripts and rcN.d links support disabled");
411#endif
412 }
413
414 return 0;
415}
416
417void lookup_paths_free(LookupPaths *p) {
418 assert(p);
419
420 strv_free(p->unit_path);
421 p->unit_path = NULL;
422
423#ifdef HAVE_SYSV_COMPAT
424 strv_free(p->sysvinit_path);
425 strv_free(p->sysvrcnd_path);
426 p->sysvinit_path = p->sysvrcnd_path = NULL;
427#endif
428}
e735f4d4
MP
429
430int lookup_paths_init_from_scope(LookupPaths *paths,
431 UnitFileScope scope,
432 const char *root_dir) {
433 assert(paths);
434 assert(scope >= 0);
435 assert(scope < _UNIT_FILE_SCOPE_MAX);
436
437 zero(*paths);
438
439 return lookup_paths_init(paths,
e3bff60a 440 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
e735f4d4
MP
441 scope == UNIT_FILE_USER,
442 root_dir,
443 NULL, NULL, NULL);
444}