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