]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/path-lookup.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[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,
7b6344d3
ZJS
172 const char *global_persistent_config,
173 const char *global_runtime_config,
ca4adeb7
ZJS
174 const char *generator,
175 const char *generator_early,
176 const char *generator_late,
177 const char *transient,
178 const char *persistent_control,
179 const char *runtime_control) {
180
181 _cleanup_strv_free_ char **config_dirs = NULL, **data_dirs = NULL;
182 _cleanup_free_ char *data_home = NULL;
183 _cleanup_strv_free_ char **res = NULL;
ca4adeb7
ZJS
184 int r;
185
186 r = xdg_user_dirs(&config_dirs, &data_dirs);
187 if (r < 0)
188 return NULL;
189
190 r = xdg_user_data_dir(&data_home, "/systemd/user");
191 if (r < 0 && r != -ENXIO)
e801700e 192 return NULL;
84e3543e
LP
193
194 /* Now merge everything we found. */
80b1ae32
LP
195 if (strv_extend(&res, persistent_control) < 0)
196 return NULL;
39591351 197
80b1ae32
LP
198 if (strv_extend(&res, runtime_control) < 0)
199 return NULL;
200
201 if (strv_extend(&res, transient) < 0)
202 return NULL;
203
204 if (strv_extend(&res, generator_early) < 0)
205 return NULL;
07719a21 206
b9418b05
LP
207 if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0)
208 return NULL;
718880ba 209
a0f84a10
LP
210 if (strv_extend(&res, persistent_config) < 0)
211 return NULL;
212
7b6344d3
ZJS
213 /* global config has lower priority than the user config of the same type */
214 if (strv_extend(&res, global_persistent_config) < 0)
215 return NULL;
216
d2561cfd 217 if (strv_extend_strv(&res, (char**) user_config_unit_paths, false) < 0)
e801700e 218 return NULL;
718880ba 219
a0f84a10 220 if (strv_extend(&res, runtime_config) < 0)
e801700e 221 return NULL;
84e3543e 222
7b6344d3
ZJS
223 if (strv_extend(&res, global_runtime_config) < 0)
224 return NULL;
225
80b1ae32
LP
226 if (strv_extend(&res, generator) < 0)
227 return NULL;
07719a21 228
80b1ae32
LP
229 if (strv_extend(&res, data_home) < 0)
230 return NULL;
84e3543e 231
b9418b05
LP
232 if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0)
233 return NULL;
84e3543e 234
d2561cfd 235 if (strv_extend_strv(&res, (char**) user_data_unit_paths, false) < 0)
e801700e 236 return NULL;
84e3543e 237
80b1ae32
LP
238 if (strv_extend(&res, generator_late) < 0)
239 return NULL;
07719a21 240
0f474365 241 if (path_strv_make_absolute_cwd(res) < 0)
e801700e 242 return NULL;
84e3543e 243
ae2a15bc 244 return TAKE_PTR(res);
e801700e 245}
84e3543e 246
d2561cfd
ZJS
247bool path_is_user_data_dir(const char *path) {
248 assert(path);
249
250 return strv_contains((char**) user_data_unit_paths, path);
251}
252
253bool path_is_user_config_dir(const char *path) {
254 assert(path);
255
256 return strv_contains((char**) user_config_unit_paths, path);
257}
258
a3c4eb07 259static int acquire_generator_dirs(
463d0d15 260 UnitFileScope scope,
a1f31f47 261 const char *tempdir,
a3c4eb07
LP
262 char **generator,
263 char **generator_early,
264 char **generator_late) {
265
266 _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL;
267 const char *prefix;
268
269 assert(generator);
270 assert(generator_early);
271 assert(generator_late);
a1f31f47 272 assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
a3c4eb07 273
a1f31f47
ZJS
274 if (scope == UNIT_FILE_GLOBAL)
275 return -EOPNOTSUPP;
463d0d15 276
a1f31f47
ZJS
277 if (tempdir)
278 prefix = tempdir;
279
280 else if (scope == UNIT_FILE_SYSTEM)
281 prefix = "/run/systemd";
a3c4eb07 282
a1f31f47 283 else if (scope == UNIT_FILE_USER) {
463d0d15 284 const char *e;
a3c4eb07
LP
285
286 e = getenv("XDG_RUNTIME_DIR");
287 if (!e)
463d0d15 288 return -ENXIO;
a3c4eb07 289
a1f31f47 290 prefix = strjoina(e, "/systemd");
a3c4eb07
LP
291 }
292
a1f31f47 293 x = strappend(prefix, "/generator");
a3c4eb07
LP
294 if (!x)
295 return -ENOMEM;
296
a1f31f47 297 y = strappend(prefix, "/generator.early");
a3c4eb07
LP
298 if (!y)
299 return -ENOMEM;
300
a1f31f47 301 z = strappend(prefix, "/generator.late");
a3c4eb07
LP
302 if (!z)
303 return -ENOMEM;
304
1cc6c93a
YW
305 *generator = TAKE_PTR(x);
306 *generator_early = TAKE_PTR(y);
307 *generator_late = TAKE_PTR(z);
a3c4eb07 308
a3c4eb07
LP
309 return 0;
310}
311
a1f31f47
ZJS
312static int acquire_transient_dir(
313 UnitFileScope scope,
314 const char *tempdir,
315 char **ret) {
39591351 316
a1f31f47 317 char *transient;
39591351 318
a1f31f47
ZJS
319 assert(ret);
320 assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
39591351 321
a1f31f47
ZJS
322 if (scope == UNIT_FILE_GLOBAL)
323 return -EOPNOTSUPP;
39591351 324
a1f31f47
ZJS
325 if (tempdir)
326 transient = strjoin(tempdir, "/transient");
327 else if (scope == UNIT_FILE_SYSTEM)
328 transient = strdup("/run/systemd/transient");
329 else
ca4adeb7 330 return xdg_user_runtime_dir(ret, "/systemd/transient");
a7527131 331
a1f31f47
ZJS
332 if (!transient)
333 return -ENOMEM;
334 *ret = transient;
335 return 0;
39591351
LP
336}
337
463d0d15 338static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
a0f84a10
LP
339 _cleanup_free_ char *a = NULL, *b = NULL;
340 int r;
341
342 assert(persistent);
343 assert(runtime);
344
463d0d15
LP
345 switch (scope) {
346
347 case UNIT_FILE_SYSTEM:
a0f84a10
LP
348 a = strdup(SYSTEM_CONFIG_UNIT_PATH);
349 b = strdup("/run/systemd/system");
463d0d15
LP
350 break;
351
352 case UNIT_FILE_GLOBAL:
353 a = strdup(USER_CONFIG_UNIT_PATH);
354 b = strdup("/run/systemd/user");
355 break;
a0f84a10 356
463d0d15 357 case UNIT_FILE_USER:
ca4adeb7 358 r = xdg_user_config_dir(&a, "/systemd/user");
c3c7eb7d 359 if (r < 0 && r != -ENXIO)
a0f84a10
LP
360 return r;
361
ca4adeb7 362 r = xdg_user_runtime_dir(runtime, "/systemd/user");
c3c7eb7d
LP
363 if (r < 0) {
364 if (r != -ENXIO)
365 return r;
366
367 /* If XDG_RUNTIME_DIR is not set, don't consider that fatal, simply initialize the runtime
368 * directory to NULL */
369 *runtime = NULL;
370 }
a0f84a10 371
ae2a15bc 372 *persistent = TAKE_PTR(a);
a0f84a10
LP
373
374 return 0;
a0f84a10 375
463d0d15
LP
376 default:
377 assert_not_reached("Hmm, unexpected scope value.");
a0f84a10
LP
378 }
379
380 if (!a || !b)
381 return -ENOMEM;
382
1cc6c93a
YW
383 *persistent = TAKE_PTR(a);
384 *runtime = TAKE_PTR(b);
a0f84a10
LP
385
386 return 0;
387}
388
80b1ae32
LP
389static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
390 _cleanup_free_ char *a = NULL;
391 int r;
392
393 assert(persistent);
394 assert(runtime);
395
396 switch (scope) {
397
398 case UNIT_FILE_SYSTEM: {
399 _cleanup_free_ char *b = NULL;
400
401 a = strdup("/etc/systemd/system.control");
402 if (!a)
403 return -ENOMEM;
404
405 b = strdup("/run/systemd/system.control");
406 if (!b)
407 return -ENOMEM;
408
ae2a15bc 409 *runtime = TAKE_PTR(b);
80b1ae32
LP
410
411 break;
412 }
413
414 case UNIT_FILE_USER:
0d6671b2 415 r = xdg_user_config_dir(&a, "/systemd/user.control");
c3c7eb7d 416 if (r < 0 && r != -ENXIO)
80b1ae32
LP
417 return r;
418
0d6671b2 419 r = xdg_user_runtime_dir(runtime, "/systemd/user.control");
c3c7eb7d
LP
420 if (r < 0) {
421 if (r != -ENXIO)
422 return r;
423
424 /* If XDG_RUNTIME_DIR is not set, don't consider this fatal, simply initialize the directory to
425 * NULL */
426 *runtime = NULL;
427 }
80b1ae32
LP
428
429 break;
430
431 case UNIT_FILE_GLOBAL:
432 return -EOPNOTSUPP;
433
434 default:
435 assert_not_reached("Hmm, unexpected scope value.");
436 }
437
ae2a15bc 438 *persistent = TAKE_PTR(a);
80b1ae32
LP
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,
7b6344d3 487 *global_persistent_config = NULL, *global_runtime_config = NULL,
e4bb56c7 488 *generator = NULL, *generator_early = NULL, *generator_late = NULL,
80b1ae32
LP
489 *transient = NULL,
490 *persistent_control = NULL, *runtime_control = NULL;
cf7d80a5 491 bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */
e215d211 492 _cleanup_strv_free_ char **paths = NULL;
a3c4eb07 493 const char *e;
0f474365 494 int r;
84e3543e
LP
495
496 assert(p);
463d0d15
LP
497 assert(scope >= 0);
498 assert(scope < _UNIT_FILE_SCOPE_MAX);
a3c4eb07 499
e4bb56c7
LP
500 if (!isempty(root_dir) && !path_equal(root_dir, "/")) {
501 if (scope == UNIT_FILE_USER)
502 return -EINVAL;
503
504 r = is_dir(root_dir, true);
505 if (r < 0)
506 return r;
507 if (r == 0)
508 return -ENOTDIR;
509
510 root = strdup(root_dir);
511 if (!root)
512 return -ENOMEM;
513 }
514
a1f31f47
ZJS
515 if (flags & LOOKUP_PATHS_TEMPORARY_GENERATED) {
516 r = mkdtemp_malloc("/tmp/systemd-temporary-XXXXXX", &tempdir);
517 if (r < 0)
518 return log_error_errno(r, "Failed to create temporary directory: %m");
519 }
520
c3c7eb7d 521 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_config to NULL */
463d0d15 522 r = acquire_config_dirs(scope, &persistent_config, &runtime_config);
c3c7eb7d 523 if (r < 0)
a0f84a10
LP
524 return r;
525
7b6344d3
ZJS
526 if (scope == UNIT_FILE_USER) {
527 r = acquire_config_dirs(UNIT_FILE_GLOBAL, &global_persistent_config, &global_runtime_config);
528 if (r < 0)
529 return r;
530 }
531
4943d143 532 if ((flags & LOOKUP_PATHS_EXCLUDE_GENERATED) == 0) {
c3c7eb7d 533 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
a1f31f47
ZJS
534 r = acquire_generator_dirs(scope, tempdir,
535 &generator, &generator_early, &generator_late);
4c701096 536 if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
4943d143
LP
537 return r;
538 }
84e3543e 539
c3c7eb7d 540 /* Note: if XDG_RUNTIME_DIR is not set, this will fail completely with ENXIO */
a1f31f47 541 r = acquire_transient_dir(scope, tempdir, &transient);
4c701096 542 if (r < 0 && !IN_SET(r, -EOPNOTSUPP, -ENXIO))
39591351
LP
543 return r;
544
c3c7eb7d 545 /* Note: when XDG_RUNTIME_DIR is not set this will not return -ENXIO, but simply set runtime_control to NULL */
80b1ae32 546 r = acquire_control_dirs(scope, &persistent_control, &runtime_control);
c3c7eb7d 547 if (r < 0 && r != -EOPNOTSUPP)
80b1ae32
LP
548 return r;
549
cd64fd56 550 /* First priority is whatever has been passed to us via env vars */
07719a21
LP
551 e = getenv("SYSTEMD_UNIT_PATH");
552 if (e) {
a3c4eb07
LP
553 const char *k;
554
555 k = endswith(e, ":");
556 if (k) {
557 e = strndupa(e, k - e);
cf7d80a5
ZJS
558 append = true;
559 }
560
f63c4aab 561 /* FIXME: empty components in other places should be rejected. */
cf7d80a5 562
e215d211 563 r = path_split_and_make_absolute(e, &paths);
0f474365
LP
564 if (r < 0)
565 return r;
e215d211 566 }
84e3543e 567
e215d211 568 if (!paths || append) {
cf7d80a5
ZJS
569 /* Let's figure something out. */
570
a3c4eb07 571 _cleanup_strv_free_ char **add = NULL;
84e3543e 572
07719a21 573 /* For the user units we include share/ in the search
cf7d80a5
ZJS
574 * path in order to comply with the XDG basedir spec.
575 * For the system stuff we avoid such nonsense. OTOH
576 * we include /lib in the search path for the system
577 * stuff but avoid it for user stuff. */
07719a21 578
463d0d15
LP
579 switch (scope) {
580
581 case UNIT_FILE_SYSTEM:
582 add = strv_new(
583 /* If you modify this you also want to modify
584 * systemdsystemunitpath= in systemd.pc.in! */
80b1ae32
LP
585 STRV_IFNOTNULL(persistent_control),
586 STRV_IFNOTNULL(runtime_control),
39591351 587 STRV_IFNOTNULL(transient),
463d0d15
LP
588 STRV_IFNOTNULL(generator_early),
589 persistent_config,
5f0a41da 590 SYSTEM_CONFIG_UNIT_PATH,
463d0d15
LP
591 "/etc/systemd/system",
592 runtime_config,
593 "/run/systemd/system",
594 STRV_IFNOTNULL(generator),
595 "/usr/local/lib/systemd/system",
596 SYSTEM_DATA_UNIT_PATH,
597 "/usr/lib/systemd/system",
349cc4a5 598#if HAVE_SPLIT_USR
463d0d15
LP
599 "/lib/systemd/system",
600#endif
601 STRV_IFNOTNULL(generator_late),
602 NULL);
603 break;
604
605 case UNIT_FILE_GLOBAL:
606 add = strv_new(
07719a21 607 /* If you modify this you also want to modify
cf7d80a5
ZJS
608 * systemduserunitpath= in systemd.pc.in, and
609 * the arrays in user_dirs() above! */
80b1ae32
LP
610 STRV_IFNOTNULL(persistent_control),
611 STRV_IFNOTNULL(runtime_control),
39591351 612 STRV_IFNOTNULL(transient),
463d0d15 613 STRV_IFNOTNULL(generator_early),
a0f84a10 614 persistent_config,
5f0a41da 615 USER_CONFIG_UNIT_PATH,
cf7d80a5 616 "/etc/systemd/user",
a0f84a10 617 runtime_config,
cf7d80a5 618 "/run/systemd/user",
463d0d15 619 STRV_IFNOTNULL(generator),
cf7d80a5 620 "/usr/local/share/systemd/user",
7e684baf
ZJS
621 "/usr/share/systemd/user",
622 "/usr/local/lib/systemd/user",
cf7d80a5
ZJS
623 USER_DATA_UNIT_PATH,
624 "/usr/lib/systemd/user",
463d0d15 625 STRV_IFNOTNULL(generator_late),
07719a21 626 NULL);
463d0d15
LP
627 break;
628
629 case UNIT_FILE_USER:
630 add = user_dirs(persistent_config, runtime_config,
7b6344d3 631 global_persistent_config, global_runtime_config,
39591351 632 generator, generator_early, generator_late,
80b1ae32 633 transient,
c59479e7 634 persistent_control, runtime_control);
463d0d15
LP
635 break;
636
637 default:
638 assert_not_reached("Hmm, unexpected scope?");
639 }
07719a21 640
a3c4eb07 641 if (!add)
cf7d80a5
ZJS
642 return -ENOMEM;
643
8f1e0ad4
LP
644 if (paths) {
645 r = strv_extend_strv(&paths, add, true);
646 if (r < 0)
a3c4eb07 647 return r;
1cc6c93a 648 } else
8f1e0ad4
LP
649 /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it,
650 * and don't have to copy anything */
1cc6c93a 651 paths = TAKE_PTR(add);
84e3543e
LP
652 }
653
e4bb56c7 654 r = patch_root_prefix(&persistent_config, root);
a0f84a10
LP
655 if (r < 0)
656 return r;
e4bb56c7 657 r = patch_root_prefix(&runtime_config, root);
a0f84a10
LP
658 if (r < 0)
659 return r;
660
e4bb56c7 661 r = patch_root_prefix(&generator, root);
a3c4eb07
LP
662 if (r < 0)
663 return r;
e4bb56c7 664 r = patch_root_prefix(&generator_early, root);
a3c4eb07
LP
665 if (r < 0)
666 return r;
e4bb56c7 667 r = patch_root_prefix(&generator_late, root);
a3c4eb07
LP
668 if (r < 0)
669 return r;
670
39591351
LP
671 r = patch_root_prefix(&transient, root);
672 if (r < 0)
673 return r;
674
80b1ae32
LP
675 r = patch_root_prefix(&persistent_control, root);
676 if (r < 0)
677 return r;
678
679 r = patch_root_prefix(&runtime_control, root);
680 if (r < 0)
681 return r;
682
e215d211 683 r = patch_root_prefix_strv(paths, root);
a1453343 684 if (r < 0)
07719a21 685 return -ENOMEM;
07459bb6 686
e215d211
ZJS
687 p->search_path = strv_uniq(paths);
688 paths = NULL;
a3c4eb07 689
1cc6c93a
YW
690 p->persistent_config = TAKE_PTR(persistent_config);
691 p->runtime_config = TAKE_PTR(runtime_config);
84e3543e 692
1cc6c93a
YW
693 p->generator = TAKE_PTR(generator);
694 p->generator_early = TAKE_PTR(generator_early);
695 p->generator_late = TAKE_PTR(generator_late);
39591351 696
1cc6c93a 697 p->transient = TAKE_PTR(transient);
80b1ae32 698
1cc6c93a
YW
699 p->persistent_control = TAKE_PTR(persistent_control);
700 p->runtime_control = TAKE_PTR(runtime_control);
e4bb56c7 701
1cc6c93a
YW
702 p->root_dir = TAKE_PTR(root);
703 p->temporary_dir = TAKE_PTR(tempdir);
a1f31f47 704
84e3543e
LP
705 return 0;
706}
707
708void lookup_paths_free(LookupPaths *p) {
a3c4eb07
LP
709 if (!p)
710 return;
84e3543e 711
a3c4eb07 712 p->search_path = strv_free(p->search_path);
a0f84a10
LP
713
714 p->persistent_config = mfree(p->persistent_config);
715 p->runtime_config = mfree(p->runtime_config);
716
a3c4eb07
LP
717 p->generator = mfree(p->generator);
718 p->generator_early = mfree(p->generator_early);
719 p->generator_late = mfree(p->generator_late);
e4bb56c7 720
39591351
LP
721 p->transient = mfree(p->transient);
722
80b1ae32
LP
723 p->persistent_control = mfree(p->persistent_control);
724 p->runtime_control = mfree(p->runtime_control);
725
e4bb56c7 726 p->root_dir = mfree(p->root_dir);
23115a60 727 p->temporary_dir = mfree(p->temporary_dir);
84e3543e 728}
cd64fd56 729
a1453343
LP
730int lookup_paths_reduce(LookupPaths *p) {
731 _cleanup_free_ struct stat *stats = NULL;
732 size_t n_stats = 0, allocated = 0;
733 unsigned c = 0;
734 int r;
735
736 assert(p);
737
738 /* Drop duplicates and non-existing directories from the search path. We figure out whether two directories are
739 * the same by comparing their device and inode numbers. Note one special tweak: when we have a root path set,
740 * we do not follow symlinks when retrieving them, because the kernel wouldn't take the root prefix into
741 * account when following symlinks. When we have no root path set this restriction does not apply however. */
742
743 if (!p->search_path)
744 return 0;
745
746 while (p->search_path[c]) {
747 struct stat st;
748 unsigned k;
749
45639f1b
LP
750 /* Never strip the transient and control directories from the path */
751 if (path_equal_ptr(p->search_path[c], p->transient) ||
752 path_equal_ptr(p->search_path[c], p->persistent_control) ||
753 path_equal_ptr(p->search_path[c], p->runtime_control)) {
754 c++;
755 continue;
756 }
757
a1453343
LP
758 if (p->root_dir)
759 r = lstat(p->search_path[c], &st);
760 else
761 r = stat(p->search_path[c], &st);
762 if (r < 0) {
763 if (errno == ENOENT)
764 goto remove_item;
765
766 /* If something we don't grok happened, let's better leave it in. */
767 log_debug_errno(errno, "Failed to stat %s: %m", p->search_path[c]);
768 c++;
769 continue;
770 }
771
772 for (k = 0; k < n_stats; k++) {
773 if (stats[k].st_dev == st.st_dev &&
774 stats[k].st_ino == st.st_ino)
775 break;
776 }
777
778 if (k < n_stats) /* Is there already an entry with the same device/inode? */
779 goto remove_item;
780
781 if (!GREEDY_REALLOC(stats, allocated, n_stats+1))
782 return -ENOMEM;
783
784 stats[n_stats++] = st;
785 c++;
786 continue;
787
788 remove_item:
789 free(p->search_path[c]);
790 memmove(p->search_path + c,
791 p->search_path + c + 1,
792 (strv_length(p->search_path + c + 1) + 1) * sizeof(char*));
793 }
794
795 if (strv_isempty(p->search_path)) {
796 log_debug("Ignoring unit files.");
797 p->search_path = strv_free(p->search_path);
798 } else {
799 _cleanup_free_ char *t;
800
801 t = strv_join(p->search_path, "\n\t");
802 if (!t)
803 return -ENOMEM;
804
805 log_debug("Looking for unit files in (higher priority first):\n\t%s", t);
806 }
807
808 return 0;
809}
810
cd64fd56
LP
811int lookup_paths_mkdir_generator(LookupPaths *p) {
812 int r, q;
813
814 assert(p);
815
4943d143
LP
816 if (!p->generator || !p->generator_early || !p->generator_late)
817 return -EINVAL;
818
cd64fd56
LP
819 r = mkdir_p_label(p->generator, 0755);
820
821 q = mkdir_p_label(p->generator_early, 0755);
822 if (q < 0 && r >= 0)
823 r = q;
824
825 q = mkdir_p_label(p->generator_late, 0755);
826 if (q < 0 && r >= 0)
827 r = q;
828
829 return r;
830}
831
832void lookup_paths_trim_generator(LookupPaths *p) {
833 assert(p);
834
835 /* Trim empty dirs */
836
837 if (p->generator)
838 (void) rmdir(p->generator);
cd64fd56
LP
839 if (p->generator_early)
840 (void) rmdir(p->generator_early);
cd64fd56
LP
841 if (p->generator_late)
842 (void) rmdir(p->generator_late);
843}
07a78643
LP
844
845void lookup_paths_flush_generator(LookupPaths *p) {
846 assert(p);
847
80b1ae32
LP
848 /* Flush the generated unit files in full */
849
07a78643
LP
850 if (p->generator)
851 (void) rm_rf(p->generator, REMOVE_ROOT);
852 if (p->generator_early)
853 (void) rm_rf(p->generator_early, REMOVE_ROOT);
854 if (p->generator_late)
855 (void) rm_rf(p->generator_late, REMOVE_ROOT);
a1f31f47
ZJS
856
857 if (p->temporary_dir)
858 (void) rm_rf(p->temporary_dir, REMOVE_ROOT);
07a78643 859}
a69b4fb0
LP
860
861char **generator_binary_paths(UnitFileScope scope) {
862
863 switch (scope) {
864
865 case UNIT_FILE_SYSTEM:
866 return strv_new("/run/systemd/system-generators",
867 "/etc/systemd/system-generators",
868 "/usr/local/lib/systemd/system-generators",
869 SYSTEM_GENERATOR_PATH,
870 NULL);
871
872 case UNIT_FILE_GLOBAL:
873 case UNIT_FILE_USER:
874 return strv_new("/run/systemd/user-generators",
875 "/etc/systemd/user-generators",
876 "/usr/local/lib/systemd/user-generators",
877 USER_GENERATOR_PATH,
878 NULL);
879
880 default:
881 assert_not_reached("Hmm, unexpected scope.");
882 }
883}