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