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