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