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