Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <dirent.h>
4 : #include <errno.h>
5 : #include <fcntl.h>
6 : #include <fnmatch.h>
7 : #include <limits.h>
8 : #include <stddef.h>
9 : #include <stdio.h>
10 : #include <stdlib.h>
11 : #include <string.h>
12 : #include <sys/stat.h>
13 : #include <unistd.h>
14 :
15 : #include "alloc-util.h"
16 : #include "conf-files.h"
17 : #include "conf-parser.h"
18 : #include "def.h"
19 : #include "dirent-util.h"
20 : #include "extract-word.h"
21 : #include "fd-util.h"
22 : #include "fileio.h"
23 : #include "fs-util.h"
24 : #include "hashmap.h"
25 : #include "install-printf.h"
26 : #include "install.h"
27 : #include "locale-util.h"
28 : #include "log.h"
29 : #include "macro.h"
30 : #include "mkdir.h"
31 : #include "path-lookup.h"
32 : #include "path-util.h"
33 : #include "rm-rf.h"
34 : #include "set.h"
35 : #include "special.h"
36 : #include "stat-util.h"
37 : #include "string-table.h"
38 : #include "string-util.h"
39 : #include "strv.h"
40 : #include "unit-file.h"
41 :
42 : #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
43 :
44 : typedef enum SearchFlags {
45 : SEARCH_LOAD = 1 << 0,
46 : SEARCH_FOLLOW_CONFIG_SYMLINKS = 1 << 1,
47 : SEARCH_DROPIN = 1 << 2,
48 : } SearchFlags;
49 :
50 : typedef struct {
51 : OrderedHashmap *will_process;
52 : OrderedHashmap *have_processed;
53 : } InstallContext;
54 :
55 : typedef enum {
56 : PRESET_UNKNOWN,
57 : PRESET_ENABLE,
58 : PRESET_DISABLE,
59 : } PresetAction;
60 :
61 : typedef struct {
62 : char *pattern;
63 : PresetAction action;
64 : char **instances;
65 : } PresetRule;
66 :
67 : typedef struct {
68 : PresetRule *rules;
69 : size_t n_rules;
70 : } Presets;
71 :
72 518 : static bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) {
73 518 : assert(i);
74 :
75 518 : return !strv_isempty(i->aliases) ||
76 800 : !strv_isempty(i->wanted_by) ||
77 282 : !strv_isempty(i->required_by);
78 : }
79 :
80 276 : static bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) {
81 276 : assert(i);
82 :
83 276 : return !strv_isempty(i->also);
84 : }
85 :
86 14 : static void presets_freep(Presets *p) {
87 : size_t i;
88 :
89 14 : if (!p)
90 0 : return;
91 :
92 32 : for (i = 0; i < p->n_rules; i++) {
93 18 : free(p->rules[i].pattern);
94 18 : strv_free(p->rules[i].instances);
95 : }
96 :
97 14 : free(p->rules);
98 14 : p->n_rules = 0;
99 : }
100 :
101 : static const char *const unit_file_type_table[_UNIT_FILE_TYPE_MAX] = {
102 : [UNIT_FILE_TYPE_REGULAR] = "regular",
103 : [UNIT_FILE_TYPE_SYMLINK] = "symlink",
104 : [UNIT_FILE_TYPE_MASKED] = "masked",
105 : };
106 :
107 2 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType);
108 :
109 31 : static int in_search_path(const LookupPaths *p, const char *path) {
110 31 : _cleanup_free_ char *parent = NULL;
111 : char **i;
112 :
113 31 : assert(path);
114 :
115 31 : parent = dirname_malloc(path);
116 31 : if (!parent)
117 0 : return -ENOMEM;
118 :
119 343 : STRV_FOREACH(i, p->search_path)
120 339 : if (path_equal(parent, *i))
121 27 : return true;
122 :
123 4 : return false;
124 : }
125 :
126 292 : static const char* skip_root(const LookupPaths *p, const char *path) {
127 : char *e;
128 :
129 292 : assert(p);
130 292 : assert(path);
131 :
132 292 : if (!p->root_dir)
133 169 : return path;
134 :
135 123 : e = path_startswith(path, p->root_dir);
136 123 : if (!e)
137 2 : return NULL;
138 :
139 : /* Make sure the returned path starts with a slash */
140 121 : if (e[0] != '/') {
141 121 : if (e == path || e[-1] != '/')
142 0 : return NULL;
143 :
144 121 : e--;
145 : }
146 :
147 121 : return e;
148 : }
149 :
150 694 : static int path_is_generator(const LookupPaths *p, const char *path) {
151 694 : _cleanup_free_ char *parent = NULL;
152 :
153 694 : assert(p);
154 694 : assert(path);
155 :
156 694 : parent = dirname_malloc(path);
157 694 : if (!parent)
158 0 : return -ENOMEM;
159 :
160 1381 : return path_equal_ptr(parent, p->generator) ||
161 1381 : path_equal_ptr(parent, p->generator_early) ||
162 687 : path_equal_ptr(parent, p->generator_late);
163 : }
164 :
165 684 : static int path_is_transient(const LookupPaths *p, const char *path) {
166 684 : _cleanup_free_ char *parent = NULL;
167 :
168 684 : assert(p);
169 684 : assert(path);
170 :
171 684 : parent = dirname_malloc(path);
172 684 : if (!parent)
173 0 : return -ENOMEM;
174 :
175 684 : return path_equal_ptr(parent, p->transient);
176 : }
177 :
178 0 : static int path_is_control(const LookupPaths *p, const char *path) {
179 0 : _cleanup_free_ char *parent = NULL;
180 :
181 0 : assert(p);
182 0 : assert(path);
183 :
184 0 : parent = dirname_malloc(path);
185 0 : if (!parent)
186 0 : return -ENOMEM;
187 :
188 0 : return path_equal_ptr(parent, p->persistent_control) ||
189 0 : path_equal_ptr(parent, p->runtime_control);
190 : }
191 :
192 12 : static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) {
193 12 : _cleanup_free_ char *parent = NULL;
194 :
195 12 : assert(p);
196 12 : assert(path);
197 :
198 : /* Note that we do *not* have generic checks for /etc or /run in place, since with
199 : * them we couldn't discern configuration from transient or generated units */
200 :
201 12 : if (check_parent) {
202 12 : parent = dirname_malloc(path);
203 12 : if (!parent)
204 0 : return -ENOMEM;
205 :
206 12 : path = parent;
207 : }
208 :
209 22 : return path_equal_ptr(path, p->persistent_config) ||
210 10 : path_equal_ptr(path, p->runtime_config);
211 : }
212 :
213 226 : static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) {
214 226 : _cleanup_free_ char *parent = NULL;
215 : const char *rpath;
216 :
217 226 : assert(p);
218 226 : assert(path);
219 :
220 : /* Everything in /run is considered runtime. On top of that we also add
221 : * explicit checks for the various runtime directories, as safety net. */
222 :
223 226 : rpath = skip_root(p, path);
224 226 : if (rpath && path_startswith(rpath, "/run"))
225 1 : return true;
226 :
227 225 : if (check_parent) {
228 5 : parent = dirname_malloc(path);
229 5 : if (!parent)
230 0 : return -ENOMEM;
231 :
232 5 : path = parent;
233 : }
234 :
235 450 : return path_equal_ptr(path, p->runtime_config) ||
236 450 : path_equal_ptr(path, p->generator) ||
237 450 : path_equal_ptr(path, p->generator_early) ||
238 450 : path_equal_ptr(path, p->generator_late) ||
239 675 : path_equal_ptr(path, p->transient) ||
240 225 : path_equal_ptr(path, p->runtime_control);
241 : }
242 :
243 4 : static int path_is_vendor(const LookupPaths *p, const char *path) {
244 : const char *rpath;
245 :
246 4 : assert(p);
247 4 : assert(path);
248 :
249 4 : rpath = skip_root(p, path);
250 4 : if (!rpath)
251 0 : return 0;
252 :
253 4 : if (path_startswith(rpath, "/usr"))
254 3 : return true;
255 :
256 : #if HAVE_SPLIT_USR
257 : if (path_startswith(rpath, "/lib"))
258 : return true;
259 : #endif
260 :
261 1 : return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
262 : }
263 :
264 122 : int unit_file_changes_add(
265 : UnitFileChange **changes,
266 : size_t *n_changes,
267 : UnitFileChangeType type,
268 : const char *path,
269 : const char *source) {
270 :
271 122 : _cleanup_free_ char *p = NULL, *s = NULL;
272 : UnitFileChange *c;
273 :
274 122 : assert(path);
275 122 : assert(!changes == !n_changes);
276 :
277 122 : if (!changes)
278 59 : return 0;
279 :
280 63 : c = reallocarray(*changes, *n_changes + 1, sizeof(UnitFileChange));
281 63 : if (!c)
282 0 : return -ENOMEM;
283 63 : *changes = c;
284 :
285 63 : p = strdup(path);
286 63 : if (source)
287 37 : s = strdup(source);
288 :
289 63 : if (!p || (source && !s))
290 0 : return -ENOMEM;
291 :
292 63 : path_simplify(p, false);
293 63 : if (s)
294 37 : path_simplify(s, false);
295 :
296 63 : c[*n_changes] = (UnitFileChange) { type, p, s };
297 63 : p = s = NULL;
298 63 : (*n_changes) ++;
299 63 : return 0;
300 : }
301 :
302 44 : void unit_file_changes_free(UnitFileChange *changes, size_t n_changes) {
303 : size_t i;
304 :
305 44 : assert(changes || n_changes == 0);
306 :
307 107 : for (i = 0; i < n_changes; i++) {
308 63 : free(changes[i].path);
309 63 : free(changes[i].source);
310 : }
311 :
312 44 : free(changes);
313 44 : }
314 :
315 0 : void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet) {
316 : size_t i;
317 0 : bool logged = false;
318 :
319 0 : assert(changes || n_changes == 0);
320 : /* If verb is not specified, errors are not allowed! */
321 0 : assert(verb || r >= 0);
322 :
323 0 : for (i = 0; i < n_changes; i++) {
324 0 : assert(verb || changes[i].type >= 0);
325 :
326 0 : switch(changes[i].type) {
327 0 : case UNIT_FILE_SYMLINK:
328 0 : if (!quiet)
329 0 : log_info("Created symlink %s %s %s.",
330 : changes[i].path,
331 : special_glyph(SPECIAL_GLYPH_ARROW),
332 : changes[i].source);
333 0 : break;
334 0 : case UNIT_FILE_UNLINK:
335 0 : if (!quiet)
336 0 : log_info("Removed %s.", changes[i].path);
337 0 : break;
338 0 : case UNIT_FILE_IS_MASKED:
339 0 : if (!quiet)
340 0 : log_info("Unit %s is masked, ignoring.", changes[i].path);
341 0 : break;
342 0 : case UNIT_FILE_IS_DANGLING:
343 0 : if (!quiet)
344 0 : log_info("Unit %s is an alias to a unit that is not present, ignoring.",
345 : changes[i].path);
346 0 : break;
347 0 : case -EEXIST:
348 0 : if (changes[i].source)
349 0 : log_error_errno(changes[i].type,
350 : "Failed to %s unit, file %s already exists and is a symlink to %s.",
351 : verb, changes[i].path, changes[i].source);
352 : else
353 0 : log_error_errno(changes[i].type,
354 : "Failed to %s unit, file %s already exists.",
355 : verb, changes[i].path);
356 0 : logged = true;
357 0 : break;
358 0 : case -ERFKILL:
359 0 : log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.",
360 : verb, changes[i].path);
361 0 : logged = true;
362 0 : break;
363 0 : case -EADDRNOTAVAIL:
364 0 : log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.",
365 : verb, changes[i].path);
366 0 : logged = true;
367 0 : break;
368 0 : case -ELOOP:
369 0 : log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s",
370 : verb, changes[i].path);
371 0 : logged = true;
372 0 : break;
373 :
374 0 : case -ENOENT:
375 0 : log_error_errno(changes[i].type, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path);
376 0 : logged = true;
377 0 : break;
378 :
379 0 : default:
380 0 : assert(changes[i].type < 0);
381 0 : log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.",
382 : verb, changes[i].path);
383 0 : logged = true;
384 : }
385 : }
386 :
387 0 : if (r < 0 && !logged)
388 0 : log_error_errno(r, "Failed to %s: %m.", verb);
389 0 : }
390 :
391 : /**
392 : * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
393 : * wc should be the full path in the host file system.
394 : */
395 2 : static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
396 2 : assert(path_is_absolute(wd));
397 :
398 : /* This will give incorrect results if the paths are relative and go outside
399 : * of the chroot. False negatives are possible. */
400 :
401 2 : if (!root)
402 0 : root = "/";
403 :
404 14 : a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
405 14 : b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
406 2 : return path_equal_or_files_same(a, b, 0);
407 : }
408 :
409 39 : static int create_symlink(
410 : const LookupPaths *paths,
411 : const char *old_path,
412 : const char *new_path,
413 : bool force,
414 : UnitFileChange **changes,
415 : size_t *n_changes) {
416 :
417 39 : _cleanup_free_ char *dest = NULL, *dirname = NULL;
418 : const char *rp;
419 : int r;
420 :
421 39 : assert(old_path);
422 39 : assert(new_path);
423 :
424 39 : rp = skip_root(paths, old_path);
425 39 : if (rp)
426 37 : old_path = rp;
427 :
428 : /* Actually create a symlink, and remember that we did. Is
429 : * smart enough to check if there's already a valid symlink in
430 : * place.
431 : *
432 : * Returns 1 if a symlink was created or already exists and points to
433 : * the right place, or negative on error.
434 : */
435 :
436 39 : mkdir_parents_label(new_path, 0755);
437 :
438 39 : if (symlink(old_path, new_path) >= 0) {
439 37 : unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
440 37 : return 1;
441 : }
442 :
443 2 : if (errno != EEXIST) {
444 0 : unit_file_changes_add(changes, n_changes, -errno, new_path, NULL);
445 0 : return -errno;
446 : }
447 :
448 2 : r = readlink_malloc(new_path, &dest);
449 2 : if (r < 0) {
450 : /* translate EINVAL (non-symlink exists) to EEXIST */
451 0 : if (r == -EINVAL)
452 0 : r = -EEXIST;
453 :
454 0 : unit_file_changes_add(changes, n_changes, r, new_path, NULL);
455 0 : return r;
456 : }
457 :
458 2 : dirname = dirname_malloc(new_path);
459 2 : if (!dirname)
460 0 : return -ENOMEM;
461 :
462 2 : if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) {
463 2 : log_debug("Symlink %s → %s already exists", new_path, dest);
464 2 : return 1;
465 : }
466 :
467 0 : if (!force) {
468 0 : unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest);
469 0 : return -EEXIST;
470 : }
471 :
472 0 : r = symlink_atomic(old_path, new_path);
473 0 : if (r < 0) {
474 0 : unit_file_changes_add(changes, n_changes, r, new_path, NULL);
475 0 : return r;
476 : }
477 :
478 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
479 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
480 :
481 0 : return 1;
482 : }
483 :
484 58 : static int mark_symlink_for_removal(
485 : Set **remove_symlinks_to,
486 : const char *p) {
487 :
488 : char *n;
489 : int r;
490 :
491 58 : assert(p);
492 :
493 58 : r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops);
494 58 : if (r < 0)
495 0 : return r;
496 :
497 58 : n = strdup(p);
498 58 : if (!n)
499 0 : return -ENOMEM;
500 :
501 58 : path_simplify(n, false);
502 :
503 58 : r = set_consume(*remove_symlinks_to, n);
504 58 : if (r == -EEXIST)
505 0 : return 0;
506 58 : if (r < 0)
507 0 : return r;
508 :
509 58 : return 1;
510 : }
511 :
512 62 : static int remove_marked_symlinks_fd(
513 : Set *remove_symlinks_to,
514 : int fd,
515 : const char *path,
516 : const char *config_path,
517 : const LookupPaths *lp,
518 : bool dry_run,
519 : bool *restart,
520 : UnitFileChange **changes,
521 : size_t *n_changes) {
522 :
523 62 : _cleanup_closedir_ DIR *d = NULL;
524 : struct dirent *de;
525 62 : int r = 0;
526 :
527 62 : assert(remove_symlinks_to);
528 62 : assert(fd >= 0);
529 62 : assert(path);
530 62 : assert(config_path);
531 62 : assert(lp);
532 62 : assert(restart);
533 :
534 62 : d = fdopendir(fd);
535 62 : if (!d) {
536 0 : safe_close(fd);
537 0 : return -errno;
538 : }
539 :
540 62 : rewinddir(d);
541 :
542 326 : FOREACH_DIRENT(de, d, return -errno) {
543 :
544 140 : dirent_ensure_type(d, de);
545 :
546 140 : if (de->d_type == DT_DIR) {
547 32 : _cleanup_free_ char *p = NULL;
548 : int nfd, q;
549 :
550 32 : nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
551 32 : if (nfd < 0) {
552 0 : if (errno == ENOENT)
553 0 : continue;
554 :
555 0 : if (r == 0)
556 0 : r = -errno;
557 0 : continue;
558 : }
559 :
560 32 : p = path_make_absolute(de->d_name, path);
561 32 : if (!p) {
562 0 : safe_close(nfd);
563 0 : return -ENOMEM;
564 : }
565 :
566 : /* This will close nfd, regardless whether it succeeds or not */
567 32 : q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, dry_run, restart, changes, n_changes);
568 32 : if (q < 0 && r == 0)
569 0 : r = q;
570 :
571 108 : } else if (de->d_type == DT_LNK) {
572 196 : _cleanup_free_ char *p = NULL, *dest = NULL;
573 : const char *rp;
574 : bool found;
575 : int q;
576 :
577 108 : if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
578 0 : continue;
579 :
580 108 : p = path_make_absolute(de->d_name, path);
581 108 : if (!p)
582 0 : return -ENOMEM;
583 108 : path_simplify(p, false);
584 :
585 108 : q = readlink_malloc(p, &dest);
586 108 : if (q == -ENOENT)
587 0 : continue;
588 108 : if (q < 0) {
589 0 : if (r == 0)
590 0 : r = q;
591 0 : continue;
592 : }
593 :
594 : /* We remove all links pointing to a file or path that is marked, as well as all files sharing
595 : * the same name as a file that is marked. */
596 :
597 216 : found = set_contains(remove_symlinks_to, dest) ||
598 216 : set_contains(remove_symlinks_to, basename(dest)) ||
599 90 : set_contains(remove_symlinks_to, de->d_name);
600 :
601 108 : if (!found)
602 88 : continue;
603 :
604 20 : if (!dry_run) {
605 20 : if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
606 0 : if (r == 0)
607 0 : r = -errno;
608 0 : unit_file_changes_add(changes, n_changes, -errno, p, NULL);
609 0 : continue;
610 : }
611 :
612 20 : (void) rmdir_parents(p, config_path);
613 : }
614 :
615 20 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
616 :
617 : /* Now, remember the full path (but with the root prefix removed) of
618 : * the symlink we just removed, and remove any symlinks to it, too. */
619 :
620 20 : rp = skip_root(lp, p);
621 20 : q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
622 20 : if (q < 0)
623 0 : return q;
624 20 : if (q > 0 && !dry_run)
625 20 : *restart = true;
626 : }
627 : }
628 :
629 62 : return r;
630 : }
631 :
632 24 : static int remove_marked_symlinks(
633 : Set *remove_symlinks_to,
634 : const char *config_path,
635 : const LookupPaths *lp,
636 : bool dry_run,
637 : UnitFileChange **changes,
638 : size_t *n_changes) {
639 :
640 24 : _cleanup_close_ int fd = -1;
641 : bool restart;
642 24 : int r = 0;
643 :
644 24 : assert(config_path);
645 24 : assert(lp);
646 :
647 24 : if (set_size(remove_symlinks_to) <= 0)
648 5 : return 0;
649 :
650 19 : fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
651 19 : if (fd < 0)
652 0 : return errno == ENOENT ? 0 : -errno;
653 :
654 : do {
655 : int q, cfd;
656 30 : restart = false;
657 :
658 30 : cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
659 30 : if (cfd < 0)
660 0 : return -errno;
661 :
662 : /* This takes possession of cfd and closes it */
663 30 : q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, dry_run, &restart, changes, n_changes);
664 30 : if (r == 0)
665 30 : r = q;
666 30 : } while (restart);
667 :
668 19 : return r;
669 : }
670 :
671 312 : static int is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *name) {
672 : int r;
673 :
674 312 : if (streq(name, i->name))
675 170 : return true;
676 :
677 142 : if (strv_contains(i->aliases, name))
678 22 : return true;
679 :
680 : /* Look for template symlink matching DefaultInstance */
681 120 : if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
682 20 : _cleanup_free_ char *s = NULL;
683 :
684 20 : r = unit_name_replace_instance(i->name, i->default_instance, &s);
685 20 : if (r < 0) {
686 0 : if (r != -EINVAL)
687 0 : return r;
688 :
689 20 : } else if (streq(name, s))
690 4 : return true;
691 : }
692 :
693 116 : return false;
694 : }
695 :
696 56948 : static int find_symlinks_fd(
697 : const char *root_dir,
698 : const UnitFileInstallInfo *i,
699 : bool match_aliases,
700 : bool ignore_same_name,
701 : int fd,
702 : const char *path,
703 : const char *config_path,
704 : bool *same_name_link) {
705 :
706 56948 : _cleanup_closedir_ DIR *d = NULL;
707 : struct dirent *de;
708 56948 : int r = 0;
709 :
710 56948 : assert(i);
711 56948 : assert(fd >= 0);
712 56948 : assert(path);
713 56948 : assert(config_path);
714 56948 : assert(same_name_link);
715 :
716 56948 : d = fdopendir(fd);
717 56948 : if (!d) {
718 0 : safe_close(fd);
719 0 : return -errno;
720 : }
721 :
722 746806 : FOREACH_DIRENT(de, d, return -errno) {
723 :
724 576960 : dirent_ensure_type(d, de);
725 :
726 576960 : if (de->d_type == DT_DIR) {
727 51112 : _cleanup_free_ char *p = NULL;
728 : int nfd, q;
729 :
730 51112 : nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
731 51112 : if (nfd < 0) {
732 0 : if (errno == ENOENT)
733 0 : continue;
734 :
735 0 : if (r == 0)
736 0 : r = -errno;
737 0 : continue;
738 : }
739 :
740 51112 : p = path_make_absolute(de->d_name, path);
741 51112 : if (!p) {
742 0 : safe_close(nfd);
743 0 : return -ENOMEM;
744 : }
745 :
746 : /* This will close nfd, regardless whether it succeeds or not */
747 51112 : q = find_symlinks_fd(root_dir, i, match_aliases, ignore_same_name, nfd,
748 : p, config_path, same_name_link);
749 51112 : if (q > 0)
750 243 : return 1;
751 50869 : if (r == 0)
752 50869 : r = q;
753 :
754 525848 : } else if (de->d_type == DT_LNK) {
755 158994 : _cleanup_free_ char *p = NULL, *dest = NULL;
756 158648 : bool found_path = false, found_dest, b = false;
757 : int q;
758 :
759 : /* Acquire symlink name */
760 158648 : p = path_make_absolute(de->d_name, path);
761 158648 : if (!p)
762 0 : return -ENOMEM;
763 :
764 : /* Acquire symlink destination */
765 158648 : q = readlink_malloc(p, &dest);
766 158648 : if (q == -ENOENT)
767 0 : continue;
768 158648 : if (q < 0) {
769 0 : if (r == 0)
770 0 : r = q;
771 0 : continue;
772 : }
773 :
774 : /* Make absolute */
775 158648 : if (!path_is_absolute(dest)) {
776 : char *x;
777 :
778 85148 : x = path_join(root_dir, dest);
779 85148 : if (!x)
780 0 : return -ENOMEM;
781 :
782 85148 : free_and_replace(dest, x);
783 : }
784 :
785 158648 : assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
786 158648 : if (!ignore_same_name)
787 : /* Check if the symlink itself matches what we are looking for.
788 : *
789 : * If ignore_same_name is specified, we are in one of the directories which
790 : * have lower priority than the unit file, and even if a file or symlink with
791 : * this name was found, we should ignore it. */
792 154339 : found_path = streq(de->d_name, i->name);
793 :
794 : /* Check if what the symlink points to matches what we are looking for */
795 158648 : found_dest = streq(basename(dest), i->name);
796 :
797 158648 : if (found_path && found_dest) {
798 238 : _cleanup_free_ char *t = NULL;
799 :
800 : /* Filter out same name links in the main
801 : * config path */
802 238 : t = path_make_absolute(i->name, config_path);
803 238 : if (!t)
804 0 : return -ENOMEM;
805 :
806 238 : b = path_equal(t, p);
807 : }
808 :
809 158648 : if (b)
810 19 : *same_name_link = true;
811 158629 : else if (found_path || found_dest) {
812 462 : if (!match_aliases)
813 150 : return 1;
814 :
815 : /* Check if symlink name is in the set of names used by [Install] */
816 312 : q = is_symlink_with_known_name(i, de->d_name);
817 312 : if (q < 0)
818 0 : return q;
819 312 : if (q > 0)
820 196 : return 1;
821 : }
822 : }
823 : }
824 :
825 56359 : return r;
826 : }
827 :
828 13219 : static int find_symlinks(
829 : const char *root_dir,
830 : const UnitFileInstallInfo *i,
831 : bool match_name,
832 : bool ignore_same_name,
833 : const char *config_path,
834 : bool *same_name_link) {
835 :
836 : int fd;
837 :
838 13219 : assert(i);
839 13219 : assert(config_path);
840 13219 : assert(same_name_link);
841 :
842 13219 : fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
843 13219 : if (fd < 0) {
844 7383 : if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
845 7383 : return 0;
846 0 : return -errno;
847 : }
848 :
849 : /* This takes possession of fd and closes it */
850 5836 : return find_symlinks_fd(root_dir, i, match_name, ignore_same_name, fd,
851 : config_path, config_path, same_name_link);
852 : }
853 :
854 1178 : static int find_symlinks_in_scope(
855 : UnitFileScope scope,
856 : const LookupPaths *paths,
857 : const UnitFileInstallInfo *i,
858 : bool match_name,
859 : UnitFileState *state) {
860 :
861 1178 : bool same_name_link_runtime = false, same_name_link_config = false;
862 1178 : bool enabled_in_runtime = false, enabled_at_all = false;
863 1178 : bool ignore_same_name = false;
864 : char **p;
865 : int r;
866 :
867 1178 : assert(paths);
868 1178 : assert(i);
869 :
870 : /* As we iterate over the list of search paths in paths->search_path, we may encounter "same name"
871 : * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
872 : * effectively masked, so we should ignore them. */
873 :
874 14266 : STRV_FOREACH(p, paths->search_path) {
875 13219 : bool same_name_link = false;
876 :
877 13219 : r = find_symlinks(paths->root_dir, i, match_name, ignore_same_name, *p, &same_name_link);
878 13219 : if (r < 0)
879 131 : return r;
880 13219 : if (r > 0) {
881 : /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
882 :
883 346 : if (path_equal_ptr(*p, paths->persistent_config)) {
884 : /* This is the best outcome, let's return it immediately. */
885 131 : *state = UNIT_FILE_ENABLED;
886 131 : return 1;
887 : }
888 :
889 : /* look for global enablement of user units */
890 215 : if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) {
891 0 : *state = UNIT_FILE_ENABLED;
892 0 : return 1;
893 : }
894 :
895 215 : r = path_is_runtime(paths, *p, false);
896 215 : if (r < 0)
897 0 : return r;
898 215 : if (r > 0)
899 1 : enabled_in_runtime = true;
900 : else
901 214 : enabled_at_all = true;
902 :
903 12873 : } else if (same_name_link) {
904 8 : if (path_equal_ptr(*p, paths->persistent_config))
905 2 : same_name_link_config = true;
906 : else {
907 6 : r = path_is_runtime(paths, *p, false);
908 6 : if (r < 0)
909 0 : return r;
910 6 : if (r > 0)
911 0 : same_name_link_runtime = true;
912 : }
913 : }
914 :
915 : /* Check if next iteration will be "below" the unit file (either a regular file
916 : * or a symlink), and hence should be ignored */
917 13088 : if (!ignore_same_name && path_startswith(i->path, *p))
918 1039 : ignore_same_name = true;
919 : }
920 :
921 1047 : if (enabled_in_runtime) {
922 1 : *state = UNIT_FILE_ENABLED_RUNTIME;
923 1 : return 1;
924 : }
925 :
926 : /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
927 : * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
928 : * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
929 : * something, and hence are a much stronger concept. */
930 1046 : if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
931 1 : *state = UNIT_FILE_STATIC;
932 1 : return 1;
933 : }
934 :
935 : /* Hmm, we didn't find it, but maybe we found the same name
936 : * link? */
937 1045 : if (same_name_link_config) {
938 2 : *state = UNIT_FILE_LINKED;
939 2 : return 1;
940 : }
941 1043 : if (same_name_link_runtime) {
942 0 : *state = UNIT_FILE_LINKED_RUNTIME;
943 0 : return 1;
944 : }
945 :
946 1043 : return 0;
947 : }
948 :
949 1025 : static void install_info_free(UnitFileInstallInfo *i) {
950 :
951 1025 : if (!i)
952 0 : return;
953 :
954 1025 : free(i->name);
955 1025 : free(i->path);
956 1025 : strv_free(i->aliases);
957 1025 : strv_free(i->wanted_by);
958 1025 : strv_free(i->required_by);
959 1025 : strv_free(i->also);
960 1025 : free(i->default_instance);
961 1025 : free(i->symlink_target);
962 1025 : free(i);
963 : }
964 :
965 829 : static void install_context_done(InstallContext *c) {
966 829 : assert(c);
967 :
968 1780 : c->will_process = ordered_hashmap_free_with_destructor(c->will_process, install_info_free);
969 903 : c->have_processed = ordered_hashmap_free_with_destructor(c->have_processed, install_info_free);
970 829 : }
971 :
972 1128 : static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
973 : UnitFileInstallInfo *i;
974 :
975 1128 : i = ordered_hashmap_get(c->have_processed, name);
976 1128 : if (i)
977 0 : return i;
978 :
979 1128 : return ordered_hashmap_get(c->will_process, name);
980 : }
981 :
982 31 : static int install_info_may_process(
983 : const UnitFileInstallInfo *i,
984 : const LookupPaths *paths,
985 : UnitFileChange **changes,
986 : size_t *n_changes) {
987 31 : assert(i);
988 31 : assert(paths);
989 :
990 : /* Checks whether the loaded unit file is one we should process, or is masked,
991 : * transient or generated and thus not subject to enable/disable operations. */
992 :
993 31 : if (i->type == UNIT_FILE_TYPE_MASKED) {
994 1 : unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
995 1 : return -ERFKILL;
996 : }
997 60 : if (path_is_generator(paths, i->path) ||
998 30 : path_is_transient(paths, i->path)) {
999 0 : unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
1000 0 : return -EADDRNOTAVAIL;
1001 : }
1002 :
1003 30 : return 0;
1004 : }
1005 :
1006 : /**
1007 : * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
1008 : * hashmap, or retrieves the existing one if already present.
1009 : *
1010 : * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1011 : */
1012 1034 : static int install_info_add(
1013 : InstallContext *c,
1014 : const char *name,
1015 : const char *path,
1016 : bool auxiliary,
1017 : UnitFileInstallInfo **ret) {
1018 :
1019 1034 : UnitFileInstallInfo *i = NULL;
1020 : int r;
1021 :
1022 1034 : assert(c);
1023 1034 : assert(name || path);
1024 :
1025 1034 : if (!name)
1026 1 : name = basename(path);
1027 :
1028 1034 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
1029 0 : return -EINVAL;
1030 :
1031 1034 : i = install_info_find(c, name);
1032 1034 : if (i) {
1033 9 : i->auxiliary = i->auxiliary && auxiliary;
1034 :
1035 9 : if (ret)
1036 9 : *ret = i;
1037 9 : return 0;
1038 : }
1039 :
1040 1025 : r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
1041 1025 : if (r < 0)
1042 0 : return r;
1043 :
1044 1025 : i = new(UnitFileInstallInfo, 1);
1045 1025 : if (!i)
1046 0 : return -ENOMEM;
1047 :
1048 1025 : *i = (UnitFileInstallInfo) {
1049 : .type = _UNIT_FILE_TYPE_INVALID,
1050 : .auxiliary = auxiliary,
1051 : };
1052 :
1053 1025 : i->name = strdup(name);
1054 1025 : if (!i->name) {
1055 0 : r = -ENOMEM;
1056 0 : goto fail;
1057 : }
1058 :
1059 1025 : if (path) {
1060 1 : i->path = strdup(path);
1061 1 : if (!i->path) {
1062 0 : r = -ENOMEM;
1063 0 : goto fail;
1064 : }
1065 : }
1066 :
1067 1025 : r = ordered_hashmap_put(c->will_process, i->name, i);
1068 1025 : if (r < 0)
1069 0 : goto fail;
1070 :
1071 1025 : if (ret)
1072 1015 : *ret = i;
1073 :
1074 1025 : return 1;
1075 :
1076 0 : fail:
1077 0 : install_info_free(i);
1078 0 : return r;
1079 : }
1080 :
1081 36 : static int config_parse_alias(
1082 : const char *unit,
1083 : const char *filename,
1084 : unsigned line,
1085 : const char *section,
1086 : unsigned section_line,
1087 : const char *lvalue,
1088 : int ltype,
1089 : const char *rvalue,
1090 : void *data,
1091 : void *userdata) {
1092 :
1093 : UnitType type;
1094 :
1095 36 : assert(unit);
1096 36 : assert(filename);
1097 36 : assert(lvalue);
1098 36 : assert(rvalue);
1099 :
1100 36 : type = unit_name_to_type(unit);
1101 36 : if (!unit_type_may_alias(type))
1102 0 : return log_syntax(unit, LOG_WARNING, filename, line, 0,
1103 : "Alias= is not allowed for %s units, ignoring.",
1104 : unit_type_to_string(type));
1105 :
1106 36 : return config_parse_strv(unit, filename, line, section, section_line,
1107 : lvalue, ltype, rvalue, data, userdata);
1108 : }
1109 :
1110 46 : static int config_parse_also(
1111 : const char *unit,
1112 : const char *filename,
1113 : unsigned line,
1114 : const char *section,
1115 : unsigned section_line,
1116 : const char *lvalue,
1117 : int ltype,
1118 : const char *rvalue,
1119 : void *data,
1120 : void *userdata) {
1121 :
1122 46 : UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
1123 46 : InstallContext *c = data;
1124 : int r;
1125 :
1126 46 : assert(unit);
1127 46 : assert(filename);
1128 46 : assert(lvalue);
1129 46 : assert(rvalue);
1130 :
1131 50 : for (;;) {
1132 142 : _cleanup_free_ char *word = NULL, *printed = NULL;
1133 :
1134 96 : r = extract_first_word(&rvalue, &word, NULL, 0);
1135 96 : if (r < 0)
1136 0 : return r;
1137 96 : if (r == 0)
1138 46 : break;
1139 :
1140 50 : r = install_full_printf(info, word, &printed);
1141 50 : if (r < 0)
1142 0 : return r;
1143 :
1144 50 : if (!unit_name_is_valid(printed, UNIT_NAME_ANY))
1145 0 : return -EINVAL;
1146 :
1147 50 : r = install_info_add(c, printed, NULL, true, &alsoinfo);
1148 50 : if (r < 0)
1149 0 : return r;
1150 :
1151 50 : r = strv_push(&info->also, printed);
1152 50 : if (r < 0)
1153 0 : return r;
1154 :
1155 50 : printed = NULL;
1156 : }
1157 :
1158 46 : return 0;
1159 : }
1160 :
1161 74 : static int config_parse_default_instance(
1162 : const char *unit,
1163 : const char *filename,
1164 : unsigned line,
1165 : const char *section,
1166 : unsigned section_line,
1167 : const char *lvalue,
1168 : int ltype,
1169 : const char *rvalue,
1170 : void *data,
1171 : void *userdata) {
1172 :
1173 74 : UnitFileInstallInfo *i = data;
1174 74 : _cleanup_free_ char *printed = NULL;
1175 : int r;
1176 :
1177 74 : assert(unit);
1178 74 : assert(filename);
1179 74 : assert(lvalue);
1180 74 : assert(rvalue);
1181 :
1182 74 : if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE))
1183 : /* When enabling an instance, we might be using a template unit file,
1184 : * but we should ignore DefaultInstance silently. */
1185 50 : return 0;
1186 24 : if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
1187 0 : return log_syntax(unit, LOG_WARNING, filename, line, 0,
1188 : "DefaultInstance= only makes sense for template units, ignoring.");
1189 :
1190 24 : r = install_full_printf(i, rvalue, &printed);
1191 24 : if (r < 0)
1192 0 : return r;
1193 :
1194 24 : if (!unit_instance_is_valid(printed))
1195 0 : return -EINVAL;
1196 :
1197 24 : return free_and_replace(i->default_instance, printed);
1198 : }
1199 :
1200 11294 : static int unit_file_load(
1201 : InstallContext *c,
1202 : UnitFileInstallInfo *info,
1203 : const char *path,
1204 : const char *root_dir,
1205 : SearchFlags flags) {
1206 :
1207 45176 : const ConfigTableItem items[] = {
1208 11294 : { "Install", "Alias", config_parse_alias, 0, &info->aliases },
1209 11294 : { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
1210 11294 : { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
1211 : { "Install", "DefaultInstance", config_parse_default_instance, 0, info },
1212 : { "Install", "Also", config_parse_also, 0, c },
1213 : {}
1214 : };
1215 :
1216 : UnitType type;
1217 11294 : _cleanup_fclose_ FILE *f = NULL;
1218 11294 : _cleanup_close_ int fd = -1;
1219 : struct stat st;
1220 : int r;
1221 :
1222 11294 : assert(info);
1223 11294 : assert(path);
1224 :
1225 11294 : if (!(flags & SEARCH_DROPIN)) {
1226 : /* Loading or checking for the main unit file… */
1227 :
1228 11263 : type = unit_name_to_type(info->name);
1229 11263 : if (type < 0)
1230 0 : return -EINVAL;
1231 11263 : if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type))
1232 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1233 : "Unit type %s cannot be templated.", unit_type_to_string(type));
1234 :
1235 11263 : if (!(flags & SEARCH_LOAD)) {
1236 1156 : r = lstat(path, &st);
1237 1156 : if (r < 0)
1238 1050 : return -errno;
1239 :
1240 106 : if (null_or_empty(&st))
1241 0 : info->type = UNIT_FILE_TYPE_MASKED;
1242 106 : else if (S_ISREG(st.st_mode))
1243 77 : info->type = UNIT_FILE_TYPE_REGULAR;
1244 29 : else if (S_ISLNK(st.st_mode))
1245 29 : return -ELOOP;
1246 0 : else if (S_ISDIR(st.st_mode))
1247 0 : return -EISDIR;
1248 : else
1249 0 : return -ENOTTY;
1250 :
1251 77 : return 0;
1252 : }
1253 :
1254 10107 : fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
1255 10107 : if (fd < 0)
1256 9399 : return -errno;
1257 : } else {
1258 : /* Operating on a drop-in file. If we aren't supposed to load the unit file drop-ins don't matter, let's hence shortcut this. */
1259 :
1260 31 : if (!(flags & SEARCH_LOAD))
1261 1 : return 0;
1262 :
1263 30 : fd = chase_symlinks_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
1264 30 : if (fd < 0)
1265 0 : return fd;
1266 : }
1267 :
1268 738 : if (fstat(fd, &st) < 0)
1269 0 : return -errno;
1270 :
1271 738 : if (null_or_empty(&st)) {
1272 0 : if ((flags & SEARCH_DROPIN) == 0)
1273 0 : info->type = UNIT_FILE_TYPE_MASKED;
1274 :
1275 0 : return 0;
1276 : }
1277 :
1278 738 : r = stat_verify_regular(&st);
1279 738 : if (r < 0)
1280 0 : return r;
1281 :
1282 738 : f = fdopen(fd, "r");
1283 738 : if (!f)
1284 0 : return -errno;
1285 738 : fd = -1;
1286 :
1287 : /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1288 738 : assert(c);
1289 :
1290 738 : r = config_parse(info->name, path, f,
1291 : NULL,
1292 : config_item_table_lookup, items,
1293 : CONFIG_PARSE_RELAXED|CONFIG_PARSE_ALLOW_INCLUDE, info);
1294 738 : if (r < 0)
1295 0 : return log_debug_errno(r, "Failed to parse %s: %m", info->name);
1296 :
1297 738 : if ((flags & SEARCH_DROPIN) == 0)
1298 708 : info->type = UNIT_FILE_TYPE_REGULAR;
1299 :
1300 : return
1301 1476 : (int) strv_length(info->aliases) +
1302 1476 : (int) strv_length(info->wanted_by) +
1303 738 : (int) strv_length(info->required_by);
1304 : }
1305 :
1306 11294 : static int unit_file_load_or_readlink(
1307 : InstallContext *c,
1308 : UnitFileInstallInfo *info,
1309 : const char *path,
1310 : const char *root_dir,
1311 : SearchFlags flags) {
1312 :
1313 11294 : _cleanup_free_ char *target = NULL;
1314 : int r;
1315 :
1316 11294 : r = unit_file_load(c, info, path, root_dir, flags);
1317 11294 : if (r != -ELOOP || (flags & SEARCH_DROPIN))
1318 11133 : return r;
1319 :
1320 : /* This is a symlink, let's read it. */
1321 :
1322 161 : r = readlink_malloc(path, &target);
1323 161 : if (r < 0)
1324 0 : return r;
1325 :
1326 161 : if (path_equal(target, "/dev/null"))
1327 6 : info->type = UNIT_FILE_TYPE_MASKED;
1328 : else {
1329 : const char *bn;
1330 : UnitType a, b;
1331 :
1332 155 : bn = basename(target);
1333 :
1334 155 : if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
1335 :
1336 128 : if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
1337 0 : return -EINVAL;
1338 :
1339 27 : } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1340 :
1341 17 : if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
1342 0 : return -EINVAL;
1343 :
1344 10 : } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
1345 :
1346 10 : if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
1347 0 : return -EINVAL;
1348 : } else
1349 0 : return -EINVAL;
1350 :
1351 : /* Enforce that the symlink destination does not
1352 : * change the unit file type. */
1353 :
1354 155 : a = unit_name_to_type(info->name);
1355 155 : b = unit_name_to_type(bn);
1356 155 : if (a < 0 || b < 0 || a != b)
1357 0 : return -EINVAL;
1358 :
1359 155 : if (path_is_absolute(target))
1360 : /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
1361 52 : info->symlink_target = path_join(root_dir, target);
1362 : else
1363 : /* This is a relative path, take it relative to the dir the symlink is located in. */
1364 103 : info->symlink_target = file_in_same_dir(path, target);
1365 155 : if (!info->symlink_target)
1366 0 : return -ENOMEM;
1367 :
1368 155 : info->type = UNIT_FILE_TYPE_SYMLINK;
1369 : }
1370 :
1371 161 : return 0;
1372 : }
1373 :
1374 1050 : static int unit_file_search(
1375 : InstallContext *c,
1376 : UnitFileInstallInfo *info,
1377 : const LookupPaths *paths,
1378 : SearchFlags flags) {
1379 :
1380 1050 : const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
1381 1050 : _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
1382 1050 : _cleanup_free_ char *template = NULL;
1383 1050 : bool found_unit = false;
1384 : int r, result;
1385 : char **p;
1386 :
1387 1050 : assert(info);
1388 1050 : assert(paths);
1389 :
1390 : /* Was this unit already loaded? */
1391 1050 : if (info->type != _UNIT_FILE_TYPE_INVALID)
1392 69 : return 0;
1393 :
1394 981 : if (info->path)
1395 1 : return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags);
1396 :
1397 980 : assert(info->name);
1398 :
1399 980 : if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1400 80 : r = unit_name_template(info->name, &template);
1401 80 : if (r < 0)
1402 0 : return r;
1403 : }
1404 :
1405 10509 : STRV_FOREACH(p, paths->search_path) {
1406 10375 : _cleanup_free_ char *path = NULL;
1407 :
1408 10375 : path = path_join(*p, info->name);
1409 10375 : if (!path)
1410 0 : return -ENOMEM;
1411 :
1412 10375 : r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
1413 10375 : if (r >= 0) {
1414 846 : info->path = TAKE_PTR(path);
1415 846 : result = r;
1416 846 : found_unit = true;
1417 846 : break;
1418 9529 : } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1419 0 : return r;
1420 : }
1421 :
1422 980 : if (!found_unit && template) {
1423 :
1424 : /* Unit file doesn't exist, however instance
1425 : * enablement was requested. We will check if it is
1426 : * possible to load template unit file. */
1427 :
1428 866 : STRV_FOREACH(p, paths->search_path) {
1429 862 : _cleanup_free_ char *path = NULL;
1430 :
1431 862 : path = path_join(*p, template);
1432 862 : if (!path)
1433 0 : return -ENOMEM;
1434 :
1435 862 : r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
1436 862 : if (r >= 0) {
1437 74 : info->path = TAKE_PTR(path);
1438 74 : result = r;
1439 74 : found_unit = true;
1440 74 : break;
1441 788 : } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1442 0 : return r;
1443 : }
1444 : }
1445 :
1446 980 : if (!found_unit)
1447 60 : return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
1448 : "Cannot find unit %s%s%s.",
1449 : info->name, template ? " or " : "", strempty(template));
1450 :
1451 920 : if (info->type == UNIT_FILE_TYPE_MASKED)
1452 6 : return result;
1453 :
1454 : /* Search for drop-in directories */
1455 :
1456 4570 : dropin_dir_name = strjoina(info->name, ".d");
1457 11871 : STRV_FOREACH(p, paths->search_path) {
1458 : char *path;
1459 :
1460 10957 : path = path_join(*p, dropin_dir_name);
1461 10957 : if (!path)
1462 0 : return -ENOMEM;
1463 :
1464 10957 : r = strv_consume(&dirs, path);
1465 10957 : if (r < 0)
1466 0 : return r;
1467 : }
1468 :
1469 914 : if (template) {
1470 380 : dropin_template_dir_name = strjoina(template, ".d");
1471 988 : STRV_FOREACH(p, paths->search_path) {
1472 : char *path;
1473 :
1474 912 : path = path_join(*p, dropin_template_dir_name);
1475 912 : if (!path)
1476 0 : return -ENOMEM;
1477 :
1478 912 : r = strv_consume(&dirs, path);
1479 912 : if (r < 0)
1480 0 : return r;
1481 : }
1482 : }
1483 :
1484 : /* Load drop-in conf files */
1485 :
1486 914 : r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs);
1487 914 : if (r < 0)
1488 0 : return log_debug_errno(r, "Failed to get list of conf files: %m");
1489 :
1490 945 : STRV_FOREACH(p, files) {
1491 31 : r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags | SEARCH_DROPIN);
1492 31 : if (r < 0)
1493 0 : return log_debug_errno(r, "Failed to load conf file %s: %m", *p);
1494 : }
1495 :
1496 914 : return result;
1497 : }
1498 :
1499 164 : static int install_info_follow(
1500 : InstallContext *c,
1501 : UnitFileInstallInfo *i,
1502 : const char *root_dir,
1503 : SearchFlags flags,
1504 : bool ignore_different_name) {
1505 :
1506 164 : assert(c);
1507 164 : assert(i);
1508 :
1509 164 : if (i->type != UNIT_FILE_TYPE_SYMLINK)
1510 0 : return -EINVAL;
1511 164 : if (!i->symlink_target)
1512 0 : return -EINVAL;
1513 :
1514 : /* If the basename doesn't match, the caller should add a
1515 : * complete new entry for this. */
1516 :
1517 164 : if (!ignore_different_name && !streq(basename(i->symlink_target), i->name))
1518 139 : return -EXDEV;
1519 :
1520 25 : free_and_replace(i->path, i->symlink_target);
1521 25 : i->type = _UNIT_FILE_TYPE_INVALID;
1522 :
1523 25 : return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
1524 : }
1525 :
1526 : /**
1527 : * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1528 : * target, maybe more than once. Propagate the instance name if present.
1529 : */
1530 910 : static int install_info_traverse(
1531 : UnitFileScope scope,
1532 : InstallContext *c,
1533 : const LookupPaths *paths,
1534 : UnitFileInstallInfo *start,
1535 : SearchFlags flags,
1536 : UnitFileInstallInfo **ret) {
1537 :
1538 : UnitFileInstallInfo *i;
1539 910 : unsigned k = 0;
1540 : int r;
1541 :
1542 910 : assert(paths);
1543 910 : assert(start);
1544 910 : assert(c);
1545 :
1546 910 : r = unit_file_search(c, start, paths, flags);
1547 910 : if (r < 0)
1548 60 : return r;
1549 :
1550 850 : i = start;
1551 1013 : while (i->type == UNIT_FILE_TYPE_SYMLINK) {
1552 : /* Follow the symlink */
1553 :
1554 163 : if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
1555 0 : return -ELOOP;
1556 :
1557 163 : if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
1558 7 : r = path_is_config(paths, i->path, true);
1559 7 : if (r < 0)
1560 0 : return r;
1561 7 : if (r > 0)
1562 0 : return -ELOOP;
1563 : }
1564 :
1565 163 : r = install_info_follow(c, i, paths->root_dir, flags, false);
1566 163 : if (r == -EXDEV) {
1567 139 : _cleanup_free_ char *buffer = NULL;
1568 : const char *bn;
1569 :
1570 : /* Target has a different name, create a new
1571 : * install info object for that, and continue
1572 : * with that. */
1573 :
1574 139 : bn = basename(i->symlink_target);
1575 :
1576 139 : if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
1577 18 : unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
1578 :
1579 18 : _cleanup_free_ char *instance = NULL;
1580 :
1581 18 : r = unit_name_to_instance(i->name, &instance);
1582 18 : if (r < 0)
1583 0 : return r;
1584 :
1585 18 : r = unit_name_replace_instance(bn, instance, &buffer);
1586 18 : if (r < 0)
1587 0 : return r;
1588 :
1589 18 : if (streq(buffer, i->name)) {
1590 :
1591 : /* We filled in the instance, and the target stayed the same? If so, then let's
1592 : * honour the link as it is. */
1593 :
1594 1 : r = install_info_follow(c, i, paths->root_dir, flags, true);
1595 1 : if (r < 0)
1596 0 : return r;
1597 :
1598 1 : continue;
1599 : }
1600 :
1601 17 : bn = buffer;
1602 : }
1603 :
1604 138 : r = install_info_add(c, bn, NULL, false, &i);
1605 138 : if (r < 0)
1606 0 : return r;
1607 :
1608 : /* Try again, with the new target we found. */
1609 138 : r = unit_file_search(c, i, paths, flags);
1610 138 : if (r == -ENOENT)
1611 : /* Translate error code to highlight this specific case */
1612 0 : return -ENOLINK;
1613 : }
1614 :
1615 162 : if (r < 0)
1616 0 : return r;
1617 : }
1618 :
1619 850 : if (ret)
1620 775 : *ret = i;
1621 :
1622 850 : return 0;
1623 : }
1624 :
1625 : /**
1626 : * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1627 : * or the name (otherwise). root_dir is prepended to the path.
1628 : */
1629 836 : static int install_info_add_auto(
1630 : InstallContext *c,
1631 : const LookupPaths *paths,
1632 : const char *name_or_path,
1633 : UnitFileInstallInfo **ret) {
1634 :
1635 836 : assert(c);
1636 836 : assert(name_or_path);
1637 :
1638 836 : if (path_is_absolute(name_or_path)) {
1639 : const char *pp;
1640 :
1641 1 : pp = prefix_roota(paths->root_dir, name_or_path);
1642 :
1643 1 : return install_info_add(c, NULL, pp, false, ret);
1644 : } else
1645 835 : return install_info_add(c, name_or_path, NULL, false, ret);
1646 : }
1647 :
1648 836 : static int install_info_discover(
1649 : UnitFileScope scope,
1650 : InstallContext *c,
1651 : const LookupPaths *paths,
1652 : const char *name,
1653 : SearchFlags flags,
1654 : UnitFileInstallInfo **ret,
1655 : UnitFileChange **changes,
1656 : size_t *n_changes) {
1657 :
1658 : UnitFileInstallInfo *i;
1659 : int r;
1660 :
1661 836 : assert(c);
1662 836 : assert(paths);
1663 836 : assert(name);
1664 :
1665 836 : r = install_info_add_auto(c, paths, name, &i);
1666 836 : if (r >= 0)
1667 836 : r = install_info_traverse(scope, c, paths, i, flags, ret);
1668 :
1669 836 : if (r < 0)
1670 60 : unit_file_changes_add(changes, n_changes, r, name, NULL);
1671 836 : return r;
1672 : }
1673 :
1674 31 : static int install_info_discover_and_check(
1675 : UnitFileScope scope,
1676 : InstallContext *c,
1677 : const LookupPaths *paths,
1678 : const char *name,
1679 : SearchFlags flags,
1680 : UnitFileInstallInfo **ret,
1681 : UnitFileChange **changes,
1682 : size_t *n_changes) {
1683 :
1684 : int r;
1685 :
1686 31 : r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes);
1687 31 : if (r < 0)
1688 1 : return r;
1689 :
1690 30 : return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes);
1691 : }
1692 :
1693 30 : static int install_info_symlink_alias(
1694 : UnitFileInstallInfo *i,
1695 : const LookupPaths *paths,
1696 : const char *config_path,
1697 : bool force,
1698 : UnitFileChange **changes,
1699 : size_t *n_changes) {
1700 :
1701 : char **s;
1702 30 : int r = 0, q;
1703 :
1704 30 : assert(i);
1705 30 : assert(paths);
1706 30 : assert(config_path);
1707 :
1708 30 : STRV_FOREACH(s, i->aliases) {
1709 0 : _cleanup_free_ char *alias_path = NULL, *dst = NULL;
1710 :
1711 0 : q = install_full_printf(i, *s, &dst);
1712 0 : if (q < 0)
1713 0 : return q;
1714 :
1715 0 : alias_path = path_make_absolute(dst, config_path);
1716 0 : if (!alias_path)
1717 0 : return -ENOMEM;
1718 :
1719 0 : q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
1720 0 : if (r == 0)
1721 0 : r = q;
1722 : }
1723 :
1724 30 : return r;
1725 : }
1726 :
1727 60 : static int install_info_symlink_wants(
1728 : UnitFileInstallInfo *i,
1729 : const LookupPaths *paths,
1730 : const char *config_path,
1731 : char **list,
1732 : const char *suffix,
1733 : UnitFileChange **changes,
1734 : size_t *n_changes) {
1735 :
1736 60 : _cleanup_free_ char *buf = NULL;
1737 : const char *n;
1738 : char **s;
1739 60 : int r = 0, q;
1740 :
1741 60 : assert(i);
1742 60 : assert(paths);
1743 60 : assert(config_path);
1744 :
1745 60 : if (strv_isempty(list))
1746 32 : return 0;
1747 :
1748 28 : if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
1749 2 : UnitFileInstallInfo instance = {
1750 : .type = _UNIT_FILE_TYPE_INVALID,
1751 : };
1752 2 : _cleanup_free_ char *path = NULL;
1753 :
1754 : /* If this is a template, and we have no instance, don't do anything */
1755 2 : if (!i->default_instance)
1756 0 : return 1;
1757 :
1758 2 : r = unit_name_replace_instance(i->name, i->default_instance, &buf);
1759 2 : if (r < 0)
1760 0 : return r;
1761 :
1762 2 : instance.name = buf;
1763 2 : r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS);
1764 2 : if (r < 0)
1765 0 : return r;
1766 :
1767 2 : path = TAKE_PTR(instance.path);
1768 :
1769 2 : if (instance.type == UNIT_FILE_TYPE_MASKED) {
1770 0 : unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL);
1771 0 : return -ERFKILL;
1772 : }
1773 :
1774 2 : n = buf;
1775 : } else
1776 26 : n = i->name;
1777 :
1778 61 : STRV_FOREACH(s, list) {
1779 33 : _cleanup_free_ char *path = NULL, *dst = NULL;
1780 :
1781 33 : q = install_full_printf(i, *s, &dst);
1782 33 : if (q < 0)
1783 0 : return q;
1784 :
1785 33 : if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
1786 0 : r = -EINVAL;
1787 0 : continue;
1788 : }
1789 :
1790 33 : path = strjoin(config_path, "/", dst, suffix, n);
1791 33 : if (!path)
1792 0 : return -ENOMEM;
1793 :
1794 33 : q = create_symlink(paths, i->path, path, true, changes, n_changes);
1795 33 : if (r == 0)
1796 28 : r = q;
1797 : }
1798 :
1799 28 : return r;
1800 : }
1801 :
1802 30 : static int install_info_symlink_link(
1803 : UnitFileInstallInfo *i,
1804 : const LookupPaths *paths,
1805 : const char *config_path,
1806 : bool force,
1807 : UnitFileChange **changes,
1808 : size_t *n_changes) {
1809 :
1810 30 : _cleanup_free_ char *path = NULL;
1811 : int r;
1812 :
1813 30 : assert(i);
1814 30 : assert(paths);
1815 30 : assert(config_path);
1816 30 : assert(i->path);
1817 :
1818 30 : r = in_search_path(paths, i->path);
1819 30 : if (r < 0)
1820 0 : return r;
1821 30 : if (r > 0)
1822 27 : return 0;
1823 :
1824 3 : path = path_join(config_path, i->name);
1825 3 : if (!path)
1826 0 : return -ENOMEM;
1827 :
1828 3 : return create_symlink(paths, i->path, path, force, changes, n_changes);
1829 : }
1830 :
1831 30 : static int install_info_apply(
1832 : UnitFileInstallInfo *i,
1833 : const LookupPaths *paths,
1834 : const char *config_path,
1835 : bool force,
1836 : UnitFileChange **changes,
1837 : size_t *n_changes) {
1838 :
1839 : int r, q;
1840 :
1841 30 : assert(i);
1842 30 : assert(paths);
1843 30 : assert(config_path);
1844 :
1845 30 : if (i->type != UNIT_FILE_TYPE_REGULAR)
1846 0 : return 0;
1847 :
1848 30 : r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
1849 :
1850 30 : q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);
1851 30 : if (r == 0)
1852 30 : r = q;
1853 :
1854 30 : q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);
1855 30 : if (r == 0)
1856 2 : r = q;
1857 :
1858 30 : q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1859 : /* Do not count links to the unit file towards the "carries_install_info" count */
1860 30 : if (r == 0 && q < 0)
1861 0 : r = q;
1862 :
1863 30 : return r;
1864 : }
1865 :
1866 27 : static int install_context_apply(
1867 : UnitFileScope scope,
1868 : InstallContext *c,
1869 : const LookupPaths *paths,
1870 : const char *config_path,
1871 : bool force,
1872 : SearchFlags flags,
1873 : UnitFileChange **changes,
1874 : size_t *n_changes) {
1875 :
1876 : UnitFileInstallInfo *i;
1877 : int r;
1878 :
1879 27 : assert(c);
1880 27 : assert(paths);
1881 27 : assert(config_path);
1882 :
1883 27 : if (ordered_hashmap_isempty(c->will_process))
1884 2 : return 0;
1885 :
1886 25 : r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
1887 25 : if (r < 0)
1888 0 : return r;
1889 :
1890 25 : r = 0;
1891 62 : while ((i = ordered_hashmap_first(c->will_process))) {
1892 : int q;
1893 :
1894 37 : q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1895 37 : if (q < 0)
1896 0 : return q;
1897 :
1898 37 : q = install_info_traverse(scope, c, paths, i, flags, NULL);
1899 37 : if (q < 0) {
1900 0 : unit_file_changes_add(changes, n_changes, r, i->name, NULL);
1901 0 : return q;
1902 : }
1903 :
1904 : /* We can attempt to process a masked unit when a different unit
1905 : * that we were processing specifies it in Also=. */
1906 37 : if (i->type == UNIT_FILE_TYPE_MASKED) {
1907 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
1908 0 : if (r >= 0)
1909 : /* Assume that something *could* have been enabled here,
1910 : * avoid "empty [Install] section" warning. */
1911 0 : r += 1;
1912 0 : continue;
1913 : }
1914 :
1915 37 : if (i->type != UNIT_FILE_TYPE_REGULAR)
1916 7 : continue;
1917 :
1918 30 : q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1919 30 : if (r >= 0) {
1920 30 : if (q < 0)
1921 0 : r = q;
1922 : else
1923 30 : r += q;
1924 : }
1925 : }
1926 :
1927 25 : return r;
1928 : }
1929 :
1930 17 : static int install_context_mark_for_removal(
1931 : UnitFileScope scope,
1932 : InstallContext *c,
1933 : const LookupPaths *paths,
1934 : Set **remove_symlinks_to,
1935 : const char *config_path,
1936 : UnitFileChange **changes,
1937 : size_t *n_changes) {
1938 :
1939 : UnitFileInstallInfo *i;
1940 : int r;
1941 :
1942 17 : assert(c);
1943 17 : assert(paths);
1944 17 : assert(config_path);
1945 :
1946 : /* Marks all items for removal */
1947 :
1948 17 : if (ordered_hashmap_isempty(c->will_process))
1949 3 : return 0;
1950 :
1951 14 : r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
1952 14 : if (r < 0)
1953 0 : return r;
1954 :
1955 51 : while ((i = ordered_hashmap_first(c->will_process))) {
1956 :
1957 37 : r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1958 37 : if (r < 0)
1959 0 : return r;
1960 :
1961 37 : r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
1962 37 : if (r == -ENOLINK) {
1963 0 : log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
1964 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL);
1965 37 : } else if (r == -ENOENT) {
1966 :
1967 0 : if (i->auxiliary) /* some unit specified in Also= or similar is missing */
1968 0 : log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
1969 : else {
1970 0 : log_debug_errno(r, "Unit %s not found, removing name.", i->name);
1971 0 : unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1972 : }
1973 :
1974 37 : } else if (r < 0) {
1975 0 : log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
1976 0 : unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1977 37 : } else if (i->type == UNIT_FILE_TYPE_MASKED) {
1978 0 : log_debug("Unit file %s is masked, ignoring.", i->name);
1979 0 : unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path ?: i->name, NULL);
1980 0 : continue;
1981 37 : } else if (i->type != UNIT_FILE_TYPE_REGULAR) {
1982 2 : log_debug("Unit %s has type %s, ignoring.", i->name, unit_file_type_to_string(i->type) ?: "invalid");
1983 2 : continue;
1984 : }
1985 :
1986 35 : r = mark_symlink_for_removal(remove_symlinks_to, i->name);
1987 35 : if (r < 0)
1988 0 : return r;
1989 : }
1990 :
1991 14 : return 0;
1992 : }
1993 :
1994 1 : int unit_file_mask(
1995 : UnitFileScope scope,
1996 : UnitFileFlags flags,
1997 : const char *root_dir,
1998 : char **files,
1999 : UnitFileChange **changes,
2000 : size_t *n_changes) {
2001 :
2002 1 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2003 : const char *config_path;
2004 : char **i;
2005 : int r;
2006 :
2007 1 : assert(scope >= 0);
2008 1 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2009 :
2010 1 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2011 1 : if (r < 0)
2012 0 : return r;
2013 :
2014 1 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2015 1 : if (!config_path)
2016 0 : return -ENXIO;
2017 :
2018 2 : STRV_FOREACH(i, files) {
2019 1 : _cleanup_free_ char *path = NULL;
2020 : int q;
2021 :
2022 1 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
2023 0 : if (r == 0)
2024 0 : r = -EINVAL;
2025 0 : continue;
2026 : }
2027 :
2028 1 : path = path_make_absolute(*i, config_path);
2029 1 : if (!path)
2030 0 : return -ENOMEM;
2031 :
2032 1 : q = create_symlink(&paths, "/dev/null", path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
2033 1 : if (q < 0 && r >= 0)
2034 0 : r = q;
2035 : }
2036 :
2037 1 : return r;
2038 : }
2039 :
2040 1 : int unit_file_unmask(
2041 : UnitFileScope scope,
2042 : UnitFileFlags flags,
2043 : const char *root_dir,
2044 : char **files,
2045 : UnitFileChange **changes,
2046 : size_t *n_changes) {
2047 :
2048 1 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2049 1 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2050 1 : _cleanup_strv_free_ char **todo = NULL;
2051 1 : size_t n_todo = 0, n_allocated = 0;
2052 : const char *config_path;
2053 : char **i;
2054 : bool dry_run;
2055 : int r, q;
2056 :
2057 1 : assert(scope >= 0);
2058 1 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2059 :
2060 1 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2061 1 : if (r < 0)
2062 0 : return r;
2063 :
2064 1 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2065 1 : if (!config_path)
2066 0 : return -ENXIO;
2067 :
2068 1 : dry_run = !!(flags & UNIT_FILE_DRY_RUN);
2069 :
2070 2 : STRV_FOREACH(i, files) {
2071 1 : _cleanup_free_ char *path = NULL;
2072 :
2073 1 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2074 0 : return -EINVAL;
2075 :
2076 1 : path = path_make_absolute(*i, config_path);
2077 1 : if (!path)
2078 0 : return -ENOMEM;
2079 :
2080 1 : r = null_or_empty_path(path);
2081 1 : if (r == -ENOENT)
2082 0 : continue;
2083 1 : if (r < 0)
2084 0 : return r;
2085 1 : if (r == 0)
2086 0 : continue;
2087 :
2088 1 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2089 0 : return -ENOMEM;
2090 :
2091 1 : todo[n_todo] = strdup(*i);
2092 1 : if (!todo[n_todo])
2093 0 : return -ENOMEM;
2094 :
2095 1 : n_todo++;
2096 : }
2097 :
2098 1 : strv_uniq(todo);
2099 :
2100 1 : r = 0;
2101 2 : STRV_FOREACH(i, todo) {
2102 1 : _cleanup_free_ char *path = NULL;
2103 : const char *rp;
2104 :
2105 1 : path = path_make_absolute(*i, config_path);
2106 1 : if (!path)
2107 0 : return -ENOMEM;
2108 :
2109 1 : if (!dry_run && unlink(path) < 0) {
2110 0 : if (errno != ENOENT) {
2111 0 : if (r >= 0)
2112 0 : r = -errno;
2113 0 : unit_file_changes_add(changes, n_changes, -errno, path, NULL);
2114 : }
2115 :
2116 0 : continue;
2117 : }
2118 :
2119 1 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
2120 :
2121 1 : rp = skip_root(&paths, path);
2122 1 : q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
2123 1 : if (q < 0)
2124 0 : return q;
2125 : }
2126 :
2127 1 : q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
2128 1 : if (r >= 0)
2129 1 : r = q;
2130 :
2131 1 : return r;
2132 : }
2133 :
2134 1 : int unit_file_link(
2135 : UnitFileScope scope,
2136 : UnitFileFlags flags,
2137 : const char *root_dir,
2138 : char **files,
2139 : UnitFileChange **changes,
2140 : size_t *n_changes) {
2141 :
2142 1 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2143 1 : _cleanup_strv_free_ char **todo = NULL;
2144 1 : size_t n_todo = 0, n_allocated = 0;
2145 : const char *config_path;
2146 : char **i;
2147 : int r, q;
2148 :
2149 1 : assert(scope >= 0);
2150 1 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2151 :
2152 1 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2153 1 : if (r < 0)
2154 0 : return r;
2155 :
2156 1 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2157 1 : if (!config_path)
2158 0 : return -ENXIO;
2159 :
2160 2 : STRV_FOREACH(i, files) {
2161 1 : _cleanup_free_ char *full = NULL;
2162 : struct stat st;
2163 : char *fn;
2164 :
2165 1 : if (!path_is_absolute(*i))
2166 0 : return -EINVAL;
2167 :
2168 1 : fn = basename(*i);
2169 1 : if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
2170 0 : return -EINVAL;
2171 :
2172 1 : full = path_join(paths.root_dir, *i);
2173 1 : if (!full)
2174 0 : return -ENOMEM;
2175 :
2176 1 : if (lstat(full, &st) < 0)
2177 0 : return -errno;
2178 1 : r = stat_verify_regular(&st);
2179 1 : if (r < 0)
2180 0 : return r;
2181 :
2182 1 : q = in_search_path(&paths, *i);
2183 1 : if (q < 0)
2184 0 : return q;
2185 1 : if (q > 0)
2186 0 : continue;
2187 :
2188 1 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2189 0 : return -ENOMEM;
2190 :
2191 1 : todo[n_todo] = strdup(*i);
2192 1 : if (!todo[n_todo])
2193 0 : return -ENOMEM;
2194 :
2195 1 : n_todo++;
2196 : }
2197 :
2198 1 : strv_uniq(todo);
2199 :
2200 1 : r = 0;
2201 2 : STRV_FOREACH(i, todo) {
2202 1 : _cleanup_free_ char *new_path = NULL;
2203 :
2204 1 : new_path = path_make_absolute(basename(*i), config_path);
2205 1 : if (!new_path)
2206 0 : return -ENOMEM;
2207 :
2208 1 : q = create_symlink(&paths, *i, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
2209 1 : if (q < 0 && r >= 0)
2210 0 : r = q;
2211 : }
2212 :
2213 1 : return r;
2214 : }
2215 :
2216 1 : static int path_shall_revert(const LookupPaths *paths, const char *path) {
2217 : int r;
2218 :
2219 1 : assert(paths);
2220 1 : assert(path);
2221 :
2222 : /* Checks whether the path is one where the drop-in directories shall be removed. */
2223 :
2224 1 : r = path_is_config(paths, path, true);
2225 1 : if (r != 0)
2226 1 : return r;
2227 :
2228 0 : r = path_is_control(paths, path);
2229 0 : if (r != 0)
2230 0 : return r;
2231 :
2232 0 : return path_is_transient(paths, path);
2233 : }
2234 :
2235 3 : int unit_file_revert(
2236 : UnitFileScope scope,
2237 : const char *root_dir,
2238 : char **files,
2239 : UnitFileChange **changes,
2240 : size_t *n_changes) {
2241 :
2242 3 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2243 3 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2244 3 : _cleanup_strv_free_ char **todo = NULL;
2245 3 : size_t n_todo = 0, n_allocated = 0;
2246 : char **i;
2247 : int r, q;
2248 :
2249 : /* Puts a unit file back into vendor state. This means:
2250 : *
2251 : * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and
2252 : * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated").
2253 : *
2254 : * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
2255 : * "config", but not in "transient" or "control" or even "generated").
2256 : *
2257 : * We remove all that in both the runtime and the persistent directories, if that applies.
2258 : */
2259 :
2260 3 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2261 3 : if (r < 0)
2262 0 : return r;
2263 :
2264 6 : STRV_FOREACH(i, files) {
2265 3 : bool has_vendor = false;
2266 : char **p;
2267 :
2268 3 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2269 0 : return -EINVAL;
2270 :
2271 39 : STRV_FOREACH(p, paths.search_path) {
2272 36 : _cleanup_free_ char *path = NULL, *dropin = NULL;
2273 : struct stat st;
2274 :
2275 36 : path = path_make_absolute(*i, *p);
2276 36 : if (!path)
2277 0 : return -ENOMEM;
2278 :
2279 36 : r = lstat(path, &st);
2280 36 : if (r < 0) {
2281 32 : if (errno != ENOENT)
2282 0 : return -errno;
2283 4 : } else if (S_ISREG(st.st_mode)) {
2284 : /* Check if there's a vendor version */
2285 4 : r = path_is_vendor(&paths, path);
2286 4 : if (r < 0)
2287 0 : return r;
2288 4 : if (r > 0)
2289 3 : has_vendor = true;
2290 : }
2291 :
2292 36 : dropin = strjoin(path, ".d");
2293 36 : if (!dropin)
2294 0 : return -ENOMEM;
2295 :
2296 36 : r = lstat(dropin, &st);
2297 36 : if (r < 0) {
2298 35 : if (errno != ENOENT)
2299 0 : return -errno;
2300 1 : } else if (S_ISDIR(st.st_mode)) {
2301 : /* Remove the drop-ins */
2302 1 : r = path_shall_revert(&paths, dropin);
2303 1 : if (r < 0)
2304 0 : return r;
2305 1 : if (r > 0) {
2306 1 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2307 0 : return -ENOMEM;
2308 :
2309 1 : todo[n_todo++] = TAKE_PTR(dropin);
2310 : }
2311 : }
2312 : }
2313 :
2314 3 : if (!has_vendor)
2315 0 : continue;
2316 :
2317 : /* OK, there's a vendor version, hence drop all configuration versions */
2318 39 : STRV_FOREACH(p, paths.search_path) {
2319 36 : _cleanup_free_ char *path = NULL;
2320 : struct stat st;
2321 :
2322 36 : path = path_make_absolute(*i, *p);
2323 36 : if (!path)
2324 0 : return -ENOMEM;
2325 :
2326 36 : r = lstat(path, &st);
2327 36 : if (r < 0) {
2328 32 : if (errno != ENOENT)
2329 0 : return -errno;
2330 4 : } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
2331 4 : r = path_is_config(&paths, path, true);
2332 4 : if (r < 0)
2333 0 : return r;
2334 4 : if (r > 0) {
2335 1 : if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2336 0 : return -ENOMEM;
2337 :
2338 1 : todo[n_todo++] = TAKE_PTR(path);
2339 : }
2340 : }
2341 : }
2342 : }
2343 :
2344 3 : strv_uniq(todo);
2345 :
2346 3 : r = 0;
2347 5 : STRV_FOREACH(i, todo) {
2348 2 : _cleanup_strv_free_ char **fs = NULL;
2349 : const char *rp;
2350 : char **j;
2351 :
2352 2 : (void) get_files_in_directory(*i, &fs);
2353 :
2354 2 : q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
2355 2 : if (q < 0 && q != -ENOENT && r >= 0) {
2356 0 : r = q;
2357 0 : continue;
2358 : }
2359 :
2360 3 : STRV_FOREACH(j, fs) {
2361 1 : _cleanup_free_ char *t = NULL;
2362 :
2363 1 : t = path_join(*i, *j);
2364 1 : if (!t)
2365 0 : return -ENOMEM;
2366 :
2367 1 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL);
2368 : }
2369 :
2370 2 : unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL);
2371 :
2372 2 : rp = skip_root(&paths, *i);
2373 2 : q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
2374 2 : if (q < 0)
2375 0 : return q;
2376 : }
2377 :
2378 3 : q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, false, changes, n_changes);
2379 3 : if (r >= 0)
2380 3 : r = q;
2381 :
2382 3 : q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, false, changes, n_changes);
2383 3 : if (r >= 0)
2384 3 : r = q;
2385 :
2386 3 : return r;
2387 : }
2388 :
2389 1 : int unit_file_add_dependency(
2390 : UnitFileScope scope,
2391 : UnitFileFlags flags,
2392 : const char *root_dir,
2393 : char **files,
2394 : const char *target,
2395 : UnitDependency dep,
2396 : UnitFileChange **changes,
2397 : size_t *n_changes) {
2398 :
2399 1 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2400 1 : _cleanup_(install_context_done) InstallContext c = {};
2401 : UnitFileInstallInfo *i, *target_info;
2402 : const char *config_path;
2403 : char **f;
2404 : int r;
2405 :
2406 1 : assert(scope >= 0);
2407 1 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2408 1 : assert(target);
2409 :
2410 1 : if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
2411 0 : return -EINVAL;
2412 :
2413 1 : if (!unit_name_is_valid(target, UNIT_NAME_ANY))
2414 0 : return -EINVAL;
2415 :
2416 1 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2417 1 : if (r < 0)
2418 0 : return r;
2419 :
2420 1 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2421 1 : if (!config_path)
2422 0 : return -ENXIO;
2423 :
2424 1 : r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2425 : &target_info, changes, n_changes);
2426 1 : if (r < 0)
2427 0 : return r;
2428 :
2429 1 : assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
2430 :
2431 2 : STRV_FOREACH(f, files) {
2432 : char ***l;
2433 :
2434 1 : r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2435 : &i, changes, n_changes);
2436 1 : if (r < 0)
2437 0 : return r;
2438 :
2439 1 : assert(i->type == UNIT_FILE_TYPE_REGULAR);
2440 :
2441 : /* We didn't actually load anything from the unit
2442 : * file, but instead just add in our new symlink to
2443 : * create. */
2444 :
2445 1 : if (dep == UNIT_WANTS)
2446 1 : l = &i->wanted_by;
2447 : else
2448 0 : l = &i->required_by;
2449 :
2450 1 : strv_free(*l);
2451 1 : *l = strv_new(target_info->name);
2452 1 : if (!*l)
2453 0 : return -ENOMEM;
2454 : }
2455 :
2456 1 : return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
2457 : }
2458 :
2459 20 : int unit_file_enable(
2460 : UnitFileScope scope,
2461 : UnitFileFlags flags,
2462 : const char *root_dir,
2463 : char **files,
2464 : UnitFileChange **changes,
2465 : size_t *n_changes) {
2466 :
2467 20 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2468 20 : _cleanup_(install_context_done) InstallContext c = {};
2469 : const char *config_path;
2470 : UnitFileInstallInfo *i;
2471 : char **f;
2472 : int r;
2473 :
2474 20 : assert(scope >= 0);
2475 20 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2476 :
2477 20 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2478 20 : if (r < 0)
2479 0 : return r;
2480 :
2481 20 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2482 20 : if (!config_path)
2483 0 : return -ENXIO;
2484 :
2485 39 : STRV_FOREACH(f, files) {
2486 20 : r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2487 : &i, changes, n_changes);
2488 20 : if (r < 0)
2489 1 : return r;
2490 :
2491 19 : assert(i->type == UNIT_FILE_TYPE_REGULAR);
2492 : }
2493 :
2494 : /* This will return the number of symlink rules that were
2495 : supposed to be created, not the ones actually created. This
2496 : is useful to determine whether the passed files had any
2497 : installation data at all. */
2498 :
2499 19 : return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_LOAD, changes, n_changes);
2500 : }
2501 :
2502 10 : int unit_file_disable(
2503 : UnitFileScope scope,
2504 : UnitFileFlags flags,
2505 : const char *root_dir,
2506 : char **files,
2507 : UnitFileChange **changes,
2508 : size_t *n_changes) {
2509 :
2510 10 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2511 10 : _cleanup_(install_context_done) InstallContext c = {};
2512 10 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2513 : const char *config_path;
2514 : char **i;
2515 : int r;
2516 :
2517 10 : assert(scope >= 0);
2518 10 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2519 :
2520 10 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2521 10 : if (r < 0)
2522 0 : return r;
2523 :
2524 10 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
2525 10 : if (!config_path)
2526 0 : return -ENXIO;
2527 :
2528 20 : STRV_FOREACH(i, files) {
2529 10 : if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2530 0 : return -EINVAL;
2531 :
2532 10 : r = install_info_add(&c, *i, NULL, false, NULL);
2533 10 : if (r < 0)
2534 0 : return r;
2535 : }
2536 :
2537 10 : r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
2538 10 : if (r < 0)
2539 0 : return r;
2540 :
2541 10 : return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
2542 : }
2543 :
2544 1 : int unit_file_reenable(
2545 : UnitFileScope scope,
2546 : UnitFileFlags flags,
2547 : const char *root_dir,
2548 : char **files,
2549 : UnitFileChange **changes,
2550 : size_t *n_changes) {
2551 :
2552 : char **n;
2553 : int r;
2554 : size_t l, i;
2555 :
2556 : /* First, we invoke the disable command with only the basename... */
2557 1 : l = strv_length(files);
2558 1 : n = newa(char*, l+1);
2559 2 : for (i = 0; i < l; i++)
2560 1 : n[i] = basename(files[i]);
2561 1 : n[i] = NULL;
2562 :
2563 1 : r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes);
2564 1 : if (r < 0)
2565 0 : return r;
2566 :
2567 : /* But the enable command with the full name */
2568 1 : return unit_file_enable(scope, flags, root_dir, files, changes, n_changes);
2569 : }
2570 :
2571 2 : int unit_file_set_default(
2572 : UnitFileScope scope,
2573 : UnitFileFlags flags,
2574 : const char *root_dir,
2575 : const char *name,
2576 : UnitFileChange **changes,
2577 : size_t *n_changes) {
2578 :
2579 2 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2580 2 : _cleanup_(install_context_done) InstallContext c = {};
2581 : UnitFileInstallInfo *i;
2582 : const char *new_path;
2583 : int r;
2584 :
2585 2 : assert(scope >= 0);
2586 2 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2587 2 : assert(name);
2588 :
2589 2 : if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
2590 0 : return -EINVAL;
2591 2 : if (streq(name, SPECIAL_DEFAULT_TARGET))
2592 0 : return -EINVAL;
2593 :
2594 2 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2595 2 : if (r < 0)
2596 0 : return r;
2597 :
2598 2 : r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes);
2599 2 : if (r < 0)
2600 1 : return r;
2601 :
2602 5 : new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
2603 1 : return create_symlink(&paths, i->path, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
2604 : }
2605 :
2606 3 : int unit_file_get_default(
2607 : UnitFileScope scope,
2608 : const char *root_dir,
2609 : char **name) {
2610 :
2611 3 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2612 3 : _cleanup_(install_context_done) InstallContext c = {};
2613 : UnitFileInstallInfo *i;
2614 : char *n;
2615 : int r;
2616 :
2617 3 : assert(scope >= 0);
2618 3 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2619 3 : assert(name);
2620 :
2621 3 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2622 3 : if (r < 0)
2623 0 : return r;
2624 :
2625 3 : r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2626 : &i, NULL, NULL);
2627 3 : if (r < 0)
2628 2 : return r;
2629 1 : r = install_info_may_process(i, &paths, NULL, 0);
2630 1 : if (r < 0)
2631 0 : return r;
2632 :
2633 1 : n = strdup(i->name);
2634 1 : if (!n)
2635 0 : return -ENOMEM;
2636 :
2637 1 : *name = n;
2638 1 : return 0;
2639 : }
2640 :
2641 707 : int unit_file_lookup_state(
2642 : UnitFileScope scope,
2643 : const LookupPaths *paths,
2644 : const char *name,
2645 : UnitFileState *ret) {
2646 :
2647 707 : _cleanup_(install_context_done) InstallContext c = {};
2648 : UnitFileInstallInfo *i;
2649 : UnitFileState state;
2650 : int r;
2651 :
2652 707 : assert(paths);
2653 707 : assert(name);
2654 :
2655 707 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2656 0 : return -EINVAL;
2657 :
2658 707 : r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2659 : &i, NULL, NULL);
2660 707 : if (r < 0)
2661 33 : return log_debug_errno(r, "Failed to discover unit %s: %m", name);
2662 :
2663 674 : assert(IN_SET(i->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED));
2664 674 : log_debug("Found unit %s at %s (%s)", name, strna(i->path),
2665 : i->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask");
2666 :
2667 : /* Shortcut things, if the caller just wants to know if this unit exists. */
2668 674 : if (!ret)
2669 5 : return 0;
2670 :
2671 669 : switch (i->type) {
2672 :
2673 5 : case UNIT_FILE_TYPE_MASKED:
2674 5 : r = path_is_runtime(paths, i->path, true);
2675 5 : if (r < 0)
2676 0 : return r;
2677 :
2678 5 : state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
2679 5 : break;
2680 :
2681 664 : case UNIT_FILE_TYPE_REGULAR:
2682 664 : r = path_is_generator(paths, i->path);
2683 664 : if (r < 0)
2684 0 : return r;
2685 664 : if (r > 0) {
2686 10 : state = UNIT_FILE_GENERATED;
2687 10 : break;
2688 : }
2689 :
2690 654 : r = path_is_transient(paths, i->path);
2691 654 : if (r < 0)
2692 0 : return r;
2693 654 : if (r > 0) {
2694 1 : state = UNIT_FILE_TRANSIENT;
2695 1 : break;
2696 : }
2697 :
2698 : /* Check if any of the Alias= symlinks have been created.
2699 : * We ignore other aliases, and only check those that would
2700 : * be created by systemctl enable for this unit. */
2701 653 : r = find_symlinks_in_scope(scope, paths, i, true, &state);
2702 653 : if (r < 0)
2703 0 : return r;
2704 653 : if (r > 0)
2705 128 : break;
2706 :
2707 : /* Check if the file is known under other names. If it is,
2708 : * it might be in use. Report that as UNIT_FILE_INDIRECT. */
2709 525 : r = find_symlinks_in_scope(scope, paths, i, false, &state);
2710 525 : if (r < 0)
2711 0 : return r;
2712 525 : if (r > 0)
2713 7 : state = UNIT_FILE_INDIRECT;
2714 : else {
2715 518 : if (unit_file_install_info_has_rules(i))
2716 242 : state = UNIT_FILE_DISABLED;
2717 276 : else if (unit_file_install_info_has_also(i))
2718 17 : state = UNIT_FILE_INDIRECT;
2719 : else
2720 259 : state = UNIT_FILE_STATIC;
2721 : }
2722 :
2723 525 : break;
2724 :
2725 0 : default:
2726 0 : assert_not_reached("Unexpected unit file type.");
2727 : }
2728 :
2729 669 : *ret = state;
2730 669 : return 0;
2731 : }
2732 :
2733 182 : int unit_file_get_state(
2734 : UnitFileScope scope,
2735 : const char *root_dir,
2736 : const char *name,
2737 : UnitFileState *ret) {
2738 :
2739 182 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
2740 : int r;
2741 :
2742 182 : assert(scope >= 0);
2743 182 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2744 182 : assert(name);
2745 :
2746 182 : r = lookup_paths_init(&paths, scope, 0, root_dir);
2747 182 : if (r < 0)
2748 0 : return r;
2749 :
2750 182 : return unit_file_lookup_state(scope, &paths, name, ret);
2751 : }
2752 :
2753 25 : int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) {
2754 25 : _cleanup_(install_context_done) InstallContext c = {};
2755 : int r;
2756 :
2757 25 : assert(paths);
2758 25 : assert(name);
2759 :
2760 25 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2761 0 : return -EINVAL;
2762 :
2763 25 : r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL);
2764 25 : if (r == -ENOENT)
2765 24 : return 0;
2766 1 : if (r < 0)
2767 0 : return r;
2768 :
2769 1 : return 1;
2770 : }
2771 :
2772 11 : static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
2773 11 : _cleanup_strv_free_ char **instances = NULL;
2774 11 : _cleanup_free_ char *unit_name = NULL;
2775 : int r;
2776 :
2777 11 : assert(pattern);
2778 11 : assert(out_instances);
2779 11 : assert(out_unit_name);
2780 :
2781 11 : r = extract_first_word(&pattern, &unit_name, NULL, EXTRACT_RETAIN_ESCAPE);
2782 11 : if (r < 0)
2783 0 : return r;
2784 :
2785 : /* We handle the instances logic when unit name is extracted */
2786 11 : if (pattern) {
2787 : /* We only create instances when a rule of templated unit
2788 : * is seen. A rule like enable foo@.service a b c will
2789 : * result in an array of (a, b, c) as instance names */
2790 2 : if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
2791 0 : return -EINVAL;
2792 :
2793 2 : instances = strv_split(pattern, WHITESPACE);
2794 2 : if (!instances)
2795 0 : return -ENOMEM;
2796 :
2797 2 : *out_instances = TAKE_PTR(instances);
2798 : }
2799 :
2800 11 : *out_unit_name = TAKE_PTR(unit_name);
2801 :
2802 11 : return 0;
2803 : }
2804 :
2805 7 : static int presets_find_config(UnitFileScope scope, const char *root_dir, char ***files) {
2806 : static const char* const system_dirs[] = {CONF_PATHS("systemd/system-preset"), NULL};
2807 : static const char* const user_dirs[] = {CONF_PATHS_USR("systemd/user-preset"), NULL};
2808 : const char* const* dirs;
2809 :
2810 7 : assert(scope >= 0);
2811 7 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2812 :
2813 7 : if (scope == UNIT_FILE_SYSTEM)
2814 7 : dirs = system_dirs;
2815 0 : else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER))
2816 0 : dirs = user_dirs;
2817 : else
2818 0 : assert_not_reached("Invalid unit file scope");
2819 :
2820 7 : return conf_files_list_strv(files, ".preset", root_dir, 0, dirs);
2821 : }
2822 :
2823 7 : static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
2824 7 : _cleanup_(presets_freep) Presets ps = {};
2825 7 : size_t n_allocated = 0;
2826 7 : _cleanup_strv_free_ char **files = NULL;
2827 : char **p;
2828 : int r;
2829 :
2830 7 : assert(scope >= 0);
2831 7 : assert(scope < _UNIT_FILE_SCOPE_MAX);
2832 7 : assert(presets);
2833 :
2834 7 : r = presets_find_config(scope, root_dir, &files);
2835 7 : if (r < 0)
2836 0 : return r;
2837 :
2838 14 : STRV_FOREACH(p, files) {
2839 7 : _cleanup_fclose_ FILE *f;
2840 7 : int n = 0;
2841 :
2842 7 : f = fopen(*p, "re");
2843 7 : if (!f) {
2844 0 : if (errno == ENOENT)
2845 0 : continue;
2846 :
2847 0 : return -errno;
2848 : }
2849 :
2850 18 : for (;;) {
2851 25 : _cleanup_free_ char *line = NULL;
2852 25 : PresetRule rule = {};
2853 : const char *parameter;
2854 : char *l;
2855 :
2856 25 : r = read_line(f, LONG_LINE_MAX, &line);
2857 25 : if (r < 0)
2858 0 : return r;
2859 25 : if (r == 0)
2860 7 : break;
2861 :
2862 18 : l = strstrip(line);
2863 18 : n++;
2864 :
2865 18 : if (isempty(l))
2866 0 : continue;
2867 18 : if (strchr(COMMENTS, *l))
2868 0 : continue;
2869 :
2870 18 : parameter = first_word(l, "enable");
2871 18 : if (parameter) {
2872 : char *unit_name;
2873 11 : char **instances = NULL;
2874 :
2875 : /* Unit_name will remain the same as parameter when no instances are specified */
2876 11 : r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
2877 11 : if (r < 0) {
2878 0 : log_syntax(NULL, LOG_WARNING, *p, n, r, "Couldn't parse line '%s'. Ignoring.", line);
2879 0 : continue;
2880 : }
2881 :
2882 11 : rule = (PresetRule) {
2883 : .pattern = unit_name,
2884 : .action = PRESET_ENABLE,
2885 : .instances = instances,
2886 : };
2887 : }
2888 :
2889 18 : parameter = first_word(l, "disable");
2890 18 : if (parameter) {
2891 : char *pattern;
2892 :
2893 7 : pattern = strdup(parameter);
2894 7 : if (!pattern)
2895 0 : return -ENOMEM;
2896 :
2897 7 : rule = (PresetRule) {
2898 : .pattern = pattern,
2899 : .action = PRESET_DISABLE,
2900 : };
2901 : }
2902 :
2903 18 : if (rule.action) {
2904 18 : if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1))
2905 0 : return -ENOMEM;
2906 :
2907 18 : ps.rules[ps.n_rules++] = rule;
2908 18 : continue;
2909 : }
2910 :
2911 0 : log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
2912 : }
2913 : }
2914 :
2915 7 : *presets = ps;
2916 7 : ps = (Presets){};
2917 :
2918 7 : return 0;
2919 : }
2920 :
2921 65 : static int pattern_match_multiple_instances(
2922 : const PresetRule rule,
2923 : const char *unit_name,
2924 : char ***ret) {
2925 :
2926 65 : _cleanup_free_ char *templated_name = NULL;
2927 : int r;
2928 :
2929 : /* If no ret is needed or the rule itself does not have instances
2930 : * initialized, we return not matching */
2931 65 : if (!ret || !rule.instances)
2932 51 : return 0;
2933 :
2934 14 : r = unit_name_template(unit_name, &templated_name);
2935 14 : if (r < 0)
2936 11 : return r;
2937 3 : if (!streq(rule.pattern, templated_name))
2938 1 : return 0;
2939 :
2940 : /* Compose a list of specified instances when unit name is a template */
2941 2 : if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
2942 1 : _cleanup_free_ char *prefix = NULL;
2943 1 : _cleanup_strv_free_ char **out_strv = NULL;
2944 : char **iter;
2945 :
2946 1 : r = unit_name_to_prefix(unit_name, &prefix);
2947 1 : if (r < 0)
2948 0 : return r;
2949 :
2950 4 : STRV_FOREACH(iter, rule.instances) {
2951 3 : _cleanup_free_ char *name = NULL;
2952 3 : r = unit_name_build(prefix, *iter, ".service", &name);
2953 3 : if (r < 0)
2954 0 : return r;
2955 3 : r = strv_extend(&out_strv, name);
2956 3 : if (r < 0)
2957 0 : return r;
2958 : }
2959 :
2960 1 : *ret = TAKE_PTR(out_strv);
2961 1 : return 1;
2962 : } else {
2963 : /* We now know the input unit name is an instance name */
2964 1 : _cleanup_free_ char *instance_name = NULL;
2965 :
2966 1 : r = unit_name_to_instance(unit_name, &instance_name);
2967 1 : if (r < 0)
2968 0 : return r;
2969 :
2970 1 : if (strv_find(rule.instances, instance_name))
2971 1 : return 1;
2972 : }
2973 0 : return 0;
2974 : }
2975 :
2976 29 : static int query_presets(const char *name, const Presets presets, char ***instance_name_list) {
2977 29 : PresetAction action = PRESET_UNKNOWN;
2978 : size_t i;
2979 : char **s;
2980 29 : if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2981 0 : return -EINVAL;
2982 :
2983 65 : for (i = 0; i < presets.n_rules; i++)
2984 128 : if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 ||
2985 63 : fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
2986 29 : action = presets.rules[i].action;
2987 29 : break;
2988 : }
2989 :
2990 29 : switch (action) {
2991 0 : case PRESET_UNKNOWN:
2992 0 : log_debug("Preset files don't specify rule for %s. Enabling.", name);
2993 0 : return 1;
2994 5 : case PRESET_ENABLE:
2995 6 : if (instance_name_list && *instance_name_list)
2996 4 : STRV_FOREACH(s, *instance_name_list)
2997 3 : log_debug("Preset files say enable %s.", *s);
2998 : else
2999 4 : log_debug("Preset files say enable %s.", name);
3000 5 : return 1;
3001 24 : case PRESET_DISABLE:
3002 24 : log_debug("Preset files say disable %s.", name);
3003 24 : return 0;
3004 0 : default:
3005 0 : assert_not_reached("invalid preset action");
3006 : }
3007 : }
3008 :
3009 0 : int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
3010 0 : _cleanup_(presets_freep) Presets presets = {};
3011 : int r;
3012 :
3013 0 : r = read_presets(scope, root_dir, &presets);
3014 0 : if (r < 0)
3015 0 : return r;
3016 :
3017 0 : return query_presets(name, presets, NULL);
3018 : }
3019 :
3020 7 : static int execute_preset(
3021 : UnitFileScope scope,
3022 : InstallContext *plus,
3023 : InstallContext *minus,
3024 : const LookupPaths *paths,
3025 : const char *config_path,
3026 : char **files,
3027 : UnitFilePresetMode mode,
3028 : bool force,
3029 : UnitFileChange **changes,
3030 : size_t *n_changes) {
3031 :
3032 : int r;
3033 :
3034 7 : assert(plus);
3035 7 : assert(minus);
3036 7 : assert(paths);
3037 7 : assert(config_path);
3038 :
3039 7 : if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
3040 7 : _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
3041 :
3042 7 : r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
3043 7 : if (r < 0)
3044 0 : return r;
3045 :
3046 7 : r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
3047 : } else
3048 0 : r = 0;
3049 :
3050 7 : if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
3051 : int q;
3052 :
3053 : /* Returns number of symlinks that where supposed to be installed. */
3054 7 : q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
3055 7 : if (r >= 0) {
3056 7 : if (q < 0)
3057 0 : r = q;
3058 : else
3059 7 : r += q;
3060 : }
3061 : }
3062 :
3063 7 : return r;
3064 : }
3065 :
3066 47 : static int preset_prepare_one(
3067 : UnitFileScope scope,
3068 : InstallContext *plus,
3069 : InstallContext *minus,
3070 : LookupPaths *paths,
3071 : const char *name,
3072 : Presets presets,
3073 : UnitFileChange **changes,
3074 : size_t *n_changes) {
3075 :
3076 47 : _cleanup_(install_context_done) InstallContext tmp = {};
3077 47 : _cleanup_strv_free_ char **instance_name_list = NULL;
3078 : UnitFileInstallInfo *i;
3079 : int r;
3080 :
3081 47 : if (install_info_find(plus, name) || install_info_find(minus, name))
3082 1 : return 0;
3083 :
3084 46 : r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
3085 : &i, changes, n_changes);
3086 46 : if (r < 0)
3087 0 : return r;
3088 46 : if (!streq(name, i->name)) {
3089 17 : log_debug("Skipping %s because it is an alias for %s.", name, i->name);
3090 17 : return 0;
3091 : }
3092 :
3093 29 : r = query_presets(name, presets, &instance_name_list);
3094 29 : if (r < 0)
3095 0 : return r;
3096 :
3097 29 : if (r > 0) {
3098 5 : if (instance_name_list) {
3099 : char **s;
3100 4 : STRV_FOREACH(s, instance_name_list) {
3101 3 : r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3102 : &i, changes, n_changes);
3103 3 : if (r < 0)
3104 0 : return r;
3105 : }
3106 : } else {
3107 4 : r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3108 : &i, changes, n_changes);
3109 4 : if (r < 0)
3110 0 : return r;
3111 : }
3112 :
3113 : } else
3114 24 : r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
3115 : &i, changes, n_changes);
3116 :
3117 29 : return r;
3118 : }
3119 :
3120 5 : int unit_file_preset(
3121 : UnitFileScope scope,
3122 : UnitFileFlags flags,
3123 : const char *root_dir,
3124 : char **files,
3125 : UnitFilePresetMode mode,
3126 : UnitFileChange **changes,
3127 : size_t *n_changes) {
3128 :
3129 5 : _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
3130 5 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
3131 5 : _cleanup_(presets_freep) Presets presets = {};
3132 : const char *config_path;
3133 : char **i;
3134 : int r;
3135 :
3136 5 : assert(scope >= 0);
3137 5 : assert(scope < _UNIT_FILE_SCOPE_MAX);
3138 5 : assert(mode < _UNIT_FILE_PRESET_MAX);
3139 :
3140 5 : r = lookup_paths_init(&paths, scope, 0, root_dir);
3141 5 : if (r < 0)
3142 0 : return r;
3143 :
3144 5 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
3145 5 : if (!config_path)
3146 0 : return -ENXIO;
3147 :
3148 5 : r = read_presets(scope, root_dir, &presets);
3149 5 : if (r < 0)
3150 0 : return r;
3151 :
3152 10 : STRV_FOREACH(i, files) {
3153 5 : r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
3154 5 : if (r < 0)
3155 0 : return r;
3156 : }
3157 :
3158 5 : return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
3159 : }
3160 :
3161 2 : int unit_file_preset_all(
3162 : UnitFileScope scope,
3163 : UnitFileFlags flags,
3164 : const char *root_dir,
3165 : UnitFilePresetMode mode,
3166 : UnitFileChange **changes,
3167 : size_t *n_changes) {
3168 :
3169 2 : _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
3170 2 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
3171 2 : _cleanup_(presets_freep) Presets presets = {};
3172 2 : const char *config_path = NULL;
3173 : char **i;
3174 : int r;
3175 :
3176 2 : assert(scope >= 0);
3177 2 : assert(scope < _UNIT_FILE_SCOPE_MAX);
3178 2 : assert(mode < _UNIT_FILE_PRESET_MAX);
3179 :
3180 2 : r = lookup_paths_init(&paths, scope, 0, root_dir);
3181 2 : if (r < 0)
3182 0 : return r;
3183 :
3184 2 : config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
3185 2 : if (!config_path)
3186 0 : return -ENXIO;
3187 :
3188 2 : r = read_presets(scope, root_dir, &presets);
3189 2 : if (r < 0)
3190 0 : return r;
3191 :
3192 26 : STRV_FOREACH(i, paths.search_path) {
3193 24 : _cleanup_closedir_ DIR *d = NULL;
3194 : struct dirent *de;
3195 :
3196 24 : d = opendir(*i);
3197 24 : if (!d) {
3198 18 : if (errno == ENOENT)
3199 18 : continue;
3200 :
3201 0 : return -errno;
3202 : }
3203 :
3204 63 : FOREACH_DIRENT(de, d, return -errno) {
3205 :
3206 45 : if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
3207 3 : continue;
3208 :
3209 42 : dirent_ensure_type(d, de);
3210 :
3211 42 : if (!IN_SET(de->d_type, DT_LNK, DT_REG))
3212 0 : continue;
3213 :
3214 : /* we don't pass changes[] in, because we want to handle errors on our own */
3215 42 : r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
3216 42 : if (r == -ERFKILL)
3217 0 : r = unit_file_changes_add(changes, n_changes,
3218 0 : UNIT_FILE_IS_MASKED, de->d_name, NULL);
3219 42 : else if (r == -ENOLINK)
3220 0 : r = unit_file_changes_add(changes, n_changes,
3221 0 : UNIT_FILE_IS_DANGLING, de->d_name, NULL);
3222 42 : else if (r == -EADDRNOTAVAIL) /* Ignore generated/transient units when applying preset */
3223 0 : continue;
3224 42 : if (r < 0)
3225 0 : return r;
3226 : }
3227 : }
3228 :
3229 2 : return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
3230 : }
3231 :
3232 525 : static void unit_file_list_free_one(UnitFileList *f) {
3233 525 : if (!f)
3234 0 : return;
3235 :
3236 525 : free(f->path);
3237 525 : free(f);
3238 : }
3239 :
3240 2 : Hashmap* unit_file_list_free(Hashmap *h) {
3241 527 : return hashmap_free_with_destructor(h, unit_file_list_free_one);
3242 : }
3243 :
3244 594 : DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
3245 :
3246 2 : int unit_file_get_list(
3247 : UnitFileScope scope,
3248 : const char *root_dir,
3249 : Hashmap *h,
3250 : char **states,
3251 : char **patterns) {
3252 :
3253 2 : _cleanup_(lookup_paths_free) LookupPaths paths = {};
3254 : char **i;
3255 : int r;
3256 :
3257 2 : assert(scope >= 0);
3258 2 : assert(scope < _UNIT_FILE_SCOPE_MAX);
3259 2 : assert(h);
3260 :
3261 2 : r = lookup_paths_init(&paths, scope, 0, root_dir);
3262 2 : if (r < 0)
3263 0 : return r;
3264 :
3265 26 : STRV_FOREACH(i, paths.search_path) {
3266 24 : _cleanup_closedir_ DIR *d = NULL;
3267 : struct dirent *de;
3268 :
3269 24 : d = opendir(*i);
3270 24 : if (!d) {
3271 15 : if (errno == ENOENT)
3272 15 : continue;
3273 0 : if (IN_SET(errno, ENOTDIR, EACCES)) {
3274 0 : log_debug_errno(errno, "Failed to open \"%s\": %m", *i);
3275 0 : continue;
3276 : }
3277 :
3278 0 : return -errno;
3279 : }
3280 :
3281 621 : FOREACH_DIRENT(de, d, return -errno) {
3282 594 : _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
3283 :
3284 594 : if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
3285 64 : continue;
3286 :
3287 530 : if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
3288 0 : continue;
3289 :
3290 530 : if (hashmap_get(h, de->d_name))
3291 5 : continue;
3292 :
3293 525 : dirent_ensure_type(d, de);
3294 :
3295 525 : if (!IN_SET(de->d_type, DT_LNK, DT_REG))
3296 0 : continue;
3297 :
3298 525 : f = new0(UnitFileList, 1);
3299 525 : if (!f)
3300 0 : return -ENOMEM;
3301 :
3302 525 : f->path = path_make_absolute(de->d_name, *i);
3303 525 : if (!f->path)
3304 0 : return -ENOMEM;
3305 :
3306 525 : r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state);
3307 525 : if (r < 0)
3308 0 : f->state = UNIT_FILE_BAD;
3309 :
3310 525 : if (!strv_isempty(states) &&
3311 0 : !strv_contains(states, unit_file_state_to_string(f->state)))
3312 0 : continue;
3313 :
3314 525 : r = hashmap_put(h, basename(f->path), f);
3315 525 : if (r < 0)
3316 0 : return r;
3317 :
3318 525 : f = NULL; /* prevent cleanup */
3319 : }
3320 : }
3321 :
3322 2 : return 0;
3323 : }
3324 :
3325 : static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
3326 : [UNIT_FILE_ENABLED] = "enabled",
3327 : [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
3328 : [UNIT_FILE_LINKED] = "linked",
3329 : [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
3330 : [UNIT_FILE_MASKED] = "masked",
3331 : [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
3332 : [UNIT_FILE_STATIC] = "static",
3333 : [UNIT_FILE_DISABLED] = "disabled",
3334 : [UNIT_FILE_INDIRECT] = "indirect",
3335 : [UNIT_FILE_GENERATED] = "generated",
3336 : [UNIT_FILE_TRANSIENT] = "transient",
3337 : [UNIT_FILE_BAD] = "bad",
3338 : };
3339 :
3340 535 : DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
3341 :
3342 : static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
3343 : [UNIT_FILE_SYMLINK] = "symlink",
3344 : [UNIT_FILE_UNLINK] = "unlink",
3345 : [UNIT_FILE_IS_MASKED] = "masked",
3346 : [UNIT_FILE_IS_DANGLING] = "dangling",
3347 : };
3348 :
3349 12 : DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
3350 :
3351 : static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
3352 : [UNIT_FILE_PRESET_FULL] = "full",
3353 : [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
3354 : [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
3355 : };
3356 :
3357 10 : DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);
|