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