]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-lookup.c
core: add configuration directories to LookupPaths
[thirdparty/systemd.git] / src / shared / path-lookup.c
CommitLineData
84e3543e
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
84e3543e
LP
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 14 Lesser General Public License for more details.
84e3543e 15
5430f7f2 16 You should have received a copy of the GNU Lesser General Public License
84e3543e
LP
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
cf0fbc49 20#include <errno.h>
84e3543e 21#include <stdio.h>
cf0fbc49 22#include <stdlib.h>
67445f4e 23#include <string.h>
84e3543e 24
b5efdb8a 25#include "alloc-util.h"
a8ffe6fb 26#include "install.h"
a8fbdf54
TA
27#include "log.h"
28#include "macro.h"
07630cea 29#include "path-lookup.h"
cf0fbc49
TA
30#include "path-util.h"
31#include "string-util.h"
32#include "strv.h"
33#include "util.h"
84e3543e 34
af2d49f7 35int user_config_home(char **config_home) {
10e87ee7 36 const char *e;
26d04f86 37 char *r;
10e87ee7 38
07719a21
LP
39 e = getenv("XDG_CONFIG_HOME");
40 if (e) {
26d04f86
LP
41 r = strappend(e, "/systemd/user");
42 if (!r)
10e87ee7
LP
43 return -ENOMEM;
44
26d04f86 45 *config_home = r;
10e87ee7
LP
46 return 1;
47 } else {
48 const char *home;
49
07719a21
LP
50 home = getenv("HOME");
51 if (home) {
26d04f86
LP
52 r = strappend(home, "/.config/systemd/user");
53 if (!r)
10e87ee7
LP
54 return -ENOMEM;
55
26d04f86 56 *config_home = r;
10e87ee7
LP
57 return 1;
58 }
59 }
60
61 return 0;
62}
63
4d5dec23 64int user_runtime_dir(char **runtime_dir) {
718880ba
SA
65 const char *e;
66 char *r;
67
68 e = getenv("XDG_RUNTIME_DIR");
69 if (e) {
70 r = strappend(e, "/systemd/user");
71 if (!r)
72 return -ENOMEM;
73
4d5dec23 74 *runtime_dir = r;
718880ba
SA
75 return 1;
76 }
77
78 return 0;
79}
80
e801700e
ZJS
81static int user_data_home_dir(char **dir, const char *suffix) {
82 const char *e;
83 char *res;
84
85 /* We don't treat /etc/xdg/systemd here as the spec
86 * suggests because we assume that that is a link to
87 * /etc/systemd/ anyway. */
88
89 e = getenv("XDG_DATA_HOME");
90 if (e)
91 res = strappend(e, suffix);
92 else {
93 const char *home;
94
95 home = getenv("HOME");
96 if (home)
97 res = strjoin(home, "/.local/share", suffix, NULL);
98 else
99 return 0;
100 }
101 if (!res)
102 return -ENOMEM;
103
104 *dir = res;
105 return 0;
106}
107
07719a21 108static char** user_dirs(
a0f84a10
LP
109 const char *persistent_config,
110 const char *runtime_config,
07719a21
LP
111 const char *generator,
112 const char *generator_early,
113 const char *generator_late) {
114
f437d5d2 115 const char * const config_unit_paths[] = {
f437d5d2 116 USER_CONFIG_UNIT_PATH,
4bf2bbb6
LP
117 "/etc/systemd/user",
118 NULL
f437d5d2
LP
119 };
120
121 const char * const data_unit_paths[] = {
122 "/usr/local/lib/systemd/user",
123 "/usr/local/share/systemd/user",
124 USER_DATA_UNIT_PATH,
125 "/usr/lib/systemd/user",
4bf2bbb6
LP
126 "/usr/share/systemd/user",
127 NULL
f437d5d2
LP
128 };
129
e801700e 130 const char *e;
4d5dec23 131 _cleanup_free_ char *config_home = NULL, *runtime_dir = NULL, *data_home = NULL;
e3e45d4f 132 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
e801700e
ZJS
133 _cleanup_free_ char **res = NULL;
134 char **tmp;
135 int r;
84e3543e
LP
136
137 /* Implement the mechanisms defined in
138 *
139 * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
140 *
141 * We look in both the config and the data dirs because we
142 * want to encourage that distributors ship their unit files
143 * as data, and allow overriding as configuration.
144 */
145
af2d49f7 146 if (user_config_home(&config_home) < 0)
e801700e 147 return NULL;
84e3543e 148
4d5dec23 149 if (user_runtime_dir(&runtime_dir) < 0)
e801700e 150 return NULL;
84e3543e 151
07719a21
LP
152 e = getenv("XDG_CONFIG_DIRS");
153 if (e) {
154 config_dirs = strv_split(e, ":");
155 if (!config_dirs)
e801700e 156 return NULL;
07719a21 157 }
84e3543e 158
e801700e
ZJS
159 r = user_data_home_dir(&data_home, "/systemd/user");
160 if (r < 0)
161 return NULL;
84e3543e 162
07719a21
LP
163 e = getenv("XDG_DATA_DIRS");
164 if (e)
84e3543e
LP
165 data_dirs = strv_split(e, ":");
166 else
ef3102bf 167 data_dirs = strv_new("/usr/local/share",
ef3102bf 168 "/usr/share",
ef3102bf 169 NULL);
84e3543e 170 if (!data_dirs)
e801700e 171 return NULL;
84e3543e
LP
172
173 /* Now merge everything we found. */
e3e45d4f 174 if (generator_early)
e801700e
ZJS
175 if (strv_extend(&res, generator_early) < 0)
176 return NULL;
07719a21 177
e3e45d4f 178 if (config_home)
e801700e
ZJS
179 if (strv_extend(&res, config_home) < 0)
180 return NULL;
84e3543e 181
aa08982d 182 if (!strv_isempty(config_dirs))
e801700e
ZJS
183 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
184 return NULL;
718880ba 185
a0f84a10
LP
186 if (strv_extend(&res, persistent_config) < 0)
187 return NULL;
188
e287086b 189 if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0)
e801700e 190 return NULL;
718880ba 191
aa08982d 192 if (runtime_dir)
e801700e
ZJS
193 if (strv_extend(&res, runtime_dir) < 0)
194 return NULL;
84e3543e 195
a0f84a10 196 if (strv_extend(&res, runtime_config) < 0)
e801700e 197 return NULL;
84e3543e 198
e3e45d4f 199 if (generator)
e801700e
ZJS
200 if (strv_extend(&res, generator) < 0)
201 return NULL;
07719a21 202
e3e45d4f 203 if (data_home)
e801700e
ZJS
204 if (strv_extend(&res, data_home) < 0)
205 return NULL;
84e3543e 206
e3e45d4f 207 if (!strv_isempty(data_dirs))
e801700e
ZJS
208 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
209 return NULL;
84e3543e 210
e287086b 211 if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0)
e801700e 212 return NULL;
84e3543e 213
e3e45d4f 214 if (generator_late)
e801700e
ZJS
215 if (strv_extend(&res, generator_late) < 0)
216 return NULL;
07719a21 217
0f474365 218 if (path_strv_make_absolute_cwd(res) < 0)
e801700e 219 return NULL;
84e3543e 220
e801700e
ZJS
221 tmp = res;
222 res = NULL;
223 return tmp;
224}
84e3543e 225
b2c23da8
LP
226char **generator_paths(ManagerRunningAs running_as) {
227 if (running_as == MANAGER_USER)
33e1e5a7
ZJS
228 return strv_new("/run/systemd/user-generators",
229 "/etc/systemd/user-generators",
e801700e
ZJS
230 "/usr/local/lib/systemd/user-generators",
231 USER_GENERATOR_PATH,
232 NULL);
233 else
33e1e5a7
ZJS
234 return strv_new("/run/systemd/system-generators",
235 "/etc/systemd/system-generators",
e801700e
ZJS
236 "/usr/local/lib/systemd/system-generators",
237 SYSTEM_GENERATOR_PATH,
238 NULL);
84e3543e
LP
239}
240
a3c4eb07
LP
241static int acquire_generator_dirs(
242 ManagerRunningAs running_as,
243 char **generator,
244 char **generator_early,
245 char **generator_late) {
246
247 _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
248 const char *prefix;
249
250 assert(generator);
251 assert(generator_early);
252 assert(generator_late);
253
254 if (running_as == MANAGER_SYSTEM)
255 prefix = "/run/systemd/";
256 else {
257 const char *e;
258
259 assert(running_as == MANAGER_USER);
260
261 e = getenv("XDG_RUNTIME_DIR");
262 if (!e)
263 return -EINVAL;
264
265 prefix = strjoina(e, "/systemd/", NULL);
266 }
267
268 x = strappend(prefix, "generator");
269 if (!x)
270 return -ENOMEM;
271
272 y = strappend(prefix, "generator.early");
273 if (!y)
274 return -ENOMEM;
275
276 z = strappend(prefix, "generator.late");
277 if (!z)
278 return -ENOMEM;
279
280 *generator = x;
281 *generator_early = y;
282 *generator_late = z;
283
284 x = y = z = NULL;
285 return 0;
286}
287
a0f84a10
LP
288static int acquire_config_dirs(ManagerRunningAs running_as, bool personal, char **persistent, char **runtime) {
289 _cleanup_free_ char *a = NULL, *b = NULL;
290 int r;
291
292 assert(persistent);
293 assert(runtime);
294
295 if (running_as == MANAGER_SYSTEM) {
296 a = strdup(SYSTEM_CONFIG_UNIT_PATH);
297 b = strdup("/run/systemd/system");
298 } else if (personal) {
299 assert(running_as == MANAGER_USER);
300
301 r = user_config_home(&a);
302 if (r < 0)
303 return r;
304
305 r = user_runtime_dir(runtime);
306 if (r < 0)
307 return r;
308
309 *persistent = a;
310 a = NULL;
311
312 return 0;
313 } else {
314 assert(running_as == MANAGER_USER);
315
316 a = strdup(USER_CONFIG_UNIT_PATH);
317 b = strdup("/run/systemd/user");
318 }
319
320 if (!a || !b)
321 return -ENOMEM;
322
323 *persistent = a;
324 *runtime = b;
325 a = b = NULL;
326
327 return 0;
328}
329
a3c4eb07
LP
330static int patch_root_prefix(char **p, const char *root_dir) {
331 char *c;
332
333 assert(p);
334
335 if (!*p)
336 return 0;
337
338 if (isempty(root_dir) || path_equal(root_dir, "/"))
339 return 0;
340
341 c = prefix_root(root_dir, *p);
342 if (!c)
343 return -ENOMEM;
344
345 free(*p);
346 *p = c;
347
348 return 0;
349}
350
07719a21
LP
351int lookup_paths_init(
352 LookupPaths *p,
b2c23da8 353 ManagerRunningAs running_as,
07719a21 354 bool personal,
a3c4eb07 355 const char *root_dir) {
07719a21 356
a0f84a10
LP
357 _cleanup_free_ char *generator = NULL, *generator_early = NULL, *generator_late = NULL,
358 *persistent_config = NULL, *runtime_config = NULL;
cf7d80a5 359 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
a3c4eb07
LP
360 char **l = NULL;
361 const char *e;
0f474365 362 int r;
84e3543e
LP
363
364 assert(p);
a3c4eb07
LP
365 assert(running_as >= 0);
366 assert(running_as < _MANAGER_RUNNING_AS_MAX);
367
a0f84a10
LP
368 r = acquire_config_dirs(running_as, personal, &persistent_config, &runtime_config);
369 if (r < 0)
370 return r;
371
a3c4eb07
LP
372 r = acquire_generator_dirs(running_as, &generator, &generator_early, &generator_late);
373 if (r < 0)
374 return r;
84e3543e
LP
375
376 /* First priority is whatever has been passed to us via env
377 * vars */
07719a21
LP
378 e = getenv("SYSTEMD_UNIT_PATH");
379 if (e) {
a3c4eb07
LP
380 const char *k;
381
382 k = endswith(e, ":");
383 if (k) {
384 e = strndupa(e, k - e);
cf7d80a5
ZJS
385 append = true;
386 }
387
388 /* FIXME: empty components in other places should be
389 * rejected. */
390
a3c4eb07 391 r = path_split_and_make_absolute(e, &l);
0f474365
LP
392 if (r < 0)
393 return r;
07719a21 394 } else
a3c4eb07 395 l = NULL;
84e3543e 396
a3c4eb07 397 if (!l || append) {
cf7d80a5
ZJS
398 /* Let's figure something out. */
399
a3c4eb07 400 _cleanup_strv_free_ char **add = NULL;
84e3543e 401
07719a21 402 /* For the user units we include share/ in the search
cf7d80a5
ZJS
403 * path in order to comply with the XDG basedir spec.
404 * For the system stuff we avoid such nonsense. OTOH
405 * we include /lib in the search path for the system
406 * stuff but avoid it for user stuff. */
07719a21 407
b2c23da8 408 if (running_as == MANAGER_USER) {
c800e483 409 if (personal)
a0f84a10
LP
410 add = user_dirs(persistent_config, runtime_config,
411 generator, generator_early, generator_late);
c800e483 412 else
a3c4eb07 413 add = strv_new(
07719a21 414 /* If you modify this you also want to modify
cf7d80a5
ZJS
415 * systemduserunitpath= in systemd.pc.in, and
416 * the arrays in user_dirs() above! */
a3c4eb07 417 generator_early,
a0f84a10 418 persistent_config,
cf7d80a5 419 "/etc/systemd/user",
a0f84a10 420 runtime_config,
cf7d80a5 421 "/run/systemd/user",
a3c4eb07 422 generator,
cf7d80a5
ZJS
423 "/usr/local/lib/systemd/user",
424 "/usr/local/share/systemd/user",
425 USER_DATA_UNIT_PATH,
426 "/usr/lib/systemd/user",
427 "/usr/share/systemd/user",
a3c4eb07 428 generator_late,
07719a21 429 NULL);
cf7d80a5 430 } else
a3c4eb07 431 add = strv_new(
cf7d80a5
ZJS
432 /* If you modify this you also want to modify
433 * systemdsystemunitpath= in systemd.pc.in! */
a3c4eb07 434 generator_early,
a0f84a10 435 persistent_config,
cf7d80a5 436 "/etc/systemd/system",
a0f84a10 437 runtime_config,
cf7d80a5 438 "/run/systemd/system",
a3c4eb07 439 generator,
cf7d80a5
ZJS
440 "/usr/local/lib/systemd/system",
441 SYSTEM_DATA_UNIT_PATH,
442 "/usr/lib/systemd/system",
443#ifdef HAVE_SPLIT_USR
444 "/lib/systemd/system",
445#endif
a3c4eb07 446 generator_late,
cf7d80a5 447 NULL);
07719a21 448
a3c4eb07 449 if (!add)
cf7d80a5
ZJS
450 return -ENOMEM;
451
a3c4eb07
LP
452 if (l) {
453 r = strv_extend_strv(&l, add, false);
454 if (r < 0)
455 return r;
456 } else {
457 l = add;
458 add = NULL;
459 }
84e3543e
LP
460 }
461
a0f84a10
LP
462 r = patch_root_prefix(&persistent_config, root_dir);
463 if (r < 0)
464 return r;
465 r = patch_root_prefix(&runtime_config, root_dir);
466 if (r < 0)
467 return r;
468
a3c4eb07
LP
469 r = patch_root_prefix(&generator, root_dir);
470 if (r < 0)
471 return r;
472 r = patch_root_prefix(&generator_early, root_dir);
473 if (r < 0)
474 return r;
475 r = patch_root_prefix(&generator_late, root_dir);
476 if (r < 0)
477 return r;
478
479 if (!path_strv_resolve_uniq(l, root_dir))
07719a21 480 return -ENOMEM;
07459bb6 481
a3c4eb07
LP
482 if (strv_isempty(l)) {
483 log_debug("Ignoring unit files.");
484 l = strv_free(l);
485 } else {
486 _cleanup_free_ char *t;
487
488 t = strv_join(l, "\n\t");
07719a21 489 if (!t)
07459bb6 490 return -ENOMEM;
a3c4eb07 491
242c4e1c 492 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
07459bb6
FF
493 }
494
a3c4eb07
LP
495 p->search_path = l;
496 l = NULL;
497
a0f84a10
LP
498 p->persistent_config = persistent_config;
499 p->runtime_config = runtime_config;
500 persistent_config = runtime_config = NULL;
501
a3c4eb07
LP
502 p->generator = generator;
503 p->generator_early = generator_early;
504 p->generator_late = generator_late;
505 generator = generator_early = generator_late = NULL;
84e3543e
LP
506
507 return 0;
508}
509
510void lookup_paths_free(LookupPaths *p) {
a3c4eb07
LP
511 if (!p)
512 return;
84e3543e 513
a3c4eb07 514 p->search_path = strv_free(p->search_path);
a0f84a10
LP
515
516 p->persistent_config = mfree(p->persistent_config);
517 p->runtime_config = mfree(p->runtime_config);
518
a3c4eb07
LP
519 p->generator = mfree(p->generator);
520 p->generator_early = mfree(p->generator_early);
521 p->generator_late = mfree(p->generator_late);
84e3543e 522}
60d27f19 523
a3c4eb07
LP
524int lookup_paths_init_from_scope(
525 LookupPaths *p,
526 UnitFileScope scope,
527 const char *root_dir) {
528
529 assert(p);
60d27f19
IS
530 assert(scope >= 0);
531 assert(scope < _UNIT_FILE_SCOPE_MAX);
532
a3c4eb07
LP
533 return lookup_paths_init(
534 p,
535 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
536 scope == UNIT_FILE_USER,
537 root_dir);
60d27f19 538}