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