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