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