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