]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-lookup.c
shell-completion: add --global and unit-paths
[thirdparty/systemd.git] / src / shared / path-lookup.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
84e3543e
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
84e3543e
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
84e3543e 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
84e3543e
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
cf0fbc49 21#include <errno.h>
84e3543e 22#include <stdio.h>
cf0fbc49 23#include <stdlib.h>
67445f4e 24#include <string.h>
84e3543e 25
b5efdb8a 26#include "alloc-util.h"
a1f31f47
ZJS
27#include "fileio.h"
28#include "fs-util.h"
a8ffe6fb 29#include "install.h"
a8fbdf54
TA
30#include "log.h"
31#include "macro.h"
cd64fd56 32#include "mkdir.h"
07630cea 33#include "path-lookup.h"
cf0fbc49 34#include "path-util.h"
80b1ae32 35#include "rm-rf.h"
e4bb56c7 36#include "stat-util.h"
cf0fbc49
TA
37#include "string-util.h"
38#include "strv.h"
c4997074 39#include "user-util.h"
cf0fbc49 40#include "util.h"
84e3543e 41
ca4adeb7 42int xdg_user_runtime_dir(char **ret, const char *suffix) {
10e87ee7 43 const char *e;
205dd21e
LP
44 char *j;
45
46 assert(ret);
80b1ae32 47 assert(suffix);
10e87ee7 48
80b1ae32
LP
49 e = getenv("XDG_RUNTIME_DIR");
50 if (!e)
51 return -ENXIO;
205dd21e 52
80b1ae32
LP
53 j = strappend(e, suffix);
54 if (!j)
55 return -ENOMEM;
10e87ee7 56
205dd21e 57 *ret = j;
10e87ee7
LP
58 return 0;
59}
60
ca4adeb7 61int xdg_user_config_dir(char **ret, const char *suffix) {
718880ba 62 const char *e;
205dd21e 63 char *j;
c4997074 64 int r;
205dd21e
LP
65
66 assert(ret);
718880ba 67
80b1ae32
LP
68 e = getenv("XDG_CONFIG_HOME");
69 if (e)
70 j = strappend(e, suffix);
71 else {
c4997074 72 _cleanup_free_ char *home = NULL;
80b1ae32 73
c4997074
LP
74 r = get_home_dir(&home);
75 if (r < 0)
76 return r;
80b1ae32 77
605405c6 78 j = strjoin(home, "/.config", suffix);
80b1ae32 79 }
718880ba 80
205dd21e
LP
81 if (!j)
82 return -ENOMEM;
718880ba 83
205dd21e 84 *ret = j;
718880ba
SA
85 return 0;
86}
87
ca4adeb7 88int xdg_user_data_dir(char **ret, const char *suffix) {
e801700e 89 const char *e;
205dd21e 90 char *j;
c4997074 91 int r;
205dd21e
LP
92
93 assert(ret);
a7527131 94 assert(suffix);
e801700e
ZJS
95
96 /* We don't treat /etc/xdg/systemd here as the spec
61233823 97 * suggests because we assume that is a link to
e801700e
ZJS
98 * /etc/systemd/ anyway. */
99
100 e = getenv("XDG_DATA_HOME");
101 if (e)
205dd21e 102 j = strappend(e, suffix);
e801700e 103 else {
c4997074 104 _cleanup_free_ char *home = NULL;
205dd21e 105
c4997074
LP
106 r = get_home_dir(&home);
107 if (r < 0)
108 return r;
205dd21e 109
605405c6 110 j = strjoin(home, "/.local/share", suffix);
e801700e 111 }
205dd21e 112 if (!j)
e801700e
ZJS
113 return -ENOMEM;
114
205dd21e 115 *ret = j;
463d0d15 116 return 1;
e801700e
ZJS
117}
118
d2561cfd
ZJS
119static const char* const user_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
128static const char* const user_config_unit_paths[] = {
129 USER_CONFIG_UNIT_PATH,
130 "/etc/systemd/user",
131 NULL
132};
133
ca4adeb7 134int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs) {
84e3543e
LP
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 */
ca4adeb7
ZJS
143 const char *e;
144 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
84e3543e 145
07719a21
LP
146 e = getenv("XDG_CONFIG_DIRS");
147 if (e) {
148 config_dirs = strv_split(e, ":");
149 if (!config_dirs)
ca4adeb7 150 return -ENOMEM;
07719a21 151 }
84e3543e 152
07719a21
LP
153 e = getenv("XDG_DATA_DIRS");
154 if (e)
84e3543e
LP
155 data_dirs = strv_split(e, ":");
156 else
ef3102bf 157 data_dirs = strv_new("/usr/local/share",
ef3102bf 158 "/usr/share",
ef3102bf 159 NULL);
84e3543e 160 if (!data_dirs)
ca4adeb7
ZJS
161 return -ENOMEM;
162
163 *ret_config_dirs = config_dirs;
164 *ret_data_dirs = data_dirs;
165 config_dirs = data_dirs = NULL;
166 return 0;
167}
168
169static char** user_dirs(
170 const char *persistent_config,
171 const char *runtime_config,
172 const char *generator,
173 const char *generator_early,
174 const char *generator_late,
175 const char *transient,
176 const char *persistent_control,
177 const char *runtime_control) {
178
179 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
180 _cleanup_free_ char *data_home = NULL;
181 _cleanup_strv_free_ char **res = NULL;
182 char **tmp;
183 int r;
184
185 r = xdg_user_dirs(&config_dirs, &data_dirs);
186 if (r < 0)
187 return NULL;
188
189 r = xdg_user_data_dir(&data_home, "/systemd/user");
190 if (r < 0 && r != -ENXIO)
e801700e 191 return NULL;
84e3543e
LP
192
193 /* Now merge everything we found. */
80b1ae32
LP
194 if (strv_extend(&res, persistent_control) < 0)
195 return NULL;
39591351 196
80b1ae32
LP
197 if (strv_extend(&res, runtime_control) < 0)
198 return NULL;
199
200 if (strv_extend(&res, transient) < 0)
201 return NULL;
202
203 if (strv_extend(&res, generator_early) < 0)
204 return NULL;
07719a21 205
b9418b05
LP
206 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
207 return NULL;
718880ba 208
a0f84a10
LP
209 if (strv_extend(&res, persistent_config) < 0)
210 return NULL;
211
d2561cfd 212 if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
e801700e 213 return NULL;
718880ba 214
a0f84a10 215 if (strv_extend(&res, runtime_config) < 0)
e801700e 216 return NULL;
84e3543e 217
80b1ae32
LP
218 if (strv_extend(&res, generator) < 0)
219 return NULL;
07719a21 220
80b1ae32
LP
221 if (strv_extend(&res, data_home) < 0)
222 return NULL;
84e3543e 223
b9418b05
LP
224 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
225 return NULL;
84e3543e 226
d2561cfd 227 if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
e801700e 228 return NULL;
84e3543e 229
80b1ae32
LP
230 if (strv_extend(&res, generator_late) < 0)
231 return NULL;
07719a21 232
0f474365 233 if (path_strv_make_absolute_cwd(res) < 0)
e801700e 234 return NULL;
84e3543e 235
e801700e
ZJS
236 tmp = res;
237 res = NULL;
b9418b05 238
e801700e
ZJS
239 return tmp;
240}
84e3543e 241
d2561cfd
ZJS
242bool path_is_user_data_dir(const char *path) {
243 assert(path);
244
245 return strv_contains((char**) user_data_unit_paths, path);
246}
247
248bool path_is_user_config_dir(const char *path) {
249 assert(path);
250
251 return strv_contains((char**) user_config_unit_paths, path);
252}
253
a3c4eb07 254static int acquire_generator_dirs(
463d0d15 255 UnitFileScope scope,
a1f31f47 256 const char *tempdir,
a3c4eb07
LP
257 char **generator,
258 char **generator_early,
259 char **generator_late) {
260
261 _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
262 const char *prefix;
263
264 assert(generator);
265 assert(generator_early);
266 assert(generator_late);
a1f31f47 267 assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
a3c4eb07 268
a1f31f47
ZJS
269 if (scope == UNIT_FILE_GLOBAL)
270 return -EOPNOTSUPP;
463d0d15 271
a1f31f47
ZJS
272 if (tempdir)
273 prefix = tempdir;
274
275 else if (scope == UNIT_FILE_SYSTEM)
276 prefix = "/run/systemd";
a3c4eb07 277
a1f31f47 278 else if (scope == UNIT_FILE_USER) {
463d0d15 279 const char *e;
a3c4eb07
LP
280
281 e = getenv("XDG_RUNTIME_DIR");
282 if (!e)
463d0d15 283 return -ENXIO;
a3c4eb07 284
a1f31f47 285 prefix = strjoina(e, "/systemd");
a3c4eb07
LP
286 }
287
a1f31f47 288 x = strappend(prefix, "/generator");
a3c4eb07
LP
289 if (!x)
290 return -ENOMEM;
291
a1f31f47 292 y = strappend(prefix, "/generator.early");
a3c4eb07
LP
293 if (!y)
294 return -ENOMEM;
295
a1f31f47 296 z = strappend(prefix, "/generator.late");
a3c4eb07
LP
297 if (!z)
298 return -ENOMEM;
299
300 *generator = x;
301 *generator_early = y;
302 *generator_late = z;
303
304 x = y = z = NULL;
305 return 0;
306}
307
a1f31f47
ZJS
308static int acquire_transient_dir(
309 UnitFileScope scope,
310 const char *tempdir,
311 char **ret) {
39591351 312
a1f31f47 313 char *transient;
39591351 314
a1f31f47
ZJS
315 assert(ret);
316 assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
39591351 317
a1f31f47
ZJS
318 if (scope == UNIT_FILE_GLOBAL)
319 return -EOPNOTSUPP;
39591351 320
a1f31f47
ZJS
321 if (tempdir)
322 transient = strjoin(tempdir, "/transient");
323 else if (scope == UNIT_FILE_SYSTEM)
324 transient = strdup("/run/systemd/transient");
325 else
ca4adeb7 326 return xdg_user_runtime_dir(ret, "/systemd/transient");
a7527131 327
a1f31f47
ZJS
328 if (!transient)
329 return -ENOMEM;
330 *ret = transient;
331 return 0;
39591351
LP
332}
333
463d0d15 334static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
a0f84a10
LP
335 _cleanup_free_ char *a = NULL, *b = NULL;
336 int r;
337
338 assert(persistent);
339 assert(runtime);
340
463d0d15
LP
341 switch (scope) {
342
343 case UNIT_FILE_SYSTEM:
a0f84a10
LP
344 a = strdup(SYSTEM_CONFIG_UNIT_PATH);
345 b = strdup("/run/systemd/system");
463d0d15
LP
346 break;
347
348 case UNIT_FILE_GLOBAL:
349 a = strdup(USER_CONFIG_UNIT_PATH);
350 b = strdup("/run/systemd/user");
351 break;
a0f84a10 352
463d0d15 353 case UNIT_FILE_USER:
ca4adeb7 354 r = xdg_user_config_dir(&a, "/systemd/user");
c3c7eb7d 355 if (r < 0 && r != -ENXIO)
a0f84a10
LP
356 return r;
357
ca4adeb7 358 r = xdg_user_runtime_dir(runtime, "/systemd/user");
c3c7eb7d
LP
359 if (r < 0) {
360 if (r != -ENXIO)
361 return r;
362
363 /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
364 * directory to NULL */
365 *runtime = NULL;
366 }
a0f84a10
LP
367
368 *persistent = a;
369 a = NULL;
370
371 return 0;
a0f84a10 372
463d0d15
LP
373 default:
374 assert_not_reached("Hmm, unexpected scope value.");
a0f84a10
LP
375 }
376
377 if (!a || !b)
378 return -ENOMEM;
379
380 *persistent = a;
381 *runtime = b;
382 a = b = NULL;
383
384 return 0;
385}
386
80b1ae32
LP
387static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
388 _cleanup_free_ char *a = NULL;
389 int r;
390
391 assert(persistent);
392 assert(runtime);
393
394 switch (scope) {
395
396 case UNIT_FILE_SYSTEM: {
397 _cleanup_free_ char *b = NULL;
398
399 a = strdup("/etc/systemd/system.control");
400 if (!a)
401 return -ENOMEM;
402
403 b = strdup("/run/systemd/system.control");
404 if (!b)
405 return -ENOMEM;
406
407 *runtime = b;
408 b = NULL;
409
410 break;
411 }
412
413 case UNIT_FILE_USER:
ca4adeb7 414 r = xdg_user_config_dir(&a, "/systemd/system.control");
c3c7eb7d 415 if (r < 0 && r != -ENXIO)
80b1ae32
LP
416 return r;
417
ca4adeb7 418 r = xdg_user_runtime_dir(runtime, "/systemd/system.control");
c3c7eb7d
LP
419 if (r < 0) {
420 if (r != -ENXIO)
421 return r;
422
423 /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
424 * NULL */
425 *runtime = NULL;
426 }
80b1ae32
LP
427
428 break;
429
430 case UNIT_FILE_GLOBAL:
431 return -EOPNOTSUPP;
432
433 default:
434 assert_not_reached("Hmm, unexpected scope value.");
435 }
436
437 *persistent = a;
438 a = NULL;
439
440 return 0;
441}
442
a3c4eb07
LP
443static int patch_root_prefix(char **p, const char *root_dir) {
444 char *c;
445
446 assert(p);
447
448 if (!*p)
449 return 0;
450
a3c4eb07
LP
451 c = prefix_root(root_dir, *p);
452 if (!c)
453 return -ENOMEM;
454
455 free(*p);
456 *p = c;
457
458 return 0;
459}
460
a1453343
LP
461static int patch_root_prefix_strv(char **l, const char *root_dir) {
462 char **i;
463 int r;
464
465 if (!root_dir)
466 return 0;
467
468 STRV_FOREACH(i, l) {
469 r = patch_root_prefix(i, root_dir);
470 if (r < 0)
471 return r;
472 }
473
474 return 0;
475}
476
07719a21
LP
477int lookup_paths_init(
478 LookupPaths *p,
463d0d15 479 UnitFileScope scope,
4943d143 480 LookupPathsFlags flags,
a3c4eb07 481 const char *root_dir) {
07719a21 482
a1f31f47 483 _cleanup_(rmdir_and_freep) char *tempdir = NULL;
e4bb56c7
LP
484 _cleanup_free_ char
485 *root = NULL,
39591351 486 *persistent_config = NULL, *runtime_config = NULL,
e4bb56c7 487 *generator = NULL, *generator_early = NULL, *generator_late = NULL,
80b1ae32
LP
488 *transient = NULL,
489 *persistent_control = NULL, *runtime_control = NULL;
cf7d80a5 490 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
e215d211 491 _cleanup_strv_free_ char **paths = NULL;
a3c4eb07 492 const char *e;
0f474365 493 int r;
84e3543e
LP
494
495 assert(p);
463d0d15
LP
496 assert(scope >= 0);
497 assert(scope < _UNIT_FILE_SCOPE_MAX);
a3c4eb07 498
e4bb56c7
LP
499 if (!isempty(root_dir) && !path_equal(root_dir, "/")) {
500 if (scope == UNIT_FILE_USER)
501 return -EINVAL;
502
503 r = is_dir(root_dir, true);
504 if (r < 0)
505 return r;
506 if (r == 0)
507 return -ENOTDIR;
508
509 root = strdup(root_dir);
510 if (!root)
511 return -ENOMEM;
512 }
513
a1f31f47
ZJS
514 if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
515 r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
516 if (r < 0)
517 return log_error_errno(r, "Failed to create temporary directory: %m");
518 }
519
c3c7eb7d 520 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
463d0d15 521 r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
c3c7eb7d 522 if (r < 0)
a0f84a10
LP
523 return r;
524
4943d143 525 if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
c3c7eb7d 526 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
a1f31f47
ZJS
527 r = acquire_generator_dirs(scope, tempdir,
528 &generator, &generator_early, &generator_late);
4c701096 529 if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
4943d143
LP
530 return r;
531 }
84e3543e 532
c3c7eb7d 533 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
a1f31f47 534 r = acquire_transient_dir(scope, tempdir, &transient);
4c701096 535 if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
39591351
LP
536 return r;
537
c3c7eb7d 538 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
80b1ae32 539 r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
c3c7eb7d 540 if (r < 0 && r != -EOPNOTSUPP)
80b1ae32
LP
541 return r;
542
cd64fd56 543 /* First priority is whatever has been passed to us via env vars */
07719a21
LP
544 e = getenv("SYSTEMD_UNIT_PATH");
545 if (e) {
a3c4eb07
LP
546 const char *k;
547
548 k = endswith(e, ":");
549 if (k) {
550 e = strndupa(e, k - e);
cf7d80a5
ZJS
551 append = true;
552 }
553
f63c4aab 554 /* FIXME: empty components in other places should be rejected. */
cf7d80a5 555
e215d211 556 r = path_split_and_make_absolute(e, &paths);
0f474365
LP
557 if (r < 0)
558 return r;
e215d211 559 }
84e3543e 560
e215d211 561 if (!paths || append) {
cf7d80a5
ZJS
562 /* Let's figure something out. */
563
a3c4eb07 564 _cleanup_strv_free_ char **add = NULL;
84e3543e 565
07719a21 566 /* For the user units we include share/ in the search
cf7d80a5
ZJS
567 * path in order to comply with the XDG basedir spec.
568 * For the system stuff we avoid such nonsense. OTOH
569 * we include /lib in the search path for the system
570 * stuff but avoid it for user stuff. */
07719a21 571
463d0d15
LP
572 switch (scope) {
573
574 case UNIT_FILE_SYSTEM:
575 add = strv_new(
576 /* If you modify this you also want to modify
577 * systemdsystemunitpath= in systemd.pc.in! */
80b1ae32
LP
578 STRV_IFNOTNULL(persistent_control),
579 STRV_IFNOTNULL(runtime_control),
39591351 580 STRV_IFNOTNULL(transient),
463d0d15
LP
581 STRV_IFNOTNULL(generator_early),
582 persistent_config,
5f0a41da 583 SYSTEM_CONFIG_UNIT_PATH,
463d0d15
LP
584 "/etc/systemd/system",
585 runtime_config,
586 "/run/systemd/system",
587 STRV_IFNOTNULL(generator),
588 "/usr/local/lib/systemd/system",
589 SYSTEM_DATA_UNIT_PATH,
590 "/usr/lib/systemd/system",
349cc4a5 591#if HAVE_SPLIT_USR
463d0d15
LP
592 "/lib/systemd/system",
593#endif
594 STRV_IFNOTNULL(generator_late),
595 NULL);
596 break;
597
598 case UNIT_FILE_GLOBAL:
599 add = strv_new(
07719a21 600 /* If you modify this you also want to modify
cf7d80a5
ZJS
601 * systemduserunitpath= in systemd.pc.in, and
602 * the arrays in user_dirs() above! */
80b1ae32
LP
603 STRV_IFNOTNULL(persistent_control),
604 STRV_IFNOTNULL(runtime_control),
39591351 605 STRV_IFNOTNULL(transient),
463d0d15 606 STRV_IFNOTNULL(generator_early),
a0f84a10 607 persistent_config,
5f0a41da 608 USER_CONFIG_UNIT_PATH,
cf7d80a5 609 "/etc/systemd/user",
a0f84a10 610 runtime_config,
cf7d80a5 611 "/run/systemd/user",
463d0d15 612 STRV_IFNOTNULL(generator),
cf7d80a5
ZJS
613 "/usr/local/lib/systemd/user",
614 "/usr/local/share/systemd/user",
615 USER_DATA_UNIT_PATH,
616 "/usr/lib/systemd/user",
617 "/usr/share/systemd/user",
463d0d15 618 STRV_IFNOTNULL(generator_late),
07719a21 619 NULL);
463d0d15
LP
620 break;
621
622 case UNIT_FILE_USER:
623 add = user_dirs(persistent_config, runtime_config,
39591351 624 generator, generator_early, generator_late,
80b1ae32
LP
625 transient,
626 persistent_config, runtime_control);
463d0d15
LP
627 break;
628
629 default:
630 assert_not_reached("Hmm, unexpected scope?");
631 }
07719a21 632
a3c4eb07 633 if (!add)
cf7d80a5
ZJS
634 return -ENOMEM;
635
8f1e0ad4
LP
636 if (paths) {
637 r = strv_extend_strv(&paths, add, true);
638 if (r < 0)
a3c4eb07 639 return r;
8f1e0ad4
LP
640 } else {
641 /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
642 * and don't have to copy anything */
643 paths = add;
644 add = NULL;
645 }
84e3543e
LP
646 }
647
e4bb56c7 648 r = patch_root_prefix(&persistent_config, root);
a0f84a10
LP
649 if (r < 0)
650 return r;
e4bb56c7 651 r = patch_root_prefix(&runtime_config, root);
a0f84a10
LP
652 if (r < 0)
653 return r;
654
e4bb56c7 655 r = patch_root_prefix(&generator, root);
a3c4eb07
LP
656 if (r < 0)
657 return r;
e4bb56c7 658 r = patch_root_prefix(&generator_early, root);
a3c4eb07
LP
659 if (r < 0)
660 return r;
e4bb56c7 661 r = patch_root_prefix(&generator_late, root);
a3c4eb07
LP
662 if (r < 0)
663 return r;
664
39591351
LP
665 r = patch_root_prefix(&transient, root);
666 if (r < 0)
667 return r;
668
80b1ae32
LP
669 r = patch_root_prefix(&persistent_control, root);
670 if (r < 0)
671 return r;
672
673 r = patch_root_prefix(&runtime_control, root);
674 if (r < 0)
675 return r;
676
e215d211 677 r = patch_root_prefix_strv(paths, root);
a1453343 678 if (r < 0)
07719a21 679 return -ENOMEM;
07459bb6 680
e215d211
ZJS
681 p->search_path = strv_uniq(paths);
682 paths = NULL;
a3c4eb07 683
a0f84a10
LP
684 p->persistent_config = persistent_config;
685 p->runtime_config = runtime_config;
686 persistent_config = runtime_config = NULL;
687
a3c4eb07
LP
688 p->generator = generator;
689 p->generator_early = generator_early;
690 p->generator_late = generator_late;
691 generator = generator_early = generator_late = NULL;
84e3543e 692
39591351
LP
693 p->transient = transient;
694 transient = NULL;
695
80b1ae32
LP
696 p->persistent_control = persistent_control;
697 p->runtime_control = runtime_control;
698 persistent_control = runtime_control = NULL;
699
e4bb56c7
LP
700 p->root_dir = root;
701 root = NULL;
702
a1f31f47
ZJS
703 p->temporary_dir = tempdir;
704 tempdir = NULL;
705
84e3543e
LP
706 return 0;
707}
708
709void lookup_paths_free(LookupPaths *p) {
a3c4eb07
LP
710 if (!p)
711 return;
84e3543e 712
a3c4eb07 713 p->search_path = strv_free(p->search_path);
a0f84a10
LP
714
715 p->persistent_config = mfree(p->persistent_config);
716 p->runtime_config = mfree(p->runtime_config);
717
a3c4eb07
LP
718 p->generator = mfree(p->generator);
719 p->generator_early = mfree(p->generator_early);
720 p->generator_late = mfree(p->generator_late);
e4bb56c7 721
39591351
LP
722 p->transient = mfree(p->transient);
723
80b1ae32
LP
724 p->persistent_control = mfree(p->persistent_control);
725 p->runtime_control = mfree(p->runtime_control);
726
e4bb56c7 727 p->root_dir = mfree(p->root_dir);
23115a60 728 p->temporary_dir = mfree(p->temporary_dir);
84e3543e 729}
cd64fd56 730
a1453343
LP
731int lookup_paths_reduce(LookupPaths *p) {
732 _cleanup_free_ struct stat *stats = NULL;
733 size_t n_stats = 0, allocated = 0;
734 unsigned c = 0;
735 int r;
736
737 assert(p);
738
739 /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
740 * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
741 * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
742 * account when following symlinks. When we have no root path set this restriction does not apply however. */
743
744 if (!p->search_path)
745 return 0;
746
747 while (p->search_path[c]) {
748 struct stat st;
749 unsigned k;
750
45639f1b
LP
751 /* Never strip the transient and control directories from the path */
752 if (path_equal_ptr(p->search_path[c], p->transient) ||
753 path_equal_ptr(p->search_path[c], p->persistent_control) ||
754 path_equal_ptr(p->search_path[c], p->runtime_control)) {
755 c++;
756 continue;
757 }
758
a1453343
LP
759 if (p->root_dir)
760 r = lstat(p->search_path[c], &st);
761 else
762 r = stat(p->search_path[c], &st);
763 if (r < 0) {
764 if (errno == ENOENT)
765 goto remove_item;
766
767 /* If something we don't grok happened, let's better leave it in. */
768 log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]);
769 c++;
770 continue;
771 }
772
773 for (k = 0; k < n_stats; k++) {
774 if (stats[k].st_dev == st.st_dev &&
775 stats[k].st_ino == st.st_ino)
776 break;
777 }
778
779 if (k < n_stats) /* Is there already an entry with the same device/inode? */
780 goto remove_item;
781
782 if (!GREEDY_REALLOC(stats, allocated, n_stats+1))
783 return -ENOMEM;
784
785 stats[n_stats++] = st;
786 c++;
787 continue;
788
789 remove_item:
790 free(p->search_path[c]);
791 memmove(p->search_path + c,
792 p->search_path + c + 1,
793 (strv_length(p->search_path + c + 1) + 1) * sizeof(char*));
794 }
795
796 if (strv_isempty(p->search_path)) {
797 log_debug("Ignoring unit files.");
798 p->search_path = strv_free(p->search_path);
799 } else {
800 _cleanup_free_ char *t;
801
802 t = strv_join(p->search_path, "\n\t");
803 if (!t)
804 return -ENOMEM;
805
806 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
807 }
808
809 return 0;
810}
811
cd64fd56
LP
812int lookup_paths_mkdir_generator(LookupPaths *p) {
813 int r, q;
814
815 assert(p);
816
4943d143
LP
817 if (!p->generator || !p->generator_early || !p->generator_late)
818 return -EINVAL;
819
cd64fd56
LP
820 r = mkdir_p_label(p->generator, 0755);
821
822 q = mkdir_p_label(p->generator_early, 0755);
823 if (q < 0 && r >= 0)
824 r = q;
825
826 q = mkdir_p_label(p->generator_late, 0755);
827 if (q < 0 && r >= 0)
828 r = q;
829
830 return r;
831}
832
833void lookup_paths_trim_generator(LookupPaths *p) {
834 assert(p);
835
836 /* Trim empty dirs */
837
838 if (p->generator)
839 (void) rmdir(p->generator);
cd64fd56
LP
840 if (p->generator_early)
841 (void) rmdir(p->generator_early);
cd64fd56
LP
842 if (p->generator_late)
843 (void) rmdir(p->generator_late);
844}
07a78643
LP
845
846void lookup_paths_flush_generator(LookupPaths *p) {
847 assert(p);
848
80b1ae32
LP
849 /* Flush the generated unit files in full */
850
07a78643
LP
851 if (p->generator)
852 (void) rm_rf(p->generator, REMOVE_ROOT);
853 if (p->generator_early)
854 (void) rm_rf(p->generator_early, REMOVE_ROOT);
855 if (p->generator_late)
856 (void) rm_rf(p->generator_late, REMOVE_ROOT);
a1f31f47
ZJS
857
858 if (p->temporary_dir)
859 (void) rm_rf(p->temporary_dir, REMOVE_ROOT);
07a78643 860}
a69b4fb0
LP
861
862char **generator_binary_paths(UnitFileScope scope) {
863
864 switch (scope) {
865
866 case UNIT_FILE_SYSTEM:
867 return strv_new("/run/systemd/system-generators",
868 "/etc/systemd/system-generators",
869 "/usr/local/lib/systemd/system-generators",
870 SYSTEM_GENERATOR_PATH,
871 NULL);
872
873 case UNIT_FILE_GLOBAL:
874 case UNIT_FILE_USER:
875 return strv_new("/run/systemd/user-generators",
876 "/etc/systemd/user-generators",
877 "/usr/local/lib/systemd/user-generators",
878 USER_GENERATOR_PATH,
879 NULL);
880
881 default:
882 assert_not_reached("Hmm, unexpected scope.");
883 }
884}