]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/install.c
meson: check for sys/auxv.h
[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,
d9b4b48f 730 bool match_aliases,
83096483
LP
731 int fd,
732 const char *path,
733 const char *config_path,
734 bool *same_name_link) {
735
7fd1b19b 736 _cleanup_closedir_ DIR *d = NULL;
d25e100b
LP
737 struct dirent *de;
738 int r = 0;
83096483 739
5cd8ae31 740 assert(i);
83096483
LP
741 assert(fd >= 0);
742 assert(path);
743 assert(config_path);
744 assert(same_name_link);
745
746 d = fdopendir(fd);
747 if (!d) {
03e334a1 748 safe_close(fd);
83096483
LP
749 return -errno;
750 }
751
d25e100b 752 FOREACH_DIRENT(de, d, return -errno) {
83096483
LP
753
754 dirent_ensure_type(d, de);
755
756 if (de->d_type == DT_DIR) {
7fd1b19b 757 _cleanup_free_ char *p = NULL;
d25e100b 758 int nfd, q;
83096483
LP
759
760 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
761 if (nfd < 0) {
762 if (errno == ENOENT)
763 continue;
764
765 if (r == 0)
766 r = -errno;
767 continue;
768 }
769
770 p = path_make_absolute(de->d_name, path);
771 if (!p) {
03e334a1 772 safe_close(nfd);
ea55addc 773 return -ENOMEM;
83096483
LP
774 }
775
776 /* This will close nfd, regardless whether it succeeds or not */
d9b4b48f 777 q = find_symlinks_fd(root_dir, i, match_aliases, nfd,
5cd8ae31 778 p, config_path, same_name_link);
ea55addc
ZJS
779 if (q > 0)
780 return 1;
83096483
LP
781 if (r == 0)
782 r = q;
783
784 } else if (de->d_type == DT_LNK) {
7fd1b19b 785 _cleanup_free_ char *p = NULL, *dest = NULL;
83096483
LP
786 bool found_path, found_dest, b = false;
787 int q;
788
789 /* Acquire symlink name */
790 p = path_make_absolute(de->d_name, path);
ea55addc
ZJS
791 if (!p)
792 return -ENOMEM;
83096483
LP
793
794 /* Acquire symlink destination */
0ec0deaa 795 q = readlink_malloc(p, &dest);
d25e100b
LP
796 if (q == -ENOENT)
797 continue;
83096483 798 if (q < 0) {
83096483
LP
799 if (r == 0)
800 r = q;
801 continue;
802 }
803
0ec0deaa
LP
804 /* Make absolute */
805 if (!path_is_absolute(dest)) {
806 char *x;
807
808 x = prefix_root(root_dir, dest);
809 if (!x)
810 return -ENOMEM;
811
812 free(dest);
813 dest = x;
814 }
815
83096483
LP
816 /* Check if the symlink itself matches what we
817 * are looking for */
5cd8ae31
ZJS
818 if (path_is_absolute(i->name))
819 found_path = path_equal(p, i->name);
83096483 820 else
5cd8ae31 821 found_path = streq(de->d_name, i->name);
83096483
LP
822
823 /* Check if what the symlink points to
824 * matches what we are looking for */
5cd8ae31
ZJS
825 if (path_is_absolute(i->name))
826 found_dest = path_equal(dest, i->name);
83096483 827 else
5cd8ae31 828 found_dest = streq(basename(dest), i->name);
83096483 829
83096483 830 if (found_path && found_dest) {
7fd1b19b 831 _cleanup_free_ char *t = NULL;
83096483
LP
832
833 /* Filter out same name links in the main
834 * config path */
5cd8ae31 835 t = path_make_absolute(i->name, config_path);
ea55addc
ZJS
836 if (!t)
837 return -ENOMEM;
83096483
LP
838
839 b = path_equal(t, p);
83096483
LP
840 }
841
83096483
LP
842 if (b)
843 *same_name_link = true;
5cd8ae31 844 else if (found_path || found_dest) {
d9b4b48f
ZJS
845 if (!match_aliases)
846 return 1;
847
5cd8ae31
ZJS
848 /* Check if symlink name is in the set of names used by [Install] */
849 q = is_symlink_with_known_name(i, de->d_name);
5cd8ae31
ZJS
850 if (q < 0)
851 return q;
852 if (q > 0)
853 return 1;
854 }
83096483
LP
855 }
856 }
d25e100b
LP
857
858 return r;
83096483
LP
859}
860
861static int find_symlinks(
0ec0deaa 862 const char *root_dir,
5cd8ae31 863 UnitFileInstallInfo *i,
d9b4b48f 864 bool match_name,
83096483
LP
865 const char *config_path,
866 bool *same_name_link) {
867
868 int fd;
869
5cd8ae31 870 assert(i);
83096483
LP
871 assert(config_path);
872 assert(same_name_link);
873
1cd1dab9 874 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
d5891fda 875 if (fd < 0) {
a1feacf7 876 if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
d5891fda 877 return 0;
83096483 878 return -errno;
d5891fda 879 }
83096483
LP
880
881 /* This takes possession of fd and closes it */
d9b4b48f 882 return find_symlinks_fd(root_dir, i, match_name, fd,
5cd8ae31 883 config_path, config_path, same_name_link);
83096483
LP
884}
885
886static int find_symlinks_in_scope(
d2561cfd 887 UnitFileScope scope,
e1c5c2b0 888 const LookupPaths *paths,
5cd8ae31 889 UnitFileInstallInfo *i,
d9b4b48f 890 bool match_name,
83096483
LP
891 UnitFileState *state) {
892
dfead90d
LP
893 bool same_name_link_runtime = false, same_name_link_config = false;
894 bool enabled_in_runtime = false, enabled_at_all = false;
895 char **p;
0ec0deaa 896 int r;
83096483 897
e1c5c2b0 898 assert(paths);
5cd8ae31 899 assert(i);
83096483 900
dfead90d
LP
901 STRV_FOREACH(p, paths->search_path) {
902 bool same_name_link = false;
903
d9b4b48f 904 r = find_symlinks(paths->root_dir, i, match_name, *p, &same_name_link);
dfead90d
LP
905 if (r < 0)
906 return r;
907 if (r > 0) {
908 /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
909
910 r = path_is_config(paths, *p, false);
911 if (r < 0)
912 return r;
913 if (r > 0) {
914 /* This is the best outcome, let's return it immediately. */
915 *state = UNIT_FILE_ENABLED;
916 return 1;
917 }
918
d2561cfd
ZJS
919 /* look for globally enablement of user units */
920 if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) {
921 *state = UNIT_FILE_ENABLED;
922 return 1;
923 }
924
dfead90d
LP
925 r = path_is_runtime(paths, *p, false);
926 if (r < 0)
927 return r;
928 if (r > 0)
929 enabled_in_runtime = true;
930 else
931 enabled_at_all = true;
932
933 } else if (same_name_link) {
934
935 r = path_is_config(paths, *p, false);
936 if (r < 0)
937 return r;
938 if (r > 0)
939 same_name_link_config = true;
940 else {
941 r = path_is_runtime(paths, *p, false);
942 if (r < 0)
943 return r;
944 if (r > 0)
945 same_name_link_runtime = true;
946 }
947 }
83096483
LP
948 }
949
dfead90d 950 if (enabled_in_runtime) {
0ec0deaa 951 *state = UNIT_FILE_ENABLED_RUNTIME;
dfead90d
LP
952 return 1;
953 }
954
955 /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
956 * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
957 * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
958 * something, and hence are a much stronger concept. */
5cd8ae31 959 if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
dfead90d
LP
960 *state = UNIT_FILE_STATIC;
961 return 1;
83096483
LP
962 }
963
964 /* Hmm, we didn't find it, but maybe we found the same name
965 * link? */
dfead90d 966 if (same_name_link_config) {
0ec0deaa
LP
967 *state = UNIT_FILE_LINKED;
968 return 1;
969 }
83096483
LP
970 if (same_name_link_runtime) {
971 *state = UNIT_FILE_LINKED_RUNTIME;
972 return 1;
83096483
LP
973 }
974
975 return 0;
976}
977
0ec0deaa 978static void install_info_free(UnitFileInstallInfo *i) {
83096483 979
0ec0deaa
LP
980 if (!i)
981 return;
83096483 982
0ec0deaa
LP
983 free(i->name);
984 free(i->path);
985 strv_free(i->aliases);
986 strv_free(i->wanted_by);
987 strv_free(i->required_by);
988 strv_free(i->also);
989 free(i->default_instance);
990 free(i->symlink_target);
991 free(i);
992}
83096483 993
0ec0deaa
LP
994static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) {
995 UnitFileInstallInfo *i;
83096483 996
0ec0deaa
LP
997 if (!m)
998 return NULL;
83096483 999
0ec0deaa
LP
1000 while ((i = ordered_hashmap_steal_first(m)))
1001 install_info_free(i);
83096483 1002
0ec0deaa
LP
1003 return ordered_hashmap_free(m);
1004}
83096483 1005
0ec0deaa
LP
1006static void install_context_done(InstallContext *c) {
1007 assert(c);
83096483 1008
0ec0deaa
LP
1009 c->will_process = install_info_hashmap_free(c->will_process);
1010 c->have_processed = install_info_hashmap_free(c->have_processed);
1011}
83096483 1012
0ec0deaa
LP
1013static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
1014 UnitFileInstallInfo *i;
83096483 1015
0ec0deaa
LP
1016 i = ordered_hashmap_get(c->have_processed, name);
1017 if (i)
1018 return i;
83096483 1019
0ec0deaa 1020 return ordered_hashmap_get(c->will_process, name);
83096483
LP
1021}
1022
af3d8113
ZJS
1023static int install_info_may_process(
1024 UnitFileInstallInfo *i,
1025 const LookupPaths *paths,
1026 UnitFileChange **changes,
1027 unsigned *n_changes) {
76adb5b8
LP
1028 assert(i);
1029 assert(paths);
1030
047d91f9
ZJS
1031 /* Checks whether the loaded unit file is one we should process, or is masked,
1032 * transient or generated and thus not subject to enable/disable operations. */
76adb5b8 1033
af3d8113
ZJS
1034 if (i->type == UNIT_FILE_TYPE_MASKED) {
1035 unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
76ec966f 1036 return -ERFKILL;
af3d8113
ZJS
1037 }
1038 if (path_is_generator(paths, i->path) ||
1039 path_is_transient(paths, i->path)) {
1040 unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
76adb5b8 1041 return -EADDRNOTAVAIL;
af3d8113 1042 }
76adb5b8
LP
1043
1044 return 0;
1045}
1046
ff56349d
ZJS
1047/**
1048 * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
1049 * hashmap, or retrieves the existing one if already present.
19539807
ZJS
1050 *
1051 * Returns negative on error, 0 if the unit was already known, 1 otherwise.
ff56349d 1052 */
83096483
LP
1053static int install_info_add(
1054 InstallContext *c,
1055 const char *name,
0ec0deaa 1056 const char *path,
19539807 1057 bool auxiliary,
0ec0deaa
LP
1058 UnitFileInstallInfo **ret) {
1059
cab6235f 1060 UnitFileInstallInfo *i = NULL;
83096483
LP
1061 int r;
1062
1063 assert(c);
1064 assert(name || path);
1065
1066 if (!name)
2b6bf07d 1067 name = basename(path);
83096483 1068
7410616c 1069 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
83096483
LP
1070 return -EINVAL;
1071
0ec0deaa
LP
1072 i = install_info_find(c, name);
1073 if (i) {
19539807
ZJS
1074 i->auxiliary = i->auxiliary && auxiliary;
1075
0ec0deaa
LP
1076 if (ret)
1077 *ret = i;
83096483 1078 return 0;
0ec0deaa 1079 }
83096483 1080
0ec0deaa 1081 r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
83096483
LP
1082 if (r < 0)
1083 return r;
1084
cab6235f 1085 i = new0(UnitFileInstallInfo, 1);
83096483
LP
1086 if (!i)
1087 return -ENOMEM;
0ec0deaa 1088 i->type = _UNIT_FILE_TYPE_INVALID;
19539807 1089 i->auxiliary = auxiliary;
83096483
LP
1090
1091 i->name = strdup(name);
1092 if (!i->name) {
1093 r = -ENOMEM;
1094 goto fail;
1095 }
1096
1097 if (path) {
1098 i->path = strdup(path);
1099 if (!i->path) {
1100 r = -ENOMEM;
1101 goto fail;
1102 }
1103 }
1104
0ec0deaa 1105 r = ordered_hashmap_put(c->will_process, i->name, i);
83096483
LP
1106 if (r < 0)
1107 goto fail;
1108
0ec0deaa
LP
1109 if (ret)
1110 *ret = i;
1111
19539807 1112 return 1;
83096483
LP
1113
1114fail:
d25e100b 1115 install_info_free(i);
83096483
LP
1116 return r;
1117}
1118
a7724589
ZJS
1119static int config_parse_alias(
1120 const char *unit,
1121 const char *filename,
1122 unsigned line,
1123 const char *section,
1124 unsigned section_line,
1125 const char *lvalue,
1126 int ltype,
1127 const char *rvalue,
1128 void *data,
1129 void *userdata) {
1130
1131 const char *name;
1132 UnitType type;
1133
1134 assert(filename);
1135 assert(lvalue);
1136 assert(rvalue);
1137
1138 name = basename(filename);
1139 type = unit_name_to_type(name);
1140 if (!unit_type_may_alias(type))
1141 return log_syntax(unit, LOG_WARNING, filename, line, 0,
3b8769bd 1142 "Alias= is not allowed for %s units, ignoring.",
a7724589
ZJS
1143 unit_type_to_string(type));
1144
1145 return config_parse_strv(unit, filename, line, section, section_line,
1146 lvalue, ltype, rvalue, data, userdata);
1147}
1148
d54c4993
LP
1149static int config_parse_also(
1150 const char *unit,
1151 const char *filename,
1152 unsigned line,
1153 const char *section,
1154 unsigned section_line,
1155 const char *lvalue,
1156 int ltype,
1157 const char *rvalue,
1158 void *data,
1159 void *userdata) {
83096483 1160
19539807 1161 UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
0ec0deaa
LP
1162 InstallContext *c = data;
1163 int r;
83096483
LP
1164
1165 assert(filename);
1166 assert(lvalue);
1167 assert(rvalue);
1168
0ec0deaa 1169 for (;;) {
a6612e65 1170 _cleanup_free_ char *word = NULL, *printed = NULL;
03da6513 1171
0ec0deaa
LP
1172 r = extract_first_word(&rvalue, &word, NULL, 0);
1173 if (r < 0)
1174 return r;
03da6513
SS
1175 if (r == 0)
1176 break;
83096483 1177
a6612e65 1178 r = install_full_printf(info, word, &printed);
d9e5e694 1179 if (r < 0)
83096483 1180 return r;
aedd4012 1181
a6612e65
ZJS
1182 if (!unit_name_is_valid(printed, UNIT_NAME_ANY))
1183 return -EINVAL;
1184
1185 r = install_info_add(c, printed, NULL, true, &alsoinfo);
d9e5e694 1186 if (r < 0)
83096483 1187 return r;
aedd4012 1188
a6612e65 1189 r = strv_push(&info->also, printed);
aedd4012
JS
1190 if (r < 0)
1191 return r;
0ec0deaa 1192
a6612e65 1193 printed = NULL;
83096483
LP
1194 }
1195
1196 return 0;
1197}
1198
d54c4993
LP
1199static int config_parse_default_instance(
1200 const char *unit,
1201 const char *filename,
1202 unsigned line,
1203 const char *section,
1204 unsigned section_line,
1205 const char *lvalue,
1206 int ltype,
1207 const char *rvalue,
1208 void *data,
1209 void *userdata) {
1210
cab6235f 1211 UnitFileInstallInfo *i = data;
6597fa61 1212 const char *name;
d7604756 1213 _cleanup_free_ char *printed = NULL;
d54c4993
LP
1214 int r;
1215
1216 assert(filename);
1217 assert(lvalue);
1218 assert(rvalue);
1219
6597fa61
ZJS
1220 name = basename(filename);
1221 if (unit_name_is_valid(name, UNIT_NAME_INSTANCE))
1222 /* When enabling an instance, we might be using a template unit file,
1223 * but we should ignore DefaultInstance silently. */
1224 return 0;
1225 if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
1226 return log_syntax(unit, LOG_WARNING, filename, line, 0,
3b8769bd 1227 "DefaultInstance= only makes sense for template units, ignoring.");
6597fa61 1228
d54c4993
LP
1229 r = install_full_printf(i, rvalue, &printed);
1230 if (r < 0)
1231 return r;
1232
d7604756 1233 if (!unit_instance_is_valid(printed))
d54c4993
LP
1234 return -EINVAL;
1235
3b319885 1236 return free_and_replace(i->default_instance, printed);
d54c4993
LP
1237}
1238
83096483
LP
1239static int unit_file_load(
1240 InstallContext *c,
cab6235f 1241 UnitFileInstallInfo *info,
83096483 1242 const char *path,
0ec0deaa 1243 SearchFlags flags) {
83096483 1244
f975e971 1245 const ConfigTableItem items[] = {
a7724589 1246 { "Install", "Alias", config_parse_alias, 0, &info->aliases },
d54c4993
LP
1247 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
1248 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
1249 { "Install", "DefaultInstance", config_parse_default_instance, 0, info },
1250 { "Install", "Also", config_parse_also, 0, c },
d54c4993 1251 {}
83096483
LP
1252 };
1253
133e5b36
ZJS
1254 const char *name;
1255 UnitType type;
7fd1b19b 1256 _cleanup_fclose_ FILE *f = NULL;
0ec0deaa
LP
1257 _cleanup_close_ int fd = -1;
1258 struct stat st;
1259 int r;
83096483 1260
83096483
LP
1261 assert(info);
1262 assert(path);
1263
133e5b36
ZJS
1264 name = basename(path);
1265 type = unit_name_to_type(name);
1266 if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
1267 !unit_type_may_template(type))
1268 return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type));
1269
0ec0deaa
LP
1270 if (!(flags & SEARCH_LOAD)) {
1271 r = lstat(path, &st);
1272 if (r < 0)
d25e100b
LP
1273 return -errno;
1274
0ec0deaa
LP
1275 if (null_or_empty(&st))
1276 info->type = UNIT_FILE_TYPE_MASKED;
1277 else if (S_ISREG(st.st_mode))
1278 info->type = UNIT_FILE_TYPE_REGULAR;
1279 else if (S_ISLNK(st.st_mode))
1280 return -ELOOP;
1281 else if (S_ISDIR(st.st_mode))
1282 return -EISDIR;
1283 else
1284 return -ENOTTY;
1285
d25e100b 1286 return 0;
e94937df
LN
1287 }
1288
047d91f9
ZJS
1289 /* c is only needed if we actually load the file */
1290 assert(c);
1291
0ec0deaa 1292 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
83096483
LP
1293 if (fd < 0)
1294 return -errno;
0ec0deaa
LP
1295 if (fstat(fd, &st) < 0)
1296 return -errno;
1297 if (null_or_empty(&st)) {
d986e364 1298 info->type = UNIT_FILE_TYPE_MASKED;
0ec0deaa
LP
1299 return 0;
1300 }
1301 if (S_ISDIR(st.st_mode))
1302 return -EISDIR;
1303 if (!S_ISREG(st.st_mode))
1304 return -ENOTTY;
83096483
LP
1305
1306 f = fdopen(fd, "re");
0ec0deaa
LP
1307 if (!f)
1308 return -errno;
1309 fd = -1;
83096483 1310
36f822c4
ZJS
1311 r = config_parse(NULL, path, f,
1312 NULL,
1313 config_item_table_lookup, items,
1314 true, true, false, info);
83096483 1315 if (r < 0)
59108fbe 1316 return log_debug_errno(r, "Failed to parse %s: %m", info->name);
83096483 1317
0ec0deaa 1318 info->type = UNIT_FILE_TYPE_REGULAR;
aedd4012 1319
78d54bd4 1320 return
693eb9a2
LP
1321 (int) strv_length(info->aliases) +
1322 (int) strv_length(info->wanted_by) +
1323 (int) strv_length(info->required_by);
83096483
LP
1324}
1325
0ec0deaa
LP
1326static int unit_file_load_or_readlink(
1327 InstallContext *c,
1328 UnitFileInstallInfo *info,
1329 const char *path,
1330 const char *root_dir,
1331 SearchFlags flags) {
1332
401017e0 1333 _cleanup_free_ char *target = NULL;
0ec0deaa
LP
1334 int r;
1335
401017e0 1336 r = unit_file_load(c, info, path, flags);
0ec0deaa
LP
1337 if (r != -ELOOP)
1338 return r;
1339
1340 /* This is a symlink, let's read it. */
1341
401017e0 1342 r = readlink_malloc(path, &target);
0ec0deaa
LP
1343 if (r < 0)
1344 return r;
1345
401017e0 1346 if (path_equal(target, "/dev/null"))
0ec0deaa
LP
1347 info->type = UNIT_FILE_TYPE_MASKED;
1348 else {
1349 const char *bn;
1350 UnitType a, b;
1351
401017e0 1352 bn = basename(target);
0ec0deaa
LP
1353
1354 if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
1355
1356 if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
1357 return -EINVAL;
1358
1359 } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
1360
1361 if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
1362 return -EINVAL;
1363
1364 } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
1365
1366 if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
1367 return -EINVAL;
1368 } else
1369 return -EINVAL;
1370
1371 /* Enforce that the symlink destination does not
1372 * change the unit file type. */
1373
1374 a = unit_name_to_type(info->name);
1375 b = unit_name_to_type(bn);
1376 if (a < 0 || b < 0 || a != b)
1377 return -EINVAL;
1378
401017e0
LP
1379 if (path_is_absolute(target))
1380 /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
1381 info->symlink_target = prefix_root(root_dir, target);
1382 else
1383 /* This is a relative path, take it relative to the dir the symlink is located in. */
1384 info->symlink_target = file_in_same_dir(path, target);
1385 if (!info->symlink_target)
1386 return -ENOMEM;
1387
0ec0deaa 1388 info->type = UNIT_FILE_TYPE_SYMLINK;
0ec0deaa
LP
1389 }
1390
1391 return 0;
1392}
1393
83096483
LP
1394static int unit_file_search(
1395 InstallContext *c,
cab6235f 1396 UnitFileInstallInfo *info,
a8ffe6fb 1397 const LookupPaths *paths,
0ec0deaa 1398 SearchFlags flags) {
83096483 1399
64f9280e 1400 _cleanup_free_ char *template = NULL;
83096483
LP
1401 char **p;
1402 int r;
1403
83096483
LP
1404 assert(info);
1405 assert(paths);
1406
0ec0deaa
LP
1407 /* Was this unit already loaded? */
1408 if (info->type != _UNIT_FILE_TYPE_INVALID)
1409 return 0;
1410
278fa575 1411 if (info->path)
e4bb56c7 1412 return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags);
83096483
LP
1413
1414 assert(info->name);
1415
a3c4eb07 1416 STRV_FOREACH(p, paths->search_path) {
e50bd775 1417 _cleanup_free_ char *path = NULL;
83096483 1418
605405c6 1419 path = strjoin(*p, "/", info->name);
83096483
LP
1420 if (!path)
1421 return -ENOMEM;
1422
e4bb56c7 1423 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
0155928c 1424 if (r >= 0) {
83096483 1425 info->path = path;
62b00233 1426 path = NULL;
e50bd775 1427 return r;
a1feacf7 1428 } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
0155928c 1429 return r;
e50bd775 1430 }
62b00233 1431
7410616c 1432 if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
e50bd775
LP
1433 /* Unit file doesn't exist, however instance
1434 * enablement was requested. We will check if it is
1435 * possible to load template unit file. */
29283ea4 1436
7410616c
LP
1437 r = unit_name_template(info->name, &template);
1438 if (r < 0)
1439 return r;
e50bd775 1440
a3c4eb07 1441 STRV_FOREACH(p, paths->search_path) {
e50bd775
LP
1442 _cleanup_free_ char *path = NULL;
1443
605405c6 1444 path = strjoin(*p, "/", template);
62b00233
ZJS
1445 if (!path)
1446 return -ENOMEM;
29283ea4 1447
e4bb56c7 1448 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
0155928c 1449 if (r >= 0) {
62b00233
ZJS
1450 info->path = path;
1451 path = NULL;
e50bd775 1452 return r;
a1feacf7 1453 } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
0155928c 1454 return r;
29283ea4 1455 }
83096483
LP
1456 }
1457
64f9280e 1458 log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template));
83096483
LP
1459 return -ENOENT;
1460}
1461
0ec0deaa
LP
1462static int install_info_follow(
1463 InstallContext *c,
1464 UnitFileInstallInfo *i,
83096483 1465 const char *root_dir,
9f6cbcf5
LP
1466 SearchFlags flags,
1467 bool ignore_different_name) {
0ec0deaa
LP
1468
1469 assert(c);
1470 assert(i);
1471
1472 if (i->type != UNIT_FILE_TYPE_SYMLINK)
1473 return -EINVAL;
1474 if (!i->symlink_target)
1475 return -EINVAL;
1476
1477 /* If the basename doesn't match, the caller should add a
1478 * complete new entry for this. */
1479
9f6cbcf5 1480 if (!ignore_different_name && !streq(basename(i->symlink_target), i->name))
0ec0deaa
LP
1481 return -EXDEV;
1482
3b319885 1483 free_and_replace(i->path, i->symlink_target);
0ec0deaa
LP
1484 i->type = _UNIT_FILE_TYPE_INVALID;
1485
1486 return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
1487}
1488
64f9280e 1489/**
ff56349d
ZJS
1490 * Search for the unit file. If the unit name is a symlink, follow the symlink to the
1491 * target, maybe more than once. Propagate the instance name if present.
64f9280e 1492 */
0ec0deaa
LP
1493static int install_info_traverse(
1494 UnitFileScope scope,
1495 InstallContext *c,
0ec0deaa
LP
1496 const LookupPaths *paths,
1497 UnitFileInstallInfo *start,
1498 SearchFlags flags,
1499 UnitFileInstallInfo **ret) {
83096483 1500
cab6235f 1501 UnitFileInstallInfo *i;
0ec0deaa 1502 unsigned k = 0;
83096483
LP
1503 int r;
1504
1505 assert(paths);
0ec0deaa
LP
1506 assert(start);
1507 assert(c);
83096483 1508
e4bb56c7 1509 r = unit_file_search(c, start, paths, flags);
83096483
LP
1510 if (r < 0)
1511 return r;
1512
0ec0deaa
LP
1513 i = start;
1514 while (i->type == UNIT_FILE_TYPE_SYMLINK) {
1515 /* Follow the symlink */
83096483 1516
0ec0deaa
LP
1517 if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
1518 return -ELOOP;
83096483 1519
e1c5c2b0 1520 if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
dfead90d 1521 r = path_is_config(paths, i->path, true);
e1c5c2b0
LP
1522 if (r < 0)
1523 return r;
1524 if (r > 0)
1525 return -ELOOP;
1526 }
83096483 1527
9f6cbcf5 1528 r = install_info_follow(c, i, paths->root_dir, flags, false);
0155928c 1529 if (r == -EXDEV) {
0ec0deaa
LP
1530 _cleanup_free_ char *buffer = NULL;
1531 const char *bn;
83096483 1532
0ec0deaa
LP
1533 /* Target has a different name, create a new
1534 * install info object for that, and continue
1535 * with that. */
83096483 1536
0ec0deaa 1537 bn = basename(i->symlink_target);
83096483 1538
0ec0deaa
LP
1539 if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
1540 unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
83096483 1541
0ec0deaa
LP
1542 _cleanup_free_ char *instance = NULL;
1543
1544 r = unit_name_to_instance(i->name, &instance);
1545 if (r < 0)
1546 return r;
1547
1548 r = unit_name_replace_instance(bn, instance, &buffer);
1549 if (r < 0)
1550 return r;
1551
9f6cbcf5
LP
1552 if (streq(buffer, i->name)) {
1553
1554 /* We filled in the instance, and the target stayed the same? If so, then let's
1555 * honour the link as it is. */
1556
1557 r = install_info_follow(c, i, paths->root_dir, flags, true);
1558 if (r < 0)
1559 return r;
1560
1561 continue;
1562 }
1563
0ec0deaa
LP
1564 bn = buffer;
1565 }
1566
19539807 1567 r = install_info_add(c, bn, NULL, false, &i);
0ec0deaa
LP
1568 if (r < 0)
1569 return r;
1570
0155928c 1571 /* Try again, with the new target we found. */
e4bb56c7 1572 r = unit_file_search(c, i, paths, flags);
893275df
ZJS
1573 if (r == -ENOENT)
1574 /* Translate error code to highlight this specific case */
1575 return -ENOLINK;
0ec0deaa
LP
1576 }
1577
0155928c
ZJS
1578 if (r < 0)
1579 return r;
83096483
LP
1580 }
1581
0ec0deaa
LP
1582 if (ret)
1583 *ret = i;
83096483 1584
0ec0deaa
LP
1585 return 0;
1586}
83096483 1587
ff56349d
ZJS
1588/**
1589 * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
1590 * or the name (otherwise). root_dir is prepended to the path.
1591 */
401017e0
LP
1592static int install_info_add_auto(
1593 InstallContext *c,
1594 const LookupPaths *paths,
1595 const char *name_or_path,
1596 UnitFileInstallInfo **ret) {
1597
1598 assert(c);
1599 assert(name_or_path);
1600
1601 if (path_is_absolute(name_or_path)) {
1602 const char *pp;
1603
1604 pp = prefix_roota(paths->root_dir, name_or_path);
1605
19539807 1606 return install_info_add(c, NULL, pp, false, ret);
401017e0 1607 } else
19539807 1608 return install_info_add(c, name_or_path, NULL, false, ret);
401017e0
LP
1609}
1610
0ec0deaa
LP
1611static int install_info_discover(
1612 UnitFileScope scope,
1613 InstallContext *c,
0ec0deaa
LP
1614 const LookupPaths *paths,
1615 const char *name,
1616 SearchFlags flags,
59108fbe
ZJS
1617 UnitFileInstallInfo **ret,
1618 UnitFileChange **changes,
1619 unsigned *n_changes) {
83096483 1620
0ec0deaa
LP
1621 UnitFileInstallInfo *i;
1622 int r;
83096483 1623
0ec0deaa
LP
1624 assert(c);
1625 assert(paths);
1626 assert(name);
1627
401017e0 1628 r = install_info_add_auto(c, paths, name, &i);
59108fbe
ZJS
1629 if (r >= 0)
1630 r = install_info_traverse(scope, c, paths, i, flags, ret);
83096483 1631
59108fbe
ZJS
1632 if (r < 0)
1633 unit_file_changes_add(changes, n_changes, r, name, NULL);
1634 return r;
83096483
LP
1635}
1636
1637static int install_info_symlink_alias(
cab6235f 1638 UnitFileInstallInfo *i,
401017e0 1639 const LookupPaths *paths,
83096483
LP
1640 const char *config_path,
1641 bool force,
1642 UnitFileChange **changes,
1643 unsigned *n_changes) {
1644
1645 char **s;
1646 int r = 0, q;
1647
1648 assert(i);
401017e0 1649 assert(paths);
83096483
LP
1650 assert(config_path);
1651
1652 STRV_FOREACH(s, i->aliases) {
7fd1b19b 1653 _cleanup_free_ char *alias_path = NULL, *dst = NULL;
83096483 1654
19f6d710
LP
1655 q = install_full_printf(i, *s, &dst);
1656 if (q < 0)
1657 return q;
83096483 1658
7584d236 1659 alias_path = path_make_absolute(dst, config_path);
83096483
LP
1660 if (!alias_path)
1661 return -ENOMEM;
1662
60bec8e4 1663 q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
83096483
LP
1664 if (r == 0)
1665 r = q;
1666 }
1667
1668 return r;
1669}
1670
1671static int install_info_symlink_wants(
cab6235f 1672 UnitFileInstallInfo *i,
401017e0 1673 const LookupPaths *paths,
83096483 1674 const char *config_path,
d54c4993
LP
1675 char **list,
1676 const char *suffix,
83096483
LP
1677 UnitFileChange **changes,
1678 unsigned *n_changes) {
1679
d54c4993
LP
1680 _cleanup_free_ char *buf = NULL;
1681 const char *n;
83096483
LP
1682 char **s;
1683 int r = 0, q;
1684
1685 assert(i);
401017e0 1686 assert(paths);
83096483
LP
1687 assert(config_path);
1688
047d91f9
ZJS
1689 if (strv_isempty(list))
1690 return 0;
1691
be704916 1692 if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance) {
047d91f9
ZJS
1693 UnitFileInstallInfo instance = {
1694 .type = _UNIT_FILE_TYPE_INVALID,
1695 };
1696 _cleanup_free_ char *path = NULL;
0a327d75 1697
7410616c
LP
1698 r = unit_name_replace_instance(i->name, i->default_instance, &buf);
1699 if (r < 0)
1700 return r;
83096483 1701
047d91f9
ZJS
1702 instance.name = buf;
1703 r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS);
1704 if (r < 0)
1705 return r;
1706
1707 path = instance.path;
1708 instance.path = NULL;
1709
1710 if (instance.type == UNIT_FILE_TYPE_MASKED) {
1711 unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL);
1712 return -ERFKILL;
1713 }
1714
d54c4993
LP
1715 n = buf;
1716 } else
1717 n = i->name;
78d54bd4 1718
d54c4993 1719 STRV_FOREACH(s, list) {
7fd1b19b 1720 _cleanup_free_ char *path = NULL, *dst = NULL;
78d54bd4 1721
19f6d710
LP
1722 q = install_full_printf(i, *s, &dst);
1723 if (q < 0)
1724 return q;
7584d236 1725
7410616c 1726 if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
78d54bd4
LP
1727 r = -EINVAL;
1728 continue;
1729 }
1730
605405c6 1731 path = strjoin(config_path, "/", dst, suffix, n);
d54c4993 1732 if (!path)
78d54bd4
LP
1733 return -ENOMEM;
1734
60bec8e4 1735 q = create_symlink(paths, i->path, path, true, changes, n_changes);
78d54bd4
LP
1736 if (r == 0)
1737 r = q;
1738 }
1739
1740 return r;
1741}
1742
83096483 1743static int install_info_symlink_link(
cab6235f 1744 UnitFileInstallInfo *i,
a8ffe6fb 1745 const LookupPaths *paths,
83096483
LP
1746 const char *config_path,
1747 bool force,
1748 UnitFileChange **changes,
1749 unsigned *n_changes) {
1750
7fd1b19b 1751 _cleanup_free_ char *path = NULL;
1dacfd2a 1752 int r;
83096483
LP
1753
1754 assert(i);
1755 assert(paths);
1756 assert(config_path);
1757 assert(i->path);
1758
32c0ed7b 1759 r = in_search_path(paths, i->path);
fe4aede9 1760 if (r < 0)
83096483 1761 return r;
fe4aede9
ZJS
1762 if (r > 0)
1763 return 0;
83096483 1764
605405c6 1765 path = strjoin(config_path, "/", i->name);
1dacfd2a 1766 if (!path)
83096483
LP
1767 return -ENOMEM;
1768
60bec8e4 1769 return create_symlink(paths, i->path, path, force, changes, n_changes);
83096483
LP
1770}
1771
1772static int install_info_apply(
cab6235f 1773 UnitFileInstallInfo *i,
a8ffe6fb 1774 const LookupPaths *paths,
83096483
LP
1775 const char *config_path,
1776 bool force,
1777 UnitFileChange **changes,
1778 unsigned *n_changes) {
1779
1780 int r, q;
1781
1782 assert(i);
1783 assert(paths);
1784 assert(config_path);
1785
0ec0deaa
LP
1786 if (i->type != UNIT_FILE_TYPE_REGULAR)
1787 return 0;
1788
401017e0 1789 r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
83096483 1790
29380daf 1791 q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);
83096483
LP
1792 if (r == 0)
1793 r = q;
1794
29380daf 1795 q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);
78d54bd4
LP
1796 if (r == 0)
1797 r = q;
1798
e4bb56c7 1799 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
fe4aede9
ZJS
1800 /* Do not count links to the unit file towards the "carries_install_info" count */
1801 if (r == 0 && q < 0)
83096483
LP
1802 r = q;
1803
1804 return r;
1805}
1806
1807static int install_context_apply(
0ec0deaa 1808 UnitFileScope scope,
83096483 1809 InstallContext *c,
a8ffe6fb 1810 const LookupPaths *paths,
83096483 1811 const char *config_path,
83096483 1812 bool force,
0ec0deaa 1813 SearchFlags flags,
83096483
LP
1814 UnitFileChange **changes,
1815 unsigned *n_changes) {
1816
cab6235f 1817 UnitFileInstallInfo *i;
0ec0deaa 1818 int r;
83096483
LP
1819
1820 assert(c);
1821 assert(paths);
1822 assert(config_path);
1823
0ec0deaa 1824 if (ordered_hashmap_isempty(c->will_process))
d25e100b 1825 return 0;
83096483 1826
0ec0deaa 1827 r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
d25e100b
LP
1828 if (r < 0)
1829 return r;
83096483 1830
2d5c93c7 1831 r = 0;
0ec0deaa
LP
1832 while ((i = ordered_hashmap_first(c->will_process))) {
1833 int q;
83096483 1834
0ec0deaa
LP
1835 q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1836 if (q < 0)
1837 return q;
83096483 1838
e4bb56c7 1839 r = install_info_traverse(scope, c, paths, i, flags, NULL);
db093eed
ZJS
1840 if (r < 0) {
1841 unit_file_changes_add(changes, n_changes, r, i->name, NULL);
83096483 1842 return r;
db093eed 1843 }
0ec0deaa 1844
f1651715 1845 /* We can attempt to process a masked unit when a different unit
047d91f9 1846 * that we were processing specifies it in Also=. */
f1651715
ZJS
1847 if (i->type == UNIT_FILE_TYPE_MASKED) {
1848 unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
1849 if (r >= 0)
047d91f9
ZJS
1850 /* Assume that something *could* have been enabled here,
1851 * avoid "empty [Install] section" warning. */
f1651715
ZJS
1852 r += 1;
1853 continue;
1854 }
1855
0ec0deaa
LP
1856 if (i->type != UNIT_FILE_TYPE_REGULAR)
1857 continue;
83096483 1858
e4bb56c7 1859 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
0ec0deaa
LP
1860 if (r >= 0) {
1861 if (q < 0)
1862 r = q;
1863 else
596fc263 1864 r += q;
0ec0deaa 1865 }
83096483
LP
1866 }
1867
1868 return r;
1869}
1870
1871static int install_context_mark_for_removal(
0ec0deaa 1872 UnitFileScope scope,
83096483 1873 InstallContext *c,
a8ffe6fb 1874 const LookupPaths *paths,
83096483 1875 Set **remove_symlinks_to,
637d6e5b
LP
1876 const char *config_path,
1877 UnitFileChange **changes,
1878 unsigned *n_changes) {
83096483 1879
cab6235f 1880 UnitFileInstallInfo *i;
0ec0deaa 1881 int r;
83096483
LP
1882
1883 assert(c);
1884 assert(paths);
1885 assert(config_path);
1886
1887 /* Marks all items for removal */
1888
0ec0deaa 1889 if (ordered_hashmap_isempty(c->will_process))
d25e100b 1890 return 0;
83096483 1891
0ec0deaa 1892 r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
d25e100b
LP
1893 if (r < 0)
1894 return r;
83096483 1895
0ec0deaa 1896 while ((i = ordered_hashmap_first(c->will_process))) {
83096483 1897
0ec0deaa
LP
1898 r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
1899 if (r < 0)
83096483 1900 return r;
29283ea4 1901
e4bb56c7 1902 r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
19539807 1903 if (r == -ENOLINK) {
637d6e5b
LP
1904 log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
1905 unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL);
1906 } else if (r == -ENOENT) {
1907
1908 if (i->auxiliary) /* some unit specified in Also= or similar is missing */
1909 log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
1910 else {
1911 log_debug_errno(r, "Unit %s not found, removing name.", i->name);
1912 unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1913 }
0ec0deaa 1914
637d6e5b
LP
1915 } else if (r < 0) {
1916 log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
1917 unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
1918 } else if (i->type == UNIT_FILE_TYPE_MASKED) {
1919 log_debug("Unit file %s is masked, ignoring.", i->name);
1920 unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path ?: i->name, NULL);
1921 continue;
1922 } else if (i->type != UNIT_FILE_TYPE_REGULAR) {
1923 log_debug("Unit %s has type %s, ignoring.", i->name, unit_file_type_to_string(i->type) ?: "invalid");
0ec0deaa 1924 continue;
64f9280e 1925 }
0ec0deaa
LP
1926
1927 r = mark_symlink_for_removal(remove_symlinks_to, i->name);
1928 if (r < 0)
1929 return r;
1930 }
1931
1932 return 0;
1933}
1934
1935int unit_file_mask(
1936 UnitFileScope scope,
b3796dd8 1937 UnitFileFlags flags,
0ec0deaa
LP
1938 const char *root_dir,
1939 char **files,
0ec0deaa
LP
1940 UnitFileChange **changes,
1941 unsigned *n_changes) {
1942
e1c5c2b0
LP
1943 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1944 const char *config_path;
0ec0deaa
LP
1945 char **i;
1946 int r;
1947
1948 assert(scope >= 0);
1949 assert(scope < _UNIT_FILE_SCOPE_MAX);
1950
4943d143 1951 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
1952 if (r < 0)
1953 return r;
1954
b3796dd8 1955 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
1956 if (!config_path)
1957 return -ENXIO;
e1c5c2b0 1958
0ec0deaa
LP
1959 STRV_FOREACH(i, files) {
1960 _cleanup_free_ char *path = NULL;
1961 int q;
1962
1963 if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
1964 if (r == 0)
1965 r = -EINVAL;
1966 continue;
1967 }
1968
e1c5c2b0 1969 path = path_make_absolute(*i, config_path);
0ec0deaa
LP
1970 if (!path)
1971 return -ENOMEM;
29283ea4 1972
b3796dd8 1973 q = create_symlink(&paths, "/dev/null", path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
0ec0deaa 1974 if (q < 0 && r >= 0)
83096483
LP
1975 r = q;
1976 }
1977
1978 return r;
1979}
1980
0ec0deaa
LP
1981int unit_file_unmask(
1982 UnitFileScope scope,
b3796dd8 1983 UnitFileFlags flags,
0ec0deaa
LP
1984 const char *root_dir,
1985 char **files,
1986 UnitFileChange **changes,
1987 unsigned *n_changes) {
1988
e1c5c2b0 1989 _cleanup_lookup_paths_free_ LookupPaths paths = {};
0ec0deaa 1990 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
dc7dd61d 1991 _cleanup_strv_free_ char **todo = NULL;
0ec0deaa 1992 size_t n_todo = 0, n_allocated = 0;
e1c5c2b0 1993 const char *config_path;
0ec0deaa 1994 char **i;
3b3557c4 1995 bool dry_run;
0ec0deaa
LP
1996 int r, q;
1997
1998 assert(scope >= 0);
1999 assert(scope < _UNIT_FILE_SCOPE_MAX);
2000
4943d143 2001 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
2002 if (r < 0)
2003 return r;
2004
b3796dd8 2005 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2006 if (!config_path)
2007 return -ENXIO;
2008
3b3557c4 2009 dry_run = !!(flags & UNIT_FILE_DRY_RUN);
e1c5c2b0 2010
0ec0deaa
LP
2011 STRV_FOREACH(i, files) {
2012 _cleanup_free_ char *path = NULL;
2013
2014 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2015 return -EINVAL;
2016
2017 path = path_make_absolute(*i, config_path);
2018 if (!path)
2019 return -ENOMEM;
2020
2021 r = null_or_empty_path(path);
2022 if (r == -ENOENT)
2023 continue;
2024 if (r < 0)
2025 return r;
2026 if (r == 0)
2027 continue;
2028
2029 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2030 return -ENOMEM;
2031
d054eae6
EV
2032 todo[n_todo] = strdup(*i);
2033 if (!todo[n_todo])
2034 return -ENOMEM;
2035
2036 n_todo++;
0ec0deaa
LP
2037 }
2038
2039 strv_uniq(todo);
2040
2041 r = 0;
2042 STRV_FOREACH(i, todo) {
2043 _cleanup_free_ char *path = NULL;
401017e0 2044 const char *rp;
0ec0deaa
LP
2045
2046 path = path_make_absolute(*i, config_path);
2047 if (!path)
2048 return -ENOMEM;
2049
3b3557c4 2050 if (!dry_run && unlink(path) < 0) {
af3d8113
ZJS
2051 if (errno != ENOENT) {
2052 if (r >= 0)
2053 r = -errno;
2054 unit_file_changes_add(changes, n_changes, -errno, path, NULL);
2055 }
0ec0deaa 2056
401017e0 2057 continue;
0ec0deaa 2058 }
401017e0
LP
2059
2060 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
2061
2062 rp = skip_root(&paths, path);
2063 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
2064 if (q < 0)
2065 return q;
0ec0deaa
LP
2066 }
2067
3b3557c4 2068 q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
0ec0deaa
LP
2069 if (r >= 0)
2070 r = q;
2071
2072 return r;
2073}
2074
2075int unit_file_link(
e94937df 2076 UnitFileScope scope,
b3796dd8 2077 UnitFileFlags flags,
e94937df
LN
2078 const char *root_dir,
2079 char **files,
e94937df
LN
2080 UnitFileChange **changes,
2081 unsigned *n_changes) {
2082
2083 _cleanup_lookup_paths_free_ LookupPaths paths = {};
8af35ba6 2084 _cleanup_strv_free_ char **todo = NULL;
0ec0deaa 2085 size_t n_todo = 0, n_allocated = 0;
e1c5c2b0 2086 const char *config_path;
e94937df 2087 char **i;
0ec0deaa 2088 int r, q;
e94937df
LN
2089
2090 assert(scope >= 0);
2091 assert(scope < _UNIT_FILE_SCOPE_MAX);
2092
4943d143 2093 r = lookup_paths_init(&paths, scope, 0, root_dir);
e94937df
LN
2094 if (r < 0)
2095 return r;
2096
b3796dd8 2097 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2098 if (!config_path)
2099 return -ENXIO;
e94937df
LN
2100
2101 STRV_FOREACH(i, files) {
0ec0deaa
LP
2102 _cleanup_free_ char *full = NULL;
2103 struct stat st;
2104 char *fn;
e94937df 2105
0ec0deaa
LP
2106 if (!path_is_absolute(*i))
2107 return -EINVAL;
e94937df 2108
0ec0deaa
LP
2109 fn = basename(*i);
2110 if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
2111 return -EINVAL;
2112
e4bb56c7 2113 full = prefix_root(paths.root_dir, *i);
0ec0deaa
LP
2114 if (!full)
2115 return -ENOMEM;
2116
2117 if (lstat(full, &st) < 0)
2118 return -errno;
2119 if (S_ISLNK(st.st_mode))
2120 return -ELOOP;
2121 if (S_ISDIR(st.st_mode))
2122 return -EISDIR;
2123 if (!S_ISREG(st.st_mode))
2124 return -ENOTTY;
2125
32c0ed7b 2126 q = in_search_path(&paths, *i);
0ec0deaa
LP
2127 if (q < 0)
2128 return q;
2129 if (q > 0)
2130 continue;
2131
2132 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2133 return -ENOMEM;
2134
8af35ba6
EV
2135 todo[n_todo] = strdup(*i);
2136 if (!todo[n_todo])
2137 return -ENOMEM;
2138
2139 n_todo++;
e94937df
LN
2140 }
2141
0ec0deaa 2142 strv_uniq(todo);
e94937df 2143
0ec0deaa
LP
2144 r = 0;
2145 STRV_FOREACH(i, todo) {
401017e0 2146 _cleanup_free_ char *new_path = NULL;
0ec0deaa 2147
401017e0
LP
2148 new_path = path_make_absolute(basename(*i), config_path);
2149 if (!new_path)
0ec0deaa
LP
2150 return -ENOMEM;
2151
b3796dd8 2152 q = create_symlink(&paths, *i, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
0ec0deaa
LP
2153 if (q < 0 && r >= 0)
2154 r = q;
2d5c93c7
MS
2155 }
2156
0ec0deaa
LP
2157 return r;
2158}
2159
344ca755
LP
2160static int path_shall_revert(const LookupPaths *paths, const char *path) {
2161 int r;
2162
2163 assert(paths);
2164 assert(path);
2165
2166 /* Checks whether the path is one where the drop-in directories shall be removed. */
2167
dfead90d 2168 r = path_is_config(paths, path, true);
344ca755
LP
2169 if (r != 0)
2170 return r;
2171
2172 r = path_is_control(paths, path);
2173 if (r != 0)
2174 return r;
2175
2176 return path_is_transient(paths, path);
2177}
2178
2179int unit_file_revert(
2180 UnitFileScope scope,
2181 const char *root_dir,
2182 char **files,
2183 UnitFileChange **changes,
2184 unsigned *n_changes) {
2185
2186 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
344ca755
LP
2187 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2188 _cleanup_strv_free_ char **todo = NULL;
2189 size_t n_todo = 0, n_allocated = 0;
2190 char **i;
2191 int r, q;
2192
2193 /* Puts a unit file back into vendor state. This means:
2194 *
2195 * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and
2196 * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated").
2197 *
2198 * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
2199 * "config", but not in "transient" or "control" or even "generated").
2200 *
4f25723c 2201 * We remove all that in both the runtime and the persistent directories, if that applies.
344ca755
LP
2202 */
2203
2204 r = lookup_paths_init(&paths, scope, 0, root_dir);
2205 if (r < 0)
2206 return r;
2207
2208 STRV_FOREACH(i, files) {
2209 bool has_vendor = false;
2210 char **p;
2211
2212 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2213 return -EINVAL;
2214
2215 STRV_FOREACH(p, paths.search_path) {
2216 _cleanup_free_ char *path = NULL, *dropin = NULL;
2217 struct stat st;
2218
2219 path = path_make_absolute(*i, *p);
2220 if (!path)
2221 return -ENOMEM;
2222
2223 r = lstat(path, &st);
2224 if (r < 0) {
2225 if (errno != ENOENT)
2226 return -errno;
2227 } else if (S_ISREG(st.st_mode)) {
2228 /* Check if there's a vendor version */
2229 r = path_is_vendor(&paths, path);
2230 if (r < 0)
2231 return r;
2232 if (r > 0)
2233 has_vendor = true;
2234 }
2235
2236 dropin = strappend(path, ".d");
2237 if (!dropin)
2238 return -ENOMEM;
2239
2240 r = lstat(dropin, &st);
2241 if (r < 0) {
2242 if (errno != ENOENT)
2243 return -errno;
2244 } else if (S_ISDIR(st.st_mode)) {
2245 /* Remove the drop-ins */
2246 r = path_shall_revert(&paths, dropin);
2247 if (r < 0)
2248 return r;
2249 if (r > 0) {
2250 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2251 return -ENOMEM;
2252
2253 todo[n_todo++] = dropin;
2254 dropin = NULL;
2255 }
2256 }
2257 }
2258
2259 if (!has_vendor)
2260 continue;
2261
2262 /* OK, there's a vendor version, hence drop all configuration versions */
2263 STRV_FOREACH(p, paths.search_path) {
2264 _cleanup_free_ char *path = NULL;
2265 struct stat st;
2266
2267 path = path_make_absolute(*i, *p);
2268 if (!path)
2269 return -ENOMEM;
2270
2271 r = lstat(path, &st);
2272 if (r < 0) {
2273 if (errno != ENOENT)
2274 return -errno;
2275 } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
dfead90d 2276 r = path_is_config(&paths, path, true);
344ca755
LP
2277 if (r < 0)
2278 return r;
2279 if (r > 0) {
2280 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
2281 return -ENOMEM;
2282
2283 todo[n_todo++] = path;
2284 path = NULL;
2285 }
2286 }
2287 }
2288 }
2289
2290 strv_uniq(todo);
2291
2292 r = 0;
2293 STRV_FOREACH(i, todo) {
2294 _cleanup_strv_free_ char **fs = NULL;
2295 const char *rp;
2296 char **j;
2297
2298 (void) get_files_in_directory(*i, &fs);
2299
2300 q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
2301 if (q < 0 && q != -ENOENT && r >= 0) {
2302 r = q;
2303 continue;
2304 }
2305
2306 STRV_FOREACH(j, fs) {
2307 _cleanup_free_ char *t = NULL;
2308
605405c6 2309 t = strjoin(*i, "/", *j);
344ca755
LP
2310 if (!t)
2311 return -ENOMEM;
2312
2313 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL);
2314 }
2315
2316 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL);
2317
2318 rp = skip_root(&paths, *i);
2319 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
2320 if (q < 0)
2321 return q;
2322 }
2323
3b3557c4 2324 q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, false, changes, n_changes);
344ca755
LP
2325 if (r >= 0)
2326 r = q;
2327
3b3557c4 2328 q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, false, changes, n_changes);
344ca755
LP
2329 if (r >= 0)
2330 r = q;
2331
2332 return r;
2333}
2334
0ec0deaa
LP
2335int unit_file_add_dependency(
2336 UnitFileScope scope,
b3796dd8 2337 UnitFileFlags flags,
0ec0deaa
LP
2338 const char *root_dir,
2339 char **files,
2340 const char *target,
2341 UnitDependency dep,
0ec0deaa
LP
2342 UnitFileChange **changes,
2343 unsigned *n_changes) {
2344
2345 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2346 _cleanup_(install_context_done) InstallContext c = {};
0ec0deaa 2347 UnitFileInstallInfo *i, *target_info;
e1c5c2b0 2348 const char *config_path;
0ec0deaa
LP
2349 char **f;
2350 int r;
2351
2352 assert(scope >= 0);
2353 assert(scope < _UNIT_FILE_SCOPE_MAX);
2354 assert(target);
2355
2356 if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
2357 return -EINVAL;
2358
2359 if (!unit_name_is_valid(target, UNIT_NAME_ANY))
2360 return -EINVAL;
2361
4943d143 2362 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
2363 if (r < 0)
2364 return r;
2365
b3796dd8 2366 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2367 if (!config_path)
2368 return -ENXIO;
0ec0deaa 2369
59108fbe
ZJS
2370 r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2371 &target_info, changes, n_changes);
0ec0deaa
LP
2372 if (r < 0)
2373 return r;
af3d8113 2374 r = install_info_may_process(target_info, &paths, changes, n_changes);
76adb5b8
LP
2375 if (r < 0)
2376 return r;
0ec0deaa
LP
2377
2378 assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
e94937df 2379
0ec0deaa
LP
2380 STRV_FOREACH(f, files) {
2381 char ***l;
2382
59108fbe
ZJS
2383 r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2384 &i, changes, n_changes);
e94937df
LN
2385 if (r < 0)
2386 return r;
af3d8113 2387 r = install_info_may_process(i, &paths, changes, n_changes);
76adb5b8
LP
2388 if (r < 0)
2389 return r;
0ec0deaa
LP
2390
2391 assert(i->type == UNIT_FILE_TYPE_REGULAR);
2392
2393 /* We didn't actually load anything from the unit
2394 * file, but instead just add in our new symlink to
2395 * create. */
e94937df
LN
2396
2397 if (dep == UNIT_WANTS)
0ec0deaa 2398 l = &i->wanted_by;
e94937df 2399 else
0ec0deaa 2400 l = &i->required_by;
e94937df 2401
0ec0deaa
LP
2402 strv_free(*l);
2403 *l = strv_new(target_info->name, NULL);
2404 if (!*l)
2405 return -ENOMEM;
e94937df
LN
2406 }
2407
b3796dd8 2408 return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
e94937df
LN
2409}
2410
83096483
LP
2411int unit_file_enable(
2412 UnitFileScope scope,
b3796dd8 2413 UnitFileFlags flags,
83096483 2414 const char *root_dir,
7195aa42 2415 char **files,
83096483
LP
2416 UnitFileChange **changes,
2417 unsigned *n_changes) {
2418
7fd1b19b 2419 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 2420 _cleanup_(install_context_done) InstallContext c = {};
e1c5c2b0 2421 const char *config_path;
0ec0deaa
LP
2422 UnitFileInstallInfo *i;
2423 char **f;
83096483
LP
2424 int r;
2425
2426 assert(scope >= 0);
2427 assert(scope < _UNIT_FILE_SCOPE_MAX);
2428
4943d143 2429 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
2430 if (r < 0)
2431 return r;
2432
b3796dd8 2433 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2434 if (!config_path)
2435 return -ENXIO;
83096483 2436
0ec0deaa 2437 STRV_FOREACH(f, files) {
59108fbe
ZJS
2438 r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2439 &i, changes, n_changes);
83096483 2440 if (r < 0)
d9e5e694 2441 return r;
af3d8113 2442 r = install_info_may_process(i, &paths, changes, n_changes);
76adb5b8
LP
2443 if (r < 0)
2444 return r;
0ec0deaa
LP
2445
2446 assert(i->type == UNIT_FILE_TYPE_REGULAR);
83096483
LP
2447 }
2448
729e3769 2449 /* This will return the number of symlink rules that were
d25e100b
LP
2450 supposed to be created, not the ones actually created. This
2451 is useful to determine whether the passed files had any
2452 installation data at all. */
b91a3b02 2453
b3796dd8 2454 return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_LOAD, changes, n_changes);
83096483
LP
2455}
2456
2457int unit_file_disable(
2458 UnitFileScope scope,
b3796dd8 2459 UnitFileFlags flags,
83096483 2460 const char *root_dir,
7195aa42 2461 char **files,
83096483
LP
2462 UnitFileChange **changes,
2463 unsigned *n_changes) {
2464
7fd1b19b 2465 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 2466 _cleanup_(install_context_done) InstallContext c = {};
7fd1b19b 2467 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
e1c5c2b0 2468 const char *config_path;
0ec0deaa
LP
2469 char **i;
2470 int r;
83096483
LP
2471
2472 assert(scope >= 0);
2473 assert(scope < _UNIT_FILE_SCOPE_MAX);
2474
4943d143 2475 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
2476 if (r < 0)
2477 return r;
2478
b3796dd8 2479 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2480 if (!config_path)
2481 return -ENXIO;
83096483
LP
2482
2483 STRV_FOREACH(i, files) {
0ec0deaa
LP
2484 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
2485 return -EINVAL;
2486
19539807 2487 r = install_info_add(&c, *i, NULL, false, NULL);
83096483 2488 if (r < 0)
d9e5e694 2489 return r;
83096483
LP
2490 }
2491
637d6e5b 2492 r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
0ec0deaa
LP
2493 if (r < 0)
2494 return r;
83096483 2495
3b3557c4 2496 return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
83096483
LP
2497}
2498
2499int unit_file_reenable(
2500 UnitFileScope scope,
b3796dd8 2501 UnitFileFlags flags,
83096483 2502 const char *root_dir,
7195aa42 2503 char **files,
83096483
LP
2504 UnitFileChange **changes,
2505 unsigned *n_changes) {
0ec0deaa
LP
2506
2507 char **n;
92d430a9 2508 int r;
0ec0deaa 2509 size_t l, i;
83096483 2510
0ec0deaa
LP
2511 /* First, we invoke the disable command with only the basename... */
2512 l = strv_length(files);
2513 n = newa(char*, l+1);
2514 for (i = 0; i < l; i++)
2515 n[i] = basename(files[i]);
2516 n[i] = NULL;
2517
b3796dd8 2518 r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes);
83096483 2519 if (r < 0)
d9e5e694 2520 return r;
83096483 2521
0ec0deaa 2522 /* But the enable command with the full name */
b3796dd8 2523 return unit_file_enable(scope, flags, root_dir, files, changes, n_changes);
83096483
LP
2524}
2525
99504dd4
VP
2526int unit_file_set_default(
2527 UnitFileScope scope,
b3796dd8 2528 UnitFileFlags flags,
99504dd4 2529 const char *root_dir,
0ec0deaa 2530 const char *name,
99504dd4
VP
2531 UnitFileChange **changes,
2532 unsigned *n_changes) {
2533
2534 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 2535 _cleanup_(install_context_done) InstallContext c = {};
0ec0deaa 2536 UnitFileInstallInfo *i;
60bec8e4 2537 const char *new_path;
99504dd4 2538 int r;
99504dd4
VP
2539
2540 assert(scope >= 0);
2541 assert(scope < _UNIT_FILE_SCOPE_MAX);
0ec0deaa 2542 assert(name);
99504dd4 2543
401017e0 2544 if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
0ec0deaa
LP
2545 return -EINVAL;
2546 if (streq(name, SPECIAL_DEFAULT_TARGET))
99504dd4
VP
2547 return -EINVAL;
2548
4943d143 2549 r = lookup_paths_init(&paths, scope, 0, root_dir);
99504dd4
VP
2550 if (r < 0)
2551 return r;
2552
59108fbe 2553 r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes);
99504dd4
VP
2554 if (r < 0)
2555 return r;
af3d8113 2556 r = install_info_may_process(i, &paths, changes, n_changes);
76adb5b8
LP
2557 if (r < 0)
2558 return r;
99504dd4 2559
401017e0 2560 new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
b3796dd8 2561 return create_symlink(&paths, i->path, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
99504dd4
VP
2562}
2563
2564int unit_file_get_default(
2565 UnitFileScope scope,
2566 const char *root_dir,
2567 char **name) {
2568
2569 _cleanup_lookup_paths_free_ LookupPaths paths = {};
0ec0deaa
LP
2570 _cleanup_(install_context_done) InstallContext c = {};
2571 UnitFileInstallInfo *i;
2572 char *n;
99504dd4
VP
2573 int r;
2574
16ed0233
LP
2575 assert(scope >= 0);
2576 assert(scope < _UNIT_FILE_SCOPE_MAX);
2577 assert(name);
2578
4943d143 2579 r = lookup_paths_init(&paths, scope, 0, root_dir);
0ec0deaa
LP
2580 if (r < 0)
2581 return r;
99504dd4 2582
59108fbe
ZJS
2583 r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2584 &i, NULL, NULL);
0ec0deaa
LP
2585 if (r < 0)
2586 return r;
af3d8113 2587 r = install_info_may_process(i, &paths, NULL, 0);
76adb5b8
LP
2588 if (r < 0)
2589 return r;
99504dd4 2590
0ec0deaa
LP
2591 n = strdup(i->name);
2592 if (!n)
2593 return -ENOMEM;
99504dd4 2594
0ec0deaa
LP
2595 *name = n;
2596 return 0;
99504dd4
VP
2597}
2598
2c52204c 2599static int unit_file_lookup_state(
83096483 2600 UnitFileScope scope,
a8ffe6fb 2601 const LookupPaths *paths,
0ec0deaa
LP
2602 const char *name,
2603 UnitFileState *ret) {
83096483 2604
0ec0deaa
LP
2605 _cleanup_(install_context_done) InstallContext c = {};
2606 UnitFileInstallInfo *i;
2607 UnitFileState state;
2608 int r;
83096483 2609
a8ffe6fb 2610 assert(paths);
0ec0deaa 2611 assert(name);
83096483 2612
7410616c 2613 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
83096483
LP
2614 return -EINVAL;
2615
59108fbe
ZJS
2616 r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2617 &i, NULL, NULL);
0ec0deaa
LP
2618 if (r < 0)
2619 return r;
83096483 2620
0ec0deaa
LP
2621 /* Shortcut things, if the caller just wants to know if this unit exists. */
2622 if (!ret)
2623 return 0;
83096483 2624
0ec0deaa 2625 switch (i->type) {
67820a0c 2626
0ec0deaa 2627 case UNIT_FILE_TYPE_MASKED:
dfead90d 2628 r = path_is_runtime(paths, i->path, true);
385eb996
LP
2629 if (r < 0)
2630 return r;
2631
2632 state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
0ec0deaa 2633 break;
83096483 2634
0ec0deaa 2635 case UNIT_FILE_TYPE_REGULAR:
f4dc1e65 2636 r = path_is_generator(paths, i->path);
f4139308
LP
2637 if (r < 0)
2638 return r;
2639 if (r > 0) {
2640 state = UNIT_FILE_GENERATED;
2641 break;
2642 }
2643
e4fca67f
LP
2644 r = path_is_transient(paths, i->path);
2645 if (r < 0)
2646 return r;
2647 if (r > 0) {
2648 state = UNIT_FILE_TRANSIENT;
2649 break;
2650 }
2651
5cd8ae31
ZJS
2652 /* Check if any of the Alias= symlinks have been created.
2653 * We ignore other aliases, and only check those that would
2654 * be created by systemctl enable for this unit. */
d2561cfd 2655 r = find_symlinks_in_scope(scope, paths, i, true, &state);
d9b4b48f
ZJS
2656 if (r < 0)
2657 return r;
2658 if (r > 0)
2659 break;
2660
2661 /* Check if the file is known under other names. If it is,
2662 * it might be in use. Report that as UNIT_FILE_INDIRECT. */
d2561cfd 2663 r = find_symlinks_in_scope(scope, paths, i, false, &state);
d9e5e694
ZJS
2664 if (r < 0)
2665 return r;
d9b4b48f
ZJS
2666 if (r > 0)
2667 state = UNIT_FILE_INDIRECT;
2668 else {
7a7ec2bf 2669 if (unit_file_install_info_has_rules(i))
0ec0deaa 2670 state = UNIT_FILE_DISABLED;
7a7ec2bf 2671 else if (unit_file_install_info_has_also(i))
0ec0deaa
LP
2672 state = UNIT_FILE_INDIRECT;
2673 else
2674 state = UNIT_FILE_STATIC;
2675 }
83096483 2676
0ec0deaa
LP
2677 break;
2678
2679 default:
2680 assert_not_reached("Unexpect unit file type.");
83096483
LP
2681 }
2682
0ec0deaa
LP
2683 *ret = state;
2684 return 0;
83096483
LP
2685}
2686
0ec0deaa 2687int unit_file_get_state(
a8ffe6fb
ZJS
2688 UnitFileScope scope,
2689 const char *root_dir,
0ec0deaa
LP
2690 const char *name,
2691 UnitFileState *ret) {
a8ffe6fb
ZJS
2692
2693 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2694 int r;
2695
2696 assert(scope >= 0);
2697 assert(scope < _UNIT_FILE_SCOPE_MAX);
2698 assert(name);
2699
4943d143 2700 r = lookup_paths_init(&paths, scope, 0, root_dir);
a8ffe6fb
ZJS
2701 if (r < 0)
2702 return r;
2703
e4bb56c7 2704 return unit_file_lookup_state(scope, &paths, name, ret);
a8ffe6fb
ZJS
2705}
2706
e735decc
LP
2707int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) {
2708 _cleanup_(install_context_done) InstallContext c = {};
2709 int r;
2710
2711 assert(paths);
2712 assert(name);
2713
2714 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2715 return -EINVAL;
2716
59108fbe 2717 r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL);
e735decc
LP
2718 if (r == -ENOENT)
2719 return 0;
2720 if (r < 0)
2721 return r;
2722
2723 return 1;
2724}
2725
8965d9f8
AC
2726static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
2727 _cleanup_(presets_freep) Presets ps = {};
2728 size_t n_allocated = 0;
7fd1b19b 2729 _cleanup_strv_free_ char **files = NULL;
cba2ef02 2730 char **p;
83096483
LP
2731 int r;
2732
2733 assert(scope >= 0);
2734 assert(scope < _UNIT_FILE_SCOPE_MAX);
8965d9f8 2735 assert(presets);
0ec0deaa 2736
83096483 2737 if (scope == UNIT_FILE_SYSTEM)
b5084605 2738 r = conf_files_list(&files, ".preset", root_dir, 0,
a7480dba
LP
2739 "/etc/systemd/system-preset",
2740 "/usr/local/lib/systemd/system-preset",
2741 "/usr/lib/systemd/system-preset",
b4bdfefa 2742#ifdef HAVE_SPLIT_USR
a7480dba 2743 "/lib/systemd/system-preset",
b4bdfefa 2744#endif
83096483
LP
2745 NULL);
2746 else if (scope == UNIT_FILE_GLOBAL)
b5084605 2747 r = conf_files_list(&files, ".preset", root_dir, 0,
a7480dba
LP
2748 "/etc/systemd/user-preset",
2749 "/usr/local/lib/systemd/user-preset",
2750 "/usr/lib/systemd/user-preset",
83096483 2751 NULL);
8965d9f8
AC
2752 else {
2753 *presets = (Presets){};
2754
2755 return 0;
2756 }
83096483
LP
2757
2758 if (r < 0)
2759 return r;
2760
cba2ef02 2761 STRV_FOREACH(p, files) {
7fd1b19b 2762 _cleanup_fclose_ FILE *f;
0ec0deaa 2763 char line[LINE_MAX];
d544d1a4 2764 int n = 0;
83096483 2765
cba2ef02 2766 f = fopen(*p, "re");
83096483
LP
2767 if (!f) {
2768 if (errno == ENOENT)
2769 continue;
2770
d9e5e694 2771 return -errno;
83096483
LP
2772 }
2773
0ec0deaa 2774 FOREACH_LINE(line, f, return -errno) {
8965d9f8 2775 PresetRule rule = {};
0ec0deaa
LP
2776 const char *parameter;
2777 char *l;
83096483
LP
2778
2779 l = strstrip(line);
d544d1a4 2780 n++;
83096483 2781
0ec0deaa
LP
2782 if (isempty(l))
2783 continue;
2784 if (strchr(COMMENTS, *l))
83096483
LP
2785 continue;
2786
0ec0deaa
LP
2787 parameter = first_word(l, "enable");
2788 if (parameter) {
8965d9f8 2789 char *pattern;
d9e5e694 2790
8965d9f8
AC
2791 pattern = strdup(parameter);
2792 if (!pattern)
2793 return -ENOMEM;
2794
2795 rule = (PresetRule) {
2796 .pattern = pattern,
2797 .action = PRESET_ENABLE,
2798 };
0ec0deaa 2799 }
83096483 2800
0ec0deaa
LP
2801 parameter = first_word(l, "disable");
2802 if (parameter) {
8965d9f8 2803 char *pattern;
d9e5e694 2804
8965d9f8
AC
2805 pattern = strdup(parameter);
2806 if (!pattern)
2807 return -ENOMEM;
2808
2809 rule = (PresetRule) {
2810 .pattern = pattern,
2811 .action = PRESET_DISABLE,
2812 };
2813 }
2814
2815 if (rule.action) {
2816 if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1))
2817 return -ENOMEM;
2818
2819 ps.rules[ps.n_rules++] = rule;
0ec0deaa
LP
2820 continue;
2821 }
2822
d544d1a4 2823 log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
83096483 2824 }
83096483
LP
2825 }
2826
8965d9f8
AC
2827 *presets = ps;
2828 ps = (Presets){};
2829
2830 return 0;
2831}
2832
2833static int query_presets(const char *name, const Presets presets) {
2834 PresetAction action = PRESET_UNKNOWN;
2835 size_t i;
2836
2837 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
2838 return -EINVAL;
2839
2840 for (i = 0; i < presets.n_rules; i++)
2841 if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
2842 action = presets.rules[i].action;
2843 break;
2844 }
2845
2846 switch (action) {
2847 case PRESET_UNKNOWN:
2848 log_debug("Preset files don't specify rule for %s. Enabling.", name);
2849 return 1;
2850 case PRESET_ENABLE:
2851 log_debug("Preset files say enable %s.", name);
2852 return 1;
2853 case PRESET_DISABLE:
2854 log_debug("Preset files say disable %s.", name);
2855 return 0;
2856 default:
2857 assert_not_reached("invalid preset action");
2858 }
2859}
2860
2861int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
2862 _cleanup_(presets_freep) Presets presets = {};
2863 int r;
2864
2865 r = read_presets(scope, root_dir, &presets);
2866 if (r < 0)
2867 return r;
2868
2869 return query_presets(name, presets);
83096483
LP
2870}
2871
0ec0deaa
LP
2872static int execute_preset(
2873 UnitFileScope scope,
2874 InstallContext *plus,
2875 InstallContext *minus,
2876 const LookupPaths *paths,
2877 const char *config_path,
0ec0deaa
LP
2878 char **files,
2879 UnitFilePresetMode mode,
2880 bool force,
2881 UnitFileChange **changes,
2882 unsigned *n_changes) {
2883
2884 int r;
2885
2886 assert(plus);
2887 assert(minus);
2888 assert(paths);
2889 assert(config_path);
2890
2891 if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
2892 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2893
637d6e5b 2894 r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
0ec0deaa
LP
2895 if (r < 0)
2896 return r;
2897
3b3557c4 2898 r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
0ec0deaa
LP
2899 } else
2900 r = 0;
2901
2902 if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
2903 int q;
2904
2905 /* Returns number of symlinks that where supposed to be installed. */
e4bb56c7 2906 q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
0ec0deaa
LP
2907 if (r >= 0) {
2908 if (q < 0)
2909 r = q;
2910 else
596fc263 2911 r += q;
0ec0deaa
LP
2912 }
2913 }
2914
2915 return r;
2916}
2917
2918static int preset_prepare_one(
2919 UnitFileScope scope,
2920 InstallContext *plus,
2921 InstallContext *minus,
2922 LookupPaths *paths,
af3d8113 2923 const char *name,
8965d9f8 2924 Presets presets,
af3d8113
ZJS
2925 UnitFileChange **changes,
2926 unsigned *n_changes) {
0ec0deaa 2927
11e11fd5 2928 _cleanup_(install_context_done) InstallContext tmp = {};
0ec0deaa
LP
2929 UnitFileInstallInfo *i;
2930 int r;
2931
11e11fd5 2932 if (install_info_find(plus, name) || install_info_find(minus, name))
0ec0deaa
LP
2933 return 0;
2934
59108fbe
ZJS
2935 r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2936 &i, changes, n_changes);
11e11fd5
ZJS
2937 if (r < 0)
2938 return r;
2939 if (!streq(name, i->name)) {
2940 log_debug("Skipping %s because is an alias for %s", name, i->name);
2941 return 0;
2942 }
2943
8965d9f8 2944 r = query_presets(name, presets);
0ec0deaa
LP
2945 if (r < 0)
2946 return r;
2947
2948 if (r > 0) {
59108fbe
ZJS
2949 r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
2950 &i, changes, n_changes);
0ec0deaa
LP
2951 if (r < 0)
2952 return r;
2953
af3d8113 2954 r = install_info_may_process(i, paths, changes, n_changes);
76adb5b8
LP
2955 if (r < 0)
2956 return r;
0ec0deaa 2957 } else
59108fbe
ZJS
2958 r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
2959 &i, changes, n_changes);
0ec0deaa
LP
2960
2961 return r;
2962}
2963
83096483
LP
2964int unit_file_preset(
2965 UnitFileScope scope,
b3796dd8 2966 UnitFileFlags flags,
83096483 2967 const char *root_dir,
7195aa42 2968 char **files,
d309c1c3 2969 UnitFilePresetMode mode,
83096483
LP
2970 UnitFileChange **changes,
2971 unsigned *n_changes) {
2972
59ccf93d 2973 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
da39f6a6 2974 _cleanup_lookup_paths_free_ LookupPaths paths = {};
8965d9f8 2975 _cleanup_(presets_freep) Presets presets = {};
e1c5c2b0 2976 const char *config_path;
da39f6a6 2977 char **i;
0ec0deaa 2978 int r;
83096483
LP
2979
2980 assert(scope >= 0);
2981 assert(scope < _UNIT_FILE_SCOPE_MAX);
86bbe5bf 2982 assert(mode < _UNIT_FILE_PRESET_MAX);
83096483 2983
4943d143 2984 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
2985 if (r < 0)
2986 return r;
2987
b3796dd8 2988 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
2989 if (!config_path)
2990 return -ENXIO;
83096483 2991
8965d9f8
AC
2992 r = read_presets(scope, root_dir, &presets);
2993 if (r < 0)
2994 return r;
83096483 2995
8965d9f8 2996 STRV_FOREACH(i, files) {
ff56349d 2997 r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
83096483 2998 if (r < 0)
d9e5e694 2999 return r;
83096483
LP
3000 }
3001
b3796dd8 3002 return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
d309c1c3
LP
3003}
3004
3005int unit_file_preset_all(
3006 UnitFileScope scope,
b3796dd8 3007 UnitFileFlags flags,
d309c1c3
LP
3008 const char *root_dir,
3009 UnitFilePresetMode mode,
d309c1c3
LP
3010 UnitFileChange **changes,
3011 unsigned *n_changes) {
3012
59ccf93d 3013 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
d309c1c3 3014 _cleanup_lookup_paths_free_ LookupPaths paths = {};
8965d9f8 3015 _cleanup_(presets_freep) Presets presets = {};
e1c5c2b0 3016 const char *config_path = NULL;
d309c1c3 3017 char **i;
0ec0deaa 3018 int r;
d309c1c3
LP
3019
3020 assert(scope >= 0);
3021 assert(scope < _UNIT_FILE_SCOPE_MAX);
86bbe5bf 3022 assert(mode < _UNIT_FILE_PRESET_MAX);
d309c1c3 3023
4943d143 3024 r = lookup_paths_init(&paths, scope, 0, root_dir);
d309c1c3
LP
3025 if (r < 0)
3026 return r;
3027
b3796dd8 3028 config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
d0fd66a3
LP
3029 if (!config_path)
3030 return -ENXIO;
d309c1c3 3031
8965d9f8
AC
3032 r = read_presets(scope, root_dir, &presets);
3033 if (r < 0)
3034 return r;
3035
a3c4eb07 3036 STRV_FOREACH(i, paths.search_path) {
d309c1c3 3037 _cleanup_closedir_ DIR *d = NULL;
d25e100b 3038 struct dirent *de;
d309c1c3 3039
401017e0 3040 d = opendir(*i);
d309c1c3
LP
3041 if (!d) {
3042 if (errno == ENOENT)
3043 continue;
3044
3045 return -errno;
3046 }
3047
d25e100b 3048 FOREACH_DIRENT(de, d, return -errno) {
d309c1c3 3049
7410616c 3050 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
d309c1c3
LP
3051 continue;
3052
3053 dirent_ensure_type(d, de);
3054
0ec0deaa 3055 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
d309c1c3
LP
3056 continue;
3057
af3d8113 3058 /* we don't pass changes[] in, because we want to handle errors on our own */
ff56349d 3059 r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
9a0a413a
ZJS
3060 if (r == -ERFKILL)
3061 r = unit_file_changes_add(changes, n_changes,
3062 UNIT_FILE_IS_MASKED, de->d_name, NULL);
893275df
ZJS
3063 else if (r == -ENOLINK)
3064 r = unit_file_changes_add(changes, n_changes,
3065 UNIT_FILE_IS_DANGLING, de->d_name, NULL);
d309c1c3
LP
3066 if (r < 0)
3067 return r;
3068 }
3069 }
3070
b3796dd8 3071 return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
83096483
LP
3072}
3073
59ccf93d
LP
3074static void unit_file_list_free_one(UnitFileList *f) {
3075 if (!f)
d9e5e694
ZJS
3076 return;
3077
59ccf93d
LP
3078 free(f->path);
3079 free(f);
d9e5e694 3080}
59ccf93d 3081
0ec0deaa
LP
3082Hashmap* unit_file_list_free(Hashmap *h) {
3083 UnitFileList *i;
3084
3085 while ((i = hashmap_steal_first(h)))
3086 unit_file_list_free_one(i);
3087
3088 return hashmap_free(h);
3089}
3090
59ccf93d 3091DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
d9e5e694 3092
83096483
LP
3093int unit_file_get_list(
3094 UnitFileScope scope,
3095 const char *root_dir,
313fe66f 3096 Hashmap *h,
3097 char **states,
3098 char **patterns) {
83096483 3099
7fd1b19b 3100 _cleanup_lookup_paths_free_ LookupPaths paths = {};
d9e5e694 3101 char **i;
83096483
LP
3102 int r;
3103
3104 assert(scope >= 0);
3105 assert(scope < _UNIT_FILE_SCOPE_MAX);
3106 assert(h);
3107
4943d143 3108 r = lookup_paths_init(&paths, scope, 0, root_dir);
83096483
LP
3109 if (r < 0)
3110 return r;
3111
a3c4eb07 3112 STRV_FOREACH(i, paths.search_path) {
da39f6a6 3113 _cleanup_closedir_ DIR *d = NULL;
d25e100b 3114 struct dirent *de;
83096483 3115
401017e0 3116 d = opendir(*i);
83096483
LP
3117 if (!d) {
3118 if (errno == ENOENT)
3119 continue;
a1feacf7 3120 if (IN_SET(errno, ENOTDIR, EACCES)) {
25f027c5 3121 log_debug_errno(errno, "Failed to open \"%s\": %m", *i);
a1feacf7
ZJS
3122 continue;
3123 }
83096483 3124
d9e5e694 3125 return -errno;
83096483
LP
3126 }
3127
d25e100b 3128 FOREACH_DIRENT(de, d, return -errno) {
59ccf93d 3129 _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
83096483 3130
7410616c 3131 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
83096483
LP
3132 continue;
3133
313fe66f 3134 if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
3135 continue;
3136
83096483
LP
3137 if (hashmap_get(h, de->d_name))
3138 continue;
3139
da39f6a6 3140 dirent_ensure_type(d, de);
83096483 3141
da39f6a6 3142 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
83096483
LP
3143 continue;
3144
3145 f = new0(UnitFileList, 1);
d9e5e694
ZJS
3146 if (!f)
3147 return -ENOMEM;
83096483 3148
401017e0 3149 f->path = path_make_absolute(de->d_name, *i);
d9e5e694
ZJS
3150 if (!f->path)
3151 return -ENOMEM;
83096483 3152
401017e0 3153 r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state);
d9e5e694 3154 if (r < 0)
0ec0deaa 3155 f->state = UNIT_FILE_BAD;
81fc054d 3156
313fe66f 3157 if (!strv_isempty(states) &&
3158 !strv_contains(states, unit_file_state_to_string(f->state)))
3159 continue;
3160
2b6bf07d 3161 r = hashmap_put(h, basename(f->path), f);
d9e5e694
ZJS
3162 if (r < 0)
3163 return r;
0ec0deaa 3164
d9e5e694 3165 f = NULL; /* prevent cleanup */
83096483
LP
3166 }
3167 }
3168
77cd2c87 3169 return 0;
83096483
LP
3170}
3171
3172static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
3173 [UNIT_FILE_ENABLED] = "enabled",
771faa9a 3174 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
83096483
LP
3175 [UNIT_FILE_LINKED] = "linked",
3176 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
3177 [UNIT_FILE_MASKED] = "masked",
3178 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
3179 [UNIT_FILE_STATIC] = "static",
b5b46d59 3180 [UNIT_FILE_DISABLED] = "disabled",
aedd4012 3181 [UNIT_FILE_INDIRECT] = "indirect",
f4139308 3182 [UNIT_FILE_GENERATED] = "generated",
e4fca67f 3183 [UNIT_FILE_TRANSIENT] = "transient",
0ec0deaa 3184 [UNIT_FILE_BAD] = "bad",
83096483
LP
3185};
3186
3187DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
c0576cd6
LP
3188
3189static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
3190 [UNIT_FILE_SYMLINK] = "symlink",
3191 [UNIT_FILE_UNLINK] = "unlink",
9a0a413a 3192 [UNIT_FILE_IS_MASKED] = "masked",
893275df 3193 [UNIT_FILE_IS_DANGLING] = "dangling",
c0576cd6
LP
3194};
3195
3196DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
d309c1c3 3197
86bbe5bf 3198static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
d309c1c3
LP
3199 [UNIT_FILE_PRESET_FULL] = "full",
3200 [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
3201 [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
3202};
3203
3204DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);