]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/shared/install.c
ci: run the musl build & test under mkosi with a postmarketOS tools tree (#42171)
[thirdparty/systemd.git] / src / shared / install.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#include <fcntl.h>
4#include <fnmatch.h>
5#include <stdio.h>
6#include <unistd.h>
7
8#include "alloc-util.h"
9#include "bus-common-errors.h"
10#include "chase.h"
11#include "conf-files.h"
12#include "conf-parser.h"
13#include "constants.h"
14#include "dirent-util.h"
15#include "errno-util.h"
16#include "extract-word.h"
17#include "fd-util.h"
18#include "fileio.h"
19#include "fs-util.h"
20#include "glyph-util.h"
21#include "hashmap.h"
22#include "install.h"
23#include "install-printf.h"
24#include "log.h"
25#include "mkdir-label.h"
26#include "path-lookup.h"
27#include "path-util.h"
28#include "rm-rf.h"
29#include "set.h"
30#include "special.h"
31#include "stat-util.h"
32#include "string-table.h"
33#include "string-util.h"
34#include "strv.h"
35#include "unit-file.h"
36#include "unit-name.h"
37
38#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
39
40typedef struct {
41 RuntimeScope scope;
42 OrderedHashmap *will_process;
43 OrderedHashmap *have_processed;
44} InstallContext;
45
46struct UnitFilePresetRule {
47 char *pattern;
48 PresetAction action;
49 char **instances;
50};
51
52/* NB! strings use past tense. */
53static const char *const preset_action_past_tense_table[_PRESET_ACTION_MAX] = {
54 [PRESET_UNKNOWN] = "unknown",
55 [PRESET_ENABLE] = "enabled",
56 [PRESET_DISABLE] = "disabled",
57 [PRESET_IGNORE] = "ignored",
58};
59
60DEFINE_STRING_TABLE_LOOKUP_TO_STRING(preset_action_past_tense, PresetAction);
61
62static bool install_info_has_rules(const InstallInfo *i) {
63 assert(i);
64
65 return !strv_isempty(i->aliases) ||
66 !strv_isempty(i->wanted_by) ||
67 !strv_isempty(i->required_by) ||
68 !strv_isempty(i->upheld_by);
69}
70
71static bool install_info_has_also(const InstallInfo *i) {
72 assert(i);
73
74 return !strv_isempty(i->also);
75}
76
77static void unit_file_preset_rule_done(UnitFilePresetRule *rule) {
78 assert(rule);
79
80 free(rule->pattern);
81 strv_free(rule->instances);
82}
83
84void unit_file_presets_done(UnitFilePresets *p) {
85 if (!p)
86 return;
87
88 FOREACH_ARRAY(rule, p->rules, p->n_rules)
89 unit_file_preset_rule_done(rule);
90
91 free(p->rules);
92 p->n_rules = 0;
93}
94
95static const char *const install_mode_table[_INSTALL_MODE_MAX] = {
96 [INSTALL_MODE_REGULAR] = "regular",
97 [INSTALL_MODE_LINKED] = "linked",
98 [INSTALL_MODE_ALIAS] = "alias",
99 [INSTALL_MODE_MASKED] = "masked",
100};
101
102DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(install_mode, InstallMode);
103
104static int in_search_path(const LookupPaths *lp, const char *path) {
105 _cleanup_free_ char *parent = NULL;
106 int r;
107
108 /* Check if 'path' is in lp->search_path. */
109
110 assert(lp);
111 assert(path);
112
113 r = path_extract_directory(path, &parent);
114 if (r < 0)
115 return r;
116
117 return path_strv_contains(lp->search_path, parent);
118}
119
120static bool underneath_search_path(const LookupPaths *lp, const char *path) {
121 /* Check if 'path' is underneath lp->search_path. */
122
123 assert(lp);
124 assert(path);
125
126 return path_startswith_strv(path, lp->search_path);
127}
128
129static const char* skip_root(const char *root_dir, const char *path) {
130 assert(path);
131
132 if (!root_dir)
133 return path;
134
135 const char *e = path_startswith(path, root_dir);
136 if (!e)
137 return NULL;
138
139 /* Make sure the returned path starts with a slash */
140 if (e[0] != '/') {
141 if (e == path || e[-1] != '/')
142 return NULL;
143
144 e--;
145 }
146
147 return e;
148}
149
150static int path_is_generator(const LookupPaths *lp, const char *path) {
151 _cleanup_free_ char *parent = NULL;
152 int r;
153
154 assert(lp);
155 assert(path);
156
157 r = path_extract_directory(path, &parent);
158 if (r < 0)
159 return r;
160
161 return PATH_IN_SET(parent,
162 lp->generator,
163 lp->generator_early,
164 lp->generator_late);
165}
166
167static int path_is_transient(const LookupPaths *lp, const char *path) {
168 _cleanup_free_ char *parent = NULL;
169 int r;
170
171 assert(lp);
172 assert(path);
173
174 r = path_extract_directory(path, &parent);
175 if (r < 0)
176 return r;
177
178 return path_equal(parent, lp->transient);
179}
180
181static int path_is_control(const LookupPaths *lp, const char *path) {
182 _cleanup_free_ char *parent = NULL;
183 int r;
184
185 assert(lp);
186 assert(path);
187
188 r = path_extract_directory(path, &parent);
189 if (r < 0)
190 return r;
191
192 return PATH_IN_SET(parent,
193 lp->persistent_control,
194 lp->runtime_control);
195}
196
197static int path_is_config(const LookupPaths *lp, const char *path, bool check_parent) {
198 _cleanup_free_ char *parent = NULL;
199 int r;
200
201 assert(lp);
202 assert(path);
203
204 /* Note that we do *not* have generic checks for /etc or /run in place, since with
205 * them we couldn't discern configuration from transient or generated units */
206
207 if (check_parent) {
208 r = path_extract_directory(path, &parent);
209 if (r < 0)
210 return r;
211
212 path = parent;
213 }
214
215 return PATH_IN_SET(path,
216 lp->persistent_config,
217 lp->runtime_config);
218}
219
220static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_parent) {
221 _cleanup_free_ char *parent = NULL;
222 const char *rpath;
223 int r;
224
225 assert(lp);
226 assert(path);
227
228 /* Everything in /run is considered runtime. On top of that we also add
229 * explicit checks for the various runtime directories, as safety net. */
230
231 rpath = skip_root(lp->root_dir, path);
232 if (rpath && path_startswith(rpath, "/run"))
233 return true;
234
235 if (check_parent) {
236 r = path_extract_directory(path, &parent);
237 if (r < 0)
238 return r;
239
240 path = parent;
241 }
242
243 return PATH_IN_SET(path,
244 lp->runtime_config,
245 lp->generator,
246 lp->generator_early,
247 lp->generator_late,
248 lp->transient,
249 lp->runtime_control);
250}
251
252static int path_is_vendor_or_generator(const LookupPaths *lp, const char *path) {
253 const char *rpath;
254
255 assert(lp);
256 assert(path);
257
258 rpath = skip_root(lp->root_dir, path);
259 if (!rpath)
260 return 0;
261
262 if (path_startswith(rpath, "/usr"))
263 return true;
264
265 if (path_is_generator(lp, rpath))
266 return true;
267
268 return path_equal(rpath, SYSTEM_DATA_UNIT_DIR);
269}
270
271static const char* config_path_from_flags(const LookupPaths *lp, UnitFileFlags flags) {
272 assert(lp);
273
274 if (FLAGS_SET(flags, UNIT_FILE_PORTABLE))
275 return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_attached : lp->persistent_attached;
276 else
277 return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_config : lp->persistent_config;
278}
279
280InstallChangeType install_changes_add(
281 InstallChange **changes,
282 size_t *n_changes,
283 InstallChangeType type, /* INSTALL_CHANGE_SYMLINK, _UNLINK, _IS_MASKED, _IS_DANGLING, … if positive or errno if negative */
284 const char *path,
285 const char *source) {
286
287 _cleanup_free_ char *p = NULL, *s = NULL;
288 int r;
289
290 assert(!changes == !n_changes);
291 assert(INSTALL_CHANGE_TYPE_VALID(type));
292
293 /* Message formatting requires <path> to be set. */
294 assert(path);
295
296 /* Register a change or error. Note that the return value may be the error
297 * that was passed in, or -ENOMEM generated internally. */
298
299 if (!changes)
300 return type;
301
302 if (!GREEDY_REALLOC(*changes, *n_changes + 1))
303 return -ENOMEM;
304
305 r = path_simplify_alloc(path, &p);
306 if (r < 0)
307 return r;
308
309 r = path_simplify_alloc(source, &s);
310 if (r < 0)
311 return r;
312
313 (*changes)[(*n_changes)++] = (InstallChange) {
314 .type = type,
315 .path = TAKE_PTR(p),
316 .source = TAKE_PTR(s),
317 };
318
319 return type;
320}
321
322static void install_change_done(InstallChange *change) {
323 assert(change);
324
325 change->path = mfree(change->path);
326 change->source = mfree(change->source);
327}
328
329DEFINE_ARRAY_FREE_FUNC(install_changes_free, InstallChange, install_change_done);
330
331static void install_change_dump_success(const InstallChange *change) {
332 assert(change);
333 assert(change->path);
334
335 switch (change->type) {
336
337 case INSTALL_CHANGE_SYMLINK:
338 return log_info("Created symlink '%s' %s '%s'.",
339 change->path, glyph(GLYPH_ARROW_RIGHT), change->source);
340
341 case INSTALL_CHANGE_UNLINK:
342 return log_info("Removed '%s'.", change->path);
343
344 case INSTALL_CHANGE_IS_MASKED:
345 return log_info("Unit %s is masked, ignoring.", change->path);
346
347 case INSTALL_CHANGE_IS_MASKED_GENERATOR:
348 return log_info("Unit %s is masked via a generator and cannot be unmasked, skipping.", change->path);
349
350 case INSTALL_CHANGE_IS_DANGLING:
351 return log_info("Unit %s is an alias to a non-existent unit, ignoring.", change->path);
352
353 case INSTALL_CHANGE_DESTINATION_NOT_PRESENT:
354 return log_warning("Unit %s is added as a dependency to a non-existent unit %s.",
355 change->source, change->path);
356
357 case INSTALL_CHANGE_AUXILIARY_FAILED:
358 return log_warning("Failed to enable auxiliary unit %s, ignoring.", change->path);
359
360 default:
361 assert_not_reached();
362 }
363}
364
365/* Generated/transient/missing/invalid units when applying presets.
366 * Coordinate with install_change_dump_error() below. */
367static bool ERRNO_IS_NEG_UNIT_ISSUE(intmax_t r) {
368 return IN_SET(r,
369 -EEXIST,
370 -ERFKILL,
371 -EADDRNOTAVAIL,
372 -ETXTBSY,
373 -EBADSLT,
374 -EIDRM,
375 -EUCLEAN,
376 -ELOOP,
377 -EXDEV,
378 -ENOENT,
379 -ENOLINK,
380 -EUNATCH);
381}
382
383int install_change_dump_error(const InstallChange *change, char **ret_errmsg, const char **ret_bus_error) {
384 char *m;
385 const char *bus_error;
386
387 /* Returns 0: known error and ret_errmsg formatted
388 * < 0: non-recognizable error */
389
390 assert(change);
391 assert(change->path);
392 assert(change->type < 0);
393 assert(ret_errmsg);
394
395 switch (change->type) {
396
397 case -EEXIST:
398 m = strjoin("File '", change->path, "' already exists",
399 change->source ? " and is a symlink to " : NULL, change->source);
400 bus_error = BUS_ERROR_UNIT_EXISTS;
401 break;
402
403 case -ERFKILL:
404 m = strjoin("Unit ", change->path, " is masked");
405 bus_error = BUS_ERROR_UNIT_MASKED;
406 break;
407
408 case -EADDRNOTAVAIL:
409 m = strjoin("Unit ", change->path, " is transient or generated");
410 bus_error = BUS_ERROR_UNIT_GENERATED;
411 break;
412
413 case -ETXTBSY:
414 m = strjoin("File '", change->path, "' is under the systemd unit hierarchy already");
415 bus_error = BUS_ERROR_UNIT_BAD_PATH;
416 break;
417
418 case -EBADSLT:
419 m = strjoin("Invalid specifier in unit ", change->path);
420 bus_error = BUS_ERROR_BAD_UNIT_SETTING;
421 break;
422
423 case -EIDRM:
424 m = strjoin("Refusing to operate on template unit ", change->source,
425 " when destination unit ", change->path, " is a non-template unit");
426 bus_error = BUS_ERROR_BAD_UNIT_SETTING;
427 break;
428
429 case -EUCLEAN:
430 m = strjoin("Invalid unit name ", change->path);
431 bus_error = BUS_ERROR_BAD_UNIT_SETTING;
432 break;
433
434 case -ELOOP:
435 m = strjoin("Refusing to operate on linked unit file ", change->path);
436 bus_error = BUS_ERROR_UNIT_LINKED;
437 break;
438
439 case -EXDEV:
440 if (change->source)
441 m = strjoin("Cannot alias ", change->source, " as ", change->path);
442 else
443 m = strjoin("Invalid unit reference ", change->path);
444 bus_error = BUS_ERROR_BAD_UNIT_SETTING;
445 break;
446
447 case -ENOENT:
448 m = strjoin("Unit ", change->path, " does not exist");
449 bus_error = BUS_ERROR_NO_SUCH_UNIT;
450 break;
451
452 case -ENOLINK:
453 m = strjoin("Unit ", change->path, " is an unresolvable alias");
454 bus_error = BUS_ERROR_NO_SUCH_UNIT;
455 break;
456
457 case -EUNATCH:
458 m = strjoin("Cannot resolve specifiers in unit ", change->path);
459 bus_error = BUS_ERROR_BAD_UNIT_SETTING;
460 break;
461
462 default:
463 return change->type;
464 }
465 if (!m)
466 return -ENOMEM;
467
468 *ret_errmsg = m;
469 if (ret_bus_error)
470 *ret_bus_error = bus_error;
471
472 return 0;
473}
474
475int install_changes_dump(
476 int error,
477 const char *verb,
478 const InstallChange *changes,
479 size_t n_changes,
480 bool quiet) {
481
482 bool err_logged = false;
483 int r;
484
485 /* If verb is not specified, errors are not allowed! */
486 assert(verb || error >= 0);
487 assert(changes || n_changes == 0);
488
489 /* An error is returned if 'error' contains an error or if any of the changes failed. */
490
491 FOREACH_ARRAY(i, changes, n_changes)
492 if (i->type >= 0) {
493 if (!quiet)
494 install_change_dump_success(i);
495 } else {
496 _cleanup_free_ char *err_message = NULL;
497
498 assert(verb);
499
500 r = install_change_dump_error(i, &err_message, /* ret_bus_error= */ NULL);
501 if (r == -ENOMEM)
502 return log_oom();
503 if (r < 0)
504 RET_GATHER(error,
505 log_error_errno(r, "Failed to %s unit %s: %m", verb, i->path));
506 else
507 RET_GATHER(error,
508 log_error_errno(i->type, "Failed to %s unit: %s", verb, err_message));
509
510 err_logged = true;
511 }
512
513 if (error < 0 && !err_logged)
514 log_error_errno(error, "Failed to %s units: %m.", verb);
515
516 return error;
517}
518
519/**
520 * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is
521 * concerned. If the target is in the unit search path, then anything with the same name is equivalent.
522 * If outside the unit search path, paths must be identical.
523 */
524static int chroot_unit_symlinks_equivalent(
525 const LookupPaths *lp,
526 const char *src,
527 const char *target_a,
528 const char *target_b) {
529
530 assert(lp);
531 assert(src);
532 assert(target_a);
533 assert(target_b);
534
535 /* This will give incorrect results if the paths are relative and go outside
536 * of the chroot. False negatives are possible. */
537
538 const char *root = lp->root_dir ?: "/";
539 _cleanup_free_ char *dirname = NULL;
540 int r;
541
542 if (!path_is_absolute(target_a) || !path_is_absolute(target_b)) {
543 r = path_extract_directory(src, &dirname);
544 if (r < 0)
545 return r;
546 }
547
548 _cleanup_free_ char *a = path_join(path_is_absolute(target_a) ? root : dirname, target_a);
549 _cleanup_free_ char *b = path_join(path_is_absolute(target_b) ? root : dirname, target_b);
550 if (!a || !b)
551 return log_oom();
552
553 r = path_equal_or_inode_same(a, b, 0);
554 if (r != 0)
555 return r;
556
557 _cleanup_free_ char *a_name = NULL, *b_name = NULL;
558 r = path_extract_filename(a, &a_name);
559 if (r < 0)
560 return r;
561 r = path_extract_filename(b, &b_name);
562 if (r < 0)
563 return r;
564
565 return streq(a_name, b_name) &&
566 path_startswith_strv(a, lp->search_path) &&
567 path_startswith_strv(b, lp->search_path);
568}
569
570static int create_symlink(
571 const LookupPaths *lp,
572 const char *old_path,
573 const char *new_path,
574 bool force,
575 InstallChange **changes,
576 size_t *n_changes) {
577
578 _cleanup_free_ char *dest = NULL;
579 const char *rp;
580 int r;
581
582 assert(old_path);
583 assert(new_path);
584
585 rp = skip_root(lp->root_dir, old_path);
586 if (rp)
587 old_path = rp;
588
589 /* Actually create a symlink, and remember that we did. This function is
590 * smart enough to check if there's already a valid symlink in place.
591 *
592 * Returns 1 if a symlink was created or already exists and points to the
593 * right place, or negative on error.
594 */
595
596 (void) mkdir_parents_label(new_path, 0755);
597
598 if (symlink(old_path, new_path) >= 0) {
599 r = install_changes_add(changes, n_changes, INSTALL_CHANGE_SYMLINK, new_path, old_path);
600 if (r < 0)
601 return r;
602 return 1;
603 }
604
605 if (errno != EEXIST)
606 return install_changes_add(changes, n_changes, -errno, new_path, NULL);
607
608 r = readlink_malloc(new_path, &dest);
609 if (r < 0) {
610 /* translate EINVAL (non-symlink exists) to EEXIST */
611 if (r == -EINVAL)
612 r = -EEXIST;
613
614 return install_changes_add(changes, n_changes, r, new_path, NULL);
615 }
616
617 if (chroot_unit_symlinks_equivalent(lp, new_path, dest, old_path)) {
618 log_debug("Symlink %s %s %s already exists",
619 new_path, glyph(GLYPH_ARROW_RIGHT), dest);
620 return 1;
621 }
622
623 if (!force)
624 return install_changes_add(changes, n_changes, -EEXIST, new_path, dest);
625
626 r = symlink_atomic(old_path, new_path);
627 if (r < 0)
628 return install_changes_add(changes, n_changes, r, new_path, NULL);
629
630 r = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, new_path, NULL);
631 if (r < 0)
632 return r;
633 r = install_changes_add(changes, n_changes, INSTALL_CHANGE_SYMLINK, new_path, old_path);
634 if (r < 0)
635 return r;
636
637 return 1;
638}
639
640static int mark_symlink_for_removal(
641 Set **remove_symlinks_to,
642 const char *p) {
643
644 char *n;
645 int r;
646
647 assert(remove_symlinks_to);
648 assert(p);
649
650 r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops_free);
651 if (r < 0)
652 return r;
653
654 r = path_simplify_alloc(p, &n);
655 if (r < 0)
656 return r;
657
658 r = set_consume(*remove_symlinks_to, n);
659 if (r == -EEXIST)
660 return 0;
661 if (r < 0)
662 return r;
663
664 return 1;
665}
666
667static int remove_marked_symlinks_fd(
668 Set *remove_symlinks_to,
669 int fd,
670 const char *path,
671 const char *config_path,
672 const LookupPaths *lp,
673 bool dry_run,
674 bool *restart,
675 InstallChange **changes,
676 size_t *n_changes) {
677
678 _cleanup_closedir_ DIR *d = NULL;
679 int r, ret = 0;
680
681 assert(remove_symlinks_to);
682 assert(fd >= 0);
683 assert(path);
684 assert(config_path);
685 assert(lp);
686 assert(restart);
687
688 d = fdopendir(fd);
689 if (!d) {
690 safe_close(fd);
691 return -errno;
692 }
693
694 rewinddir(d);
695
696 FOREACH_DIRENT(de, d, return -errno)
697 if (de->d_type == DT_DIR) {
698 _cleanup_close_ int nfd = -EBADF;
699 _cleanup_free_ char *p = NULL;
700
701 nfd = RET_NERRNO(openat(fd, de->d_name, O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW));
702 if (nfd < 0) {
703 if (nfd != -ENOENT)
704 RET_GATHER(ret, nfd);
705 continue;
706 }
707
708 p = path_make_absolute(de->d_name, path);
709 if (!p)
710 return -ENOMEM;
711
712 /* This will close nfd, regardless whether it succeeds or not */
713 RET_GATHER(ret, remove_marked_symlinks_fd(remove_symlinks_to,
714 TAKE_FD(nfd), p,
715 config_path, lp,
716 dry_run,
717 restart,
718 changes, n_changes));
719
720 } else if (de->d_type == DT_LNK) {
721 _cleanup_free_ char *p = NULL;
722 bool found;
723
724 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
725 continue;
726
727 p = path_make_absolute(de->d_name, path);
728 if (!p)
729 return -ENOMEM;
730 path_simplify(p);
731
732 /* We remove all links pointing to a file or path that is marked, as well as all
733 * files sharing the same name as a file that is marked, and files sharing the same
734 * name after the instance has been removed. Do path chasing only if we don't already
735 * know that we want to remove the symlink. */
736 found = set_contains(remove_symlinks_to, de->d_name);
737
738 if (!found) {
739 _cleanup_free_ char *template = NULL;
740
741 r = unit_name_template(de->d_name, &template);
742 if (r < 0 && r != -EINVAL)
743 return r;
744 if (r >= 0)
745 found = set_contains(remove_symlinks_to, template);
746 }
747
748 if (!found) {
749 _cleanup_free_ char *dest = NULL, *dest_name = NULL;
750
751 r = chase(p, lp->root_dir, CHASE_NONEXISTENT, &dest, NULL);
752 if (r == -ENOENT)
753 continue;
754 if (r < 0) {
755 log_debug_errno(r, "Failed to resolve symlink \"%s\": %m", p);
756 RET_GATHER(ret, install_changes_add(changes, n_changes, r, p, NULL));
757 continue;
758 }
759
760 r = path_extract_filename(dest, &dest_name);
761 if (r < 0)
762 return r;
763
764 found = set_contains(remove_symlinks_to, dest) ||
765 set_contains(remove_symlinks_to, dest_name);
766 }
767
768 if (!found)
769 continue;
770
771 if (!dry_run) {
772 if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
773 RET_GATHER(ret, install_changes_add(changes, n_changes, -errno, p, NULL));
774 continue;
775 }
776
777 (void) rmdir_parents(p, config_path);
778 }
779
780 r = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, p, NULL);
781 if (r < 0)
782 return r;
783
784 /* Now, remember the full path (but with the root prefix removed) of
785 * the symlink we just removed, and remove any symlinks to it, too. */
786
787 const char *rp = skip_root(lp->root_dir, p);
788 r = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
789 if (r < 0)
790 return r;
791 if (r > 0 && !dry_run)
792 *restart = true;
793 }
794
795 return ret;
796}
797
798static int remove_marked_symlinks(
799 Set *remove_symlinks_to,
800 const char *config_path,
801 const LookupPaths *lp,
802 bool dry_run,
803 InstallChange **changes,
804 size_t *n_changes) {
805
806 _cleanup_close_ int fd = -EBADF;
807 bool restart;
808 int r = 0;
809
810 assert(config_path);
811 assert(lp);
812
813 if (set_isempty(remove_symlinks_to))
814 return 0;
815
816 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
817 if (fd < 0)
818 return errno == ENOENT ? 0 : -errno;
819
820 do {
821 int cfd;
822 restart = false;
823
824 cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
825 if (cfd < 0)
826 return -errno;
827
828 /* This takes possession of cfd and closes it */
829 RET_GATHER(r, remove_marked_symlinks_fd(remove_symlinks_to,
830 cfd, config_path,
831 config_path, lp,
832 dry_run,
833 &restart,
834 changes, n_changes));
835 } while (restart);
836
837 return r;
838}
839
840static int is_symlink_with_known_name(const InstallInfo *i, const char *name) {
841 int r;
842
843 assert(i);
844 assert(name);
845
846 if (streq(name, i->name))
847 return true;
848
849 if (strv_contains(i->aliases, name))
850 return true;
851
852 /* Look for template symlink matching DefaultInstance */
853 if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
854 _cleanup_free_ char *s = NULL;
855
856 r = unit_name_replace_instance(i->name, i->default_instance, &s);
857 if (r < 0) {
858 if (r != -EINVAL)
859 return r;
860
861 } else if (streq(name, s))
862 return true;
863 }
864
865 return false;
866}
867
868static int find_symlinks_in_directory(
869 DIR *dir,
870 const char *dir_path,
871 const char *root_dir,
872 const InstallInfo *info,
873 bool ignore_destination,
874 bool match_name,
875 bool ignore_same_name,
876 const char *config_path,
877 bool *same_name_link) {
878
879 int r, ret = 0;
880
881 assert(dir);
882 assert(dir_path);
883 assert(info);
884 assert(unit_name_is_valid(info->name, UNIT_NAME_ANY));
885 assert(config_path);
886 assert(same_name_link);
887
888 FOREACH_DIRENT(de, dir, return -errno) {
889 bool found_path = false, found_dest = false, b = false;
890
891 if (de->d_type != DT_LNK)
892 continue;
893
894 if (!ignore_destination) {
895 _cleanup_free_ char *dest = NULL;
896
897 /* Acquire symlink destination */
898 r = readlinkat_malloc(dirfd(dir), de->d_name, &dest);
899 if (r < 0) {
900 if (r != -ENOENT)
901 RET_GATHER(ret, r);
902 continue;
903 }
904
905 /* Check if what the symlink points to matches what we are looking for */
906 found_dest = path_equal_filename(dest, info->name);
907 }
908
909 /* Check if the symlink itself matches what we are looking for.
910 *
911 * If ignore_destination is specified, we only look at the source name.
912 *
913 * If ignore_same_name is specified, we are in one of the directories which
914 * have lower priority than the unit file, and even if a file or symlink with
915 * this name was found, we should ignore it. */
916
917 if (ignore_destination || !ignore_same_name)
918 found_path = streq(de->d_name, info->name);
919
920 if (!found_path && ignore_destination) {
921 _cleanup_free_ char *template = NULL;
922
923 r = unit_name_template(de->d_name, &template);
924 if (r < 0 && r != -EINVAL)
925 return r;
926 if (r >= 0)
927 found_dest = streq(template, info->name);
928 }
929
930 if (found_path && found_dest) {
931 _cleanup_free_ char *p = NULL, *t = NULL;
932
933 /* Filter out same name links in the main config path */
934 p = path_make_absolute(de->d_name, dir_path);
935 t = path_make_absolute(info->name, config_path);
936 if (!p || !t)
937 return -ENOMEM;
938
939 b = path_equal(p, t);
940 }
941
942 if (b)
943 *same_name_link = true;
944 else if (found_path || found_dest) {
945 if (!match_name)
946 return 1;
947
948 /* Check if symlink name is in the set of names used by [Install] */
949 r = is_symlink_with_known_name(info, de->d_name);
950 if (r != 0)
951 return r;
952 }
953 }
954
955 return ret;
956}
957
958static int find_symlinks(
959 const char *root_dir,
960 const InstallInfo *i,
961 bool match_name,
962 bool ignore_same_name,
963 const char *config_path,
964 bool *same_name_link) {
965
966 _cleanup_closedir_ DIR *config_dir = NULL;
967 int r;
968
969 assert(i);
970 assert(config_path);
971 assert(same_name_link);
972
973 config_dir = opendir(config_path);
974 if (!config_dir) {
975 if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
976 return 0;
977 return -errno;
978 }
979
980 FOREACH_DIRENT(de, config_dir, return -errno) {
981 const char *suffix;
982 _cleanup_free_ const char *path = NULL;
983 _cleanup_closedir_ DIR *d = NULL;
984
985 if (de->d_type != DT_DIR)
986 continue;
987
988 suffix = strrchr(de->d_name, '.');
989 if (!STRPTR_IN_SET(suffix, ".wants", ".requires", ".upholds"))
990 continue;
991
992 path = path_join(config_path, de->d_name);
993 if (!path)
994 return -ENOMEM;
995
996 d = opendir(path);
997 if (!d) {
998 log_error_errno(errno, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path);
999 continue;
1000 }
1001
1002 r = find_symlinks_in_directory(d, path, root_dir, i,
1003 /* ignore_destination= */ true,
1004 /* match_name= */ match_name,
1005 /* ignore_same_name= */ ignore_same_name,
1006 config_path,
1007 same_name_link);
1008 if (r > 0)
1009 return 1;
1010 if (r < 0)
1011 log_debug_errno(r, "Failed to look up symlinks in \"%s\": %m", path);
1012 }
1013
1014 /* We didn't find any suitable symlinks in .wants, .requires or .upholds directories,
1015 * let's look for linked unit files in this directory. */
1016 rewinddir(config_dir);
1017 return find_symlinks_in_directory(config_dir, config_path, root_dir, i,
1018 /* ignore_destination= */ false,
1019 /* match_name= */ match_name,
1020 /* ignore_same_name= */ ignore_same_name,
1021 config_path,
1022 same_name_link);
1023}
1024
1025static int find_symlinks_in_scope(
1026 RuntimeScope scope,
1027 const LookupPaths *lp,
1028 const InstallInfo *info,
1029 bool match_name,
1030 UnitFileState *state) {
1031
1032 bool same_name_link_runtime = false, same_name_link_config = false;
1033 bool enabled_in_runtime = false, enabled_at_all = false;
1034 bool ignore_same_name = false;
1035 int r;
1036
1037 assert(lp);
1038 assert(info);
1039 assert(state);
1040
1041 /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name"
1042 * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
1043 * effectively masked, so we should ignore them. */
1044
1045 STRV_FOREACH(p, lp->search_path) {
1046 bool same_name_link = false;
1047
1048 r = find_symlinks(lp->root_dir, info, match_name, ignore_same_name, *p, &same_name_link);
1049 if (r < 0)
1050 return r;
1051 if (r > 0) {
1052 /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
1053
1054 if (path_equal(*p, lp->persistent_config)) {
1055 /* This is the best outcome, let's return it immediately. */
1056 *state = UNIT_FILE_ENABLED;
1057 return 1;
1058 }
1059
1060 /* look for global enablement of user units */
1061 if (scope == RUNTIME_SCOPE_USER && path_is_user_config_dir(*p)) {
1062 *state = UNIT_FILE_ENABLED;
1063 return 1;
1064 }
1065
1066 r = path_is_runtime(lp, *p, false);
1067 if (r < 0)
1068 return r;
1069 if (r > 0)
1070 enabled_in_runtime = true;
1071 else
1072 enabled_at_all = true;
1073
1074 } else if (same_name_link) {
1075 if (path_equal(*p, lp->persistent_config))
1076 same_name_link_config = true;
1077 else {
1078 r = path_is_runtime(lp, *p, false);
1079 if (r < 0)
1080 return r;
1081 if (r > 0)
1082 same_name_link_runtime = true;
1083 }
1084 }
1085
1086 /* Check if next iteration will be "below" the unit file (either a regular file
1087 * or a symlink), and hence should be ignored */
1088 if (!ignore_same_name && path_startswith(info->path, *p))
1089 ignore_same_name = true;
1090 }
1091
1092 if (enabled_in_runtime) {
1093 *state = UNIT_FILE_ENABLED_RUNTIME;
1094 return 1;
1095 }
1096
1097 /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
1098 * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
1099 * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
1100 * something, and hence are a much stronger concept. */
1101 if (enabled_at_all && unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1102 *state = UNIT_FILE_STATIC;
1103 return 1;
1104 }
1105
1106 /* Hmm, we didn't find it, but maybe we found the same name
1107 * link? */
1108 if (same_name_link_config) {
1109 *state = UNIT_FILE_LINKED;
1110 return 1;
1111 }
1112 if (same_name_link_runtime) {
1113 *state = UNIT_FILE_LINKED_RUNTIME;
1114 return 1;
1115 }
1116
1117 return 0;
1118}
1119
1120static void install_info_clear(InstallInfo *i) {
1121 if (!i)
1122 return;
1123
1124 i->name = mfree(i->name);
1125 i->path = mfree(i->path);
1126 i->root = mfree(i->root);
1127 i->aliases = strv_free(i->aliases);
1128 i->wanted_by = strv_free(i->wanted_by);
1129 i->required_by = strv_free(i->required_by);
1130 i->upheld_by = strv_free(i->upheld_by);
1131 i->also = strv_free(i->also);
1132 i->default_instance = mfree(i->default_instance);
1133 i->symlink_target = mfree(i->symlink_target);
1134}
1135
1136static InstallInfo* install_info_free(InstallInfo *i) {
1137 install_info_clear(i);
1138 return mfree(i);
1139}
1140
1141DEFINE_TRIVIAL_CLEANUP_FUNC(InstallInfo*, install_info_free);
1142
1143DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
1144 install_info_hash_ops,
1145 char, string_hash_func, string_compare_func,
1146 InstallInfo, install_info_free);
1147
1148static void install_context_done(InstallContext *ctx) {
1149 assert(ctx);
1150
1151 ctx->will_process = ordered_hashmap_free(ctx->will_process);
1152 ctx->have_processed = ordered_hashmap_free(ctx->have_processed);
1153}
1154
1155static InstallInfo *install_info_find(InstallContext *ctx, const char *name) {
1156 InstallInfo *i;
1157
1158 i = ordered_hashmap_get(ctx->have_processed, name);
1159 if (i)
1160 return i;
1161
1162 return ordered_hashmap_get(ctx->will_process, name);
1163}
1164
1165static int install_info_may_process(
1166 const InstallInfo *i,
1167 const LookupPaths *lp,
1168 InstallChange **changes,
1169 size_t *n_changes) {
1170 assert(i);
1171 assert(lp);
1172
1173 /* Checks whether the loaded unit file is one we should process, or is masked,
1174 * transient or generated and thus not subject to enable/disable operations. */
1175
1176 if (i->install_mode == INSTALL_MODE_MASKED)
1177 return install_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
1178 if (path_is_generator(lp, i->path) ||
1179 path_is_transient(lp, i->path))
1180 return install_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
1181
1182 return 0;
1183}
1184
1185/**
1186 * Adds a new InstallInfo entry under name in the InstallContext.will_process
1187 * hashmap, or retrieves the existing one if already present.
1188 *
1189 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
1190 */
1191static int install_info_add(
1192 InstallContext *ctx,
1193 const char *name,
1194 const char *path,
1195 const char *root,
1196 bool auxiliary,
1197 InstallInfo **ret) {
1198
1199 _cleanup_free_ char *name_alloc = NULL;
1200 int r;
1201
1202 assert(ctx);
1203
1204 if (!name) {
1205 assert(path);
1206
1207 r = path_extract_filename(path, &name_alloc);
1208 if (r < 0)
1209 return r;
1210
1211 name = name_alloc;
1212 }
1213
1214 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
1215 return -EINVAL;
1216
1217 InstallInfo *i = install_info_find(ctx, name);
1218 if (i) {
1219 i->auxiliary = i->auxiliary && auxiliary;
1220
1221 if (ret)
1222 *ret = i;
1223 return 0;
1224 }
1225
1226 _cleanup_(install_info_freep) InstallInfo *new_info = new(InstallInfo, 1);
1227 if (!new_info)
1228 return -ENOMEM;
1229
1230 *new_info = (InstallInfo) {
1231 .install_mode = _INSTALL_MODE_INVALID,
1232 .auxiliary = auxiliary,
1233 };
1234
1235 if (name_alloc)
1236 new_info->name = TAKE_PTR(name_alloc);
1237 else {
1238 new_info->name = strdup(name);
1239 if (!new_info->name)
1240 return -ENOMEM;
1241 }
1242
1243 r = strdup_to(&new_info->root, root);
1244 if (r < 0)
1245 return r;
1246
1247 r = strdup_to(&new_info->path, path);
1248 if (r < 0)
1249 return r;
1250
1251 r = ordered_hashmap_ensure_put(&ctx->will_process, &install_info_hash_ops, new_info->name, new_info);
1252 if (r < 0)
1253 return r;
1254 i = TAKE_PTR(new_info);
1255
1256 if (ret)
1257 *ret = i;
1258 return 1;
1259}
1260
1261static int config_parse_alias(
1262 const char *unit,
1263 const char *filename,
1264 unsigned line,
1265 const char *section,
1266 unsigned section_line,
1267 const char *lvalue,
1268 int ltype,
1269 const char *rvalue,
1270 void *data,
1271 void *userdata) {
1272
1273 UnitType type;
1274
1275 assert(unit);
1276 assert(filename);
1277 assert(lvalue);
1278 assert(rvalue);
1279
1280 type = unit_name_to_type(unit);
1281 if (!unit_type_may_alias(type))
1282 return log_syntax(unit, LOG_WARNING, filename, line, 0,
1283 "Alias= is not allowed for %s units, ignoring.",
1284 unit_type_to_string(type));
1285
1286 return config_parse_strv(unit, filename, line, section, section_line,
1287 lvalue, ltype, rvalue, data, userdata);
1288}
1289
1290static int config_parse_also(
1291 const char *unit,
1292 const char *filename,
1293 unsigned line,
1294 const char *section,
1295 unsigned section_line,
1296 const char *lvalue,
1297 int ltype,
1298 const char *rvalue,
1299 void *data,
1300 void *userdata) {
1301
1302 InstallInfo *info = ASSERT_PTR(userdata);
1303 InstallContext *ctx = ASSERT_PTR(data);
1304 int r;
1305
1306 assert(unit);
1307 assert(filename);
1308 assert(lvalue);
1309 assert(rvalue);
1310
1311 for (;;) {
1312 _cleanup_free_ char *word = NULL, *printed = NULL;
1313
1314 r = extract_first_word(&rvalue, &word, NULL, 0);
1315 if (r < 0)
1316 return r;
1317 if (r == 0)
1318 break;
1319
1320 r = install_name_printf(ctx->scope, info, word, &printed);
1321 if (r < 0)
1322 return log_syntax(unit, LOG_WARNING, filename, line, r,
1323 "Failed to resolve unit name in Also=\"%s\": %m", word);
1324
1325 r = install_info_add(ctx, printed, NULL, info->root, /* auxiliary= */ true, NULL);
1326 if (r < 0)
1327 return r;
1328
1329 r = strv_push(&info->also, printed);
1330 if (r < 0)
1331 return r;
1332
1333 printed = NULL;
1334 }
1335
1336 return 0;
1337}
1338
1339static int config_parse_default_instance(
1340 const char *unit,
1341 const char *filename,
1342 unsigned line,
1343 const char *section,
1344 unsigned section_line,
1345 const char *lvalue,
1346 int ltype,
1347 const char *rvalue,
1348 void *data,
1349 void *userdata) {
1350
1351 InstallContext *ctx = ASSERT_PTR(data);
1352 InstallInfo *info = ASSERT_PTR(userdata);
1353 _cleanup_free_ char *printed = NULL;
1354 int r;
1355
1356 assert(unit);
1357 assert(filename);
1358 assert(lvalue);
1359 assert(rvalue);
1360
1361 if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE))
1362 /* When enabling an instance, we might be using a template unit file,
1363 * but we should ignore DefaultInstance silently. */
1364 return 0;
1365 if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
1366 return log_syntax(unit, LOG_WARNING, filename, line, 0,
1367 "DefaultInstance= only makes sense for template units, ignoring.");
1368
1369 r = install_name_printf(ctx->scope, info, rvalue, &printed);
1370 if (r < 0)
1371 return log_syntax(unit, LOG_WARNING, filename, line, r,
1372 "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue);
1373
1374 if (isempty(printed))
1375 printed = mfree(printed);
1376
1377 if (printed && !unit_instance_is_valid(printed))
1378 return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL),
1379 "Invalid DefaultInstance= value \"%s\".", printed);
1380
1381 return free_and_replace(info->default_instance, printed);
1382}
1383
1384static int unit_file_load(
1385 InstallContext *ctx,
1386 InstallInfo *info,
1387 const char *path,
1388 const char *root_dir,
1389 SearchFlags flags) {
1390
1391 const ConfigTableItem items[] = {
1392 { "Install", "Alias", config_parse_alias, 0, &info->aliases },
1393 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
1394 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
1395 { "Install", "UpheldBy", config_parse_strv, 0, &info->upheld_by },
1396 { "Install", "DefaultInstance", config_parse_default_instance, 0, info },
1397 { "Install", "Also", config_parse_also, 0, ctx },
1398 {}
1399 };
1400
1401 UnitType type;
1402 _cleanup_fclose_ FILE *f = NULL;
1403 _cleanup_close_ int fd = -EBADF;
1404 struct stat st;
1405 int r;
1406
1407 assert(info);
1408 assert(path);
1409
1410 if (!(flags & SEARCH_DROPIN)) {
1411 /* Loading or checking for the main unit file… */
1412
1413 type = unit_name_to_type(info->name);
1414 if (type < 0)
1415 return -EINVAL;
1416 if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type))
1417 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1418 "%s: unit type %s cannot be templated, ignoring.", path, unit_type_to_string(type));
1419
1420 if (!(flags & SEARCH_LOAD)) {
1421 if (lstat(path, &st) < 0)
1422 return -errno;
1423
1424 if (null_or_empty(&st))
1425 info->install_mode = INSTALL_MODE_MASKED;
1426 else if (S_ISREG(st.st_mode))
1427 info->install_mode = INSTALL_MODE_REGULAR;
1428 else if (S_ISLNK(st.st_mode))
1429 return -ELOOP;
1430 else if (S_ISDIR(st.st_mode))
1431 return -EISDIR;
1432 else
1433 return -ENOTTY;
1434
1435 return 0;
1436 }
1437
1438 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
1439 if (fd < 0)
1440 return -errno;
1441 } else {
1442 /* 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. */
1443
1444 if (!(flags & SEARCH_LOAD))
1445 return 0;
1446
1447 fd = chase_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
1448 if (fd < 0)
1449 return fd;
1450 }
1451
1452 if (fstat(fd, &st) < 0)
1453 return -errno;
1454
1455 if (null_or_empty(&st)) {
1456 if ((flags & SEARCH_DROPIN) == 0)
1457 info->install_mode = INSTALL_MODE_MASKED;
1458
1459 return 0;
1460 }
1461
1462 r = stat_verify_regular(&st);
1463 if (r < 0)
1464 return r;
1465
1466 f = take_fdopen(&fd, "r");
1467 if (!f)
1468 return -errno;
1469
1470 /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
1471 assert(ctx);
1472
1473 r = config_parse(info->name, path, f,
1474 "Install\0"
1475 "-Unit\0"
1476 "-Automount\0"
1477 "-Device\0"
1478 "-Mount\0"
1479 "-Path\0"
1480 "-Scope\0"
1481 "-Service\0"
1482 "-Slice\0"
1483 "-Socket\0"
1484 "-Swap\0"
1485 "-Target\0"
1486 "-Timer\0",
1487 config_item_table_lookup, items,
1488 0, info,
1489 NULL);
1490 if (r < 0)
1491 return log_debug_errno(r, "Failed to parse \"%s\": %m", info->name);
1492
1493 if ((flags & SEARCH_DROPIN) == 0)
1494 info->install_mode = INSTALL_MODE_REGULAR;
1495
1496 return
1497 (int) strv_length(info->aliases) +
1498 (int) strv_length(info->wanted_by) +
1499 (int) strv_length(info->required_by) +
1500 (int) strv_length(info->upheld_by);
1501}
1502
1503static int unit_file_load_or_readlink(
1504 InstallContext *ctx,
1505 InstallInfo *info,
1506 const char *path,
1507 const LookupPaths *lp,
1508 SearchFlags flags) {
1509 int r;
1510
1511 r = unit_file_load(ctx, info, path, lp->root_dir, flags);
1512 if (r != -ELOOP || (flags & SEARCH_DROPIN))
1513 return r;
1514
1515 /* This is a symlink, let's read and verify it. */
1516 r = unit_file_resolve_symlink(lp->root_dir, lp->search_path,
1517 NULL, AT_FDCWD, path,
1518 true, &info->symlink_target);
1519 if (r < 0)
1520 return r;
1521 bool outside_search_path = r > 0;
1522
1523 r = null_or_empty_path_with_root(info->symlink_target, lp->root_dir);
1524 if (r < 0 && r != -ENOENT)
1525 return log_debug_errno(r, "Failed to stat %s: %m", info->symlink_target);
1526 if (r > 0)
1527 info->install_mode = INSTALL_MODE_MASKED;
1528 else if (outside_search_path)
1529 info->install_mode = INSTALL_MODE_LINKED;
1530 else
1531 info->install_mode = INSTALL_MODE_ALIAS;
1532
1533 return 0;
1534}
1535
1536static int unit_file_search(
1537 InstallContext *ctx,
1538 InstallInfo *info,
1539 const LookupPaths *lp,
1540 SearchFlags flags) {
1541
1542 const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
1543 _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
1544 _cleanup_free_ char *template = NULL;
1545 bool found_unit = false;
1546 int r, result;
1547
1548 assert(info);
1549 assert(lp);
1550
1551 /* Was this unit already loaded? */
1552 if (info->install_mode != _INSTALL_MODE_INVALID)
1553 return 0;
1554
1555 if (info->path)
1556 return unit_file_load_or_readlink(ctx, info, info->path, lp, flags);
1557
1558 assert(info->name);
1559
1560 if (!FLAGS_SET(flags, SEARCH_IGNORE_TEMPLATE) && unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1561 r = unit_name_template(info->name, &template);
1562 if (r < 0)
1563 return r;
1564 }
1565
1566 STRV_FOREACH(p, lp->search_path) {
1567 _cleanup_free_ char *path = NULL;
1568
1569 path = path_join(*p, info->name);
1570 if (!path)
1571 return -ENOMEM;
1572
1573 r = unit_file_load_or_readlink(ctx, info, path, lp, flags);
1574 if (r >= 0) {
1575 info->path = TAKE_PTR(path);
1576 result = r;
1577 found_unit = true;
1578 break;
1579 } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1580 return r;
1581 }
1582
1583 if (!found_unit && template) {
1584
1585 /* Unit file doesn't exist, however instance
1586 * enablement was requested. We will check if it is
1587 * possible to load template unit file. */
1588
1589 STRV_FOREACH(p, lp->search_path) {
1590 _cleanup_free_ char *path = NULL;
1591
1592 path = path_join(*p, template);
1593 if (!path)
1594 return -ENOMEM;
1595
1596 r = unit_file_load_or_readlink(ctx, info, path, lp, flags);
1597 if (r >= 0) {
1598 info->path = TAKE_PTR(path);
1599 result = r;
1600 found_unit = true;
1601 break;
1602 } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
1603 return r;
1604 }
1605 }
1606
1607 if (!found_unit)
1608 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
1609 "Cannot find unit %s%s%s.",
1610 info->name, template ? " or " : "", strempty(template));
1611
1612 if (info->install_mode == INSTALL_MODE_MASKED)
1613 return result;
1614
1615 /* Search for drop-in directories */
1616
1617 dropin_dir_name = strjoina(info->name, ".d");
1618 STRV_FOREACH(p, lp->search_path) {
1619 char *path;
1620
1621 path = path_join(*p, dropin_dir_name);
1622 if (!path)
1623 return -ENOMEM;
1624
1625 r = strv_consume(&dirs, path);
1626 if (r < 0)
1627 return r;
1628 }
1629
1630 if (template) {
1631 dropin_template_dir_name = strjoina(template, ".d");
1632 STRV_FOREACH(p, lp->search_path) {
1633 char *path;
1634
1635 path = path_join(*p, dropin_template_dir_name);
1636 if (!path)
1637 return -ENOMEM;
1638
1639 r = strv_consume(&dirs, path);
1640 if (r < 0)
1641 return r;
1642 }
1643 }
1644
1645 /* Load drop-in conf files */
1646
1647 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs);
1648 if (r < 0)
1649 return log_debug_errno(r, "Failed to get list of conf files: %m");
1650
1651 STRV_FOREACH(p, files) {
1652 r = unit_file_load_or_readlink(ctx, info, *p, lp, flags | SEARCH_DROPIN);
1653 if (r < 0)
1654 return log_debug_errno(r, "Failed to load conf file \"%s\": %m", *p);
1655 }
1656
1657 return result;
1658}
1659
1660static int install_info_follow(
1661 InstallContext *ctx,
1662 InstallInfo *info,
1663 const LookupPaths *lp,
1664 SearchFlags flags,
1665 bool ignore_different_name) {
1666
1667 assert(ctx);
1668 assert(info);
1669
1670 if (!IN_SET(info->install_mode, INSTALL_MODE_ALIAS, INSTALL_MODE_LINKED))
1671 return -EINVAL;
1672 if (!info->symlink_target)
1673 return -EINVAL;
1674
1675 /* If the basename doesn't match, the caller should add a complete new entry for this. */
1676
1677 if (!ignore_different_name && !path_equal_filename(info->symlink_target, info->name))
1678 return -EXDEV;
1679
1680 free_and_replace(info->path, info->symlink_target);
1681 info->install_mode = _INSTALL_MODE_INVALID;
1682
1683 return unit_file_load_or_readlink(ctx, info, info->path, lp, flags);
1684}
1685
1686/**
1687 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1688 * target, maybe more than once. Propagate the instance name if present.
1689 */
1690static int install_info_traverse(
1691 InstallContext *ctx,
1692 const LookupPaths *lp,
1693 InstallInfo *start,
1694 SearchFlags flags,
1695 InstallInfo **ret) {
1696
1697 InstallInfo *i;
1698 unsigned k = 0;
1699 int r;
1700
1701 assert(ctx);
1702 assert(lp);
1703 assert(start);
1704
1705 r = unit_file_search(ctx, start, lp, flags);
1706 if (r < 0)
1707 return r;
1708
1709 i = start;
1710 while (IN_SET(i->install_mode, INSTALL_MODE_ALIAS, INSTALL_MODE_LINKED)) {
1711 /* Follow the symlink */
1712
1713 if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
1714 return -ELOOP;
1715
1716 if (!FLAGS_SET(flags, SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
1717 r = path_is_config(lp, i->path, true);
1718 if (r < 0)
1719 return r;
1720 if (r > 0)
1721 return -ELOOP;
1722 }
1723
1724 r = install_info_follow(ctx, i, lp, flags,
1725 /* If linked, don't look at the target name */
1726 /* ignore_different_name= */ i->install_mode == INSTALL_MODE_LINKED);
1727 if (r == -EXDEV && i->symlink_target) {
1728 _cleanup_free_ char *target_name = NULL, *unit_instance = NULL;
1729 const char *bn;
1730
1731 /* Target is an alias, create a new install info object and continue with that. */
1732
1733 r = path_extract_filename(i->symlink_target, &target_name);
1734 if (r < 0)
1735 return r;
1736
1737 if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
1738 unit_name_is_valid(target_name, UNIT_NAME_TEMPLATE)) {
1739
1740 _cleanup_free_ char *instance = NULL;
1741
1742 r = unit_name_to_instance(i->name, &instance);
1743 if (r < 0)
1744 return r;
1745
1746 r = unit_name_replace_instance(target_name, instance, &unit_instance);
1747 if (r < 0)
1748 return r;
1749
1750 if (streq(unit_instance, i->name)) {
1751 /* We filled in the instance, and the target stayed the same? If so,
1752 * then let's honour the link as it is. */
1753
1754 r = install_info_follow(ctx, i, lp, flags, true);
1755 if (r < 0)
1756 return r;
1757
1758 continue;
1759 }
1760
1761 bn = unit_instance;
1762 } else
1763 bn = target_name;
1764
1765 r = install_info_add(ctx, bn, NULL, lp->root_dir, /* auxiliary= */ false, &i);
1766 if (r < 0)
1767 return r;
1768
1769 /* Try again, with the new target we found. */
1770 r = unit_file_search(ctx, i, lp, flags);
1771 if (r == -ENOENT)
1772 /* Translate error code to highlight this specific case */
1773 return -ENOLINK;
1774 }
1775 if (r < 0)
1776 return r;
1777 }
1778
1779 if (ret)
1780 *ret = i;
1781
1782 return 0;
1783}
1784
1785/**
1786 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1787 * or the name (otherwise). root_dir is prepended to the path.
1788 */
1789static int install_info_add_auto(
1790 InstallContext *ctx,
1791 const LookupPaths *lp,
1792 const char *name_or_path,
1793 InstallInfo **ret) {
1794
1795 assert(ctx);
1796 assert(name_or_path);
1797
1798 if (path_is_absolute(name_or_path)) {
1799 _cleanup_free_ char *pp = path_join(lp->root_dir, name_or_path);
1800 if (!pp)
1801 return -ENOMEM;
1802
1803 return install_info_add(ctx, NULL, pp, lp->root_dir, /* auxiliary= */ false, ret);
1804 } else
1805 return install_info_add(ctx, name_or_path, NULL, lp->root_dir, /* auxiliary= */ false, ret);
1806}
1807
1808static int install_info_discover(
1809 InstallContext *ctx,
1810 const LookupPaths *lp,
1811 const char *name_or_path,
1812 SearchFlags flags,
1813 InstallInfo **ret,
1814 InstallChange **changes,
1815 size_t *n_changes) {
1816
1817 InstallInfo *info;
1818 int r;
1819
1820 assert(ctx);
1821 assert(lp);
1822 assert(name_or_path);
1823
1824 r = install_info_add_auto(ctx, lp, name_or_path, &info);
1825 if (r >= 0)
1826 r = install_info_traverse(ctx, lp, info, flags, ret);
1827
1828 if (r < 0)
1829 return install_changes_add(changes, n_changes, r, name_or_path, NULL);
1830
1831 return r;
1832}
1833
1834static int install_info_discover_and_check(
1835 InstallContext *ctx,
1836 const LookupPaths *lp,
1837 const char *name_or_path,
1838 SearchFlags flags,
1839 InstallInfo **ret,
1840 InstallChange **changes,
1841 size_t *n_changes) {
1842
1843 int r;
1844
1845 POINTER_MAY_BE_NULL(ret);
1846
1847 r = install_info_discover(ctx, lp, name_or_path, flags, ret, changes, n_changes);
1848 if (r < 0)
1849 return r;
1850
1851 return install_info_may_process(ret ? *ret : NULL, lp, changes, n_changes);
1852}
1853
1854int unit_file_verify_alias(
1855 const InstallInfo *info,
1856 const char *dst,
1857 char **ret_dst,
1858 InstallChange **changes,
1859 size_t *n_changes) {
1860
1861 _cleanup_free_ char *dst_updated = NULL;
1862 int r;
1863
1864 assert(ret_dst);
1865
1866 /* Verify that dst is a valid either a valid alias or a valid .wants/.requires symlink for the target
1867 * unit *i. Return negative on error or if not compatible, zero on success.
1868 *
1869 * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is
1870 * inserted into dst. It is not normally set, even on success, so that the caller can easily
1871 * distinguish the case where instance propagation occurred.
1872 *
1873 * Returns:
1874 * -EXDEV when the alias doesn't match the unit,
1875 * -EUCLEAN when the name is invalid,
1876 * -ELOOP when the alias it to the unit itself.
1877 */
1878
1879 const char *path_alias = strrchr(dst, '/');
1880 if (path_alias) {
1881 /* This branch covers legacy Alias= function of creating .wants and .requires symlinks. */
1882 _cleanup_free_ char *dir = NULL;
1883 char *p;
1884
1885 path_alias++; /* skip over slash */
1886
1887 r = path_extract_directory(dst, &dir);
1888 if (r < 0)
1889 return log_error_errno(r, "Failed to extract parent directory from '%s': %m", dst);
1890
1891 p = endswith(dir, ".wants");
1892 if (!p)
1893 p = endswith(dir, ".requires");
1894 if (!p) {
1895 r = install_changes_add(changes, n_changes, -EXDEV, dst, NULL);
1896 if (r != -EXDEV)
1897 return r;
1898
1899 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), "Invalid path \"%s\" in alias.", dir);
1900 }
1901
1902 *p = '\0'; /* dir should now be a unit name */
1903
1904 UnitNameFlags type = unit_name_classify(dir);
1905 if (type < 0) {
1906 r = install_changes_add(changes, n_changes, -EXDEV, dst, NULL);
1907 if (r != -EXDEV)
1908 return r;
1909 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
1910 "Invalid unit name component \"%s\" in alias.", dir);
1911 }
1912
1913 const bool instance_propagation = type == UNIT_NAME_TEMPLATE;
1914
1915 /* That's the name we want to use for verification. */
1916 r = unit_symlink_name_compatible(path_alias, info->name, instance_propagation);
1917 if (r < 0)
1918 return log_error_errno(r, "Failed to verify alias validity: %m");
1919 if (r == 0) {
1920 r = install_changes_add(changes, n_changes, -EXDEV, dst, info->name);
1921 if (r != -EXDEV)
1922 return r;
1923
1924 return log_debug_errno(SYNTHETIC_ERRNO(EXDEV),
1925 "Invalid unit \"%s\" symlink \"%s\".",
1926 info->name, dst);
1927 }
1928
1929 } else {
1930 /* If the symlink target has an instance set and the symlink source doesn't, we "propagate
1931 * the instance", i.e. instantiate the symlink source with the target instance. */
1932 if (unit_name_is_valid(dst, UNIT_NAME_TEMPLATE)) {
1933 _cleanup_free_ char *inst = NULL;
1934
1935 UnitNameFlags type = unit_name_to_instance(info->name, &inst);
1936 if (type < 0) {
1937 r = install_changes_add(changes, n_changes, -EUCLEAN, info->name, NULL);
1938 if (r != -EUCLEAN)
1939 return r;
1940 return log_debug_errno(type, "Failed to extract instance name from \"%s\": %m", info->name);
1941 }
1942
1943 if (type == UNIT_NAME_INSTANCE) {
1944 r = unit_name_replace_instance(dst, inst, &dst_updated);
1945 if (r < 0)
1946 return log_error_errno(r, "Failed to build unit name from %s+%s: %m",
1947 dst, inst);
1948 }
1949 }
1950
1951 r = unit_validate_alias_symlink_or_warn(LOG_DEBUG, dst_updated ?: dst, info->name);
1952 if (r == -ELOOP) /* -ELOOP means self-alias, which we (quietly) ignore */
1953 return r;
1954 if (r < 0)
1955 return install_changes_add(changes, n_changes,
1956 r == -EINVAL ? -EXDEV : r,
1957 dst_updated ?: dst,
1958 info->name);
1959 }
1960
1961 *ret_dst = TAKE_PTR(dst_updated);
1962 return 0;
1963}
1964
1965static int install_info_symlink_alias(
1966 RuntimeScope scope,
1967 UnitFileFlags file_flags,
1968 InstallInfo *info,
1969 const LookupPaths *lp,
1970 const char *config_path,
1971 bool force,
1972 InstallChange **changes,
1973 size_t *n_changes) {
1974
1975 int r, ret = 0;
1976
1977 assert(info);
1978 assert(lp);
1979 assert(config_path);
1980
1981 STRV_FOREACH(s, info->aliases) {
1982 _cleanup_free_ char *alias_path = NULL, *alias_target = NULL, *dst = NULL, *dst_updated = NULL;
1983
1984 r = install_name_printf(scope, info, *s, &dst);
1985 if (r < 0) {
1986 RET_GATHER(ret, install_changes_add(changes, n_changes, r, *s, NULL));
1987 continue;
1988 }
1989
1990 r = unit_file_verify_alias(info, dst, &dst_updated, changes, n_changes);
1991 if (r < 0) {
1992 if (r != -ELOOP)
1993 RET_GATHER(ret, r);
1994 continue;
1995 }
1996
1997 alias_path = path_make_absolute(dst_updated ?: dst, config_path);
1998 if (!alias_path)
1999 return -ENOMEM;
2000
2001 r = in_search_path(lp, info->path);
2002 if (r < 0)
2003 return r;
2004 if (r == 0) {
2005 /* The unit path itself is outside of the search path. To
2006 * correctly apply the alias, we need the alias symlink to
2007 * point to the symlink that was created in the search path. */
2008 alias_target = path_join(config_path, info->name);
2009 if (!alias_target)
2010 return -ENOMEM;
2011 }
2012
2013 bool broken;
2014 r = chase(alias_path, lp->root_dir, CHASE_NONEXISTENT, /* ret_path= */ NULL, /* ret_fd= */ NULL);
2015 if (r < 0 && r != -ENOENT) {
2016 RET_GATHER(ret, r);
2017 continue;
2018 }
2019 broken = r == 0; /* symlink target does not exist? */
2020
2021 r = create_symlink(lp, alias_target ?: info->path, alias_path, force || broken, changes, n_changes);
2022 if (r == -EEXIST && FLAGS_SET(file_flags, UNIT_FILE_IGNORE_AUXILIARY_FAILURE))
2023 /* We cannot realize the alias because a conflicting alias exists.
2024 * Do not propagate this as error. */
2025 continue;
2026 if (r != 0 && ret >= 0)
2027 ret = r;
2028 }
2029
2030 return ret;
2031}
2032
2033static int install_info_symlink_wants(
2034 RuntimeScope scope,
2035 UnitFileFlags file_flags,
2036 InstallInfo *info,
2037 const LookupPaths *lp,
2038 const char *config_path,
2039 char **list,
2040 const char *suffix,
2041 InstallChange **changes,
2042 size_t *n_changes) {
2043
2044 _cleanup_(install_info_clear) InstallInfo instance = {
2045 .install_mode = _INSTALL_MODE_INVALID,
2046 };
2047
2048 UnitNameFlags valid_dst_type = UNIT_NAME_ANY;
2049 const char *n;
2050 int r, q;
2051
2052 assert(info);
2053 assert(lp);
2054 assert(config_path);
2055
2056 if (strv_isempty(list))
2057 return 0;
2058
2059 if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE))
2060 /* Not a template unit. Use the name directly. */
2061 n = info->name;
2062
2063 else if (info->default_instance) {
2064 /* If this is a template, and we have a default instance, use it. */
2065
2066 r = unit_name_replace_instance(info->name, info->default_instance, &instance.name);
2067 if (r < 0)
2068 return r;
2069
2070 r = unit_file_search(NULL, &instance, lp, SEARCH_FOLLOW_CONFIG_SYMLINKS);
2071 if (r < 0)
2072 return r;
2073
2074 if (instance.install_mode == INSTALL_MODE_MASKED)
2075 return install_changes_add(changes, n_changes, -ERFKILL, instance.path, NULL);
2076
2077 n = instance.name;
2078
2079 } else {
2080 /* We have a template, but no instance yet. When used with an instantiated unit, we will get
2081 * the instance from that unit. Cannot be used with non-instance units. */
2082
2083 valid_dst_type = UNIT_NAME_INSTANCE | UNIT_NAME_TEMPLATE;
2084 n = info->name;
2085 }
2086
2087 r = 0;
2088 STRV_FOREACH(s, list) {
2089 _cleanup_free_ char *path = NULL, *dst = NULL;
2090
2091 q = install_name_printf(scope, info, *s, &dst);
2092 if (q < 0) {
2093 RET_GATHER(r, install_changes_add(changes, n_changes, q, *s, NULL));
2094 continue;
2095 }
2096
2097 if (!unit_name_is_valid(dst, valid_dst_type)) {
2098 /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the
2099 * template status doesn't match. If we are doing presets don't bother reporting the
2100 * error. This also covers cases like 'systemctl preset serial-getty@.service', which
2101 * has no DefaultInstance, so there is nothing we can do. At the same time,
2102 * 'systemctl enable serial-getty@.service' should fail, the user should specify an
2103 * instance like in 'systemctl enable serial-getty@ttyS0.service'.
2104 */
2105 if (FLAGS_SET(file_flags, UNIT_FILE_IGNORE_AUXILIARY_FAILURE))
2106 continue;
2107
2108 if (unit_name_is_valid(dst, UNIT_NAME_ANY))
2109 RET_GATHER(r, install_changes_add(changes, n_changes, -EIDRM, dst, n));
2110 else
2111 RET_GATHER(r, install_changes_add(changes, n_changes, -EUCLEAN, dst, NULL));
2112
2113 continue;
2114 }
2115
2116 path = strjoin(config_path, "/", dst, suffix, n);
2117 if (!path)
2118 return -ENOMEM;
2119
2120 q = create_symlink(lp, info->path, path, /* force= */ true, changes, n_changes);
2121 if (q != 0 && r >= 0)
2122 r = q;
2123
2124 if (unit_file_exists(scope, lp, dst) == 0) {
2125 q = install_changes_add(changes, n_changes, INSTALL_CHANGE_DESTINATION_NOT_PRESENT, dst, info->path);
2126 if (q < 0)
2127 return q;
2128 }
2129 }
2130
2131 return r;
2132}
2133
2134static int install_info_symlink_link(
2135 InstallInfo *info,
2136 const LookupPaths *lp,
2137 const char *config_path,
2138 bool force,
2139 InstallChange **changes,
2140 size_t *n_changes) {
2141
2142 _cleanup_free_ char *path = NULL;
2143 int r;
2144
2145 assert(info);
2146 assert(lp);
2147 assert(config_path);
2148 assert(info->path);
2149
2150 r = in_search_path(lp, info->path);
2151 if (r < 0)
2152 return r;
2153 if (r > 0)
2154 return 0;
2155
2156 path = path_join(config_path, info->name);
2157 if (!path)
2158 return -ENOMEM;
2159
2160 return create_symlink(lp, info->path, path, force, changes, n_changes);
2161}
2162
2163static int install_info_apply(
2164 RuntimeScope scope,
2165 UnitFileFlags file_flags,
2166 InstallInfo *info,
2167 const LookupPaths *lp,
2168 const char *config_path,
2169 InstallChange **changes,
2170 size_t *n_changes) {
2171
2172 int r, q;
2173
2174 assert(info);
2175 assert(lp);
2176 assert(config_path);
2177
2178 if (info->install_mode != INSTALL_MODE_REGULAR)
2179 return 0;
2180
2181 bool force = file_flags & UNIT_FILE_FORCE;
2182
2183 r = install_info_symlink_link(info, lp, config_path, force, changes, n_changes);
2184 /* Do not count links to the unit file towards the "carries_install_info" count */
2185 if (r < 0)
2186 /* If linking of the file failed, do not try to create other symlinks,
2187 * because they might would pointing to a non-existent or wrong unit. */
2188 return r;
2189
2190 r = install_info_symlink_alias(scope, file_flags, info, lp, config_path, force, changes, n_changes);
2191
2192 q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes);
2193 if (q != 0 && r >= 0)
2194 r = q;
2195
2196 q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->required_by, ".requires/", changes, n_changes);
2197 if (q != 0 && r >= 0)
2198 r = q;
2199
2200 q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->upheld_by, ".upholds/", changes, n_changes);
2201 if (q != 0 && r >= 0)
2202 r = q;
2203
2204 return r;
2205}
2206
2207static int install_context_apply(
2208 InstallContext *ctx,
2209 const LookupPaths *lp,
2210 UnitFileFlags file_flags,
2211 const char *config_path,
2212 SearchFlags flags,
2213 InstallChange **changes,
2214 size_t *n_changes) {
2215
2216 InstallInfo *i;
2217 int r;
2218
2219 assert(ctx);
2220 assert(lp);
2221 assert(config_path);
2222
2223 if (ordered_hashmap_isempty(ctx->will_process))
2224 return 0;
2225
2226 r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &install_info_hash_ops);
2227 if (r < 0)
2228 return r;
2229
2230 r = 0;
2231 while ((i = ordered_hashmap_first(ctx->will_process))) {
2232 int q;
2233
2234 q = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name);
2235 if (q < 0)
2236 return q;
2237
2238 q = install_info_traverse(ctx, lp, i, flags, NULL);
2239 if (q < 0) {
2240 if (i->auxiliary) {
2241 q = install_changes_add(changes, n_changes, INSTALL_CHANGE_AUXILIARY_FAILED, i->name, NULL);
2242 if (q < 0)
2243 return q;
2244 continue;
2245 }
2246
2247 return install_changes_add(changes, n_changes, q, i->name, NULL);
2248 }
2249
2250 /* We can attempt to process a masked unit when a different unit
2251 * that we were processing specifies it in Also=. */
2252 if (i->install_mode == INSTALL_MODE_MASKED) {
2253 q = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_MASKED, i->path, NULL);
2254 if (q < 0)
2255 return q;
2256 if (r >= 0)
2257 /* Assume that something *could* have been enabled here,
2258 * avoid "empty [Install] section" warning. */
2259 r += 1;
2260 continue;
2261 }
2262
2263 if (i->install_mode != INSTALL_MODE_REGULAR)
2264 continue;
2265
2266 q = install_info_apply(ctx->scope, file_flags, i, lp, config_path, changes, n_changes);
2267 if (r >= 0) {
2268 if (q < 0)
2269 r = q;
2270 else
2271 r += q;
2272 }
2273 }
2274
2275 return r;
2276}
2277
2278static int install_context_mark_for_removal(
2279 InstallContext *ctx,
2280 const LookupPaths *lp,
2281 Set **remove_symlinks_to,
2282 const char *config_path,
2283 InstallChange **changes,
2284 size_t *n_changes) {
2285
2286 InstallInfo *i;
2287 int r;
2288
2289 assert(ctx);
2290 assert(lp);
2291 assert(config_path);
2292
2293 /* Marks all items for removal */
2294
2295 if (ordered_hashmap_isempty(ctx->will_process))
2296 return 0;
2297
2298 r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &install_info_hash_ops);
2299 if (r < 0)
2300 return r;
2301
2302 while ((i = ordered_hashmap_first(ctx->will_process))) {
2303
2304 r = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name);
2305 if (r < 0)
2306 return r;
2307
2308 r = install_info_traverse(ctx, lp, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
2309 if (r == -ENOLINK) {
2310 log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
2311 r = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_DANGLING, i->path ?: i->name, NULL);
2312 if (r < 0)
2313 return r;
2314 } else if (r == -ENOENT) {
2315 if (i->auxiliary) /* some unit specified in Also= or similar is missing */
2316 log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
2317 else {
2318 log_debug_errno(r, "Unit %s not found, removing name.", i->name);
2319 r = install_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
2320 /* In case there's no unit, we still want to remove any leftover symlink, even if
2321 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2322 if (r != -ENOENT)
2323 return r;
2324 }
2325 } else if (r < 0) {
2326 log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
2327 int k = install_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
2328 if (k != r)
2329 return k;
2330 } else if (i->install_mode == INSTALL_MODE_MASKED) {
2331 log_debug("Unit file %s is masked, ignoring.", i->name);
2332 r = install_changes_add(changes, n_changes, INSTALL_CHANGE_IS_MASKED, i->path ?: i->name, NULL);
2333 if (r < 0)
2334 return r;
2335 continue;
2336 } else if (i->install_mode != INSTALL_MODE_REGULAR) {
2337 log_debug("Unit %s has install mode %s, ignoring.",
2338 i->name, install_mode_to_string(i->install_mode) ?: "invalid");
2339 continue;
2340 }
2341
2342 r = mark_symlink_for_removal(remove_symlinks_to, i->name);
2343 if (r < 0)
2344 return r;
2345 }
2346
2347 return 0;
2348}
2349
2350int unit_file_mask(
2351 RuntimeScope scope,
2352 UnitFileFlags flags,
2353 const char *root_dir,
2354 char * const *names,
2355 InstallChange **changes,
2356 size_t *n_changes) {
2357
2358 _cleanup_(lookup_paths_done) LookupPaths lp = {};
2359 const char *config_path;
2360 int r;
2361
2362 assert(scope >= 0);
2363 assert(scope < _RUNTIME_SCOPE_MAX);
2364
2365 r = lookup_paths_init(&lp, scope, 0, root_dir);
2366 if (r < 0)
2367 return r;
2368
2369 config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
2370 if (!config_path)
2371 return -ENXIO;
2372
2373 r = 0;
2374
2375 STRV_FOREACH(name, names) {
2376 _cleanup_free_ char *path = NULL;
2377
2378 if (!unit_name_is_valid(*name, UNIT_NAME_ANY)) {
2379 RET_GATHER(r, -EINVAL);
2380 continue;
2381 }
2382
2383 path = path_make_absolute(*name, config_path);
2384 if (!path)
2385 return -ENOMEM;
2386
2387 RET_GATHER(r, create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes));
2388 }
2389
2390 return r;
2391}
2392
2393int unit_file_unmask(
2394 RuntimeScope scope,
2395 UnitFileFlags flags,
2396 const char *root_dir,
2397 char * const *names,
2398 InstallChange **changes,
2399 size_t *n_changes) {
2400
2401 _cleanup_(lookup_paths_done) LookupPaths lp = {};
2402 _cleanup_set_free_ Set *remove_symlinks_to = NULL;
2403 _cleanup_strv_free_ char **todo = NULL;
2404 const char *config_path;
2405 size_t n_todo = 0;
2406 int r, q;
2407
2408 assert(scope >= 0);
2409 assert(scope < _RUNTIME_SCOPE_MAX);
2410
2411 r = lookup_paths_init(&lp, scope, 0, root_dir);
2412 if (r < 0)
2413 return r;
2414
2415 config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
2416 if (!config_path)
2417 return -ENXIO;
2418
2419 bool dry_run = flags & UNIT_FILE_DRY_RUN;
2420
2421 STRV_FOREACH(name, names) {
2422 if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
2423 return -EINVAL;
2424
2425 /* If root_dir is set, we don't care about kernel command line or generators.
2426 * But if it is not set, we need to check for interference. */
2427 if (!root_dir) {
2428 _cleanup_(install_info_clear) InstallInfo info = {
2429 .name = *name, /* We borrow *name temporarily… */
2430 .install_mode = _INSTALL_MODE_INVALID,
2431 };
2432
2433 r = unit_file_search(NULL, &info, &lp, 0);
2434 if (r < 0) {
2435 if (r != -ENOENT)
2436 log_debug_errno(r, "Failed to look up unit %s, ignoring: %m", info.name);
2437 } else if (info.install_mode == INSTALL_MODE_MASKED &&
2438 path_is_generator(&lp, info.path)) {
2439 r = install_changes_add(changes, n_changes,
2440 INSTALL_CHANGE_IS_MASKED_GENERATOR, info.name, info.path);
2441 if (r < 0)
2442 return r;
2443 }
2444
2445 TAKE_PTR(info.name); /* … and give it back here */
2446 }
2447
2448 _cleanup_free_ char *path = path_make_absolute(*name, config_path);
2449 if (!path)
2450 return -ENOMEM;
2451
2452 r = null_or_empty_path(path);
2453 if (r == -ENOENT)
2454 continue;
2455 if (r < 0)
2456 return r;
2457 if (r == 0)
2458 continue;
2459
2460 if (!GREEDY_REALLOC0(todo, n_todo + 2))
2461 return -ENOMEM;
2462
2463 todo[n_todo] = strdup(*name);
2464 if (!todo[n_todo])
2465 return -ENOMEM;
2466
2467 n_todo++;
2468 }
2469
2470 strv_uniq(todo);
2471
2472 r = 0;
2473 STRV_FOREACH(i, todo) {
2474 _cleanup_free_ char *path = NULL;
2475 const char *rp;
2476
2477 path = path_make_absolute(*i, config_path);
2478 if (!path)
2479 return -ENOMEM;
2480
2481 if (!dry_run && unlink(path) < 0) {
2482 if (errno != ENOENT)
2483 RET_GATHER(r, install_changes_add(changes, n_changes, -errno, path, NULL));
2484
2485 continue;
2486 }
2487
2488 q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, path, NULL);
2489 if (q < 0)
2490 return q;
2491
2492 rp = skip_root(lp.root_dir, path);
2493 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
2494 if (q < 0)
2495 return q;
2496 }
2497
2498 RET_GATHER(r, remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes));
2499
2500 return r;
2501}
2502
2503int unit_file_link(
2504 RuntimeScope scope,
2505 UnitFileFlags flags,
2506 const char *root_dir,
2507 char * const *files,
2508 InstallChange **changes,
2509 size_t *n_changes) {
2510
2511 _cleanup_(lookup_paths_done) LookupPaths lp = {};
2512 _cleanup_ordered_hashmap_free_ OrderedHashmap *todo = NULL;
2513 const char *config_path;
2514 int r;
2515
2516 assert(scope >= 0);
2517 assert(scope < _RUNTIME_SCOPE_MAX);
2518 assert(changes);
2519 assert(n_changes);
2520
2521 r = lookup_paths_init(&lp, scope, 0, root_dir);
2522 if (r < 0)
2523 return r;
2524
2525 config_path = FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
2526 if (!config_path)
2527 return -ENXIO;
2528
2529 STRV_FOREACH(file, files) {
2530 _cleanup_free_ char *fn = NULL, *path = NULL, *full = NULL;
2531
2532 if (ordered_hashmap_contains(todo, *file))
2533 continue;
2534
2535 if (!path_is_absolute(*file))
2536 return install_changes_add(changes, n_changes, -EINVAL, *file, NULL);
2537
2538 r = path_extract_filename(*file, &fn);
2539 if (r < 0)
2540 return install_changes_add(changes, n_changes, r, *file, NULL);
2541
2542 if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
2543 return install_changes_add(changes, n_changes, -EUCLEAN, *file, NULL);
2544
2545 full = path_join(lp.root_dir, *file);
2546 if (!full)
2547 return -ENOMEM;
2548
2549 r = verify_regular_at(AT_FDCWD, full, /* follow= */ false);
2550 if (r < 0)
2551 return install_changes_add(changes, n_changes, r, *file, NULL);
2552
2553 r = in_search_path(&lp, *file);
2554 if (r < 0)
2555 return install_changes_add(changes, n_changes, r, *file, NULL);
2556 if (r > 0)
2557 /* A silent noop if the file is already in the search path. */
2558 continue;
2559
2560 if (underneath_search_path(&lp, *file))
2561 return install_changes_add(changes, n_changes, -ETXTBSY, *file, NULL);
2562
2563 path = strdup(*file);
2564 if (!path)
2565 return -ENOMEM;
2566
2567 r = ordered_hashmap_ensure_put(&todo, &path_hash_ops_free_free, path, fn);
2568 if (r < 0)
2569 return r;
2570 assert(r > 0);
2571
2572 TAKE_PTR(path);
2573 TAKE_PTR(fn);
2574 }
2575
2576 r = 0;
2577
2578 const char *fn, *path;
2579 ORDERED_HASHMAP_FOREACH_KEY(fn, path, todo) {
2580 _cleanup_free_ char *new_path = NULL;
2581
2582 new_path = path_make_absolute(fn, config_path);
2583 if (!new_path)
2584 return -ENOMEM;
2585
2586 RET_GATHER(r, create_symlink(&lp, path, new_path, FLAGS_SET(flags, UNIT_FILE_FORCE), changes, n_changes));
2587 }
2588
2589 return r;
2590}
2591
2592static int path_shall_revert(const LookupPaths *lp, const char *path) {
2593 int r;
2594
2595 assert(lp);
2596 assert(path);
2597
2598 /* Checks whether the path is one where the drop-in directories shall be removed. */
2599
2600 r = path_is_config(lp, path, true);
2601 if (r != 0)
2602 return r;
2603
2604 r = path_is_control(lp, path);
2605 if (r != 0)
2606 return r;
2607
2608 return path_is_transient(lp, path);
2609}
2610
2611int unit_file_revert(
2612 RuntimeScope scope,
2613 const char *root_dir,
2614 char * const *names,
2615 InstallChange **changes,
2616 size_t *n_changes) {
2617
2618 _cleanup_set_free_ Set *remove_symlinks_to = NULL;
2619 _cleanup_(lookup_paths_done) LookupPaths lp = {};
2620 _cleanup_strv_free_ char **todo = NULL;
2621 size_t n_todo = 0;
2622 int r, q;
2623
2624 /* Puts a unit file back into vendor state. This means:
2625 *
2626 * a) we remove all drop-in snippets added by the user ("config"), add to transient units
2627 * ("transient"), and added via "systemctl set-property" ("control"), but not if the drop-in is
2628 * generated ("generated").
2629 *
2630 * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files
2631 * (i.e. in "config", but not in "transient" or "control" or even "generated").
2632 *
2633 * We remove all that in both the runtime and the persistent directories, if that applies.
2634 */
2635
2636 r = lookup_paths_init(&lp, scope, 0, root_dir);
2637 if (r < 0)
2638 return r;
2639
2640 STRV_FOREACH(name, names) {
2641 bool has_vendor = false;
2642
2643 if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
2644 return -EINVAL;
2645
2646 STRV_FOREACH(p, lp.search_path) {
2647 _cleanup_free_ char *path = NULL, *dropin = NULL;
2648 struct stat st;
2649
2650 path = path_make_absolute(*name, *p);
2651 if (!path)
2652 return -ENOMEM;
2653
2654 r = RET_NERRNO(lstat(path, &st));
2655 if (r < 0) {
2656 if (r != -ENOENT)
2657 return install_changes_add(changes, n_changes, r, path, NULL);
2658 } else if (S_ISREG(st.st_mode)) {
2659 /* Check if there's a vendor version */
2660 r = path_is_vendor_or_generator(&lp, path);
2661 if (r < 0)
2662 return install_changes_add(changes, n_changes, r, path, NULL);
2663 if (r > 0)
2664 has_vendor = true;
2665 }
2666
2667 dropin = strjoin(path, ".d");
2668 if (!dropin)
2669 return -ENOMEM;
2670
2671 r = RET_NERRNO(lstat(dropin, &st));
2672 if (r < 0) {
2673 if (r != -ENOENT)
2674 return install_changes_add(changes, n_changes, r, dropin, NULL);
2675 } else if (S_ISDIR(st.st_mode)) {
2676 /* Remove the drop-ins */
2677 r = path_shall_revert(&lp, dropin);
2678 if (r < 0)
2679 return install_changes_add(changes, n_changes, r, dropin, NULL);
2680 if (r > 0) {
2681 if (!GREEDY_REALLOC0(todo, n_todo + 2))
2682 return -ENOMEM;
2683
2684 todo[n_todo++] = TAKE_PTR(dropin);
2685 }
2686 }
2687 }
2688
2689 if (!has_vendor)
2690 continue;
2691
2692 /* OK, there's a vendor version, hence drop all configuration versions */
2693 STRV_FOREACH(p, lp.search_path) {
2694 _cleanup_free_ char *path = NULL;
2695 struct stat st;
2696
2697 path = path_make_absolute(*name, *p);
2698 if (!path)
2699 return -ENOMEM;
2700
2701 r = RET_NERRNO(lstat(path, &st));
2702 if (r < 0) {
2703 if (r != -ENOENT)
2704 return install_changes_add(changes, n_changes, r, path, NULL);
2705 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
2706 r = path_is_config(&lp, path, true);
2707 if (r < 0)
2708 return install_changes_add(changes, n_changes, r, path, NULL);
2709 if (r > 0) {
2710 if (!GREEDY_REALLOC0(todo, n_todo + 2))
2711 return -ENOMEM;
2712
2713 todo[n_todo++] = TAKE_PTR(path);
2714 }
2715 }
2716 }
2717 }
2718
2719 strv_uniq(todo);
2720
2721 r = 0;
2722 STRV_FOREACH(i, todo) {
2723 _cleanup_strv_free_ char **fs = NULL;
2724 const char *rp;
2725
2726 (void) get_files_in_directory(*i, &fs);
2727
2728 q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
2729 if (q < 0 && q != -ENOENT && r >= 0) {
2730 r = q;
2731 continue;
2732 }
2733
2734 STRV_FOREACH(j, fs) {
2735 _cleanup_free_ char *t = NULL;
2736
2737 t = path_join(*i, *j);
2738 if (!t)
2739 return -ENOMEM;
2740
2741 q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, t, NULL);
2742 if (q < 0)
2743 return q;
2744 }
2745
2746 q = install_changes_add(changes, n_changes, INSTALL_CHANGE_UNLINK, *i, NULL);
2747 if (q < 0)
2748 return q;
2749
2750 rp = skip_root(lp.root_dir, *i);
2751 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
2752 if (q < 0)
2753 return q;
2754 }
2755
2756 q = remove_marked_symlinks(remove_symlinks_to, lp.runtime_config, &lp, false, changes, n_changes);
2757 if (r >= 0)
2758 r = q;
2759
2760 q = remove_marked_symlinks(remove_symlinks_to, lp.persistent_config, &lp, false, changes, n_changes);
2761 if (r >= 0)
2762 r = q;
2763
2764 return r;
2765}
2766
2767int unit_file_add_dependency(
2768 RuntimeScope scope,
2769 UnitFileFlags file_flags,
2770 const char *root_dir,
2771 char * const *names,
2772 const char *target,
2773 UnitDependency dep,
2774 InstallChange **changes,
2775 size_t *n_changes) {
2776
2777 _cleanup_(lookup_paths_done) LookupPaths lp = {};
2778 _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
2779 InstallInfo *info, *target_info;
2780 const char *config_path;
2781 int r;
2782
2783 assert(scope >= 0);
2784 assert(scope < _RUNTIME_SCOPE_MAX);
2785 assert(target);
2786 assert(IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES));
2787
2788 if (!unit_name_is_valid(target, UNIT_NAME_ANY))
2789 return install_changes_add(changes, n_changes, -EUCLEAN, target, NULL);
2790
2791 r = lookup_paths_init(&lp, scope, 0, root_dir);
2792 if (r < 0)
2793 return r;
2794
2795 config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
2796 if (!config_path)
2797 return -ENXIO;
2798
2799 r = install_info_discover_and_check(&ctx, &lp, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2800 &target_info, changes, n_changes);
2801 if (r < 0)
2802 return r;
2803
2804 assert(target_info->install_mode == INSTALL_MODE_REGULAR);
2805
2806 STRV_FOREACH(name, names) {
2807 char ***l;
2808
2809 r = install_info_discover_and_check(&ctx, &lp, *name,
2810 SEARCH_FOLLOW_CONFIG_SYMLINKS,
2811 &info, changes, n_changes);
2812 if (r < 0)
2813 return r;
2814
2815 assert(info->install_mode == INSTALL_MODE_REGULAR);
2816
2817 /* We didn't actually load anything from the unit
2818 * file, but instead just add in our new symlink to
2819 * create. */
2820
2821 if (dep == UNIT_WANTS)
2822 l = &info->wanted_by;
2823 else if (dep == UNIT_REQUIRES)
2824 l = &info->required_by;
2825 else
2826 l = &info->upheld_by;
2827
2828 strv_free(*l);
2829 *l = strv_new(target_info->name);
2830 if (!*l)
2831 return -ENOMEM;
2832 }
2833
2834 return install_context_apply(&ctx, &lp, file_flags, config_path,
2835 SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
2836}
2837
2838static int do_unit_file_enable(
2839 const LookupPaths *lp,
2840 RuntimeScope scope,
2841 UnitFileFlags flags,
2842 const char *config_path,
2843 char * const *names_or_paths,
2844 InstallChange **changes,
2845 size_t *n_changes) {
2846
2847 _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
2848 InstallInfo *info;
2849 int r;
2850
2851 STRV_FOREACH(name, names_or_paths) {
2852 r = install_info_discover_and_check(&ctx, lp, *name,
2853 SEARCH_LOAD | SEARCH_FOLLOW_CONFIG_SYMLINKS,
2854 &info, changes, n_changes);
2855 if (r < 0)
2856 return r;
2857
2858 assert(info->install_mode == INSTALL_MODE_REGULAR);
2859 }
2860
2861 /* This will return the number of symlink rules that were
2862 supposed to be created, not the ones actually created. This
2863 is useful to determine whether the passed units had any
2864 installation data at all. */
2865
2866 return install_context_apply(&ctx, lp, flags, config_path,
2867 SEARCH_LOAD, changes, n_changes);
2868}
2869
2870int unit_file_enable(
2871 RuntimeScope scope,
2872 UnitFileFlags flags,
2873 const char *root_dir,
2874 char * const *names_or_paths,
2875 InstallChange **changes,
2876 size_t *n_changes) {
2877
2878 _cleanup_(lookup_paths_done) LookupPaths lp = {};
2879 int r;
2880
2881 assert(scope >= 0);
2882 assert(scope < _RUNTIME_SCOPE_MAX);
2883
2884 r = lookup_paths_init(&lp, scope, 0, root_dir);
2885 if (r < 0)
2886 return r;
2887
2888 const char *config_path = config_path_from_flags(&lp, flags);
2889 if (!config_path)
2890 return -ENXIO;
2891
2892 return do_unit_file_enable(&lp, scope, flags, config_path, names_or_paths, changes, n_changes);
2893}
2894
2895static int do_unit_file_disable(
2896 const LookupPaths *lp,
2897 RuntimeScope scope,
2898 UnitFileFlags flags,
2899 const char *config_path,
2900 char * const *names,
2901 InstallChange **changes,
2902 size_t *n_changes) {
2903
2904 _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
2905 bool has_install_info = false;
2906 int r;
2907
2908 assert(changes);
2909 assert(n_changes);
2910
2911 STRV_FOREACH(name, names) {
2912 InstallInfo *info;
2913
2914 if (!unit_name_is_valid(*name, UNIT_NAME_ANY))
2915 return install_changes_add(changes, n_changes, -EUCLEAN, *name, NULL);
2916
2917 r = install_info_add(&ctx, *name, NULL, lp->root_dir, /* auxiliary= */ false, &info);
2918 if (r >= 0)
2919 r = install_info_traverse(&ctx, lp, info, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
2920 if (r < 0) {
2921 r = install_changes_add(changes, n_changes, r, *name, NULL);
2922 /* In case there's no unit, we still want to remove any leftover symlink, even if
2923 * the unit might have been removed already, hence treating ENOENT as non-fatal. */
2924 if (r != -ENOENT)
2925 return r;
2926 }
2927
2928 /* If we enable multiple units, some with install info and others without,
2929 * the "empty [Install] section" warning is not shown. Let's make the behavior
2930 * of disable align with that. */
2931 has_install_info = has_install_info || install_info_has_rules(info) || install_info_has_also(info);
2932 }
2933
2934 _cleanup_set_free_ Set *remove_symlinks_to = NULL;
2935 r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes);
2936 if (r >= 0)
2937 r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes);
2938 if (r < 0)
2939 return r;
2940
2941 /* The warning is shown only if it's a no-op */
2942 return install_changes_have_modification(*changes, *n_changes) || has_install_info;
2943}
2944
2945int unit_file_disable(
2946 RuntimeScope scope,
2947 UnitFileFlags flags,
2948 const char *root_dir,
2949 char * const *files,
2950 InstallChange **changes,
2951 size_t *n_changes) {
2952
2953 _cleanup_(lookup_paths_done) LookupPaths lp = {};
2954 int r;
2955
2956 assert(scope >= 0);
2957 assert(scope < _RUNTIME_SCOPE_MAX);
2958
2959 r = lookup_paths_init(&lp, scope, 0, root_dir);
2960 if (r < 0)
2961 return r;
2962
2963 const char *config_path = config_path_from_flags(&lp, flags);
2964 if (!config_path)
2965 return -ENXIO;
2966
2967 return do_unit_file_disable(&lp, scope, flags, config_path, files, changes, n_changes);
2968}
2969
2970static int normalize_linked_files(
2971 RuntimeScope scope,
2972 const LookupPaths *lp,
2973 char * const *names_or_paths,
2974 char ***ret_names,
2975 char ***ret_files) {
2976
2977 /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/,
2978 * but operates on real unit names. For each argument we look up the actual path
2979 * where the unit is found. This way linked units can be re-enabled successfully. */
2980
2981 _cleanup_strv_free_ char **files = NULL, **names = NULL;
2982 int r;
2983
2984 STRV_FOREACH(a, names_or_paths) {
2985 _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
2986 InstallInfo *i = NULL;
2987 _cleanup_free_ char *n = NULL;
2988
2989 r = path_extract_filename(*a, &n);
2990 if (r < 0)
2991 return r;
2992 if (r == O_DIRECTORY)
2993 return log_debug_errno(SYNTHETIC_ERRNO(EISDIR),
2994 "Unexpected path to a directory \"%s\", refusing.", *a);
2995
2996 if (!is_path(*a) && !unit_name_is_valid(*a, UNIT_NAME_INSTANCE)) {
2997 r = install_info_discover(&ctx, lp, n, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i, NULL, NULL);
2998 if (r < 0)
2999 log_debug_errno(r, "Failed to discover unit \"%s\", operating on name: %m", n);
3000 }
3001
3002 r = strv_consume(&names, TAKE_PTR(n));
3003 if (r < 0)
3004 return r;
3005
3006 const char *p = NULL;
3007 if (i && i->path && i->root)
3008 /* Use startswith here, because we know that paths are normalized, and
3009 * path_startswith() would give us a relative path, but we need an absolute path
3010 * relative to i->root.
3011 *
3012 * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service
3013 * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute"
3014 * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/.
3015 */
3016 p = startswith(i->path, i->root);
3017
3018 r = strv_extend(&files, p ?: *a);
3019 if (r < 0)
3020 return r;
3021 }
3022
3023 *ret_names = TAKE_PTR(names);
3024 *ret_files = TAKE_PTR(files);
3025 return 0;
3026}
3027
3028int unit_file_reenable(
3029 RuntimeScope scope,
3030 UnitFileFlags flags,
3031 const char *root_dir,
3032 char * const *names_or_paths,
3033 InstallChange **changes,
3034 size_t *n_changes) {
3035
3036 _cleanup_(lookup_paths_done) LookupPaths lp = {};
3037 _cleanup_strv_free_ char **names = NULL, **files = NULL;
3038 int r;
3039
3040 assert(scope >= 0);
3041 assert(scope < _RUNTIME_SCOPE_MAX);
3042
3043 r = lookup_paths_init(&lp, scope, 0, root_dir);
3044 if (r < 0)
3045 return r;
3046
3047 const char *config_path = config_path_from_flags(&lp, flags);
3048 if (!config_path)
3049 return -ENXIO;
3050
3051 r = normalize_linked_files(scope, &lp, names_or_paths, &names, &files);
3052 if (r < 0)
3053 return r;
3054
3055 /* First, we invoke the disable command with only the basename... */
3056 r = do_unit_file_disable(&lp, scope, flags, config_path, names, changes, n_changes);
3057 if (r < 0)
3058 return r;
3059
3060 /* But the enable command with the full name */
3061 return do_unit_file_enable(&lp, scope, flags, config_path, files, changes, n_changes);
3062}
3063
3064int unit_file_set_default(
3065 RuntimeScope scope,
3066 UnitFileFlags flags,
3067 const char *root_dir,
3068 const char *name,
3069 InstallChange **changes,
3070 size_t *n_changes) {
3071
3072 _cleanup_(lookup_paths_done) LookupPaths lp = {};
3073 _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
3074 InstallInfo *info;
3075 const char *new_path;
3076 int r;
3077
3078 assert(scope >= 0);
3079 assert(scope < _RUNTIME_SCOPE_MAX);
3080 assert(name);
3081
3082 if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
3083 return -EINVAL;
3084 if (streq(name, SPECIAL_DEFAULT_TARGET))
3085 return -EINVAL;
3086
3087 r = lookup_paths_init(&lp, scope, 0, root_dir);
3088 if (r < 0)
3089 return r;
3090
3091 r = install_info_discover_and_check(&ctx, &lp, name, 0, &info, changes, n_changes);
3092 if (r < 0)
3093 return r;
3094
3095 new_path = strjoina(lp.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
3096 return create_symlink(&lp, info->path, new_path, flags & UNIT_FILE_FORCE, changes, n_changes);
3097}
3098
3099int unit_file_get_default(
3100 RuntimeScope scope,
3101 const char *root_dir,
3102 char **ret) {
3103
3104 _cleanup_(lookup_paths_done) LookupPaths lp = {};
3105 _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
3106 InstallInfo *info;
3107 int r;
3108
3109 assert(scope >= 0);
3110 assert(scope < _RUNTIME_SCOPE_MAX);
3111 assert(ret);
3112
3113 r = lookup_paths_init(&lp, scope, 0, root_dir);
3114 if (r < 0)
3115 return r;
3116
3117 r = install_info_discover(&ctx, &lp, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
3118 &info, NULL, NULL);
3119 if (r < 0)
3120 return r;
3121
3122 return strdup_to(ret, info->name);
3123}
3124
3125int unit_file_lookup_state(
3126 RuntimeScope scope,
3127 const LookupPaths *lp,
3128 const char *name,
3129 UnitFileState *ret) {
3130
3131 _cleanup_(install_context_done) InstallContext ctx = { .scope = scope };
3132 InstallInfo *info;
3133 UnitFileState state;
3134 int r;
3135
3136 assert(lp);
3137 assert(name);
3138
3139 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
3140 return -EINVAL;
3141
3142 r = install_info_discover(&ctx, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3143 &info, NULL, NULL);
3144 if (r < 0)
3145 return log_debug_errno(r, "Failed to discover unit %s: %m", name);
3146
3147 assert(IN_SET(info->install_mode, INSTALL_MODE_REGULAR, INSTALL_MODE_MASKED));
3148 log_debug("Found unit %s at %s (%s)", name, strna(info->path),
3149 info->install_mode == INSTALL_MODE_REGULAR ? "regular file" : "mask");
3150
3151 /* Shortcut things, if the caller just wants to know if this unit exists. */
3152 if (!ret)
3153 return 0;
3154
3155 switch (info->install_mode) {
3156
3157 case INSTALL_MODE_MASKED:
3158 r = path_is_runtime(lp, info->path, true);
3159 if (r < 0)
3160 return r;
3161
3162 state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
3163 break;
3164
3165 case INSTALL_MODE_REGULAR:
3166 /* Check if the name we were querying is actually an alias */
3167 if (!path_equal_filename(name, info->path) && !unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
3168 state = UNIT_FILE_ALIAS;
3169 break;
3170 }
3171
3172 r = path_is_generator(lp, info->path);
3173 if (r < 0)
3174 return r;
3175 if (r > 0) {
3176 state = UNIT_FILE_GENERATED;
3177 break;
3178 }
3179
3180 r = path_is_transient(lp, info->path);
3181 if (r < 0)
3182 return r;
3183 if (r > 0) {
3184 state = UNIT_FILE_TRANSIENT;
3185 break;
3186 }
3187
3188 /* Check if any of the Alias= symlinks have been created.
3189 * We ignore other aliases, and only check those that would
3190 * be created by systemctl enable for this unit. */
3191 r = find_symlinks_in_scope(scope, lp, info, true, &state);
3192 if (r < 0)
3193 return r;
3194 if (r > 0)
3195 break;
3196
3197 /* Check if the file is known under other names. If it is,
3198 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
3199 r = find_symlinks_in_scope(scope, lp, info, false, &state);
3200 if (r < 0)
3201 return r;
3202 if (r > 0)
3203 state = UNIT_FILE_INDIRECT;
3204 else {
3205 if (install_info_has_rules(info))
3206 state = UNIT_FILE_DISABLED;
3207 else if (install_info_has_also(info))
3208 state = UNIT_FILE_INDIRECT;
3209 else
3210 state = UNIT_FILE_STATIC;
3211 }
3212
3213 break;
3214
3215 default:
3216 assert_not_reached();
3217 }
3218
3219 *ret = state;
3220 return 0;
3221}
3222
3223int unit_file_get_state(
3224 RuntimeScope scope,
3225 const char *root_dir,
3226 const char *name,
3227 UnitFileState *ret) {
3228
3229 _cleanup_(lookup_paths_done) LookupPaths lp = {};
3230 int r;
3231
3232 assert(scope >= 0);
3233 assert(scope < _RUNTIME_SCOPE_MAX);
3234 assert(name);
3235
3236 r = lookup_paths_init(&lp, scope, 0, root_dir);
3237 if (r < 0)
3238 return r;
3239
3240 return unit_file_lookup_state(scope, &lp, name, ret);
3241}
3242
3243int unit_file_exists_full(
3244 RuntimeScope scope,
3245 const LookupPaths *lp,
3246 SearchFlags flags,
3247 const char *name,
3248 char **ret_path) {
3249
3250 _cleanup_(install_context_done) InstallContext c = {
3251 .scope = scope,
3252 };
3253 int r;
3254
3255 assert(lp);
3256 assert(name);
3257
3258 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
3259 return -EINVAL;
3260
3261 InstallInfo *info = NULL;
3262 r = install_info_discover(
3263 &c,
3264 lp,
3265 name,
3266 flags,
3267 ret_path ? &info : NULL,
3268 /* changes= */ NULL,
3269 /* n_changes= */ NULL);
3270 if (r == -ENOENT) {
3271 if (ret_path)
3272 *ret_path = NULL;
3273 return 0;
3274 }
3275 if (r < 0)
3276 return r;
3277
3278 if (ret_path) {
3279 assert(info);
3280
3281 r = strdup_to(ret_path, info->path);
3282 if (r < 0)
3283 return r;
3284 }
3285
3286 return 1;
3287}
3288
3289static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
3290 _cleanup_strv_free_ char **instances = NULL;
3291 _cleanup_free_ char *unit_name = NULL;
3292 int r;
3293
3294 assert(pattern);
3295 assert(out_instances);
3296 assert(out_unit_name);
3297
3298 r = extract_first_word(&pattern, &unit_name, NULL, EXTRACT_RETAIN_ESCAPE);
3299 if (r < 0)
3300 return r;
3301
3302 /* We handle the instances logic when unit name is extracted */
3303 if (pattern) {
3304 /* We only create instances when a rule of templated unit
3305 * is seen. A rule like enable foo@.service a b c will
3306 * result in an array of (a, b, c) as instance names */
3307 if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
3308 return -EINVAL;
3309
3310 instances = strv_split(pattern, WHITESPACE);
3311 if (!instances)
3312 return -ENOMEM;
3313
3314 *out_instances = TAKE_PTR(instances);
3315 }
3316
3317 *out_unit_name = TAKE_PTR(unit_name);
3318
3319 return 0;
3320}
3321
3322static int presets_find_config(RuntimeScope scope, const char *root_dir, char ***ret) {
3323 static const char* const initrd_dirs[] = { CONF_PATHS("systemd/initrd-preset"), NULL };
3324 static const char* const system_dirs[] = { CONF_PATHS("systemd/system-preset"), NULL };
3325 static const char* const user_dirs[] = { CONF_PATHS("systemd/user-preset"), NULL };
3326 const char* const* dirs;
3327 int r;
3328
3329 assert(scope >= 0);
3330 assert(scope < _RUNTIME_SCOPE_MAX);
3331
3332 if (scope == RUNTIME_SCOPE_SYSTEM) {
3333 r = chase_and_access("/etc/initrd-release", root_dir, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
3334 if (r < 0 && r != -ENOENT)
3335 return r;
3336
3337 /* Make sure that we fall back to the system preset directories if we're operating on a root
3338 * directory without initrd preset directories. This makes sure that we don't regress when
3339 * using a newer systemctl to operate on a root directory with an older version of systemd
3340 * installed that doesn't yet known about initrd preset directories. */
3341 if (r >= 0)
3342 STRV_FOREACH(d, initrd_dirs) {
3343 r = chase_and_access(*d, root_dir, CHASE_PREFIX_ROOT, F_OK, /* ret_path= */ NULL);
3344 if (r >= 0)
3345 return conf_files_list_strv(ret, ".preset", root_dir, 0, initrd_dirs);
3346 if (r != -ENOENT)
3347 return r;
3348 }
3349
3350 dirs = system_dirs;
3351 } else if (IN_SET(scope, RUNTIME_SCOPE_GLOBAL, RUNTIME_SCOPE_USER))
3352 dirs = user_dirs;
3353 else
3354 assert_not_reached();
3355
3356 return conf_files_list_strv(ret, ".preset", root_dir, 0, dirs);
3357}
3358
3359static int read_presets(RuntimeScope scope, const char *root_dir, UnitFilePresets *presets) {
3360 _cleanup_(unit_file_presets_done) UnitFilePresets ps = {};
3361 _cleanup_strv_free_ char **files = NULL;
3362 int r;
3363
3364 assert(scope >= 0);
3365 assert(scope < _RUNTIME_SCOPE_MAX);
3366 assert(presets);
3367
3368 r = presets_find_config(scope, root_dir, &files);
3369 if (r < 0)
3370 return r;
3371
3372 STRV_FOREACH(p, files) {
3373 _cleanup_fclose_ FILE *f = NULL;
3374 int n = 0;
3375
3376 f = fopen(*p, "re");
3377 if (!f) {
3378 if (errno == ENOENT)
3379 continue;
3380
3381 return -errno;
3382 }
3383
3384 for (;;) {
3385 _cleanup_free_ char *line = NULL;
3386 _cleanup_(unit_file_preset_rule_done) UnitFilePresetRule rule = {};
3387 const char *parameter;
3388
3389 r = read_stripped_line(f, LONG_LINE_MAX, &line);
3390 if (r < 0)
3391 return r;
3392 if (r == 0)
3393 break;
3394
3395 n++;
3396
3397 if (isempty(line))
3398 continue;
3399 if (strchr(COMMENTS, line[0]))
3400 continue;
3401
3402 if ((parameter = first_word(line, "enable"))) {
3403 char *unit_name;
3404 char **instances = NULL;
3405
3406 /* Unit_name will remain the same as parameter when no instances are specified */
3407 r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
3408 if (r < 0) {
3409 log_syntax(NULL, LOG_WARNING, *p, n, r, "Couldn't parse line '%s'. Ignoring.", line);
3410 continue;
3411 }
3412
3413 rule = (UnitFilePresetRule) {
3414 .pattern = unit_name,
3415 .action = PRESET_ENABLE,
3416 .instances = instances,
3417 };
3418
3419 } else if ((parameter = first_word(line, "disable"))) {
3420 char *pattern;
3421
3422 pattern = strdup(parameter);
3423 if (!pattern)
3424 return -ENOMEM;
3425
3426 rule = (UnitFilePresetRule) {
3427 .pattern = pattern,
3428 .action = PRESET_DISABLE,
3429 };
3430
3431 } else if ((parameter = first_word(line, "ignore"))) {
3432 char *pattern;
3433
3434 pattern = strdup(parameter);
3435 if (!pattern)
3436 return -ENOMEM;
3437
3438 rule = (UnitFilePresetRule) {
3439 .pattern = pattern,
3440 .action = PRESET_IGNORE,
3441 };
3442 }
3443
3444 if (rule.action != 0) {
3445 if (!GREEDY_REALLOC(ps.rules, ps.n_rules + 1))
3446 return -ENOMEM;
3447
3448 ps.rules[ps.n_rules++] = TAKE_STRUCT(rule);
3449 continue;
3450 }
3451
3452 log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
3453 }
3454 }
3455
3456 ps.initialized = true;
3457 *presets = TAKE_STRUCT(ps);
3458
3459 return 0;
3460}
3461
3462static int pattern_match_multiple_instances(
3463 const UnitFilePresetRule rule,
3464 const char *unit_name,
3465 char ***ret) {
3466
3467 _cleanup_free_ char *templated_name = NULL;
3468 int r;
3469
3470 assert(unit_name);
3471
3472 /* If no ret is needed or the rule itself does not have instances
3473 * initialized, we return not matching */
3474 if (!ret || !rule.instances)
3475 return 0;
3476
3477 r = unit_name_template(unit_name, &templated_name);
3478 if (r < 0)
3479 return r;
3480 if (!streq(rule.pattern, templated_name))
3481 return 0;
3482
3483 /* Compose a list of specified instances when unit name is a template */
3484 if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
3485 _cleanup_strv_free_ char **out_strv = NULL;
3486
3487 STRV_FOREACH(iter, rule.instances) {
3488 _cleanup_free_ char *name = NULL;
3489
3490 r = unit_name_replace_instance(unit_name, *iter, &name);
3491 if (r < 0)
3492 return r;
3493
3494 r = strv_consume(&out_strv, TAKE_PTR(name));
3495 if (r < 0)
3496 return r;
3497 }
3498
3499 *ret = TAKE_PTR(out_strv);
3500 return 1;
3501 } else {
3502 /* We now know the input unit name is an instance name */
3503 _cleanup_free_ char *instance_name = NULL;
3504
3505 r = unit_name_to_instance(unit_name, &instance_name);
3506 if (r < 0)
3507 return r;
3508
3509 if (strv_contains(rule.instances, instance_name))
3510 return 1;
3511 }
3512 return 0;
3513}
3514
3515static int query_presets(const char *name, const UnitFilePresets *presets, char ***instance_name_list) {
3516 PresetAction action = PRESET_UNKNOWN;
3517
3518 assert(name);
3519 assert(presets);
3520 POINTER_MAY_BE_NULL(instance_name_list);
3521
3522 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
3523 return -EINVAL;
3524
3525 FOREACH_ARRAY(i, presets->rules, presets->n_rules)
3526 if (pattern_match_multiple_instances(*i, name, instance_name_list) > 0 ||
3527 fnmatch(i->pattern, name, FNM_NOESCAPE) == 0) {
3528 action = i->action;
3529 break;
3530 }
3531
3532 switch (action) {
3533
3534 case PRESET_UNKNOWN:
3535 log_debug("Preset files don't specify rule for %s. Enabling.", name);
3536 return PRESET_ENABLE;
3537
3538 case PRESET_ENABLE:
3539 if (instance_name_list && *instance_name_list)
3540 STRV_FOREACH(s, *instance_name_list)
3541 log_debug("Preset files say enable %s.", *s);
3542 else
3543 log_debug("Preset files say enable %s.", name);
3544 return PRESET_ENABLE;
3545
3546 case PRESET_DISABLE:
3547 log_debug("Preset files say disable %s.", name);
3548 return PRESET_DISABLE;
3549
3550 case PRESET_IGNORE:
3551 log_debug("Preset files say ignore %s.", name);
3552 return PRESET_IGNORE;
3553
3554 default:
3555 assert_not_reached();
3556 }
3557}
3558
3559PresetAction unit_file_query_preset(RuntimeScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) {
3560 _cleanup_(unit_file_presets_done) UnitFilePresets tmp = {};
3561 int r;
3562
3563 if (!cached)
3564 cached = &tmp;
3565 if (!cached->initialized) {
3566 r = read_presets(scope, root_dir, cached);
3567 if (r < 0)
3568 return r;
3569 }
3570
3571 return query_presets(name, cached, NULL);
3572}
3573
3574static int execute_preset(
3575 UnitFileFlags file_flags,
3576 InstallContext *plus,
3577 InstallContext *minus,
3578 const LookupPaths *lp,
3579 const char *config_path,
3580 char * const *files,
3581 UnitFilePresetMode mode,
3582 InstallChange **changes,
3583 size_t *n_changes) {
3584
3585 int r;
3586
3587 assert(plus);
3588 assert(minus);
3589 assert(lp);
3590 assert(config_path);
3591
3592 if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
3593 _cleanup_set_free_ Set *remove_symlinks_to = NULL;
3594
3595 r = install_context_mark_for_removal(minus, lp, &remove_symlinks_to, config_path, changes, n_changes);
3596 if (r < 0)
3597 return r;
3598
3599 r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, false, changes, n_changes);
3600 } else
3601 r = 0;
3602
3603 if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
3604 int q;
3605
3606 /* Returns number of symlinks that where supposed to be installed. */
3607 q = install_context_apply(plus, lp,
3608 file_flags | UNIT_FILE_IGNORE_AUXILIARY_FAILURE,
3609 config_path,
3610 SEARCH_LOAD, changes, n_changes);
3611 if (r >= 0) {
3612 if (q < 0)
3613 r = q;
3614 else
3615 r += q;
3616 }
3617 }
3618
3619 return r;
3620}
3621
3622static int preset_prepare_one(
3623 RuntimeScope scope,
3624 InstallContext *plus,
3625 InstallContext *minus,
3626 LookupPaths *lp,
3627 const char *name,
3628 const UnitFilePresets *presets,
3629 InstallChange **changes,
3630 size_t *n_changes) {
3631
3632 _cleanup_(install_context_done) InstallContext tmp = { .scope = scope };
3633 _cleanup_strv_free_ char **instance_name_list = NULL;
3634 InstallInfo *info;
3635 int r;
3636
3637 if (install_info_find(plus, name) || install_info_find(minus, name))
3638 return 0;
3639
3640 r = install_info_discover(&tmp, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
3641 &info, changes, n_changes);
3642 if (r < 0)
3643 return r;
3644
3645 if (!streq(name, info->name)) {
3646 log_debug("Skipping %s because it is an alias for %s.", name, info->name);
3647 return 0;
3648 }
3649
3650 r = query_presets(name, presets, &instance_name_list);
3651 if (r < 0)
3652 return r;
3653
3654 if (r == PRESET_ENABLE) {
3655 if (instance_name_list)
3656 STRV_FOREACH(s, instance_name_list) {
3657 r = install_info_discover_and_check(plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3658 &info, changes, n_changes);
3659 if (r < 0)
3660 return r;
3661 }
3662 else {
3663 r = install_info_discover_and_check(plus, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
3664 &info, changes, n_changes);
3665 if (r < 0)
3666 return r;
3667 }
3668
3669 } else if (r == PRESET_DISABLE)
3670 r = install_info_discover(minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
3671 &info, changes, n_changes);
3672
3673 return r;
3674}
3675
3676int unit_file_preset(
3677 RuntimeScope scope,
3678 UnitFileFlags file_flags,
3679 const char *root_dir,
3680 char * const *names,
3681 UnitFilePresetMode mode,
3682 InstallChange **changes,
3683 size_t *n_changes) {
3684
3685 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
3686 _cleanup_(lookup_paths_done) LookupPaths lp = {};
3687 _cleanup_(unit_file_presets_done) UnitFilePresets presets = {};
3688 const char *config_path;
3689 int r;
3690
3691 assert(scope >= 0);
3692 assert(scope < _RUNTIME_SCOPE_MAX);
3693 assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
3694
3695 r = lookup_paths_init(&lp, scope, 0, root_dir);
3696 if (r < 0)
3697 return r;
3698
3699 config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
3700 if (!config_path)
3701 return -ENXIO;
3702
3703 r = read_presets(scope, root_dir, &presets);
3704 if (r < 0)
3705 return r;
3706
3707 STRV_FOREACH(name, names) {
3708 r = preset_prepare_one(scope, &plus, &minus, &lp, *name, &presets, changes, n_changes);
3709 if (r < 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r))
3710 return r;
3711 }
3712
3713 return execute_preset(file_flags, &plus, &minus, &lp, config_path, names, mode, changes, n_changes);
3714}
3715
3716int unit_file_preset_all(
3717 RuntimeScope scope,
3718 UnitFileFlags file_flags,
3719 const char *root_dir,
3720 UnitFilePresetMode mode,
3721 InstallChange **changes,
3722 size_t *n_changes) {
3723
3724 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
3725 _cleanup_(lookup_paths_done) LookupPaths lp = {};
3726 _cleanup_(unit_file_presets_done) UnitFilePresets presets = {};
3727 const char *config_path = NULL;
3728 int r;
3729
3730 assert(scope >= 0);
3731 assert(scope < _RUNTIME_SCOPE_MAX);
3732 assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
3733
3734 r = lookup_paths_init(&lp, scope, 0, root_dir);
3735 if (r < 0)
3736 return r;
3737
3738 config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config;
3739 if (!config_path)
3740 return -ENXIO;
3741
3742 r = read_presets(scope, root_dir, &presets);
3743 if (r < 0)
3744 return r;
3745
3746 STRV_FOREACH(i, lp.search_path) {
3747 _cleanup_closedir_ DIR *d = NULL;
3748
3749 d = opendir(*i);
3750 if (!d) {
3751 if (errno == ENOENT)
3752 continue;
3753
3754 return log_debug_errno(errno, "Failed to opendir %s: %m", *i);
3755 }
3756
3757 FOREACH_DIRENT(de, d,
3758 return log_debug_errno(errno, "Failed to read directory %s: %m", *i)) {
3759
3760 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
3761 continue;
3762
3763 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
3764 continue;
3765
3766 r = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes);
3767 if (r < 0 && !ERRNO_IS_NEG_UNIT_ISSUE(r))
3768 return r;
3769 }
3770 }
3771
3772 return execute_preset(file_flags, &plus, &minus, &lp, config_path, NULL, mode, changes, n_changes);
3773}
3774
3775static UnitFileList* unit_file_list_free(UnitFileList *f) {
3776 if (!f)
3777 return NULL;
3778
3779 free(f->path);
3780 return mfree(f);
3781}
3782
3783DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free);
3784
3785DEFINE_PRIVATE_HASH_OPS_FULL(unit_file_list_hash_ops_free_free,
3786 char, string_hash_func, string_compare_func, free,
3787 UnitFileList, unit_file_list_free);
3788
3789int unit_file_get_list(
3790 RuntimeScope scope,
3791 const char *root_dir,
3792 char * const *states,
3793 char * const *patterns,
3794 Hashmap **ret) {
3795
3796 _cleanup_(lookup_paths_done) LookupPaths lp = {};
3797 _cleanup_hashmap_free_ Hashmap *h = NULL;
3798 int r;
3799
3800 assert(scope >= 0);
3801 assert(scope < _RUNTIME_SCOPE_MAX);
3802 assert(ret);
3803
3804 r = lookup_paths_init(&lp, scope, 0, root_dir);
3805 if (r < 0)
3806 return r;
3807
3808 STRV_FOREACH(dirname, lp.search_path) {
3809 _cleanup_closedir_ DIR *d = NULL;
3810
3811 d = opendir(*dirname);
3812 if (!d) {
3813 if (errno == ENOENT)
3814 continue;
3815 if (IN_SET(errno, ENOTDIR, EACCES)) {
3816 log_debug_errno(errno, "Failed to open \"%s\": %m", *dirname);
3817 continue;
3818 }
3819
3820 return -errno;
3821 }
3822
3823 FOREACH_DIRENT(de, d, return -errno) {
3824 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
3825 continue;
3826
3827 if (hashmap_contains(h, de->d_name))
3828 continue;
3829
3830 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
3831 continue;
3832
3833 if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
3834 continue;
3835
3836 UnitFileState state;
3837
3838 r = unit_file_lookup_state(scope, &lp, de->d_name, &state);
3839 if (r < 0)
3840 state = UNIT_FILE_BAD;
3841
3842 if (!strv_isempty(states) &&
3843 !strv_contains(states, unit_file_state_to_string(state)))
3844 continue;
3845
3846 _cleanup_(unit_file_list_freep) UnitFileList *f = new(UnitFileList, 1);
3847 if (!f)
3848 return -ENOMEM;
3849
3850 *f = (UnitFileList) {
3851 .path = path_make_absolute(de->d_name, *dirname),
3852 .state = state,
3853 };
3854 if (!f->path)
3855 return -ENOMEM;
3856
3857 _cleanup_free_ char *unit_name = strdup(de->d_name);
3858 if (!unit_name)
3859 return -ENOMEM;
3860
3861 r = hashmap_ensure_put(&h, &unit_file_list_hash_ops_free_free, unit_name, f);
3862 if (r < 0)
3863 return r;
3864 assert(r > 0);
3865
3866 TAKE_PTR(unit_name);
3867 TAKE_PTR(f);
3868 }
3869 }
3870
3871 *ret = TAKE_PTR(h);
3872 return 0;
3873}
3874
3875static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
3876 [UNIT_FILE_ENABLED] = "enabled",
3877 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
3878 [UNIT_FILE_LINKED] = "linked",
3879 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
3880 [UNIT_FILE_ALIAS] = "alias",
3881 [UNIT_FILE_MASKED] = "masked",
3882 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
3883 [UNIT_FILE_STATIC] = "static",
3884 [UNIT_FILE_DISABLED] = "disabled",
3885 [UNIT_FILE_INDIRECT] = "indirect",
3886 [UNIT_FILE_GENERATED] = "generated",
3887 [UNIT_FILE_TRANSIENT] = "transient",
3888 [UNIT_FILE_BAD] = "bad",
3889};
3890
3891DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
3892
3893static const char* const install_change_type_table[_INSTALL_CHANGE_TYPE_MAX] = {
3894 [INSTALL_CHANGE_SYMLINK] = "symlink",
3895 [INSTALL_CHANGE_UNLINK] = "unlink",
3896 [INSTALL_CHANGE_IS_MASKED] = "masked",
3897 [INSTALL_CHANGE_IS_MASKED_GENERATOR] = "masked by generator",
3898 [INSTALL_CHANGE_IS_DANGLING] = "dangling",
3899 [INSTALL_CHANGE_DESTINATION_NOT_PRESENT] = "destination not present",
3900 [INSTALL_CHANGE_AUXILIARY_FAILED] = "auxiliary unit failed",
3901};
3902
3903DEFINE_STRING_TABLE_LOOKUP(install_change_type, InstallChangeType);
3904
3905static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MODE_MAX] = {
3906 [UNIT_FILE_PRESET_FULL] = "full",
3907 [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
3908 [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
3909};
3910
3911DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);