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