]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/install.c
install: only consider names in Alias= as "enabling"
[thirdparty/systemd.git] / src / shared / install.c
CommitLineData
83096483
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2011 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
83096483
LP
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty <of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 14 Lesser General Public License for more details.
83096483 15
5430f7f2 16 You should have received a copy of the GNU Lesser General Public License
83096483
LP
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
a8fbdf54 20#include <dirent.h>
83096483
LP
21#include <errno.h>
22#include <fcntl.h>
83096483 23#include <fnmatch.h>
a8fbdf54
TA
24#include <limits.h>
25#include <stddef.h>
26#include <stdio.h>
27#include <stdlib.h>
07630cea 28#include <string.h>
a8fbdf54 29#include <sys/stat.h>
07630cea 30#include <unistd.h>
83096483 31
b5efdb8a 32#include "alloc-util.h"
07630cea
LP
33#include "conf-files.h"
34#include "conf-parser.h"
a0956174 35#include "dirent-util.h"
a8fbdf54 36#include "extract-word.h"
a0956174 37#include "fd-util.h"
0ec0deaa 38#include "fileio.h"
f4f15635 39#include "fs-util.h"
83096483 40#include "hashmap.h"
07630cea 41#include "install-printf.h"
a0956174 42#include "install.h"
a760db24 43#include "locale-util.h"
a8fbdf54
TA
44#include "log.h"
45#include "macro.h"
07630cea 46#include "mkdir.h"
83096483 47#include "path-lookup.h"
07630cea 48#include "path-util.h"
344ca755 49#include "rm-rf.h"
07630cea
LP
50#include "set.h"
51#include "special.h"
8fcde012 52#include "stat-util.h"
8b43440b 53#include "string-table.h"
07630cea 54#include "string-util.h"
83096483
LP
55#include "strv.h"
56#include "unit-name.h"
83096483 57
0ec0deaa
LP
58#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
59
60typedef enum SearchFlags {
61 SEARCH_LOAD = 1,
62 SEARCH_FOLLOW_CONFIG_SYMLINKS = 2,
63} SearchFlags;
64
83096483 65typedef struct {
0ec0deaa
LP
66 OrderedHashmap *will_process;
67 OrderedHashmap *have_processed;
83096483
LP
68} InstallContext;
69
8965d9f8
AC
70typedef enum {
71 PRESET_UNKNOWN,
72 PRESET_ENABLE,
73 PRESET_DISABLE,
74} PresetAction;
75
76typedef struct {
77 char *pattern;
78 PresetAction action;
79} PresetRule;
80
81typedef struct {
82 PresetRule *rules;
83 size_t n_rules;
84} Presets;
85
7a7ec2bf
ZJS
86static inline bool unit_file_install_info_has_rules(UnitFileInstallInfo *i) {
87 assert(i);
88
89 return !strv_isempty(i->aliases) ||
90 !strv_isempty(i->wanted_by) ||
91 !strv_isempty(i->required_by);
92}
93
94static inline bool unit_file_install_info_has_also(UnitFileInstallInfo *i) {
95 assert(i);
96
97 return !strv_isempty(i->also);
98}
99
8965d9f8
AC
100static inline void presets_freep(Presets *p) {
101 size_t i;
102
103 if (!p)
104 return;
105
106 for (i = 0; i < p->n_rules; i++)
107 free(p->rules[i].pattern);
108
109 free(p->rules);
110 p->n_rules = 0;
111}
112
2c52204c
LP
113static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret);
114
8a993b61
ZJS
115bool unit_type_may_alias(UnitType type) {
116 return IN_SET(type,
117 UNIT_SERVICE,
118 UNIT_SOCKET,
119 UNIT_TARGET,
120 UNIT_DEVICE,
121 UNIT_TIMER,
122 UNIT_PATH);
123}
124
ce99c68a
ZJS
125bool unit_type_may_template(UnitType type) {
126 return IN_SET(type,
127 UNIT_SERVICE,
128 UNIT_SOCKET,
129 UNIT_TARGET,
130 UNIT_TIMER,
131 UNIT_PATH);
132}
133
64f9280e
ZJS
134static const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = {
135 [UNIT_FILE_TYPE_REGULAR] = "regular",
136 [UNIT_FILE_TYPE_SYMLINK] = "symlink",
137 [UNIT_FILE_TYPE_MASKED] = "masked",
138};
139
140DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType);
141
32c0ed7b 142static int in_search_path(const LookupPaths *p, const char *path) {
8f294b45 143 _cleanup_free_ char *parent = NULL;
0ec0deaa 144 char **i;
8f294b45
LP
145
146 assert(path);
147
5f311f8c
LP
148 parent = dirname_malloc(path);
149 if (!parent)
150 return -ENOMEM;
8f294b45 151
32c0ed7b 152 STRV_FOREACH(i, p->search_path)
0ec0deaa
LP
153 if (path_equal(parent, *i))
154 return true;
155
156 return false;
8f294b45
LP
157}
158
401017e0 159static const char* skip_root(const LookupPaths *p, const char *path) {
8f9364f9 160 char *e;
401017e0 161
8f9364f9
LP
162 assert(p);
163 assert(path);
401017e0 164
8f9364f9
LP
165 if (!p->root_dir)
166 return path;
401017e0 167
8f9364f9
LP
168 e = path_startswith(path, p->root_dir);
169 if (!e)
170 return NULL;
171
172 /* Make sure the returned path starts with a slash */
173 if (e[0] != '/') {
174 if (e == path || e[-1] != '/')
175 return NULL;
401017e0 176
8f9364f9 177 e--;
401017e0
LP
178 }
179
8f9364f9 180 return e;
401017e0
LP
181}
182
f4dc1e65 183static int path_is_generator(const LookupPaths *p, const char *path) {
f4139308
LP
184 _cleanup_free_ char *parent = NULL;
185
e1c5c2b0 186 assert(p);
f4139308
LP
187 assert(path);
188
189 parent = dirname_malloc(path);
190 if (!parent)
191 return -ENOMEM;
192
24737c29
ZJS
193 return path_equal_ptr(parent, p->generator) ||
194 path_equal_ptr(parent, p->generator_early) ||
195 path_equal_ptr(parent, p->generator_late);
f4139308
LP
196}
197
e4fca67f
LP
198static int path_is_transient(const LookupPaths *p, const char *path) {
199 _cleanup_free_ char *parent = NULL;
200
201 assert(p);
202 assert(path);
203
204 parent = dirname_malloc(path);
205 if (!parent)
206 return -ENOMEM;
207
24737c29 208 return path_equal_ptr(parent, p->transient);
e4fca67f
LP
209}
210
344ca755 211static int path_is_control(const LookupPaths *p, const char *path) {
e1c5c2b0 212 _cleanup_free_ char *parent = NULL;
0ec0deaa 213
e1c5c2b0 214 assert(p);
0ec0deaa
LP
215 assert(path);
216
344ca755
LP
217 parent = dirname_malloc(path);
218 if (!parent)
219 return -ENOMEM;
0ec0deaa 220
24737c29
ZJS
221 return path_equal_ptr(parent, p->persistent_control) ||
222 path_equal_ptr(parent, p->runtime_control);
344ca755
LP
223}
224
dfead90d 225static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) {
344ca755
LP
226 _cleanup_free_ char *parent = NULL;
227
228 assert(p);
229 assert(path);
230
d7604756
ZJS
231 /* Note that we do *not* have generic checks for /etc or /run in place, since with
232 * them we couldn't discern configuration from transient or generated units */
0ec0deaa 233
dfead90d
LP
234 if (check_parent) {
235 parent = dirname_malloc(path);
236 if (!parent)
237 return -ENOMEM;
238
239 path = parent;
240 }
0ec0deaa 241
dfead90d
LP
242 return path_equal_ptr(path, p->persistent_config) ||
243 path_equal_ptr(path, p->runtime_config);
0ec0deaa
LP
244}
245
dfead90d 246static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) {
385eb996 247 _cleanup_free_ char *parent = NULL;
401017e0 248 const char *rpath;
385eb996
LP
249
250 assert(p);
251 assert(path);
252
d7604756
ZJS
253 /* Everything in /run is considered runtime. On top of that we also add
254 * explicit checks for the various runtime directories, as safety net. */
344ca755 255
401017e0
LP
256 rpath = skip_root(p, path);
257 if (rpath && path_startswith(rpath, "/run"))
385eb996
LP
258 return true;
259
dfead90d
LP
260 if (check_parent) {
261 parent = dirname_malloc(path);
262 if (!parent)
263 return -ENOMEM;
385eb996 264
dfead90d
LP
265 path = parent;
266 }
267
268 return path_equal_ptr(path, p->runtime_config) ||
269 path_equal_ptr(path, p->generator) ||
270 path_equal_ptr(path, p->generator_early) ||
271 path_equal_ptr(path, p->generator_late) ||
272 path_equal_ptr(path, p->transient) ||
273 path_equal_ptr(path, p->runtime_control);
344ca755
LP
274}
275
276static int path_is_vendor(const LookupPaths *p, const char *path) {
277 const char *rpath;
278
279 assert(p);
280 assert(path);
281
282 rpath = skip_root(p, path);
283 if (!rpath)
284 return 0;
285
286 if (path_startswith(rpath, "/usr"))
287 return true;
288
289#ifdef HAVE_SPLIT_USR
290 if (path_startswith(rpath, "/lib"))
291 return true;
292#endif
293
294 return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
385eb996
LP
295}
296
0ec0deaa
LP
297int unit_file_changes_add(
298 UnitFileChange **changes,
299 unsigned *n_changes,
300 UnitFileChangeType type,
301 const char *path,
302 const char *source) {
303
12bf0ae4 304 _cleanup_free_ char *p = NULL, *s = NULL;
0ec0deaa 305 UnitFileChange *c;
0ec0deaa
LP
306
307 assert(path);
308 assert(!changes == !n_changes);
309
310 if (!changes)
311 return 0;
312
313 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
314 if (!c)
315 return -ENOMEM;
0ec0deaa 316 *changes = c;
0ec0deaa 317
12bf0ae4
ZJS
318 p = strdup(path);
319 if (source)
320 s = strdup(source);
0ec0deaa 321
12bf0ae4
ZJS
322 if (!p || (source && !s))
323 return -ENOMEM;
0ec0deaa 324
12bf0ae4
ZJS
325 path_kill_slashes(p);
326 if (s)
327 path_kill_slashes(s);
0ec0deaa 328
12bf0ae4
ZJS
329 c[*n_changes] = (UnitFileChange) { type, p, s };
330 p = s = NULL;
331 (*n_changes) ++;
0ec0deaa
LP
332 return 0;
333}
334
335void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
336 unsigned i;
337
338 assert(changes || n_changes == 0);
339
0ec0deaa
LP
340 for (i = 0; i < n_changes; i++) {
341 free(changes[i].path);
342 free(changes[i].source);
343 }
344
345 free(changes);
346}
347
af3d8113
ZJS
348void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) {
349 unsigned i;
350 bool logged = false;
351
352 assert(changes || n_changes == 0);
353 /* If verb is not specified, errors are not allowed! */
354 assert(verb || r >= 0);
355
356 for (i = 0; i < n_changes; i++) {
357 assert(verb || changes[i].type >= 0);
358
359 switch(changes[i].type) {
360 case UNIT_FILE_SYMLINK:
361 if (!quiet)
a760db24
ZJS
362 log_info("Created symlink %s %s %s.",
363 changes[i].path,
323b7dc9 364 special_glyph(ARROW),
a760db24 365 changes[i].source);
af3d8113
ZJS
366 break;
367 case UNIT_FILE_UNLINK:
368 if (!quiet)
369 log_info("Removed %s.", changes[i].path);
370 break;
371 case UNIT_FILE_IS_MASKED:
372 if (!quiet)
373 log_info("Unit %s is masked, ignoring.", changes[i].path);
374 break;
893275df
ZJS
375 case UNIT_FILE_IS_DANGLING:
376 if (!quiet)
377 log_info("Unit %s is an alias to a unit that is not present, ignoring.",
378 changes[i].path);
379 break;
af3d8113
ZJS
380 case -EEXIST:
381 if (changes[i].source)
382 log_error_errno(changes[i].type,
383 "Failed to %s unit, file %s already exists and is a symlink to %s.",
384 verb, changes[i].path, changes[i].source);
385 else
386 log_error_errno(changes[i].type,
387 "Failed to %s unit, file %s already exists.",
388 verb, changes[i].path);
389 logged = true;
390 break;
391 case -ERFKILL:
392 log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.",
393 verb, changes[i].path);
394 logged = true;
395 break;
396 case -EADDRNOTAVAIL:
397 log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.",
398 verb, changes[i].path);
399 logged = true;
400 break;
401 case -ELOOP:
402 log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s",
403 verb, changes[i].path);
404 logged = true;
405 break;
637d6e5b
LP
406
407 case -ENOENT:
408 log_error_errno(changes[i].type, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path);
409 logged = true;
410 break;
411
af3d8113
ZJS
412 default:
413 assert(changes[i].type < 0);
414 log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.",
415 verb, changes[i].path);
416 logged = true;
417 }
418 }
419
420 if (r < 0 && !logged)
421 log_error_errno(r, "Failed to %s: %m.", verb);
422}
423
25ea9277
ZJS
424/**
425 * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
426 * wc should be the full path in the host file system.
427 */
428static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
429 assert(path_is_absolute(wd));
430
431 /* This will give incorrect results if the paths are relative and go outside
432 * of the chroot. False negatives are possible. */
433
ae9efab7
ZJS
434 if (!root)
435 root = "/";
436
25ea9277
ZJS
437 a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
438 b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
e3f791a2 439 return path_equal_or_files_same(a, b, 0);
25ea9277
ZJS
440}
441
0ec0deaa 442static int create_symlink(
60bec8e4 443 const LookupPaths *paths,
0ec0deaa
LP
444 const char *old_path,
445 const char *new_path,
446 bool force,
447 UnitFileChange **changes,
448 unsigned *n_changes) {
449
25ea9277 450 _cleanup_free_ char *dest = NULL, *dirname = NULL;
60bec8e4 451 const char *rp;
0ec0deaa
LP
452 int r;
453
454 assert(old_path);
455 assert(new_path);
456
60bec8e4
ZJS
457 rp = skip_root(paths, old_path);
458 if (rp)
459 old_path = rp;
460
0ec0deaa
LP
461 /* Actually create a symlink, and remember that we did. Is
462 * smart enough to check if there's already a valid symlink in
85158303
ZJS
463 * place.
464 *
465 * Returns 1 if a symlink was created or already exists and points to
466 * the right place, or negative on error.
467 */
0ec0deaa
LP
468
469 mkdir_parents_label(new_path, 0755);
470
471 if (symlink(old_path, new_path) >= 0) {
472 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
3de15214 473 return 1;
0ec0deaa
LP
474 }
475
af3d8113
ZJS
476 if (errno != EEXIST) {
477 unit_file_changes_add(changes, n_changes, -errno, new_path, NULL);
0ec0deaa 478 return -errno;
af3d8113 479 }
0ec0deaa
LP
480
481 r = readlink_malloc(new_path, &dest);
7d782f26
ZJS
482 if (r < 0) {
483 /* translate EINVAL (non-symlink exists) to EEXIST */
484 if (r == -EINVAL)
485 r = -EEXIST;
486
487 unit_file_changes_add(changes, n_changes, r, new_path, NULL);
0ec0deaa 488 return r;
7d782f26 489 }
0ec0deaa 490
25ea9277
ZJS
491 dirname = dirname_malloc(new_path);
492 if (!dirname)
493 return -ENOMEM;
494
495 if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path))
85158303 496 return 1;
0ec0deaa 497
af3d8113
ZJS
498 if (!force) {
499 unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest);
0ec0deaa 500 return -EEXIST;
af3d8113 501 }
0ec0deaa
LP
502
503 r = symlink_atomic(old_path, new_path);
7d782f26
ZJS
504 if (r < 0) {
505 unit_file_changes_add(changes, n_changes, r, new_path, NULL);
0ec0deaa 506 return r;
7d782f26 507 }
0ec0deaa
LP
508
509 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
510 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
511
3de15214 512 return 1;
0ec0deaa
LP
513}
514
83096483
LP
515static int mark_symlink_for_removal(
516 Set **remove_symlinks_to,
517 const char *p) {
518
519 char *n;
520 int r;
521
522 assert(p);
523
d5099efc 524 r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops);
83096483
LP
525 if (r < 0)
526 return r;
527
528 n = strdup(p);
529 if (!n)
530 return -ENOMEM;
531
532 path_kill_slashes(n);
533
ef42202a 534 r = set_consume(*remove_symlinks_to, n);
d25e100b
LP
535 if (r == -EEXIST)
536 return 0;
ef42202a 537 if (r < 0)
d25e100b 538 return r;
83096483 539
0ec0deaa 540 return 1;
83096483
LP
541}
542
543static int remove_marked_symlinks_fd(
544 Set *remove_symlinks_to,
545 int fd,
546 const char *path,
547 const char *config_path,
401017e0 548 const LookupPaths *lp,
3b3557c4 549 bool dry_run,
0ec0deaa 550 bool *restart,
83096483 551 UnitFileChange **changes,
0ec0deaa 552 unsigned *n_changes) {
83096483 553
7fd1b19b 554 _cleanup_closedir_ DIR *d = NULL;
d25e100b 555 struct dirent *de;
bcafe923 556 int r = 0;
83096483
LP
557
558 assert(remove_symlinks_to);
559 assert(fd >= 0);
560 assert(path);
561 assert(config_path);
401017e0 562 assert(lp);
0ec0deaa 563 assert(restart);
83096483
LP
564
565 d = fdopendir(fd);
566 if (!d) {
03e334a1 567 safe_close(fd);
83096483
LP
568 return -errno;
569 }
570
571 rewinddir(d);
572
d25e100b 573 FOREACH_DIRENT(de, d, return -errno) {
83096483
LP
574
575 dirent_ensure_type(d, de);
576
577 if (de->d_type == DT_DIR) {
7fd1b19b 578 _cleanup_free_ char *p = NULL;
d25e100b 579 int nfd, q;
83096483
LP
580
581 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
582 if (nfd < 0) {
583 if (errno == ENOENT)
584 continue;
585
586 if (r == 0)
587 r = -errno;
588 continue;
589 }
590
591 p = path_make_absolute(de->d_name, path);
592 if (!p) {
03e334a1 593 safe_close(nfd);
d9e5e694 594 return -ENOMEM;
83096483
LP
595 }
596
597 /* This will close nfd, regardless whether it succeeds or not */
3b3557c4 598 q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, dry_run, restart, changes, n_changes);
bcafe923 599 if (q < 0 && r == 0)
83096483
LP
600 r = q;
601
602 } else if (de->d_type == DT_LNK) {
7fd1b19b 603 _cleanup_free_ char *p = NULL, *dest = NULL;
401017e0 604 const char *rp;
83096483 605 bool found;
0ec0deaa 606 int q;
83096483 607
7410616c 608 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
bcafe923
LP
609 continue;
610
83096483 611 p = path_make_absolute(de->d_name, path);
d9e5e694
ZJS
612 if (!p)
613 return -ENOMEM;
af3d8113 614 path_kill_slashes(p);
83096483 615
0ec0deaa 616 q = readlink_malloc(p, &dest);
401017e0
LP
617 if (q == -ENOENT)
618 continue;
83096483 619 if (q < 0) {
83096483
LP
620 if (r == 0)
621 r = q;
622 continue;
623 }
624
401017e0
LP
625 /* We remove all links pointing to a file or path that is marked, as well as all files sharing
626 * the same name as a file that is marked. */
0ec0deaa 627
596fc263 628 found = set_contains(remove_symlinks_to, dest) ||
0ec0deaa
LP
629 set_contains(remove_symlinks_to, basename(dest)) ||
630 set_contains(remove_symlinks_to, de->d_name);
83096483 631
1dacfd2a
LP
632 if (!found)
633 continue;
83096483 634
3b3557c4
JS
635 if (!dry_run) {
636 if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
637 if (r == 0)
638 r = -errno;
639 unit_file_changes_add(changes, n_changes, -errno, p, NULL);
640 continue;
641 }
bcafe923 642
3b3557c4
JS
643 (void) rmdir_parents(p, config_path);
644 }
83096483 645
0ec0deaa 646 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
1dacfd2a 647
12bf0ae4
ZJS
648 /* Now, remember the full path (but with the root prefix removed) of
649 * the symlink we just removed, and remove any symlinks to it, too. */
401017e0
LP
650
651 rp = skip_root(lp, p);
652 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
0ec0deaa
LP
653 if (q < 0)
654 return q;
3b3557c4 655 if (q > 0 && !dry_run)
0ec0deaa 656 *restart = true;
83096483
LP
657 }
658 }
659
83096483
LP
660 return r;
661}
662
663static int remove_marked_symlinks(
664 Set *remove_symlinks_to,
665 const char *config_path,
401017e0 666 const LookupPaths *lp,
3b3557c4 667 bool dry_run,
83096483 668 UnitFileChange **changes,
0ec0deaa 669 unsigned *n_changes) {
83096483 670
da39f6a6 671 _cleanup_close_ int fd = -1;
0ec0deaa 672 bool restart;
d25e100b 673 int r = 0;
83096483
LP
674
675 assert(config_path);
401017e0 676 assert(lp);
83096483
LP
677
678 if (set_size(remove_symlinks_to) <= 0)
679 return 0;
680
67852d08 681 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
83096483 682 if (fd < 0)
32d9493e 683 return errno == ENOENT ? 0 : -errno;
83096483
LP
684
685 do {
686 int q, cfd;
0ec0deaa 687 restart = false;
83096483 688
ead34950 689 cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
d25e100b
LP
690 if (cfd < 0)
691 return -errno;
83096483
LP
692
693 /* This takes possession of cfd and closes it */
3b3557c4 694 q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, dry_run, &restart, changes, n_changes);
83096483
LP
695 if (r == 0)
696 r = q;
0ec0deaa 697 } while (restart);
83096483 698
83096483
LP
699 return r;
700}
701
5cd8ae31
ZJS
702static bool is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *name) {
703 int r;
704
705 if (streq(name, i->name))
706 return true;
707
708 if (strv_contains(i->aliases, name))
709 return true;
710
711 /* Look for template symlink matching DefaultInstance */
712 if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
713 _cleanup_free_ char *s = NULL;
714
715 r = unit_name_replace_instance(i->name, i->default_instance, &s);
716 if (r < 0) {
717 if (r != -EINVAL)
718 return r;
719
720 } else if (streq(name, s))
721 return true;
722 }
723
724 return false;
725}
726
83096483 727static int find_symlinks_fd(
0ec0deaa 728 const char *root_dir,
5cd8ae31 729 UnitFileInstallInfo *i,
83096483
LP
730 int fd,
731 const char *path,
732 const char *config_path,
733 bool *same_name_link) {
734
7fd1b19b 735 _cleanup_closedir_ DIR *d = NULL;
d25e100b
LP
736 struct dirent *de;
737 int r = 0;
83096483 738
5cd8ae31 739 assert(i);
83096483
LP
740 assert(fd >= 0);
741 assert(path);
742 assert(config_path);
743 assert(same_name_link);
744
745 d = fdopendir(fd);
746 if (!d) {
03e334a1 747 safe_close(fd);
83096483
LP
748 return -errno;
749 }
750
d25e100b 751 FOREACH_DIRENT(de, d, return -errno) {
83096483
LP
752
753 dirent_ensure_type(d, de);
754
755 if (de->d_type == DT_DIR) {
7fd1b19b 756 _cleanup_free_ char *p = NULL;
d25e100b 757 int nfd, q;
83096483
LP
758
759 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
760 if (nfd < 0) {
761 if (errno == ENOENT)
762 continue;
763
764 if (r == 0)
765 r = -errno;
766 continue;
767 }
768
769 p = path_make_absolute(de->d_name, path);
770 if (!p) {
03e334a1 771 safe_close(nfd);
ea55addc 772 return -ENOMEM;
83096483
LP
773 }
774
775 /* This will close nfd, regardless whether it succeeds or not */
5cd8ae31
ZJS
776 q = find_symlinks_fd(root_dir, i, nfd,
777 p, config_path, same_name_link);
ea55addc
ZJS
778 if (q > 0)
779 return 1;
83096483
LP
780 if (r == 0)
781 r = q;
782
783 } else if (de->d_type == DT_LNK) {
7fd1b19b 784 _cleanup_free_ char *p = NULL, *dest = NULL;
83096483
LP
785 bool found_path, found_dest, b = false;
786 int q;
787
788 /* Acquire symlink name */
789 p = path_make_absolute(de->d_name, path);
ea55addc
ZJS
790 if (!p)
791 return -ENOMEM;
83096483
LP
792
793 /* Acquire symlink destination */
0ec0deaa 794 q = readlink_malloc(p, &dest);
d25e100b
LP
795 if (q == -ENOENT)
796 continue;
83096483 797 if (q < 0) {
83096483
LP
798 if (r == 0)
799 r = q;
800 continue;
801 }
802
0ec0deaa
LP
803 /* Make absolute */
804 if (!path_is_absolute(dest)) {
805 char *x;
806
807 x = prefix_root(root_dir, dest);
808 if (!x)
809 return -ENOMEM;
810
811 free(dest);
812 dest = x;
813 }
814
83096483
LP
815 /* Check if the symlink itself matches what we
816 * are looking for */
5cd8ae31
ZJS
817 if (path_is_absolute(i->name))
818 found_path = path_equal(p, i->name);
83096483 819 else
5cd8ae31 820 found_path = streq(de->d_name, i->name);
83096483
LP
821
822 /* Check if what the symlink points to
823 * matches what we are looking for */
5cd8ae31
ZJS
824 if (path_is_absolute(i->name))
825 found_dest = path_equal(dest, i->name);
83096483 826 else
5cd8ae31 827 found_dest = streq(basename(dest), i->name);
83096483 828
83096483 829 if (found_path && found_dest) {
7fd1b19b 830 _cleanup_free_ char *t = NULL;
83096483
LP
831
832 /* Filter out same name links in the main
833 * config path */
5cd8ae31 834 t = path_make_absolute(i->name, config_path);
ea55addc
ZJS
835 if (!t)
836 return -ENOMEM;
83096483
LP
837
838 b = path_equal(t, p);
83096483
LP
839 }
840
83096483
LP
841 if (b)
842 *same_name_link = true;
5cd8ae31
ZJS
843 else if (found_path || found_dest) {
844 /* Check if symlink name is in the set of names used by [Install] */
845 q = is_symlink_with_known_name(i, de->d_name);
846 log_info("is_symlink_with_known_name(%s, %s) → %d", i->name, de->d_name, q);
847 if (q < 0)
848 return q;
849 if (q > 0)
850 return 1;
851 }
83096483
LP
852 }
853 }
d25e100b
LP
854
855 return r;
83096483
LP
856}
857
858static int find_symlinks(
0ec0deaa 859 const char *root_dir,
5cd8ae31 860 UnitFileInstallInfo *i,
83096483
LP
861 const char *config_path,
862 bool *same_name_link) {
863
864 int fd;
865
5cd8ae31 866 assert(i);
83096483
LP
867 assert(config_path);
868 assert(same_name_link);
869
1cd1dab9 870 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
d5891fda 871 if (fd < 0) {
a1feacf7 872 if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
d5891fda 873 return 0;
83096483 874 return -errno;
d5891fda 875 }
83096483
LP
876
877 /* This takes possession of fd and closes it */
5cd8ae31
ZJS
878 return find_symlinks_fd(root_dir, i, fd,
879 config_path, config_path, same_name_link);
83096483
LP
880}
881
882static int find_symlinks_in_scope(
e1c5c2b0 883 const LookupPaths *paths,
5cd8ae31 884 UnitFileInstallInfo *i,
83096483
LP
885 UnitFileState *state) {
886
dfead90d
LP
887 bool same_name_link_runtime = false, same_name_link_config = false;
888 bool enabled_in_runtime = false, enabled_at_all = false;
889 char **p;
0ec0deaa 890 int r;
83096483 891
e1c5c2b0 892 assert(paths);
5cd8ae31 893 assert(i);
83096483 894
dfead90d
LP
895 STRV_FOREACH(p, paths->search_path) {
896 bool same_name_link = false;
897
5cd8ae31 898 r = find_symlinks(paths->root_dir, i, *p, &same_name_link);
dfead90d
LP
899 if (r < 0)
900 return r;
901 if (r > 0) {
902 /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
903
904 r = path_is_config(paths, *p, false);
905 if (r < 0)
906 return r;
907 if (r > 0) {
908 /* This is the best outcome, let's return it immediately. */
909 *state = UNIT_FILE_ENABLED;
910 return 1;
911 }
912
913 r = path_is_runtime(paths, *p, false);
914 if (r < 0)
915 return r;
916 if (r > 0)
917 enabled_in_runtime = true;
918 else
919 enabled_at_all = true;
920
921 } else if (same_name_link) {
922
923 r = path_is_config(paths, *p, false);
924 if (r < 0)
925 return r;
926 if (r > 0)
927 same_name_link_config = true;
928 else {
929 r = path_is_runtime(paths, *p, false);
930 if (r < 0)
931 return r;
932 if (r > 0)
933 same_name_link_runtime = true;
934 }
935 }
83096483
LP
936 }
937
dfead90d 938 if (enabled_in_runtime) {
0ec0deaa 939 *state = UNIT_FILE_ENABLED_RUNTIME;
dfead90d
LP
940 return 1;
941 }
942
943 /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
944 * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
945 * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
946 * something, and hence are a much stronger concept. */
5cd8ae31 947 if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
dfead90d
LP
948 *state = UNIT_FILE_STATIC;
949 return 1;
83096483
LP
950 }
951
952 /* Hmm, we didn't find it, but maybe we found the same name
953 * link? */
dfead90d 954 if (same_name_link_config) {
0ec0deaa
LP
955 *state = UNIT_FILE_LINKED;
956 return 1;
957 }
83096483
LP
958 if (same_name_link_runtime) {
959 *state = UNIT_FILE_LINKED_RUNTIME;
960 return 1;
83096483
LP
961 }
962
963 return 0;
964}
965
0ec0deaa 966static void install_info_free(UnitFileInstallInfo *i) {
83096483 967
0ec0deaa
LP
968 if (!i)
969 return;
83096483 970
0ec0deaa
LP
971 free(i->name);
972 free(i->path);
973 strv_free(i->aliases);
974 strv_free(i->wanted_by);
975 strv_free(i->required_by);
976 strv_free(i->also);
977 free(i->default_instance);
978 free(i->symlink_target);
979 free(i);
980}
83096483 981
0ec0deaa
LP
982static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) {
983 UnitFileInstallInfo *i;
83096483 984
0ec0deaa
LP
985 if (!m)
986 return NULL;
83096483 987
0ec0deaa
LP
988 while ((i = ordered_hashmap_steal_first(m)))
989 install_info_free(i);
83096483 990
0ec0deaa
LP
991 return ordered_hashmap_free(m);
992}
83096483 993
0ec0deaa
LP
994static void install_context_done(InstallContext *c) {
995 assert(c);
83096483 996
0ec0deaa
LP
997 c->will_process = install_info_hashmap_free(c->will_process);
998 c->have_processed = install_info_hashmap_free(c->have_processed);
999}
83096483 1000
0ec0deaa
LP
1001static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
1002 UnitFileInstallInfo *i;
83096483 1003
0ec0deaa
LP
1004 i = ordered_hashmap_get(c->have_processed, name);
1005 if (i)
1006 return i;
83096483 1007
0ec0deaa 1008 return ordered_hashmap_get(c->will_process, name);
83096483
LP
1009}
1010
af3d8113
ZJS
1011static int install_info_may_process(
1012 UnitFileInstallInfo *i,
1013 const LookupPaths *paths,
1014 UnitFileChange **changes,
1015 unsigned *n_changes) {
76adb5b8
LP
1016 assert(i);
1017 assert(paths);
1018
047d91f9
ZJS
1019 /* Checks whether the loaded unit file is one we should process, or is masked,
1020 * transient or generated and thus not subject to enable/disable operations. */
76adb5b8 1021
af3d8113
ZJS
1022 if (i->type == UNIT_FILE_TYPE_MASKED) {
1023 unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
76ec966f 1024 return -ERFKILL;
af3d8113
ZJS
1025 }
1026 if (path_is_generator(paths, i->path) ||
1027 path_is_transient(paths, i->path)) {
1028 unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
76adb5b8 1029 return -EADDRNOTAVAIL;
af3d8113 1030 }
76adb5b8
LP
1031
1032 return 0;
1033}
1034
ff56349d
ZJS
1035/**
1036 * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
1037 * hashmap, or retrieves the existing one if already present.
19539807
ZJS
1038 *
1039 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
ff56349d 1040 */
83096483
LP
1041static int install_info_add(
1042 InstallContext *c,
1043 const char *name,
0ec0deaa 1044 const char *path,
19539807 1045 bool auxiliary,
0ec0deaa
LP
1046 UnitFileInstallInfo **ret) {
1047
cab6235f 1048 UnitFileInstallInfo *i = NULL;
83096483
LP
1049 int r;
1050
1051 assert(c);
1052 assert(name || path);
1053
1054 if (!name)
2b6bf07d 1055 name = basename(path);
83096483 1056
7410616c 1057 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
83096483
LP
1058 return -EINVAL;
1059
0ec0deaa
LP
1060 i = install_info_find(c, name);
1061 if (i) {
19539807
ZJS
1062 i->auxiliary = i->auxiliary && auxiliary;
1063
0ec0deaa
LP
1064 if (ret)
1065 *ret = i;
83096483 1066 return 0;
0ec0deaa 1067 }
83096483 1068
0ec0deaa 1069 r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
83096483
LP
1070 if (r < 0)
1071 return r;
1072
cab6235f 1073 i = new0(UnitFileInstallInfo, 1);
83096483
LP
1074 if (!i)
1075 return -ENOMEM;
0ec0deaa 1076 i->type = _UNIT_FILE_TYPE_INVALID;
19539807 1077 i->auxiliary = auxiliary;
83096483
LP
1078
1079 i->name = strdup(name);
1080 if (!i->name) {
1081 r = -ENOMEM;
1082 goto fail;
1083 }
1084
1085 if (path) {
1086 i->path = strdup(path);
1087 if (!i->path) {
1088 r = -ENOMEM;
1089 goto fail;
1090 }
1091 }
1092
0ec0deaa 1093 r = ordered_hashmap_put(c->will_process, i->name, i);
83096483
LP
1094 if (r < 0)
1095 goto fail;
1096
0ec0deaa
LP
1097 if (ret)
1098 *ret = i;
1099
19539807 1100 return 1;
83096483
LP
1101
1102fail:
d25e100b 1103 install_info_free(i);
83096483
LP
1104 return r;
1105}
1106
a7724589
ZJS
1107static int config_parse_alias(
1108 const char *unit,
1109 const char *filename,
1110 unsigned line,
1111 const char *section,
1112 unsigned section_line,
1113 const char *lvalue,
1114 int ltype,
1115 const char *rvalue,
1116 void *data,
1117 void *userdata) {
1118
1119 const char *name;
1120 UnitType type;
1121
1122 assert(filename);
1123 assert(lvalue);
1124 assert(rvalue);
1125
1126 name = basename(filename);
1127 type = unit_name_to_type(name);
1128 if (!unit_type_may_alias(type))
1129 return log_syntax(unit, LOG_WARNING, filename, line, 0,
3b8769bd 1130 "Alias= is not allowed for %s units, ignoring.",
a7724589
ZJS
1131 unit_type_to_string(type));
1132
1133 return config_parse_strv(unit, filename, line, section, section_line,
1134 lvalue, ltype, rvalue, data, userdata);
1135}
1136
d54c4993
LP
1137static int config_parse_also(
1138 const char *unit,
1139 const char *filename,
1140 unsigned line,
1141 const char *section,
1142 unsigned section_line,
1143 const char *lvalue,
1144 int ltype,
1145 const char *rvalue,
1146 void *data,
1147 void *userdata) {
83096483 1148
19539807 1149 UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
0ec0deaa
LP
1150 InstallContext *c = data;
1151 int r;
83096483
LP
1152
1153 assert(filename);
1154 assert(lvalue);
1155 assert(rvalue);
1156
0ec0deaa 1157 for (;;) {
a6612e65 1158 _cleanup_free_ char *word = NULL, *printed = NULL;
03da6513 1159
0ec0deaa
LP
1160 r = extract_first_word(&rvalue, &word, NULL, 0);
1161 if (r < 0)
1162 return r;
03da6513
SS
1163 if (r == 0)
1164 break;
83096483 1165
a6612e65 1166 r = install_full_printf(info, word, &printed);
d9e5e694 1167 if (r < 0)
83096483 1168 return r;
aedd4012 1169
a6612e65
ZJS
1170 if (!unit_name_is_valid(printed, UNIT_NAME_ANY))
1171 return -EINVAL;
1172
1173 r = install_info_add(c, printed, NULL, true, &alsoinfo);
d9e5e694 1174 if (r < 0)
83096483 1175 return r;
aedd4012 1176
a6612e65 1177 r = strv_push(&info->also, printed);
aedd4012
JS
1178 if (r < 0)
1179 return r;
0ec0deaa 1180
a6612e65 1181 printed = NULL;
83096483
LP
1182 }
1183
1184 return 0;
1185}
1186
d54c4993
LP
1187static int config_parse_default_instance(
1188 const char *unit,
1189 const char *filename,
1190 unsigned line,
1191 const char *section,
1192 unsigned section_line,
1193 const char *lvalue,
1194 int ltype,
1195 const char *rvalue,
1196 void *data,
1197 void *userdata) {
1198
cab6235f 1199 UnitFileInstallInfo *i = data;
6597fa61 1200 const char *name;
d7604756 1201 _cleanup_free_ char *printed = NULL;
d54c4993
LP
1202 int r;
1203
1204 assert(filename);
1205 assert(lvalue);
1206 assert(rvalue);
1207
6597fa61
ZJS
1208 name = basename(filename);
1209 if (unit_name_is_valid(name, UNIT_NAME_INSTANCE))
1210 /* When enabling an instance, we might be using a template unit file,
1211 * but we should ignore DefaultInstance silently. */
1212 return 0;
1213 if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
1214 return log_syntax(unit, LOG_WARNING, filename, line, 0,
3b8769bd 1215 "DefaultInstance= only makes sense for template units, ignoring.");
6597fa61 1216
d54c4993
LP
1217 r = install_full_printf(i, rvalue, &printed);
1218 if (r < 0)
1219 return r;
1220
d7604756 1221 if (!unit_instance_is_valid(printed))
d54c4993
LP
1222 return -EINVAL;
1223
3b319885 1224 return free_and_replace(i->default_instance, printed);
d54c4993
LP
1225}
1226
83096483
LP
1227static int unit_file_load(
1228 InstallContext *c,
cab6235f 1229 UnitFileInstallInfo *info,
83096483 1230 const char *path,
0ec0deaa 1231 SearchFlags flags) {
83096483 1232
f975e971 1233 const ConfigTableItem items[] = {
a7724589 1234 { "Install", "Alias", config_parse_alias, 0, &info->aliases },
d54c4993
LP
1235 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
1236 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
1237 { "Install", "DefaultInstance", config_parse_default_instance, 0, info },
1238 { "Install", "Also", config_parse_also, 0, c },
d54c4993 1239 {}
83096483
LP
1240 };
1241
133e5b36
ZJS
1242 const char *name;
1243 UnitType type;
7fd1b19b 1244 _cleanup_fclose_ FILE *f = NULL;
0ec0deaa
LP
1245 _cleanup_close_ int fd = -1;
1246 struct stat st;
1247 int r;
83096483 1248
83096483
LP
1249 assert(info);
1250 assert(path);
1251
133e5b36
ZJS
1252 name = basename(path);
1253 type = unit_name_to_type(name);
1254 if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
1255 !unit_type_may_template(type))
1256 return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type));
1257
0ec0deaa
LP
1258 if (!(flags & SEARCH_LOAD)) {
1259 r = lstat(path, &st);
1260 if (r < 0)
d25e100b
LP
1261 return -errno;
1262
0ec0deaa
LP
1263 if (null_or_empty(&st))
1264 info->type = UNIT_FILE_TYPE_MASKED;
1265 else if (S_ISREG(st.st_mode))
1266 info->type = UNIT_FILE_TYPE_REGULAR;
1267 else if (S_ISLNK(st.st_mode))
1268 return -ELOOP;
1269 else if (S_ISDIR(st.st_mode))
1270 return -EISDIR;
1271 else
1272 return -ENOTTY;
1273
d25e100b 1274 return 0;
e94937df
LN
1275 }
1276
047d91f9
ZJS
1277 /* c is only needed if we actually load the file */
1278 assert(c);
1279
0ec0deaa 1280 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
83096483
LP
1281 if (fd < 0)
1282 return -errno;
0ec0deaa
LP
1283 if (fstat(fd, &st) < 0)
1284 return -errno;
1285 if (null_or_empty(&st)) {
d986e364 1286 info->type = UNIT_FILE_TYPE_MASKED;
0ec0deaa
LP
1287 return 0;
1288 }
1289 if (S_ISDIR(st.st_mode))
1290 return -EISDIR;
1291 if (!S_ISREG(st.st_mode))
1292 return -ENOTTY;
83096483
LP
1293
1294 f = fdopen(fd, "re");
0ec0deaa
LP
1295 if (!f)
1296 return -errno;
1297 fd = -1;
83096483 1298
36f822c4
ZJS
1299 r = config_parse(NULL, path, f,
1300 NULL,
1301 config_item_table_lookup, items,
1302 true, true, false, info);
83096483 1303 if (r < 0)
59108fbe 1304 return log_debug_errno(r, "Failed to parse %s: %m", info->name);
83096483 1305
0ec0deaa 1306 info->type = UNIT_FILE_TYPE_REGULAR;
aedd4012 1307
78d54bd4 1308 return
693eb9a2
LP
1309 (int) strv_length(info->aliases) +
1310 (int) strv_length(info->wanted_by) +
1311 (int) strv_length(info->required_by);
83096483
LP
1312}
1313
0ec0deaa
LP
1314static int unit_file_load_or_readlink(
1315 InstallContext *c,
1316 UnitFileInstallInfo *info,
1317 const char *path,
1318 const char *root_dir,
1319 SearchFlags flags) {
1320
401017e0 1321 _cleanup_free_ char *target = NULL;
0ec0deaa
LP
1322 int r;
1323
401017e0 1324 r = unit_file_load(c, info, path, flags);
0ec0deaa
LP
1325 if (r != -ELOOP)
1326 return r;
1327
1328 /* This is a symlink, let's read it. */
1329
401017e0 1330 r = readlink_malloc(path, &target);
0ec0deaa
LP
1331 if (r < 0)
1332 return r;
1333
401017e0 1334 if (path_equal(target, "/dev/null"))
0ec0deaa
LP
1335 info->type = UNIT_FILE_TYPE_MASKED;
1336 else {
1337 const char *bn;
1338 UnitType a, b;
1339
401017e0 1340 bn = basename(target);
0ec0deaa
LP
1341
1342 if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
1343
1344 if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
1345 return -EINVAL;
1346
1347 } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1348
1349 if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
1350 return -EINVAL;
1351
1352 } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
1353
1354 if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
1355 return -EINVAL;
1356 } else
1357 return -EINVAL;
1358
1359 /* Enforce that the symlink destination does not
1360 * change the unit file type. */
1361
1362 a = unit_name_to_type(info->name);
1363 b = unit_name_to_type(bn);
1364 if (a < 0 || b < 0 || a != b)
1365 return -EINVAL;
1366
401017e0
LP
1367 if (path_is_absolute(target))
1368 /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
1369 info->symlink_target = prefix_root(root_dir, target);
1370 else
1371 /* This is a relative path, take it relative to the dir the symlink is located in. */
1372 info->symlink_target = file_in_same_dir(path, target);
1373 if (!info->symlink_target)
1374 return -ENOMEM;
1375
0ec0deaa 1376 info->type = UNIT_FILE_TYPE_SYMLINK;
0ec0deaa
LP
1377 }
1378
1379 return 0;
1380}
1381
83096483
LP
1382static int unit_file_search(
1383 InstallContext *c,
cab6235f 1384 UnitFileInstallInfo *info,
a8ffe6fb 1385 const LookupPaths *paths,
0ec0deaa 1386 SearchFlags flags) {
83096483 1387
64f9280e 1388 _cleanup_free_ char *template = NULL;
83096483
LP
1389 char **p;
1390 int r;
1391
83096483
LP
1392 assert(info);
1393 assert(paths);
1394
0ec0deaa
LP
1395 /* Was this unit already loaded? */
1396 if (info->type != _UNIT_FILE_TYPE_INVALID)
1397 return 0;
1398
278fa575 1399 if (info->path)
e4bb56c7 1400 return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags);
83096483
LP
1401
1402 assert(info->name);
1403
a3c4eb07 1404 STRV_FOREACH(p, paths->search_path) {
e50bd775 1405 _cleanup_free_ char *path = NULL;
83096483 1406
605405c6 1407 path = strjoin(*p, "/", info->name);
83096483
LP
1408 if (!path)
1409 return -ENOMEM;
1410
e4bb56c7 1411 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
0155928c 1412 if (r >= 0) {
83096483 1413 info->path = path;
62b00233 1414 path = NULL;
e50bd775 1415 return r;
a1feacf7 1416 } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
0155928c 1417 return r;
e50bd775 1418 }
62b00233 1419
7410616c 1420 if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
e50bd775
LP
1421 /* Unit file doesn't exist, however instance
1422 * enablement was requested. We will check if it is
1423 * possible to load template unit file. */
29283ea4 1424
7410616c
LP
1425 r = unit_name_template(info->name, &template);
1426 if (r < 0)
1427 return r;
e50bd775 1428
a3c4eb07 1429 STRV_FOREACH(p, paths->search_path) {
e50bd775
LP
1430 _cleanup_free_ char *path = NULL;
1431
605405c6 1432 path = strjoin(*p, "/", template);
62b00233
ZJS
1433 if (!path)
1434 return -ENOMEM;
29283ea4 1435
e4bb56c7 1436 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
0155928c 1437 if (r >= 0) {
62b00233
ZJS
1438 info->path = path;
1439 path = NULL;
e50bd775 1440 return r;
a1feacf7 1441 } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
0155928c 1442 return r;
29283ea4 1443 }
83096483
LP
1444 }
1445
64f9280e 1446 log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template));
83096483
LP
1447 return -ENOENT;
1448}
1449
0ec0deaa
LP
1450static int install_info_follow(
1451 InstallContext *c,
1452 UnitFileInstallInfo *i,
83096483 1453 const char *root_dir,
9f6cbcf5
LP
1454 SearchFlags flags,
1455 bool ignore_different_name) {
0ec0deaa
LP
1456
1457 assert(c);
1458 assert(i);
1459
1460 if (i->type != UNIT_FILE_TYPE_SYMLINK)
1461 return -EINVAL;
1462 if (!i->symlink_target)
1463 return -EINVAL;
1464
1465 /* If the basename doesn't match, the caller should add a
1466 * complete new entry for this. */
1467
9f6cbcf5 1468 if (!ignore_different_name && !streq(basename(i->symlink_target), i->name))
0ec0deaa
LP
1469 return -EXDEV;
1470
3b319885 1471 free_and_replace(i->path, i->symlink_target);
0ec0deaa
LP
1472 i->type = _UNIT_FILE_TYPE_INVALID;
1473
1474 return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
1475}
1476
64f9280e 1477/**
ff56349d
ZJS
1478 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1479 * target, maybe more than once. Propagate the instance name if present.
64f9280e 1480 */
0ec0deaa
LP
1481static int install_info_traverse(
1482 UnitFileScope scope,
1483 InstallContext *c,
0ec0deaa
LP
1484 const LookupPaths *paths,
1485 UnitFileInstallInfo *start,
1486 SearchFlags flags,
1487 UnitFileInstallInfo **ret) {
83096483 1488
cab6235f 1489 UnitFileInstallInfo *i;
0ec0deaa 1490 unsigned k = 0;
83096483
LP
1491 int r;
1492
1493 assert(paths);
0ec0deaa
LP
1494 assert(start);
1495 assert(c);
83096483 1496
e4bb56c7 1497 r = unit_file_search(c, start, paths, flags);
83096483
LP
1498 if (r < 0)
1499 return r;
1500
0ec0deaa
LP
1501 i = start;
1502 while (i->type == UNIT_FILE_TYPE_SYMLINK) {
1503 /* Follow the symlink */
83096483 1504
0ec0deaa
LP
1505 if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
1506 return -ELOOP;
83096483 1507
e1c5c2b0 1508 if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
dfead90d 1509 r = path_is_config(paths, i->path, true);
e1c5c2b0
LP
1510 if (r < 0)
1511 return r;
1512 if (r > 0)
1513 return -ELOOP;
1514 }
83096483 1515
9f6cbcf5 1516 r = install_info_follow(c, i, paths->root_dir, flags, false);
0155928c 1517 if (r == -EXDEV) {
0ec0deaa
LP
1518 _cleanup_free_ char *buffer = NULL;
1519 const char *bn;
83096483 1520
0ec0deaa
LP
1521 /* Target has a different name, create a new
1522 * install info object for that, and continue
1523 * with that. */
83096483 1524
0ec0deaa 1525 bn = basename(i->symlink_target);
83096483 1526
0ec0deaa
LP
1527 if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
1528 unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
83096483 1529
0ec0deaa
LP
1530 _cleanup_free_ char *instance = NULL;
1531
1532 r = unit_name_to_instance(i->name, &instance);
1533 if (r < 0)
1534 return r;
1535
1536 r = unit_name_replace_instance(bn, instance, &buffer);
1537 if (r < 0)
1538 return r;
1539
9f6cbcf5
LP
1540 if (streq(buffer, i->name)) {
1541
1542 /* We filled in the instance, and the target stayed the same? If so, then let's
1543 * honour the link as it is. */
1544
1545 r = install_info_follow(c, i, paths->root_dir, flags, true);
1546 if (r < 0)
1547 return r;
1548
1549 continue;
1550 }
1551
0ec0deaa
LP
1552 bn = buffer;
1553 }
1554
19539807 1555 r = install_info_add(c, bn, NULL, false, &i);
0ec0deaa
LP
1556 if (r < 0)
1557 return r;
1558
0155928c 1559 /* Try again, with the new target we found. */
e4bb56c7 1560 r = unit_file_search(c, i, paths, flags);
893275df
ZJS
1561 if (r == -ENOENT)
1562 /* Translate error code to highlight this specific case */
1563 return -ENOLINK;
0ec0deaa
LP
1564 }
1565
0155928c
ZJS
1566 if (r < 0)
1567 return r;
83096483
LP
1568 }
1569
0ec0deaa
LP
1570 if (ret)
1571 *ret = i;
83096483 1572
0ec0deaa
LP
1573 return 0;
1574}
83096483 1575
ff56349d
ZJS
1576/**
1577 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1578 * or the name (otherwise). root_dir is prepended to the path.
1579 */
401017e0
LP
1580static int install_info_add_auto(
1581 InstallContext *c,
1582 const LookupPaths *paths,
1583 const char *name_or_path,
1584 UnitFileInstallInfo **ret) {
1585
1586 assert(c);
1587 assert(name_or_path);
1588
1589 if (path_is_absolute(name_or_path)) {
1590 const char *pp;
1591
1592 pp = prefix_roota(paths->root_dir, name_or_path);
1593
19539807 1594 return install_info_add(c, NULL, pp, false, ret);
401017e0 1595 } else
19539807 1596 return install_info_add(c, name_or_path, NULL, false, ret);
401017e0
LP
1597}
1598
0ec0deaa
LP
1599static int install_info_discover(
1600 UnitFileScope scope,
1601 InstallContext *c,
0ec0deaa
LP
1602 const LookupPaths *paths,
1603 const char *name,
1604 SearchFlags flags,
59108fbe
ZJS
1605 UnitFileInstallInfo **ret,
1606 UnitFileChange **changes,
1607 unsigned *n_changes) {
83096483 1608
0ec0deaa
LP
1609 UnitFileInstallInfo *i;
1610 int r;
83096483 1611
0ec0deaa
LP
1612 assert(c);
1613 assert(paths);
1614 assert(name);
1615
401017e0 1616 r = install_info_add_auto(c, paths, name, &i);
59108fbe
ZJS
1617 if (r >= 0)
1618 r = install_info_traverse(scope, c, paths, i, flags, ret);
83096483 1619
59108fbe
ZJS
1620 if (r < 0)
1621 unit_file_changes_add(changes, n_changes, r, name, NULL);
1622 return r;
83096483
LP
1623}
1624
1625static int install_info_symlink_alias(
cab6235f 1626 UnitFileInstallInfo *i,
401017e0 1627 const LookupPaths *paths,
83096483
LP
1628 const char *config_path,
1629 bool force,
1630 UnitFileChange **changes,
1631 unsigned *n_changes) {
1632
1633 char **s;
1634 int r = 0, q;
1635
1636 assert(i);
401017e0 1637 assert(paths);
83096483
LP
1638 assert(config_path);
1639
1640 STRV_FOREACH(s, i->aliases) {
7fd1b19b 1641 _cleanup_free_ char *alias_path = NULL, *dst = NULL;
83096483 1642
19f6d710
LP
1643 q = install_full_printf(i, *s, &dst);
1644 if (q < 0)
1645 return q;
83096483 1646
7584d236 1647 alias_path = path_make_absolute(dst, config_path);
83096483
LP
1648 if (!alias_path)
1649 return -ENOMEM;
1650
60bec8e4 1651 q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
83096483
LP
1652 if (r == 0)
1653 r = q;
1654 }
1655
1656 return r;
1657}
1658
1659static int install_info_symlink_wants(
cab6235f 1660 UnitFileInstallInfo *i,
401017e0 1661 const LookupPaths *paths,
83096483 1662 const char *config_path,
d54c4993
LP
1663 char **list,
1664 const char *suffix,
83096483
LP
1665 UnitFileChange **changes,
1666 unsigned *n_changes) {
1667
d54c4993
LP
1668 _cleanup_free_ char *buf = NULL;
1669 const char *n;
83096483
LP
1670 char **s;
1671 int r = 0, q;
1672
1673 assert(i);
401017e0 1674 assert(paths);
83096483
LP
1675 assert(config_path);
1676
047d91f9
ZJS
1677 if (strv_isempty(list))
1678 return 0;
1679
be704916 1680 if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance) {
047d91f9
ZJS
1681 UnitFileInstallInfo instance = {
1682 .type = _UNIT_FILE_TYPE_INVALID,
1683 };
1684 _cleanup_free_ char *path = NULL;
0a327d75 1685
7410616c
LP
1686 r = unit_name_replace_instance(i->name, i->default_instance, &buf);
1687 if (r < 0)
1688 return r;
83096483 1689
047d91f9
ZJS
1690 instance.name = buf;
1691 r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS);
1692 if (r < 0)
1693 return r;
1694
1695 path = instance.path;
1696 instance.path = NULL;
1697
1698 if (instance.type == UNIT_FILE_TYPE_MASKED) {
1699 unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL);
1700 return -ERFKILL;
1701 }
1702
d54c4993
LP
1703 n = buf;
1704 } else
1705 n = i->name;
78d54bd4 1706
d54c4993 1707 STRV_FOREACH(s, list) {
7fd1b19b 1708 _cleanup_free_ char *path = NULL, *dst = NULL;
78d54bd4 1709
19f6d710
LP
1710 q = install_full_printf(i, *s, &dst);
1711 if (q < 0)
1712 return q;
7584d236 1713
7410616c 1714 if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
78d54bd4
LP
1715 r = -EINVAL;
1716 continue;
1717 }
1718
605405c6 1719 path = strjoin(config_path, "/", dst, suffix, n);
d54c4993 1720 if (!path)
78d54bd4
LP
1721 return -ENOMEM;
1722
60bec8e4 1723 q = create_symlink(paths, i->path, path, true, changes, n_changes);
78d54bd4
LP
1724 if (r == 0)
1725 r = q;
1726 }
1727
1728 return r;
1729}
1730
83096483 1731static int install_info_symlink_link(
cab6235f 1732 UnitFileInstallInfo *i,
a8ffe6fb 1733 const LookupPaths *paths,
83096483
LP
1734 const char *config_path,
1735 bool force,
1736 UnitFileChange **changes,
1737 unsigned *n_changes) {
1738
7fd1b19b 1739 _cleanup_free_ char *path = NULL;
1dacfd2a 1740 int r;
83096483
LP
1741
1742 assert(i);
1743 assert(paths);
1744 assert(config_path);
1745 assert(i->path);
1746
32c0ed7b 1747 r = in_search_path(paths, i->path);
fe4aede9 1748 if (r < 0)
83096483 1749 return r;
fe4aede9
ZJS
1750 if (r > 0)
1751 return 0;
83096483 1752
605405c6 1753 path = strjoin(config_path, "/", i->name);
1dacfd2a 1754 if (!path)
83096483
LP
1755 return -ENOMEM;
1756
60bec8e4 1757 return create_symlink(paths, i->path, path, force, changes, n_changes);
83096483
LP
1758}
1759
1760static int install_info_apply(
cab6235f 1761 UnitFileInstallInfo *i,
a8ffe6fb 1762 const LookupPaths *paths,
83096483
LP
1763 const char *config_path,
1764 bool force,
1765 UnitFileChange **changes,
1766 unsigned *n_changes) {
1767
1768 int r, q;
1769
1770 assert(i);
1771 assert(paths);
1772 assert(config_path);
1773
0ec0deaa
LP
1774 if (i->type != UNIT_FILE_TYPE_REGULAR)
1775 return 0;
1776
401017e0 1777 r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
83096483 1778
29380daf 1779 q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);
83096483
LP
1780 if (r == 0)
1781 r = q;
1782
29380daf 1783 q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);
78d54bd4
LP
1784 if (r == 0)
1785 r = q;
1786
e4bb56c7 1787 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
fe4aede9
ZJS
1788 /* Do not count links to the unit file towards the "carries_install_info" count */
1789 if (r == 0 && q < 0)
83096483
LP
1790 r = q;
1791
1792 return r;
1793}
1794
1795static int install_context_apply(
0ec0deaa 1796 UnitFileScope scope,
83096483 1797 InstallContext *c,
a8ffe6fb 1798 const LookupPaths *paths,
83096483 1799 const char *config_path,
83096483 1800 bool force,
0ec0deaa 1801 SearchFlags flags,
83096483
LP
1802 UnitFileChange **changes,
1803 unsigned *n_changes) {
1804
cab6235f 1805 UnitFileInstallInfo *i;
0ec0deaa 1806 int r;
83096483
LP
1807
1808 assert(c);
1809 assert(paths);
1810 assert(config_path);
1811
0ec0deaa 1812 if (ordered_hashmap_isempty(c->will_process))
d25e100b 1813 return 0;
83096483 1814
0ec0deaa 1815 r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
d25e100b
LP
1816 if (r < 0)
1817 return r;
83096483 1818
2d5c93c7 1819 r = 0;
0ec0deaa
LP
1820 while ((i = ordered_hashmap_first(c->will_process))) {
1821 int q;
83096483 1822
0ec0deaa
LP
1823 q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1824 if (q < 0)
1825 return q;
83096483 1826
e4bb56c7 1827 r = install_info_traverse(scope, c, paths, i, flags, NULL);
db093eed
ZJS
1828 if (r < 0) {
1829 unit_file_changes_add(changes, n_changes, r, i->name, NULL);
83096483 1830 return r;
db093eed 1831 }
0ec0deaa 1832
f1651715 1833 /* We can attempt to process a masked unit when a different unit
047d91f9 1834 * that we were processing specifies it in Also=. */
f1651715
ZJS
1835 if (i->type == UNIT_FILE_TYPE_MASKED) {
1836 unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
1837 if (r >= 0)
047d91f9
ZJS
1838 /* Assume that something *could* have been enabled here,
1839 * avoid "empty [Install] section" warning. */
f1651715
ZJS
1840 r += 1;
1841 continue;
1842 }
1843
0ec0deaa
LP
1844 if (i->type != UNIT_FILE_TYPE_REGULAR)
1845 continue;
83096483 1846
e4bb56c7 1847 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
0ec0deaa
LP
1848 if (r >= 0) {
1849 if (q < 0)
1850 r = q;
1851 else
596fc263 1852 r += q;
0ec0deaa 1853 }
83096483
LP
1854 }
1855
1856 return r;
1857}
1858
1859static int install_context_mark_for_removal(
0ec0deaa 1860 UnitFileScope scope,
83096483 1861 InstallContext *c,
a8ffe6fb 1862 const LookupPaths *paths,
83096483 1863 Set **remove_symlinks_to,
637d6e5b
LP
1864 const char *config_path,
1865 UnitFileChange **changes,
1866 unsigned *n_changes) {
83096483 1867
cab6235f 1868 UnitFileInstallInfo *i;
0ec0deaa 1869 int r;
83096483
LP
1870
1871 assert(c);
1872 assert(paths);
1873 assert(config_path);
1874
1875 /* Marks all items for removal */
1876
0ec0deaa 1877 if (ordered_hashmap_isempty(c->will_process))
d25e100b 1878 return 0;
83096483 1879
0ec0deaa 1880 r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
d25e100b
LP
1881 if (r < 0)
1882 return r;
83096483 1883
0ec0deaa 1884 while ((i = ordered_hashmap_first(c->will_process))) {
83096483 1885
0ec0deaa
LP
1886 r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1887 if (r < 0)
83096483 1888 return r;
29283ea4 1889
e4bb56c7 1890 r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
19539807 1891 if (r == -ENOLINK) {
637d6e5b
LP
1892 log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
1893 unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL);
1894 } else if (r == -ENOENT) {
1895
1896 if (i->auxiliary) /* some unit specified in Also= or similar is missing */
1897 log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
1898 else {
1899 log_debug_errno(r, "Unit %s not found, removing name.", i->name);
1900 unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1901 }
0ec0deaa 1902
637d6e5b
LP
1903 } else if (r < 0) {
1904 log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
1905 unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1906 } else if (i->type == UNIT_FILE_TYPE_MASKED) {
1907 log_debug("Unit file %s is masked, ignoring.", i->name);
1908 unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path ?: i->name, NULL);
1909 continue;
1910 } else if (i->type != UNIT_FILE_TYPE_REGULAR) {
1911 log_debug("Unit %s has type %s, ignoring.", i->name, unit_file_type_to_string(i->type) ?: "invalid");
0ec0deaa 1912 continue;
64f9280e 1913 }
0ec0deaa
LP
1914
1915 r = mark_symlink_for_removal(remove_symlinks_to, i->name);
1916 if (r < 0)
1917 return r;
1918 }
1919
1920 return 0;
1921}
1922
1923int unit_file_mask(
1924 UnitFileScope scope,
b3796dd8 1925 UnitFileFlags flags,
0ec0deaa
LP
1926 const char *root_dir,
1927 char **files,
0ec0deaa
LP
1928 UnitFileChange **changes,
1929 unsigned *n_changes) {
1930
e1c5c2b0
LP
1931 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1932 const char *config_path;
0ec0deaa
LP
1933 char **i;
1934 int r;
1935
1936 assert(scope >= 0);
1937 assert(scope < _UNIT_FILE_SCOPE_MAX);
1938
4943d143 1939 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
1940 if (r < 0)
1941 return r;
1942
b3796dd8 1943 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
1944 if (!config_path)
1945 return -ENXIO;
e1c5c2b0 1946
0ec0deaa
LP
1947 STRV_FOREACH(i, files) {
1948 _cleanup_free_ char *path = NULL;
1949 int q;
1950
1951 if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
1952 if (r == 0)
1953 r = -EINVAL;
1954 continue;
1955 }
1956
e1c5c2b0 1957 path = path_make_absolute(*i, config_path);
0ec0deaa
LP
1958 if (!path)
1959 return -ENOMEM;
29283ea4 1960
b3796dd8 1961 q = create_symlink(&paths, "/dev/null", path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
0ec0deaa 1962 if (q < 0 && r >= 0)
83096483
LP
1963 r = q;
1964 }
1965
1966 return r;
1967}
1968
0ec0deaa
LP
1969int unit_file_unmask(
1970 UnitFileScope scope,
b3796dd8 1971 UnitFileFlags flags,
0ec0deaa
LP
1972 const char *root_dir,
1973 char **files,
1974 UnitFileChange **changes,
1975 unsigned *n_changes) {
1976
e1c5c2b0 1977 _cleanup_lookup_paths_free_ LookupPaths paths = {};
0ec0deaa 1978 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
dc7dd61d 1979 _cleanup_strv_free_ char **todo = NULL;
0ec0deaa 1980 size_t n_todo = 0, n_allocated = 0;
e1c5c2b0 1981 const char *config_path;
0ec0deaa 1982 char **i;
3b3557c4 1983 bool dry_run;
0ec0deaa
LP
1984 int r, q;
1985
1986 assert(scope >= 0);
1987 assert(scope < _UNIT_FILE_SCOPE_MAX);
1988
4943d143 1989 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
1990 if (r < 0)
1991 return r;
1992
b3796dd8 1993 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
1994 if (!config_path)
1995 return -ENXIO;
1996
3b3557c4 1997 dry_run = !!(flags & UNIT_FILE_DRY_RUN);
e1c5c2b0 1998
0ec0deaa
LP
1999 STRV_FOREACH(i, files) {
2000 _cleanup_free_ char *path = NULL;
2001
2002 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2003 return -EINVAL;
2004
2005 path = path_make_absolute(*i, config_path);
2006 if (!path)
2007 return -ENOMEM;
2008
2009 r = null_or_empty_path(path);
2010 if (r == -ENOENT)
2011 continue;
2012 if (r < 0)
2013 return r;
2014 if (r == 0)
2015 continue;
2016
2017 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2018 return -ENOMEM;
2019
d054eae6
EV
2020 todo[n_todo] = strdup(*i);
2021 if (!todo[n_todo])
2022 return -ENOMEM;
2023
2024 n_todo++;
0ec0deaa
LP
2025 }
2026
2027 strv_uniq(todo);
2028
2029 r = 0;
2030 STRV_FOREACH(i, todo) {
2031 _cleanup_free_ char *path = NULL;
401017e0 2032 const char *rp;
0ec0deaa
LP
2033
2034 path = path_make_absolute(*i, config_path);
2035 if (!path)
2036 return -ENOMEM;
2037
3b3557c4 2038 if (!dry_run && unlink(path) < 0) {
af3d8113
ZJS
2039 if (errno != ENOENT) {
2040 if (r >= 0)
2041 r = -errno;
2042 unit_file_changes_add(changes, n_changes, -errno, path, NULL);
2043 }
0ec0deaa 2044
401017e0 2045 continue;
0ec0deaa 2046 }
401017e0
LP
2047
2048 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
2049
2050 rp = skip_root(&paths, path);
2051 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
2052 if (q < 0)
2053 return q;
0ec0deaa
LP
2054 }
2055
3b3557c4 2056 q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
0ec0deaa
LP
2057 if (r >= 0)
2058 r = q;
2059
2060 return r;
2061}
2062
2063int unit_file_link(
e94937df 2064 UnitFileScope scope,
b3796dd8 2065 UnitFileFlags flags,
e94937df
LN
2066 const char *root_dir,
2067 char **files,
e94937df
LN
2068 UnitFileChange **changes,
2069 unsigned *n_changes) {
2070
2071 _cleanup_lookup_paths_free_ LookupPaths paths = {};
8af35ba6 2072 _cleanup_strv_free_ char **todo = NULL;
0ec0deaa 2073 size_t n_todo = 0, n_allocated = 0;
e1c5c2b0 2074 const char *config_path;
e94937df 2075 char **i;
0ec0deaa 2076 int r, q;
e94937df
LN
2077
2078 assert(scope >= 0);
2079 assert(scope < _UNIT_FILE_SCOPE_MAX);
2080
4943d143 2081 r = lookup_paths_init(&paths, scope, 0, root_dir);
e94937df
LN
2082 if (r < 0)
2083 return r;
2084
b3796dd8 2085 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2086 if (!config_path)
2087 return -ENXIO;
e94937df
LN
2088
2089 STRV_FOREACH(i, files) {
0ec0deaa
LP
2090 _cleanup_free_ char *full = NULL;
2091 struct stat st;
2092 char *fn;
e94937df 2093
0ec0deaa
LP
2094 if (!path_is_absolute(*i))
2095 return -EINVAL;
e94937df 2096
0ec0deaa
LP
2097 fn = basename(*i);
2098 if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
2099 return -EINVAL;
2100
e4bb56c7 2101 full = prefix_root(paths.root_dir, *i);
0ec0deaa
LP
2102 if (!full)
2103 return -ENOMEM;
2104
2105 if (lstat(full, &st) < 0)
2106 return -errno;
2107 if (S_ISLNK(st.st_mode))
2108 return -ELOOP;
2109 if (S_ISDIR(st.st_mode))
2110 return -EISDIR;
2111 if (!S_ISREG(st.st_mode))
2112 return -ENOTTY;
2113
32c0ed7b 2114 q = in_search_path(&paths, *i);
0ec0deaa
LP
2115 if (q < 0)
2116 return q;
2117 if (q > 0)
2118 continue;
2119
2120 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2121 return -ENOMEM;
2122
8af35ba6
EV
2123 todo[n_todo] = strdup(*i);
2124 if (!todo[n_todo])
2125 return -ENOMEM;
2126
2127 n_todo++;
e94937df
LN
2128 }
2129
0ec0deaa 2130 strv_uniq(todo);
e94937df 2131
0ec0deaa
LP
2132 r = 0;
2133 STRV_FOREACH(i, todo) {
401017e0 2134 _cleanup_free_ char *new_path = NULL;
0ec0deaa 2135
401017e0
LP
2136 new_path = path_make_absolute(basename(*i), config_path);
2137 if (!new_path)
0ec0deaa
LP
2138 return -ENOMEM;
2139
b3796dd8 2140 q = create_symlink(&paths, *i, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
0ec0deaa
LP
2141 if (q < 0 && r >= 0)
2142 r = q;
2d5c93c7
MS
2143 }
2144
0ec0deaa
LP
2145 return r;
2146}
2147
344ca755
LP
2148static int path_shall_revert(const LookupPaths *paths, const char *path) {
2149 int r;
2150
2151 assert(paths);
2152 assert(path);
2153
2154 /* Checks whether the path is one where the drop-in directories shall be removed. */
2155
dfead90d 2156 r = path_is_config(paths, path, true);
344ca755
LP
2157 if (r != 0)
2158 return r;
2159
2160 r = path_is_control(paths, path);
2161 if (r != 0)
2162 return r;
2163
2164 return path_is_transient(paths, path);
2165}
2166
2167int unit_file_revert(
2168 UnitFileScope scope,
2169 const char *root_dir,
2170 char **files,
2171 UnitFileChange **changes,
2172 unsigned *n_changes) {
2173
2174 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
344ca755
LP
2175 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2176 _cleanup_strv_free_ char **todo = NULL;
2177 size_t n_todo = 0, n_allocated = 0;
2178 char **i;
2179 int r, q;
2180
2181 /* Puts a unit file back into vendor state. This means:
2182 *
2183 * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and
2184 * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated").
2185 *
2186 * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
2187 * "config", but not in "transient" or "control" or even "generated").
2188 *
4f25723c 2189 * We remove all that in both the runtime and the persistent directories, if that applies.
344ca755
LP
2190 */
2191
2192 r = lookup_paths_init(&paths, scope, 0, root_dir);
2193 if (r < 0)
2194 return r;
2195
2196 STRV_FOREACH(i, files) {
2197 bool has_vendor = false;
2198 char **p;
2199
2200 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2201 return -EINVAL;
2202
2203 STRV_FOREACH(p, paths.search_path) {
2204 _cleanup_free_ char *path = NULL, *dropin = NULL;
2205 struct stat st;
2206
2207 path = path_make_absolute(*i, *p);
2208 if (!path)
2209 return -ENOMEM;
2210
2211 r = lstat(path, &st);
2212 if (r < 0) {
2213 if (errno != ENOENT)
2214 return -errno;
2215 } else if (S_ISREG(st.st_mode)) {
2216 /* Check if there's a vendor version */
2217 r = path_is_vendor(&paths, path);
2218 if (r < 0)
2219 return r;
2220 if (r > 0)
2221 has_vendor = true;
2222 }
2223
2224 dropin = strappend(path, ".d");
2225 if (!dropin)
2226 return -ENOMEM;
2227
2228 r = lstat(dropin, &st);
2229 if (r < 0) {
2230 if (errno != ENOENT)
2231 return -errno;
2232 } else if (S_ISDIR(st.st_mode)) {
2233 /* Remove the drop-ins */
2234 r = path_shall_revert(&paths, dropin);
2235 if (r < 0)
2236 return r;
2237 if (r > 0) {
2238 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2239 return -ENOMEM;
2240
2241 todo[n_todo++] = dropin;
2242 dropin = NULL;
2243 }
2244 }
2245 }
2246
2247 if (!has_vendor)
2248 continue;
2249
2250 /* OK, there's a vendor version, hence drop all configuration versions */
2251 STRV_FOREACH(p, paths.search_path) {
2252 _cleanup_free_ char *path = NULL;
2253 struct stat st;
2254
2255 path = path_make_absolute(*i, *p);
2256 if (!path)
2257 return -ENOMEM;
2258
2259 r = lstat(path, &st);
2260 if (r < 0) {
2261 if (errno != ENOENT)
2262 return -errno;
2263 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
dfead90d 2264 r = path_is_config(&paths, path, true);
344ca755
LP
2265 if (r < 0)
2266 return r;
2267 if (r > 0) {
2268 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2269 return -ENOMEM;
2270
2271 todo[n_todo++] = path;
2272 path = NULL;
2273 }
2274 }
2275 }
2276 }
2277
2278 strv_uniq(todo);
2279
2280 r = 0;
2281 STRV_FOREACH(i, todo) {
2282 _cleanup_strv_free_ char **fs = NULL;
2283 const char *rp;
2284 char **j;
2285
2286 (void) get_files_in_directory(*i, &fs);
2287
2288 q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
2289 if (q < 0 && q != -ENOENT && r >= 0) {
2290 r = q;
2291 continue;
2292 }
2293
2294 STRV_FOREACH(j, fs) {
2295 _cleanup_free_ char *t = NULL;
2296
605405c6 2297 t = strjoin(*i, "/", *j);
344ca755
LP
2298 if (!t)
2299 return -ENOMEM;
2300
2301 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL);
2302 }
2303
2304 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL);
2305
2306 rp = skip_root(&paths, *i);
2307 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
2308 if (q < 0)
2309 return q;
2310 }
2311
3b3557c4 2312 q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, false, changes, n_changes);
344ca755
LP
2313 if (r >= 0)
2314 r = q;
2315
3b3557c4 2316 q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, false, changes, n_changes);
344ca755
LP
2317 if (r >= 0)
2318 r = q;
2319
2320 return r;
2321}
2322
0ec0deaa
LP
2323int unit_file_add_dependency(
2324 UnitFileScope scope,
b3796dd8 2325 UnitFileFlags flags,
0ec0deaa
LP
2326 const char *root_dir,
2327 char **files,
2328 const char *target,
2329 UnitDependency dep,
0ec0deaa
LP
2330 UnitFileChange **changes,
2331 unsigned *n_changes) {
2332
2333 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2334 _cleanup_(install_context_done) InstallContext c = {};
0ec0deaa 2335 UnitFileInstallInfo *i, *target_info;
e1c5c2b0 2336 const char *config_path;
0ec0deaa
LP
2337 char **f;
2338 int r;
2339
2340 assert(scope >= 0);
2341 assert(scope < _UNIT_FILE_SCOPE_MAX);
2342 assert(target);
2343
2344 if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
2345 return -EINVAL;
2346
2347 if (!unit_name_is_valid(target, UNIT_NAME_ANY))
2348 return -EINVAL;
2349
4943d143 2350 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
2351 if (r < 0)
2352 return r;
2353
b3796dd8 2354 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2355 if (!config_path)
2356 return -ENXIO;
0ec0deaa 2357
59108fbe
ZJS
2358 r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2359 &target_info, changes, n_changes);
0ec0deaa
LP
2360 if (r < 0)
2361 return r;
af3d8113 2362 r = install_info_may_process(target_info, &paths, changes, n_changes);
76adb5b8
LP
2363 if (r < 0)
2364 return r;
0ec0deaa
LP
2365
2366 assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
e94937df 2367
0ec0deaa
LP
2368 STRV_FOREACH(f, files) {
2369 char ***l;
2370
59108fbe
ZJS
2371 r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2372 &i, changes, n_changes);
e94937df
LN
2373 if (r < 0)
2374 return r;
af3d8113 2375 r = install_info_may_process(i, &paths, changes, n_changes);
76adb5b8
LP
2376 if (r < 0)
2377 return r;
0ec0deaa
LP
2378
2379 assert(i->type == UNIT_FILE_TYPE_REGULAR);
2380
2381 /* We didn't actually load anything from the unit
2382 * file, but instead just add in our new symlink to
2383 * create. */
e94937df
LN
2384
2385 if (dep == UNIT_WANTS)
0ec0deaa 2386 l = &i->wanted_by;
e94937df 2387 else
0ec0deaa 2388 l = &i->required_by;
e94937df 2389
0ec0deaa
LP
2390 strv_free(*l);
2391 *l = strv_new(target_info->name, NULL);
2392 if (!*l)
2393 return -ENOMEM;
e94937df
LN
2394 }
2395
b3796dd8 2396 return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
e94937df
LN
2397}
2398
83096483
LP
2399int unit_file_enable(
2400 UnitFileScope scope,
b3796dd8 2401 UnitFileFlags flags,
83096483 2402 const char *root_dir,
7195aa42 2403 char **files,
83096483
LP
2404 UnitFileChange **changes,
2405 unsigned *n_changes) {
2406
7fd1b19b 2407 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 2408 _cleanup_(install_context_done) InstallContext c = {};
e1c5c2b0 2409 const char *config_path;
0ec0deaa
LP
2410 UnitFileInstallInfo *i;
2411 char **f;
83096483
LP
2412 int r;
2413
2414 assert(scope >= 0);
2415 assert(scope < _UNIT_FILE_SCOPE_MAX);
2416
4943d143 2417 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
2418 if (r < 0)
2419 return r;
2420
b3796dd8 2421 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2422 if (!config_path)
2423 return -ENXIO;
83096483 2424
0ec0deaa 2425 STRV_FOREACH(f, files) {
59108fbe
ZJS
2426 r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2427 &i, changes, n_changes);
83096483 2428 if (r < 0)
d9e5e694 2429 return r;
af3d8113 2430 r = install_info_may_process(i, &paths, changes, n_changes);
76adb5b8
LP
2431 if (r < 0)
2432 return r;
0ec0deaa
LP
2433
2434 assert(i->type == UNIT_FILE_TYPE_REGULAR);
83096483
LP
2435 }
2436
729e3769 2437 /* This will return the number of symlink rules that were
d25e100b
LP
2438 supposed to be created, not the ones actually created. This
2439 is useful to determine whether the passed files had any
2440 installation data at all. */
b91a3b02 2441
b3796dd8 2442 return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_LOAD, changes, n_changes);
83096483
LP
2443}
2444
2445int unit_file_disable(
2446 UnitFileScope scope,
b3796dd8 2447 UnitFileFlags flags,
83096483 2448 const char *root_dir,
7195aa42 2449 char **files,
83096483
LP
2450 UnitFileChange **changes,
2451 unsigned *n_changes) {
2452
7fd1b19b 2453 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 2454 _cleanup_(install_context_done) InstallContext c = {};
7fd1b19b 2455 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
e1c5c2b0 2456 const char *config_path;
0ec0deaa
LP
2457 char **i;
2458 int r;
83096483
LP
2459
2460 assert(scope >= 0);
2461 assert(scope < _UNIT_FILE_SCOPE_MAX);
2462
4943d143 2463 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
2464 if (r < 0)
2465 return r;
2466
b3796dd8 2467 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2468 if (!config_path)
2469 return -ENXIO;
83096483
LP
2470
2471 STRV_FOREACH(i, files) {
0ec0deaa
LP
2472 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2473 return -EINVAL;
2474
19539807 2475 r = install_info_add(&c, *i, NULL, false, NULL);
83096483 2476 if (r < 0)
d9e5e694 2477 return r;
83096483
LP
2478 }
2479
637d6e5b 2480 r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
0ec0deaa
LP
2481 if (r < 0)
2482 return r;
83096483 2483
3b3557c4 2484 return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
83096483
LP
2485}
2486
2487int unit_file_reenable(
2488 UnitFileScope scope,
b3796dd8 2489 UnitFileFlags flags,
83096483 2490 const char *root_dir,
7195aa42 2491 char **files,
83096483
LP
2492 UnitFileChange **changes,
2493 unsigned *n_changes) {
0ec0deaa
LP
2494
2495 char **n;
92d430a9 2496 int r;
0ec0deaa 2497 size_t l, i;
83096483 2498
0ec0deaa
LP
2499 /* First, we invoke the disable command with only the basename... */
2500 l = strv_length(files);
2501 n = newa(char*, l+1);
2502 for (i = 0; i < l; i++)
2503 n[i] = basename(files[i]);
2504 n[i] = NULL;
2505
b3796dd8 2506 r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes);
83096483 2507 if (r < 0)
d9e5e694 2508 return r;
83096483 2509
0ec0deaa 2510 /* But the enable command with the full name */
b3796dd8 2511 return unit_file_enable(scope, flags, root_dir, files, changes, n_changes);
83096483
LP
2512}
2513
99504dd4
VP
2514int unit_file_set_default(
2515 UnitFileScope scope,
b3796dd8 2516 UnitFileFlags flags,
99504dd4 2517 const char *root_dir,
0ec0deaa 2518 const char *name,
99504dd4
VP
2519 UnitFileChange **changes,
2520 unsigned *n_changes) {
2521
2522 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 2523 _cleanup_(install_context_done) InstallContext c = {};
0ec0deaa 2524 UnitFileInstallInfo *i;
60bec8e4 2525 const char *new_path;
99504dd4 2526 int r;
99504dd4
VP
2527
2528 assert(scope >= 0);
2529 assert(scope < _UNIT_FILE_SCOPE_MAX);
0ec0deaa 2530 assert(name);
99504dd4 2531
401017e0 2532 if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
0ec0deaa
LP
2533 return -EINVAL;
2534 if (streq(name, SPECIAL_DEFAULT_TARGET))
99504dd4
VP
2535 return -EINVAL;
2536
4943d143 2537 r = lookup_paths_init(&paths, scope, 0, root_dir);
99504dd4
VP
2538 if (r < 0)
2539 return r;
2540
59108fbe 2541 r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes);
99504dd4
VP
2542 if (r < 0)
2543 return r;
af3d8113 2544 r = install_info_may_process(i, &paths, changes, n_changes);
76adb5b8
LP
2545 if (r < 0)
2546 return r;
99504dd4 2547
401017e0 2548 new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
b3796dd8 2549 return create_symlink(&paths, i->path, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
99504dd4
VP
2550}
2551
2552int unit_file_get_default(
2553 UnitFileScope scope,
2554 const char *root_dir,
2555 char **name) {
2556
2557 _cleanup_lookup_paths_free_ LookupPaths paths = {};
0ec0deaa
LP
2558 _cleanup_(install_context_done) InstallContext c = {};
2559 UnitFileInstallInfo *i;
2560 char *n;
99504dd4
VP
2561 int r;
2562
16ed0233
LP
2563 assert(scope >= 0);
2564 assert(scope < _UNIT_FILE_SCOPE_MAX);
2565 assert(name);
2566
4943d143 2567 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
2568 if (r < 0)
2569 return r;
99504dd4 2570
59108fbe
ZJS
2571 r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2572 &i, NULL, NULL);
0ec0deaa
LP
2573 if (r < 0)
2574 return r;
af3d8113 2575 r = install_info_may_process(i, &paths, NULL, 0);
76adb5b8
LP
2576 if (r < 0)
2577 return r;
99504dd4 2578
0ec0deaa
LP
2579 n = strdup(i->name);
2580 if (!n)
2581 return -ENOMEM;
99504dd4 2582
0ec0deaa
LP
2583 *name = n;
2584 return 0;
99504dd4
VP
2585}
2586
2c52204c 2587static int unit_file_lookup_state(
83096483 2588 UnitFileScope scope,
a8ffe6fb 2589 const LookupPaths *paths,
0ec0deaa
LP
2590 const char *name,
2591 UnitFileState *ret) {
83096483 2592
0ec0deaa
LP
2593 _cleanup_(install_context_done) InstallContext c = {};
2594 UnitFileInstallInfo *i;
2595 UnitFileState state;
2596 int r;
83096483 2597
a8ffe6fb 2598 assert(paths);
0ec0deaa 2599 assert(name);
83096483 2600
7410616c 2601 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
83096483
LP
2602 return -EINVAL;
2603
59108fbe
ZJS
2604 r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2605 &i, NULL, NULL);
0ec0deaa
LP
2606 if (r < 0)
2607 return r;
83096483 2608
0ec0deaa
LP
2609 /* Shortcut things, if the caller just wants to know if this unit exists. */
2610 if (!ret)
2611 return 0;
83096483 2612
0ec0deaa 2613 switch (i->type) {
67820a0c 2614
0ec0deaa 2615 case UNIT_FILE_TYPE_MASKED:
dfead90d 2616 r = path_is_runtime(paths, i->path, true);
385eb996
LP
2617 if (r < 0)
2618 return r;
2619
2620 state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
0ec0deaa 2621 break;
83096483 2622
0ec0deaa 2623 case UNIT_FILE_TYPE_REGULAR:
f4dc1e65 2624 r = path_is_generator(paths, i->path);
f4139308
LP
2625 if (r < 0)
2626 return r;
2627 if (r > 0) {
2628 state = UNIT_FILE_GENERATED;
2629 break;
2630 }
2631
e4fca67f
LP
2632 r = path_is_transient(paths, i->path);
2633 if (r < 0)
2634 return r;
2635 if (r > 0) {
2636 state = UNIT_FILE_TRANSIENT;
2637 break;
2638 }
2639
5cd8ae31
ZJS
2640 /* Check if any of the Alias= symlinks have been created.
2641 * We ignore other aliases, and only check those that would
2642 * be created by systemctl enable for this unit. */
2643 r = find_symlinks_in_scope(paths, i, &state);
d9e5e694
ZJS
2644 if (r < 0)
2645 return r;
0ec0deaa 2646 if (r == 0) {
7a7ec2bf 2647 if (unit_file_install_info_has_rules(i))
0ec0deaa 2648 state = UNIT_FILE_DISABLED;
7a7ec2bf 2649 else if (unit_file_install_info_has_also(i))
0ec0deaa
LP
2650 state = UNIT_FILE_INDIRECT;
2651 else
2652 state = UNIT_FILE_STATIC;
2653 }
83096483 2654
0ec0deaa
LP
2655 break;
2656
2657 default:
2658 assert_not_reached("Unexpect unit file type.");
83096483
LP
2659 }
2660
0ec0deaa
LP
2661 *ret = state;
2662 return 0;
83096483
LP
2663}
2664
0ec0deaa 2665int unit_file_get_state(
a8ffe6fb
ZJS
2666 UnitFileScope scope,
2667 const char *root_dir,
0ec0deaa
LP
2668 const char *name,
2669 UnitFileState *ret) {
a8ffe6fb
ZJS
2670
2671 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2672 int r;
2673
2674 assert(scope >= 0);
2675 assert(scope < _UNIT_FILE_SCOPE_MAX);
2676 assert(name);
2677
4943d143 2678 r = lookup_paths_init(&paths, scope, 0, root_dir);
a8ffe6fb
ZJS
2679 if (r < 0)
2680 return r;
2681
e4bb56c7 2682 return unit_file_lookup_state(scope, &paths, name, ret);
a8ffe6fb
ZJS
2683}
2684
e735decc
LP
2685int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) {
2686 _cleanup_(install_context_done) InstallContext c = {};
2687 int r;
2688
2689 assert(paths);
2690 assert(name);
2691
2692 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2693 return -EINVAL;
2694
59108fbe 2695 r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL);
e735decc
LP
2696 if (r == -ENOENT)
2697 return 0;
2698 if (r < 0)
2699 return r;
2700
2701 return 1;
2702}
2703
8965d9f8
AC
2704static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
2705 _cleanup_(presets_freep) Presets ps = {};
2706 size_t n_allocated = 0;
7fd1b19b 2707 _cleanup_strv_free_ char **files = NULL;
cba2ef02 2708 char **p;
83096483
LP
2709 int r;
2710
2711 assert(scope >= 0);
2712 assert(scope < _UNIT_FILE_SCOPE_MAX);
8965d9f8 2713 assert(presets);
0ec0deaa 2714
83096483 2715 if (scope == UNIT_FILE_SYSTEM)
b5084605 2716 r = conf_files_list(&files, ".preset", root_dir, 0,
a7480dba
LP
2717 "/etc/systemd/system-preset",
2718 "/usr/local/lib/systemd/system-preset",
2719 "/usr/lib/systemd/system-preset",
b4bdfefa 2720#ifdef HAVE_SPLIT_USR
a7480dba 2721 "/lib/systemd/system-preset",
b4bdfefa 2722#endif
83096483
LP
2723 NULL);
2724 else if (scope == UNIT_FILE_GLOBAL)
b5084605 2725 r = conf_files_list(&files, ".preset", root_dir, 0,
a7480dba
LP
2726 "/etc/systemd/user-preset",
2727 "/usr/local/lib/systemd/user-preset",
2728 "/usr/lib/systemd/user-preset",
83096483 2729 NULL);
8965d9f8
AC
2730 else {
2731 *presets = (Presets){};
2732
2733 return 0;
2734 }
83096483
LP
2735
2736 if (r < 0)
2737 return r;
2738
cba2ef02 2739 STRV_FOREACH(p, files) {
7fd1b19b 2740 _cleanup_fclose_ FILE *f;
0ec0deaa 2741 char line[LINE_MAX];
d544d1a4 2742 int n = 0;
83096483 2743
cba2ef02 2744 f = fopen(*p, "re");
83096483
LP
2745 if (!f) {
2746 if (errno == ENOENT)
2747 continue;
2748
d9e5e694 2749 return -errno;
83096483
LP
2750 }
2751
0ec0deaa 2752 FOREACH_LINE(line, f, return -errno) {
8965d9f8 2753 PresetRule rule = {};
0ec0deaa
LP
2754 const char *parameter;
2755 char *l;
83096483
LP
2756
2757 l = strstrip(line);
d544d1a4 2758 n++;
83096483 2759
0ec0deaa
LP
2760 if (isempty(l))
2761 continue;
2762 if (strchr(COMMENTS, *l))
83096483
LP
2763 continue;
2764
0ec0deaa
LP
2765 parameter = first_word(l, "enable");
2766 if (parameter) {
8965d9f8 2767 char *pattern;
d9e5e694 2768
8965d9f8
AC
2769 pattern = strdup(parameter);
2770 if (!pattern)
2771 return -ENOMEM;
2772
2773 rule = (PresetRule) {
2774 .pattern = pattern,
2775 .action = PRESET_ENABLE,
2776 };
0ec0deaa 2777 }
83096483 2778
0ec0deaa
LP
2779 parameter = first_word(l, "disable");
2780 if (parameter) {
8965d9f8 2781 char *pattern;
d9e5e694 2782
8965d9f8
AC
2783 pattern = strdup(parameter);
2784 if (!pattern)
2785 return -ENOMEM;
2786
2787 rule = (PresetRule) {
2788 .pattern = pattern,
2789 .action = PRESET_DISABLE,
2790 };
2791 }
2792
2793 if (rule.action) {
2794 if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1))
2795 return -ENOMEM;
2796
2797 ps.rules[ps.n_rules++] = rule;
0ec0deaa
LP
2798 continue;
2799 }
2800
d544d1a4 2801 log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
83096483 2802 }
83096483
LP
2803 }
2804
8965d9f8
AC
2805 *presets = ps;
2806 ps = (Presets){};
2807
2808 return 0;
2809}
2810
2811static int query_presets(const char *name, const Presets presets) {
2812 PresetAction action = PRESET_UNKNOWN;
2813 size_t i;
2814
2815 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2816 return -EINVAL;
2817
2818 for (i = 0; i < presets.n_rules; i++)
2819 if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
2820 action = presets.rules[i].action;
2821 break;
2822 }
2823
2824 switch (action) {
2825 case PRESET_UNKNOWN:
2826 log_debug("Preset files don't specify rule for %s. Enabling.", name);
2827 return 1;
2828 case PRESET_ENABLE:
2829 log_debug("Preset files say enable %s.", name);
2830 return 1;
2831 case PRESET_DISABLE:
2832 log_debug("Preset files say disable %s.", name);
2833 return 0;
2834 default:
2835 assert_not_reached("invalid preset action");
2836 }
2837}
2838
2839int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
2840 _cleanup_(presets_freep) Presets presets = {};
2841 int r;
2842
2843 r = read_presets(scope, root_dir, &presets);
2844 if (r < 0)
2845 return r;
2846
2847 return query_presets(name, presets);
83096483
LP
2848}
2849
0ec0deaa
LP
2850static int execute_preset(
2851 UnitFileScope scope,
2852 InstallContext *plus,
2853 InstallContext *minus,
2854 const LookupPaths *paths,
2855 const char *config_path,
0ec0deaa
LP
2856 char **files,
2857 UnitFilePresetMode mode,
2858 bool force,
2859 UnitFileChange **changes,
2860 unsigned *n_changes) {
2861
2862 int r;
2863
2864 assert(plus);
2865 assert(minus);
2866 assert(paths);
2867 assert(config_path);
2868
2869 if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
2870 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2871
637d6e5b 2872 r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
0ec0deaa
LP
2873 if (r < 0)
2874 return r;
2875
3b3557c4 2876 r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
0ec0deaa
LP
2877 } else
2878 r = 0;
2879
2880 if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
2881 int q;
2882
2883 /* Returns number of symlinks that where supposed to be installed. */
e4bb56c7 2884 q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
0ec0deaa
LP
2885 if (r >= 0) {
2886 if (q < 0)
2887 r = q;
2888 else
596fc263 2889 r += q;
0ec0deaa
LP
2890 }
2891 }
2892
2893 return r;
2894}
2895
2896static int preset_prepare_one(
2897 UnitFileScope scope,
2898 InstallContext *plus,
2899 InstallContext *minus,
2900 LookupPaths *paths,
af3d8113 2901 const char *name,
8965d9f8 2902 Presets presets,
af3d8113
ZJS
2903 UnitFileChange **changes,
2904 unsigned *n_changes) {
0ec0deaa 2905
11e11fd5 2906 _cleanup_(install_context_done) InstallContext tmp = {};
0ec0deaa
LP
2907 UnitFileInstallInfo *i;
2908 int r;
2909
11e11fd5 2910 if (install_info_find(plus, name) || install_info_find(minus, name))
0ec0deaa
LP
2911 return 0;
2912
59108fbe
ZJS
2913 r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2914 &i, changes, n_changes);
11e11fd5
ZJS
2915 if (r < 0)
2916 return r;
2917 if (!streq(name, i->name)) {
2918 log_debug("Skipping %s because is an alias for %s", name, i->name);
2919 return 0;
2920 }
2921
8965d9f8 2922 r = query_presets(name, presets);
0ec0deaa
LP
2923 if (r < 0)
2924 return r;
2925
2926 if (r > 0) {
59108fbe
ZJS
2927 r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2928 &i, changes, n_changes);
0ec0deaa
LP
2929 if (r < 0)
2930 return r;
2931
af3d8113 2932 r = install_info_may_process(i, paths, changes, n_changes);
76adb5b8
LP
2933 if (r < 0)
2934 return r;
0ec0deaa 2935 } else
59108fbe
ZJS
2936 r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2937 &i, changes, n_changes);
0ec0deaa
LP
2938
2939 return r;
2940}
2941
83096483
LP
2942int unit_file_preset(
2943 UnitFileScope scope,
b3796dd8 2944 UnitFileFlags flags,
83096483 2945 const char *root_dir,
7195aa42 2946 char **files,
d309c1c3 2947 UnitFilePresetMode mode,
83096483
LP
2948 UnitFileChange **changes,
2949 unsigned *n_changes) {
2950
59ccf93d 2951 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
da39f6a6 2952 _cleanup_lookup_paths_free_ LookupPaths paths = {};
8965d9f8 2953 _cleanup_(presets_freep) Presets presets = {};
e1c5c2b0 2954 const char *config_path;
da39f6a6 2955 char **i;
0ec0deaa 2956 int r;
83096483
LP
2957
2958 assert(scope >= 0);
2959 assert(scope < _UNIT_FILE_SCOPE_MAX);
86bbe5bf 2960 assert(mode < _UNIT_FILE_PRESET_MAX);
83096483 2961
4943d143 2962 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
2963 if (r < 0)
2964 return r;
2965
b3796dd8 2966 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2967 if (!config_path)
2968 return -ENXIO;
83096483 2969
8965d9f8
AC
2970 r = read_presets(scope, root_dir, &presets);
2971 if (r < 0)
2972 return r;
83096483 2973
8965d9f8 2974 STRV_FOREACH(i, files) {
ff56349d 2975 r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
83096483 2976 if (r < 0)
d9e5e694 2977 return r;
83096483
LP
2978 }
2979
b3796dd8 2980 return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
d309c1c3
LP
2981}
2982
2983int unit_file_preset_all(
2984 UnitFileScope scope,
b3796dd8 2985 UnitFileFlags flags,
d309c1c3
LP
2986 const char *root_dir,
2987 UnitFilePresetMode mode,
d309c1c3
LP
2988 UnitFileChange **changes,
2989 unsigned *n_changes) {
2990
59ccf93d 2991 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
d309c1c3 2992 _cleanup_lookup_paths_free_ LookupPaths paths = {};
8965d9f8 2993 _cleanup_(presets_freep) Presets presets = {};
e1c5c2b0 2994 const char *config_path = NULL;
d309c1c3 2995 char **i;
0ec0deaa 2996 int r;
d309c1c3
LP
2997
2998 assert(scope >= 0);
2999 assert(scope < _UNIT_FILE_SCOPE_MAX);
86bbe5bf 3000 assert(mode < _UNIT_FILE_PRESET_MAX);
d309c1c3 3001
4943d143 3002 r = lookup_paths_init(&paths, scope, 0, root_dir);
d309c1c3
LP
3003 if (r < 0)
3004 return r;
3005
b3796dd8 3006 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
3007 if (!config_path)
3008 return -ENXIO;
d309c1c3 3009
8965d9f8
AC
3010 r = read_presets(scope, root_dir, &presets);
3011 if (r < 0)
3012 return r;
3013
a3c4eb07 3014 STRV_FOREACH(i, paths.search_path) {
d309c1c3 3015 _cleanup_closedir_ DIR *d = NULL;
d25e100b 3016 struct dirent *de;
d309c1c3 3017
401017e0 3018 d = opendir(*i);
d309c1c3
LP
3019 if (!d) {
3020 if (errno == ENOENT)
3021 continue;
3022
3023 return -errno;
3024 }
3025
d25e100b 3026 FOREACH_DIRENT(de, d, return -errno) {
d309c1c3 3027
7410616c 3028 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
d309c1c3
LP
3029 continue;
3030
3031 dirent_ensure_type(d, de);
3032
0ec0deaa 3033 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
d309c1c3
LP
3034 continue;
3035
af3d8113 3036 /* we don't pass changes[] in, because we want to handle errors on our own */
ff56349d 3037 r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
9a0a413a
ZJS
3038 if (r == -ERFKILL)
3039 r = unit_file_changes_add(changes, n_changes,
3040 UNIT_FILE_IS_MASKED, de->d_name, NULL);
893275df
ZJS
3041 else if (r == -ENOLINK)
3042 r = unit_file_changes_add(changes, n_changes,
3043 UNIT_FILE_IS_DANGLING, de->d_name, NULL);
d309c1c3
LP
3044 if (r < 0)
3045 return r;
3046 }
3047 }
3048
b3796dd8 3049 return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
83096483
LP
3050}
3051
59ccf93d
LP
3052static void unit_file_list_free_one(UnitFileList *f) {
3053 if (!f)
d9e5e694
ZJS
3054 return;
3055
59ccf93d
LP
3056 free(f->path);
3057 free(f);
d9e5e694 3058}
59ccf93d 3059
0ec0deaa
LP
3060Hashmap* unit_file_list_free(Hashmap *h) {
3061 UnitFileList *i;
3062
3063 while ((i = hashmap_steal_first(h)))
3064 unit_file_list_free_one(i);
3065
3066 return hashmap_free(h);
3067}
3068
59ccf93d 3069DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
d9e5e694 3070
83096483
LP
3071int unit_file_get_list(
3072 UnitFileScope scope,
3073 const char *root_dir,
313fe66f 3074 Hashmap *h,
3075 char **states,
3076 char **patterns) {
83096483 3077
7fd1b19b 3078 _cleanup_lookup_paths_free_ LookupPaths paths = {};
d9e5e694 3079 char **i;
83096483
LP
3080 int r;
3081
3082 assert(scope >= 0);
3083 assert(scope < _UNIT_FILE_SCOPE_MAX);
3084 assert(h);
3085
4943d143 3086 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
3087 if (r < 0)
3088 return r;
3089
a3c4eb07 3090 STRV_FOREACH(i, paths.search_path) {
da39f6a6 3091 _cleanup_closedir_ DIR *d = NULL;
d25e100b 3092 struct dirent *de;
83096483 3093
401017e0 3094 d = opendir(*i);
83096483
LP
3095 if (!d) {
3096 if (errno == ENOENT)
3097 continue;
a1feacf7 3098 if (IN_SET(errno, ENOTDIR, EACCES)) {
25f027c5 3099 log_debug_errno(errno, "Failed to open \"%s\": %m", *i);
a1feacf7
ZJS
3100 continue;
3101 }
83096483 3102
d9e5e694 3103 return -errno;
83096483
LP
3104 }
3105
d25e100b 3106 FOREACH_DIRENT(de, d, return -errno) {
59ccf93d 3107 _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
83096483 3108
7410616c 3109 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
83096483
LP
3110 continue;
3111
313fe66f 3112 if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
3113 continue;
3114
83096483
LP
3115 if (hashmap_get(h, de->d_name))
3116 continue;
3117
da39f6a6 3118 dirent_ensure_type(d, de);
83096483 3119
da39f6a6 3120 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
83096483
LP
3121 continue;
3122
3123 f = new0(UnitFileList, 1);
d9e5e694
ZJS
3124 if (!f)
3125 return -ENOMEM;
83096483 3126
401017e0 3127 f->path = path_make_absolute(de->d_name, *i);
d9e5e694
ZJS
3128 if (!f->path)
3129 return -ENOMEM;
83096483 3130
401017e0 3131 r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state);
d9e5e694 3132 if (r < 0)
0ec0deaa 3133 f->state = UNIT_FILE_BAD;
81fc054d 3134
313fe66f 3135 if (!strv_isempty(states) &&
3136 !strv_contains(states, unit_file_state_to_string(f->state)))
3137 continue;
3138
2b6bf07d 3139 r = hashmap_put(h, basename(f->path), f);
d9e5e694
ZJS
3140 if (r < 0)
3141 return r;
0ec0deaa 3142
d9e5e694 3143 f = NULL; /* prevent cleanup */
83096483
LP
3144 }
3145 }
3146
77cd2c87 3147 return 0;
83096483
LP
3148}
3149
3150static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
3151 [UNIT_FILE_ENABLED] = "enabled",
771faa9a 3152 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
83096483
LP
3153 [UNIT_FILE_LINKED] = "linked",
3154 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
3155 [UNIT_FILE_MASKED] = "masked",
3156 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
3157 [UNIT_FILE_STATIC] = "static",
b5b46d59 3158 [UNIT_FILE_DISABLED] = "disabled",
aedd4012 3159 [UNIT_FILE_INDIRECT] = "indirect",
f4139308 3160 [UNIT_FILE_GENERATED] = "generated",
e4fca67f 3161 [UNIT_FILE_TRANSIENT] = "transient",
0ec0deaa 3162 [UNIT_FILE_BAD] = "bad",
83096483
LP
3163};
3164
3165DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
c0576cd6
LP
3166
3167static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
3168 [UNIT_FILE_SYMLINK] = "symlink",
3169 [UNIT_FILE_UNLINK] = "unlink",
9a0a413a 3170 [UNIT_FILE_IS_MASKED] = "masked",
893275df 3171 [UNIT_FILE_IS_DANGLING] = "dangling",
c0576cd6
LP
3172};
3173
3174DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
d309c1c3 3175
86bbe5bf 3176static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
d309c1c3
LP
3177 [UNIT_FILE_PRESET_FULL] = "full",
3178 [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
3179 [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
3180};
3181
3182DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);