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