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