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