]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/install.c
util-lib: split out file attribute calls to chattr-util.[ch]
[thirdparty/systemd.git] / src / shared / install.c
CommitLineData
83096483
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
83096483
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty <of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
83096483 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
83096483
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <errno.h>
23#include <fcntl.h>
83096483 24#include <fnmatch.h>
07630cea
LP
25#include <string.h>
26#include <unistd.h>
83096483 27
07630cea
LP
28#include "conf-files.h"
29#include "conf-parser.h"
a0956174
LP
30#include "dirent-util.h"
31#include "fd-util.h"
83096483 32#include "hashmap.h"
07630cea 33#include "install-printf.h"
a0956174 34#include "install.h"
07630cea 35#include "mkdir.h"
83096483 36#include "path-lookup.h"
07630cea
LP
37#include "path-util.h"
38#include "set.h"
39#include "special.h"
40#include "string-util.h"
83096483
LP
41#include "strv.h"
42#include "unit-name.h"
07630cea 43#include "util.h"
83096483
LP
44
45typedef struct {
9f03ee51
MS
46 OrderedHashmap *will_install;
47 OrderedHashmap *have_installed;
83096483
LP
48} InstallContext;
49
278fa575 50static int in_search_path(const char *path, char **search) {
8f294b45 51 _cleanup_free_ char *parent = NULL;
8f294b45
LP
52
53 assert(path);
54
5f311f8c
LP
55 parent = dirname_malloc(path);
56 if (!parent)
57 return -ENOMEM;
8f294b45 58
278fa575 59 return strv_contains(search, parent);
8f294b45
LP
60}
61
83096483
LP
62static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
63 char *p = NULL;
64 int r;
65
66 assert(scope >= 0);
67 assert(scope < _UNIT_FILE_SCOPE_MAX);
68 assert(ret);
69
70 switch (scope) {
71
72 case UNIT_FILE_SYSTEM:
73
0c6ea3a4
ZJS
74 if (runtime)
75 p = path_join(root_dir, "/run/systemd/system", NULL);
76 else
77 p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL);
83096483
LP
78 break;
79
80 case UNIT_FILE_GLOBAL:
81
82 if (root_dir)
83 return -EINVAL;
84
85 if (runtime)
86 p = strdup("/run/systemd/user");
87 else
88 p = strdup(USER_CONFIG_UNIT_PATH);
89 break;
90
91 case UNIT_FILE_USER:
92
718880ba 93 if (root_dir)
83096483
LP
94 return -EINVAL;
95
718880ba 96 if (runtime)
4d5dec23 97 r = user_runtime_dir(&p);
718880ba
SA
98 else
99 r = user_config_home(&p);
100
83096483
LP
101 if (r <= 0)
102 return r < 0 ? r : -ENOENT;
103
104 break;
105
106 default:
107 assert_not_reached("Bad scope");
108 }
109
110 if (!p)
111 return -ENOMEM;
112
113 *ret = p;
114 return 0;
115}
116
83096483
LP
117static int mark_symlink_for_removal(
118 Set **remove_symlinks_to,
119 const char *p) {
120
121 char *n;
122 int r;
123
124 assert(p);
125
d5099efc 126 r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops);
83096483
LP
127 if (r < 0)
128 return r;
129
130 n = strdup(p);
131 if (!n)
132 return -ENOMEM;
133
134 path_kill_slashes(n);
135
ef42202a
ZJS
136 r = set_consume(*remove_symlinks_to, n);
137 if (r < 0)
83096483 138 return r == -EEXIST ? 0 : r;
83096483
LP
139
140 return 0;
141}
142
143static int remove_marked_symlinks_fd(
144 Set *remove_symlinks_to,
145 int fd,
146 const char *path,
147 const char *config_path,
148 bool *deleted,
149 UnitFileChange **changes,
29283ea4 150 unsigned *n_changes,
bcafe923 151 char** instance_whitelist) {
83096483 152
7fd1b19b 153 _cleanup_closedir_ DIR *d = NULL;
bcafe923 154 int r = 0;
83096483
LP
155
156 assert(remove_symlinks_to);
157 assert(fd >= 0);
158 assert(path);
159 assert(config_path);
160 assert(deleted);
161
162 d = fdopendir(fd);
163 if (!d) {
03e334a1 164 safe_close(fd);
83096483
LP
165 return -errno;
166 }
167
168 rewinddir(d);
169
170 for (;;) {
7d5e9c0f 171 struct dirent *de;
83096483 172
4d993c8c
FW
173 errno = 0;
174 de = readdir(d);
175 if (!de && errno != 0) {
83096483
LP
176 r = -errno;
177 break;
178 }
179
180 if (!de)
181 break;
182
a34bf9db 183 if (hidden_file(de->d_name))
83096483
LP
184 continue;
185
186 dirent_ensure_type(d, de);
187
188 if (de->d_type == DT_DIR) {
189 int nfd, q;
7fd1b19b 190 _cleanup_free_ char *p = NULL;
83096483
LP
191
192 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
193 if (nfd < 0) {
194 if (errno == ENOENT)
195 continue;
196
197 if (r == 0)
198 r = -errno;
199 continue;
200 }
201
202 p = path_make_absolute(de->d_name, path);
203 if (!p) {
03e334a1 204 safe_close(nfd);
d9e5e694 205 return -ENOMEM;
83096483
LP
206 }
207
208 /* This will close nfd, regardless whether it succeeds or not */
bcafe923
LP
209 q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist);
210 if (q < 0 && r == 0)
83096483
LP
211 r = q;
212
213 } else if (de->d_type == DT_LNK) {
7fd1b19b 214 _cleanup_free_ char *p = NULL, *dest = NULL;
83096483
LP
215 int q;
216 bool found;
217
7410616c 218 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
bcafe923
LP
219 continue;
220
7410616c 221 if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) &&
bcafe923 222 instance_whitelist &&
559367ad
LP
223 !strv_contains(instance_whitelist, de->d_name)) {
224
25d40bf5 225 _cleanup_free_ char *w = NULL;
559367ad
LP
226
227 /* OK, the file is not listed directly
228 * in the whitelist, so let's check if
229 * the template of it might be
230 * listed. */
231
7410616c
LP
232 r = unit_name_template(de->d_name, &w);
233 if (r < 0)
234 return r;
559367ad
LP
235
236 if (!strv_contains(instance_whitelist, w))
237 continue;
238 }
bcafe923 239
83096483 240 p = path_make_absolute(de->d_name, path);
d9e5e694
ZJS
241 if (!p)
242 return -ENOMEM;
83096483
LP
243
244 q = readlink_and_canonicalize(p, &dest);
245 if (q < 0) {
83096483
LP
246 if (q == -ENOENT)
247 continue;
248
249 if (r == 0)
250 r = q;
251 continue;
252 }
253
254 found =
255 set_get(remove_symlinks_to, dest) ||
2b6bf07d 256 set_get(remove_symlinks_to, basename(dest));
83096483 257
1dacfd2a
LP
258 if (!found)
259 continue;
83096483 260
1dacfd2a 261 if (unlink(p) < 0 && errno != ENOENT) {
1dacfd2a
LP
262 if (r == 0)
263 r = -errno;
264 continue;
265 }
bcafe923 266
1dacfd2a 267 path_kill_slashes(p);
559367ad 268 rmdir_parents(p, config_path);
57ab2eab 269 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
83096483 270
1dacfd2a
LP
271 if (!set_get(remove_symlinks_to, p)) {
272
273 q = mark_symlink_for_removal(&remove_symlinks_to, p);
274 if (q < 0) {
275 if (r == 0)
276 r = q;
277 } else
278 *deleted = true;
83096483 279 }
83096483
LP
280 }
281 }
282
83096483
LP
283 return r;
284}
285
286static int remove_marked_symlinks(
287 Set *remove_symlinks_to,
288 const char *config_path,
289 UnitFileChange **changes,
29283ea4 290 unsigned *n_changes,
bcafe923 291 char** instance_whitelist) {
83096483 292
da39f6a6
LP
293 _cleanup_close_ int fd = -1;
294 int r = 0;
83096483
LP
295 bool deleted;
296
297 assert(config_path);
298
299 if (set_size(remove_symlinks_to) <= 0)
300 return 0;
301
302 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
303 if (fd < 0)
304 return -errno;
305
306 do {
307 int q, cfd;
308 deleted = false;
309
ead34950 310 cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
83096483
LP
311 if (cfd < 0) {
312 r = -errno;
313 break;
314 }
315
316 /* This takes possession of cfd and closes it */
bcafe923 317 q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist);
83096483
LP
318 if (r == 0)
319 r = q;
320 } while (deleted);
321
83096483
LP
322 return r;
323}
324
325static int find_symlinks_fd(
326 const char *name,
327 int fd,
328 const char *path,
329 const char *config_path,
330 bool *same_name_link) {
331
332 int r = 0;
7fd1b19b 333 _cleanup_closedir_ DIR *d = NULL;
83096483
LP
334
335 assert(name);
336 assert(fd >= 0);
337 assert(path);
338 assert(config_path);
339 assert(same_name_link);
340
341 d = fdopendir(fd);
342 if (!d) {
03e334a1 343 safe_close(fd);
83096483
LP
344 return -errno;
345 }
346
347 for (;;) {
7d5e9c0f 348 struct dirent *de;
83096483 349
4d993c8c
FW
350 errno = 0;
351 de = readdir(d);
352 if (!de && errno != 0)
ea55addc 353 return -errno;
83096483
LP
354
355 if (!de)
ea55addc 356 return r;
83096483 357
a34bf9db 358 if (hidden_file(de->d_name))
83096483
LP
359 continue;
360
361 dirent_ensure_type(d, de);
362
363 if (de->d_type == DT_DIR) {
364 int nfd, q;
7fd1b19b 365 _cleanup_free_ char *p = NULL;
83096483
LP
366
367 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
368 if (nfd < 0) {
369 if (errno == ENOENT)
370 continue;
371
372 if (r == 0)
373 r = -errno;
374 continue;
375 }
376
377 p = path_make_absolute(de->d_name, path);
378 if (!p) {
03e334a1 379 safe_close(nfd);
ea55addc 380 return -ENOMEM;
83096483
LP
381 }
382
383 /* This will close nfd, regardless whether it succeeds or not */
384 q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
ea55addc
ZJS
385 if (q > 0)
386 return 1;
83096483
LP
387 if (r == 0)
388 r = q;
389
390 } else if (de->d_type == DT_LNK) {
7fd1b19b 391 _cleanup_free_ char *p = NULL, *dest = NULL;
83096483
LP
392 bool found_path, found_dest, b = false;
393 int q;
394
395 /* Acquire symlink name */
396 p = path_make_absolute(de->d_name, path);
ea55addc
ZJS
397 if (!p)
398 return -ENOMEM;
83096483
LP
399
400 /* Acquire symlink destination */
401 q = readlink_and_canonicalize(p, &dest);
402 if (q < 0) {
83096483
LP
403 if (q == -ENOENT)
404 continue;
405
406 if (r == 0)
407 r = q;
408 continue;
409 }
410
411 /* Check if the symlink itself matches what we
412 * are looking for */
413 if (path_is_absolute(name))
414 found_path = path_equal(p, name);
415 else
416 found_path = streq(de->d_name, name);
417
418 /* Check if what the symlink points to
419 * matches what we are looking for */
420 if (path_is_absolute(name))
421 found_dest = path_equal(dest, name);
422 else
2b6bf07d 423 found_dest = streq(basename(dest), name);
83096483 424
83096483 425 if (found_path && found_dest) {
7fd1b19b 426 _cleanup_free_ char *t = NULL;
83096483
LP
427
428 /* Filter out same name links in the main
429 * config path */
430 t = path_make_absolute(name, config_path);
ea55addc
ZJS
431 if (!t)
432 return -ENOMEM;
83096483
LP
433
434 b = path_equal(t, p);
83096483
LP
435 }
436
83096483
LP
437 if (b)
438 *same_name_link = true;
ea55addc
ZJS
439 else if (found_path || found_dest)
440 return 1;
83096483
LP
441 }
442 }
83096483
LP
443}
444
445static int find_symlinks(
446 const char *name,
447 const char *config_path,
448 bool *same_name_link) {
449
450 int fd;
451
452 assert(name);
453 assert(config_path);
454 assert(same_name_link);
455
456 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
d5891fda
ZJS
457 if (fd < 0) {
458 if (errno == ENOENT)
459 return 0;
83096483 460 return -errno;
d5891fda 461 }
83096483
LP
462
463 /* This takes possession of fd and closes it */
464 return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
465}
466
467static int find_symlinks_in_scope(
468 UnitFileScope scope,
469 const char *root_dir,
470 const char *name,
471 UnitFileState *state) {
472
473 int r;
0d674488 474 _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL;
83096483
LP
475 bool same_name_link_runtime = false, same_name_link = false;
476
477 assert(scope >= 0);
478 assert(scope < _UNIT_FILE_SCOPE_MAX);
479 assert(name);
480
718880ba 481 /* First look in runtime config path */
0d674488 482 r = get_config_path(scope, true, root_dir, &normal_path);
718880ba
SA
483 if (r < 0)
484 return r;
83096483 485
0d674488 486 r = find_symlinks(name, normal_path, &same_name_link_runtime);
718880ba
SA
487 if (r < 0)
488 return r;
489 else if (r > 0) {
490 *state = UNIT_FILE_ENABLED_RUNTIME;
491 return r;
83096483
LP
492 }
493
494 /* Then look in the normal config path */
0d674488 495 r = get_config_path(scope, false, root_dir, &runtime_path);
83096483
LP
496 if (r < 0)
497 return r;
498
0d674488 499 r = find_symlinks(name, runtime_path, &same_name_link);
83096483
LP
500 if (r < 0)
501 return r;
502 else if (r > 0) {
503 *state = UNIT_FILE_ENABLED;
504 return r;
505 }
506
507 /* Hmm, we didn't find it, but maybe we found the same name
508 * link? */
509 if (same_name_link_runtime) {
510 *state = UNIT_FILE_LINKED_RUNTIME;
511 return 1;
512 } else if (same_name_link) {
513 *state = UNIT_FILE_LINKED;
514 return 1;
515 }
516
517 return 0;
518}
519
520int unit_file_mask(
521 UnitFileScope scope,
522 bool runtime,
523 const char *root_dir,
7195aa42 524 char **files,
83096483
LP
525 bool force,
526 UnitFileChange **changes,
527 unsigned *n_changes) {
528
d8831ed5 529 char **i;
22321774 530 _cleanup_free_ char *prefix = NULL;
83096483
LP
531 int r;
532
533 assert(scope >= 0);
534 assert(scope < _UNIT_FILE_SCOPE_MAX);
535
536 r = get_config_path(scope, runtime, root_dir, &prefix);
537 if (r < 0)
538 return r;
539
540 STRV_FOREACH(i, files) {
7fd1b19b 541 _cleanup_free_ char *path = NULL;
83096483 542
7410616c 543 if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
83096483
LP
544 if (r == 0)
545 r = -EINVAL;
546 continue;
547 }
548
549 path = path_make_absolute(*i, prefix);
550 if (!path) {
551 r = -ENOMEM;
552 break;
553 }
554
555 if (symlink("/dev/null", path) >= 0) {
57ab2eab 556 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
83096483
LP
557 continue;
558 }
559
560 if (errno == EEXIST) {
561
d8831ed5 562 if (null_or_empty_path(path) > 0)
83096483 563 continue;
83096483
LP
564
565 if (force) {
1f8c4604 566 if (symlink_atomic("/dev/null", path) >= 0) {
57ab2eab
JS
567 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
568 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
83096483
LP
569 continue;
570 }
571 }
572
573 if (r == 0)
574 r = -EEXIST;
575 } else {
576 if (r == 0)
577 r = -errno;
578 }
83096483
LP
579 }
580
83096483
LP
581 return r;
582}
583
584int unit_file_unmask(
585 UnitFileScope scope,
586 bool runtime,
587 const char *root_dir,
7195aa42 588 char **files,
83096483
LP
589 UnitFileChange **changes,
590 unsigned *n_changes) {
591
592 char **i, *config_path = NULL;
593 int r, q;
594 Set *remove_symlinks_to = NULL;
595
596 assert(scope >= 0);
597 assert(scope < _UNIT_FILE_SCOPE_MAX);
598
599 r = get_config_path(scope, runtime, root_dir, &config_path);
600 if (r < 0)
601 goto finish;
602
603 STRV_FOREACH(i, files) {
56e73b34 604 _cleanup_free_ char *path = NULL;
83096483 605
7410616c 606 if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
83096483
LP
607 if (r == 0)
608 r = -EINVAL;
609 continue;
610 }
611
612 path = path_make_absolute(*i, config_path);
613 if (!path) {
614 r = -ENOMEM;
615 break;
616 }
617
618 q = null_or_empty_path(path);
619 if (q > 0) {
56e73b34
TG
620 if (unlink(path) < 0)
621 q = -errno;
622 else {
623 q = mark_symlink_for_removal(&remove_symlinks_to, path);
57ab2eab 624 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
83096483 625 }
83096483
LP
626 }
627
628 if (q != -ENOENT && r == 0)
629 r = q;
83096483
LP
630 }
631
632
633finish:
29283ea4 634 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
83096483
LP
635 if (r == 0)
636 r = q;
637
638 set_free_free(remove_symlinks_to);
639 free(config_path);
640
641 return r;
642}
643
644int unit_file_link(
645 UnitFileScope scope,
646 bool runtime,
647 const char *root_dir,
7195aa42 648 char **files,
83096483
LP
649 bool force,
650 UnitFileChange **changes,
651 unsigned *n_changes) {
652
7fd1b19b 653 _cleanup_lookup_paths_free_ LookupPaths paths = {};
d9e5e694 654 char **i;
7fd1b19b 655 _cleanup_free_ char *config_path = NULL;
83096483
LP
656 int r, q;
657
658 assert(scope >= 0);
659 assert(scope < _UNIT_FILE_SCOPE_MAX);
660
12ed81d9 661 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
83096483
LP
662 if (r < 0)
663 return r;
664
665 r = get_config_path(scope, runtime, root_dir, &config_path);
666 if (r < 0)
d9e5e694 667 return r;
83096483
LP
668
669 STRV_FOREACH(i, files) {
7fd1b19b 670 _cleanup_free_ char *path = NULL;
d9e5e694 671 char *fn;
83096483
LP
672 struct stat st;
673
2b6bf07d 674 fn = basename(*i);
83096483
LP
675
676 if (!path_is_absolute(*i) ||
7410616c 677 !unit_name_is_valid(fn, UNIT_NAME_ANY)) {
83096483
LP
678 if (r == 0)
679 r = -EINVAL;
680 continue;
681 }
682
683 if (lstat(*i, &st) < 0) {
684 if (r == 0)
685 r = -errno;
686 continue;
687 }
688
689 if (!S_ISREG(st.st_mode)) {
690 r = -ENOENT;
691 continue;
692 }
693
278fa575 694 q = in_search_path(*i, paths.unit_path);
d9e5e694
ZJS
695 if (q < 0)
696 return q;
83096483
LP
697
698 if (q > 0)
699 continue;
700
701 path = path_make_absolute(fn, config_path);
d9e5e694
ZJS
702 if (!path)
703 return -ENOMEM;
83096483
LP
704
705 if (symlink(*i, path) >= 0) {
57ab2eab 706 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
83096483
LP
707 continue;
708 }
709
710 if (errno == EEXIST) {
7fd1b19b 711 _cleanup_free_ char *dest = NULL;
83096483
LP
712
713 q = readlink_and_make_absolute(path, &dest);
83096483 714 if (q < 0 && errno != ENOENT) {
83096483
LP
715 if (r == 0)
716 r = q;
83096483
LP
717 continue;
718 }
719
d9e5e694 720 if (q >= 0 && path_equal(dest, *i))
83096483 721 continue;
83096483
LP
722
723 if (force) {
1f8c4604 724 if (symlink_atomic(*i, path) >= 0) {
57ab2eab
JS
725 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
726 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
83096483
LP
727 continue;
728 }
729 }
730
731 if (r == 0)
732 r = -EEXIST;
733 } else {
734 if (r == 0)
735 r = -errno;
736 }
83096483
LP
737 }
738
83096483
LP
739 return r;
740}
741
742void unit_file_list_free(Hashmap *h) {
743 UnitFileList *i;
744
745 while ((i = hashmap_steal_first(h))) {
746 free(i->path);
747 free(i);
748 }
749
750 hashmap_free(h);
751}
752
57ab2eab
JS
753int unit_file_changes_add(
754 UnitFileChange **changes,
755 unsigned *n_changes,
756 UnitFileChangeType type,
757 const char *path,
758 const char *source) {
759
760 UnitFileChange *c;
761 unsigned i;
762
763 assert(path);
764 assert(!changes == !n_changes);
765
766 if (!changes)
767 return 0;
768
769 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
770 if (!c)
771 return -ENOMEM;
772
773 *changes = c;
774 i = *n_changes;
775
776 c[i].type = type;
777 c[i].path = strdup(path);
778 if (!c[i].path)
779 return -ENOMEM;
780
781 path_kill_slashes(c[i].path);
782
783 if (source) {
784 c[i].source = strdup(source);
785 if (!c[i].source) {
786 free(c[i].path);
787 return -ENOMEM;
788 }
789
790 path_kill_slashes(c[i].path);
791 } else
792 c[i].source = NULL;
793
794 *n_changes = i+1;
795 return 0;
796}
797
83096483
LP
798void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
799 unsigned i;
800
801 assert(changes || n_changes == 0);
802
803 if (!changes)
804 return;
805
806 for (i = 0; i < n_changes; i++) {
807 free(changes[i].path);
808 free(changes[i].source);
809 }
810
811 free(changes);
812}
813
cab6235f 814static void install_info_free(UnitFileInstallInfo *i) {
83096483
LP
815 assert(i);
816
817 free(i->name);
818 free(i->path);
819 strv_free(i->aliases);
820 strv_free(i->wanted_by);
78d54bd4 821 strv_free(i->required_by);
aedd4012 822 strv_free(i->also);
d54c4993 823 free(i->default_instance);
83096483
LP
824 free(i);
825}
826
9f03ee51 827static void install_info_hashmap_free(OrderedHashmap *m) {
cab6235f 828 UnitFileInstallInfo *i;
83096483
LP
829
830 if (!m)
831 return;
832
9f03ee51 833 while ((i = ordered_hashmap_steal_first(m)))
83096483
LP
834 install_info_free(i);
835
9f03ee51 836 ordered_hashmap_free(m);
83096483
LP
837}
838
839static void install_context_done(InstallContext *c) {
840 assert(c);
841
842 install_info_hashmap_free(c->will_install);
843 install_info_hashmap_free(c->have_installed);
844
845 c->will_install = c->have_installed = NULL;
846}
847
848static int install_info_add(
849 InstallContext *c,
850 const char *name,
851 const char *path) {
cab6235f 852 UnitFileInstallInfo *i = NULL;
83096483
LP
853 int r;
854
855 assert(c);
856 assert(name || path);
857
858 if (!name)
2b6bf07d 859 name = basename(path);
83096483 860
7410616c 861 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
83096483
LP
862 return -EINVAL;
863
9f03ee51
MS
864 if (ordered_hashmap_get(c->have_installed, name) ||
865 ordered_hashmap_get(c->will_install, name))
83096483
LP
866 return 0;
867
9f03ee51 868 r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops);
83096483
LP
869 if (r < 0)
870 return r;
871
cab6235f 872 i = new0(UnitFileInstallInfo, 1);
83096483
LP
873 if (!i)
874 return -ENOMEM;
875
876 i->name = strdup(name);
877 if (!i->name) {
878 r = -ENOMEM;
879 goto fail;
880 }
881
882 if (path) {
883 i->path = strdup(path);
884 if (!i->path) {
885 r = -ENOMEM;
886 goto fail;
887 }
888 }
889
9f03ee51 890 r = ordered_hashmap_put(c->will_install, i->name, i);
83096483
LP
891 if (r < 0)
892 goto fail;
893
894 return 0;
895
896fail:
897 if (i)
898 install_info_free(i);
899
900 return r;
901}
902
903static int install_info_add_auto(
904 InstallContext *c,
905 const char *name_or_path) {
906
907 assert(c);
908 assert(name_or_path);
909
910 if (path_is_absolute(name_or_path))
911 return install_info_add(c, NULL, name_or_path);
912 else
913 return install_info_add(c, name_or_path, NULL);
914}
915
d54c4993
LP
916static int config_parse_also(
917 const char *unit,
918 const char *filename,
919 unsigned line,
920 const char *section,
921 unsigned section_line,
922 const char *lvalue,
923 int ltype,
924 const char *rvalue,
925 void *data,
926 void *userdata) {
83096483 927
83096483 928 size_t l;
a2a5291b 929 const char *word, *state;
83096483 930 InstallContext *c = data;
cab6235f 931 UnitFileInstallInfo *i = userdata;
83096483
LP
932
933 assert(filename);
934 assert(lvalue);
935 assert(rvalue);
936
a2a5291b 937 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
7fd1b19b 938 _cleanup_free_ char *n;
83096483
LP
939 int r;
940
a2a5291b 941 n = strndup(word, l);
83096483
LP
942 if (!n)
943 return -ENOMEM;
944
945 r = install_info_add(c, n, NULL);
d9e5e694 946 if (r < 0)
83096483 947 return r;
aedd4012
JS
948
949 r = strv_extend(&i->also, n);
950 if (r < 0)
951 return r;
83096483 952 }
b2fadec6 953 if (!isempty(state))
12ca818f 954 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
83096483
LP
955
956 return 0;
957}
958
d54c4993
LP
959static int config_parse_user(
960 const char *unit,
961 const char *filename,
962 unsigned line,
963 const char *section,
964 unsigned section_line,
965 const char *lvalue,
966 int ltype,
967 const char *rvalue,
968 void *data,
969 void *userdata) {
3f0b2f0f 970
cab6235f 971 UnitFileInstallInfo *i = data;
d54c4993 972 char *printed;
19f6d710 973 int r;
3f0b2f0f
ZJS
974
975 assert(filename);
976 assert(lvalue);
977 assert(rvalue);
978
19f6d710
LP
979 r = install_full_printf(i, rvalue, &printed);
980 if (r < 0)
981 return r;
3f0b2f0f
ZJS
982
983 free(i->user);
984 i->user = printed;
985
986 return 0;
987}
988
d54c4993
LP
989static int config_parse_default_instance(
990 const char *unit,
991 const char *filename,
992 unsigned line,
993 const char *section,
994 unsigned section_line,
995 const char *lvalue,
996 int ltype,
997 const char *rvalue,
998 void *data,
999 void *userdata) {
1000
cab6235f 1001 UnitFileInstallInfo *i = data;
d54c4993
LP
1002 char *printed;
1003 int r;
1004
1005 assert(filename);
1006 assert(lvalue);
1007 assert(rvalue);
1008
1009 r = install_full_printf(i, rvalue, &printed);
1010 if (r < 0)
1011 return r;
1012
d9ab174b
AH
1013 if (!unit_instance_is_valid(printed)) {
1014 free(printed);
d54c4993 1015 return -EINVAL;
d9ab174b 1016 }
d54c4993
LP
1017
1018 free(i->default_instance);
1019 i->default_instance = printed;
1020
1021 return 0;
1022}
1023
83096483
LP
1024static int unit_file_load(
1025 InstallContext *c,
cab6235f 1026 UnitFileInstallInfo *info,
83096483 1027 const char *path,
278fa575 1028 const char *root_dir,
e94937df 1029 bool allow_symlink,
aedd4012
JS
1030 bool load,
1031 bool *also) {
83096483 1032
f975e971 1033 const ConfigTableItem items[] = {
d54c4993
LP
1034 { "Install", "Alias", config_parse_strv, 0, &info->aliases },
1035 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
1036 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
1037 { "Install", "DefaultInstance", config_parse_default_instance, 0, info },
1038 { "Install", "Also", config_parse_also, 0, c },
1039 { "Exec", "User", config_parse_user, 0, info },
1040 {}
83096483
LP
1041 };
1042
7fd1b19b 1043 _cleanup_fclose_ FILE *f = NULL;
278fa575 1044 int fd, r;
83096483
LP
1045
1046 assert(c);
1047 assert(info);
1048 assert(path);
1049
278fa575 1050 if (!isempty(root_dir))
63c372cb 1051 path = strjoina(root_dir, "/", path);
278fa575 1052
e94937df
LN
1053 if (!load) {
1054 r = access(path, F_OK) ? -errno : 0;
1055 return r;
1056 }
1057
83096483
LP
1058 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
1059 if (fd < 0)
1060 return -errno;
1061
1062 f = fdopen(fd, "re");
1063 if (!f) {
03e334a1 1064 safe_close(fd);
83096483
LP
1065 return -ENOMEM;
1066 }
1067
36f822c4
ZJS
1068 r = config_parse(NULL, path, f,
1069 NULL,
1070 config_item_table_lookup, items,
1071 true, true, false, info);
83096483
LP
1072 if (r < 0)
1073 return r;
1074
aedd4012
JS
1075 if (also)
1076 *also = !strv_isempty(info->also);
1077
78d54bd4 1078 return
693eb9a2
LP
1079 (int) strv_length(info->aliases) +
1080 (int) strv_length(info->wanted_by) +
1081 (int) strv_length(info->required_by);
83096483
LP
1082}
1083
1084static int unit_file_search(
1085 InstallContext *c,
cab6235f 1086 UnitFileInstallInfo *info,
a8ffe6fb 1087 const LookupPaths *paths,
83096483 1088 const char *root_dir,
e94937df 1089 bool allow_symlink,
aedd4012
JS
1090 bool load,
1091 bool *also) {
83096483
LP
1092
1093 char **p;
1094 int r;
1095
1096 assert(c);
1097 assert(info);
1098 assert(paths);
1099
278fa575 1100 if (info->path)
aedd4012 1101 return unit_file_load(c, info, info->path, root_dir, allow_symlink, load, also);
83096483
LP
1102
1103 assert(info->name);
1104
1105 STRV_FOREACH(p, paths->unit_path) {
e50bd775 1106 _cleanup_free_ char *path = NULL;
83096483 1107
278fa575 1108 path = strjoin(*p, "/", info->name, NULL);
83096483
LP
1109 if (!path)
1110 return -ENOMEM;
1111
aedd4012 1112 r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also);
62b00233 1113 if (r >= 0) {
83096483 1114 info->path = path;
62b00233 1115 path = NULL;
e50bd775
LP
1116 return r;
1117 }
1118 if (r != -ENOENT && r != -ELOOP)
1119 return r;
1120 }
62b00233 1121
7410616c 1122 if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
29283ea4 1123
e50bd775
LP
1124 /* Unit file doesn't exist, however instance
1125 * enablement was requested. We will check if it is
1126 * possible to load template unit file. */
29283ea4 1127
de228aab 1128 _cleanup_free_ char *template = NULL;
e50bd775 1129
7410616c
LP
1130 r = unit_name_template(info->name, &template);
1131 if (r < 0)
1132 return r;
e50bd775
LP
1133
1134 STRV_FOREACH(p, paths->unit_path) {
1135 _cleanup_free_ char *path = NULL;
1136
278fa575 1137 path = strjoin(*p, "/", template, NULL);
62b00233
ZJS
1138 if (!path)
1139 return -ENOMEM;
29283ea4 1140
aedd4012 1141 r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also);
62b00233
ZJS
1142 if (r >= 0) {
1143 info->path = path;
1144 path = NULL;
e50bd775 1145 return r;
29283ea4 1146 }
e50bd775
LP
1147 if (r != -ENOENT && r != -ELOOP)
1148 return r;
29283ea4 1149 }
83096483
LP
1150 }
1151
1152 return -ENOENT;
1153}
1154
1155static int unit_file_can_install(
a8ffe6fb 1156 const LookupPaths *paths,
83096483
LP
1157 const char *root_dir,
1158 const char *name,
aedd4012
JS
1159 bool allow_symlink,
1160 bool *also) {
83096483 1161
59ccf93d 1162 _cleanup_(install_context_done) InstallContext c = {};
cab6235f 1163 UnitFileInstallInfo *i;
83096483
LP
1164 int r;
1165
1166 assert(paths);
1167 assert(name);
1168
83096483
LP
1169 r = install_info_add_auto(&c, name);
1170 if (r < 0)
1171 return r;
1172
9f03ee51 1173 assert_se(i = ordered_hashmap_first(c.will_install));
83096483 1174
aedd4012 1175 r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true, also);
83096483
LP
1176
1177 if (r >= 0)
78d54bd4 1178 r =
693eb9a2
LP
1179 (int) strv_length(i->aliases) +
1180 (int) strv_length(i->wanted_by) +
1181 (int) strv_length(i->required_by);
83096483 1182
83096483
LP
1183 return r;
1184}
1185
1186static int create_symlink(
1187 const char *old_path,
1188 const char *new_path,
1189 bool force,
1190 UnitFileChange **changes,
1191 unsigned *n_changes) {
1192
7fd1b19b 1193 _cleanup_free_ char *dest = NULL;
83096483
LP
1194 int r;
1195
1196 assert(old_path);
1197 assert(new_path);
1198
d2e54fae 1199 mkdir_parents_label(new_path, 0755);
83096483
LP
1200
1201 if (symlink(old_path, new_path) >= 0) {
57ab2eab 1202 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
83096483
LP
1203 return 0;
1204 }
1205
1206 if (errno != EEXIST)
1207 return -errno;
1208
1209 r = readlink_and_make_absolute(new_path, &dest);
1210 if (r < 0)
1211 return r;
1212
d9e5e694 1213 if (path_equal(dest, old_path))
83096483 1214 return 0;
83096483 1215
ba49b4a1 1216 if (!force)
83096483
LP
1217 return -EEXIST;
1218
1f8c4604
LP
1219 r = symlink_atomic(old_path, new_path);
1220 if (r < 0)
1221 return r;
83096483 1222
57ab2eab
JS
1223 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
1224 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
83096483 1225
1f8c4604 1226 return 0;
83096483
LP
1227}
1228
1229static int install_info_symlink_alias(
cab6235f 1230 UnitFileInstallInfo *i,
83096483
LP
1231 const char *config_path,
1232 bool force,
1233 UnitFileChange **changes,
1234 unsigned *n_changes) {
1235
1236 char **s;
1237 int r = 0, q;
1238
1239 assert(i);
1240 assert(config_path);
1241
1242 STRV_FOREACH(s, i->aliases) {
7fd1b19b 1243 _cleanup_free_ char *alias_path = NULL, *dst = NULL;
83096483 1244
19f6d710
LP
1245 q = install_full_printf(i, *s, &dst);
1246 if (q < 0)
1247 return q;
83096483 1248
7584d236 1249 alias_path = path_make_absolute(dst, config_path);
83096483
LP
1250 if (!alias_path)
1251 return -ENOMEM;
1252
1253 q = create_symlink(i->path, alias_path, force, changes, n_changes);
83096483
LP
1254 if (r == 0)
1255 r = q;
1256 }
1257
1258 return r;
1259}
1260
1261static int install_info_symlink_wants(
cab6235f 1262 UnitFileInstallInfo *i,
83096483 1263 const char *config_path,
d54c4993
LP
1264 char **list,
1265 const char *suffix,
83096483
LP
1266 bool force,
1267 UnitFileChange **changes,
1268 unsigned *n_changes) {
1269
d54c4993
LP
1270 _cleanup_free_ char *buf = NULL;
1271 const char *n;
83096483
LP
1272 char **s;
1273 int r = 0, q;
1274
1275 assert(i);
1276 assert(config_path);
1277
7410616c 1278 if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
0a327d75
LP
1279
1280 /* Don't install any symlink if there's no default
1281 * instance configured */
1282
1283 if (!i->default_instance)
1284 return 0;
1285
7410616c
LP
1286 r = unit_name_replace_instance(i->name, i->default_instance, &buf);
1287 if (r < 0)
1288 return r;
83096483 1289
d54c4993
LP
1290 n = buf;
1291 } else
1292 n = i->name;
78d54bd4 1293
d54c4993 1294 STRV_FOREACH(s, list) {
7fd1b19b 1295 _cleanup_free_ char *path = NULL, *dst = NULL;
78d54bd4 1296
19f6d710
LP
1297 q = install_full_printf(i, *s, &dst);
1298 if (q < 0)
1299 return q;
7584d236 1300
7410616c 1301 if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
78d54bd4
LP
1302 r = -EINVAL;
1303 continue;
1304 }
1305
d54c4993
LP
1306 path = strjoin(config_path, "/", dst, suffix, n, NULL);
1307 if (!path)
78d54bd4
LP
1308 return -ENOMEM;
1309
1310 q = create_symlink(i->path, path, force, changes, n_changes);
78d54bd4
LP
1311 if (r == 0)
1312 r = q;
1313 }
1314
1315 return r;
1316}
1317
83096483 1318static int install_info_symlink_link(
cab6235f 1319 UnitFileInstallInfo *i,
a8ffe6fb 1320 const LookupPaths *paths,
83096483 1321 const char *config_path,
8f294b45 1322 const char *root_dir,
83096483
LP
1323 bool force,
1324 UnitFileChange **changes,
1325 unsigned *n_changes) {
1326
7fd1b19b 1327 _cleanup_free_ char *path = NULL;
1dacfd2a 1328 int r;
83096483
LP
1329
1330 assert(i);
1331 assert(paths);
1332 assert(config_path);
1333 assert(i->path);
1334
278fa575 1335 r = in_search_path(i->path, paths->unit_path);
83096483
LP
1336 if (r != 0)
1337 return r;
1338
1dacfd2a
LP
1339 path = strjoin(config_path, "/", i->name, NULL);
1340 if (!path)
83096483
LP
1341 return -ENOMEM;
1342
1dacfd2a 1343 return create_symlink(i->path, path, force, changes, n_changes);
83096483
LP
1344}
1345
1346static int install_info_apply(
cab6235f 1347 UnitFileInstallInfo *i,
a8ffe6fb 1348 const LookupPaths *paths,
83096483 1349 const char *config_path,
8f294b45 1350 const char *root_dir,
83096483
LP
1351 bool force,
1352 UnitFileChange **changes,
1353 unsigned *n_changes) {
1354
1355 int r, q;
1356
1357 assert(i);
1358 assert(paths);
1359 assert(config_path);
1360
1361 r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
1362
d54c4993 1363 q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
83096483
LP
1364 if (r == 0)
1365 r = q;
1366
d54c4993 1367 q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes);
78d54bd4
LP
1368 if (r == 0)
1369 r = q;
1370
8f294b45 1371 q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes);
83096483
LP
1372 if (r == 0)
1373 r = q;
1374
1375 return r;
1376}
1377
1378static int install_context_apply(
1379 InstallContext *c,
a8ffe6fb 1380 const LookupPaths *paths,
83096483
LP
1381 const char *config_path,
1382 const char *root_dir,
1383 bool force,
1384 UnitFileChange **changes,
1385 unsigned *n_changes) {
1386
cab6235f 1387 UnitFileInstallInfo *i;
2d5c93c7 1388 int r, q;
83096483
LP
1389
1390 assert(c);
1391 assert(paths);
1392 assert(config_path);
1393
2d5c93c7
MS
1394 if (!ordered_hashmap_isempty(c->will_install)) {
1395 r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
1396 if (r < 0)
1397 return r;
83096483 1398
2d5c93c7
MS
1399 r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
1400 if (r < 0)
1401 return r;
1402 }
83096483 1403
2d5c93c7
MS
1404 r = 0;
1405 while ((i = ordered_hashmap_first(c->will_install))) {
9f03ee51 1406 assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
83096483 1407
aedd4012 1408 q = unit_file_search(c, i, paths, root_dir, false, true, NULL);
83096483
LP
1409 if (q < 0) {
1410 if (r >= 0)
1411 r = q;
1412
1413 return r;
1414 } else if (r >= 0)
1415 r += q;
1416
8f294b45 1417 q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes);
83096483
LP
1418 if (r >= 0 && q < 0)
1419 r = q;
1420 }
1421
1422 return r;
1423}
1424
1425static int install_context_mark_for_removal(
1426 InstallContext *c,
a8ffe6fb 1427 const LookupPaths *paths,
83096483
LP
1428 Set **remove_symlinks_to,
1429 const char *config_path,
1430 const char *root_dir) {
1431
cab6235f 1432 UnitFileInstallInfo *i;
2d5c93c7 1433 int r, q;
83096483
LP
1434
1435 assert(c);
1436 assert(paths);
1437 assert(config_path);
1438
1439 /* Marks all items for removal */
1440
2d5c93c7
MS
1441 if (!ordered_hashmap_isempty(c->will_install)) {
1442 r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
1443 if (r < 0)
1444 return r;
83096483 1445
2d5c93c7
MS
1446 r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
1447 if (r < 0)
1448 return r;
1449 }
83096483 1450
2d5c93c7
MS
1451 r = 0;
1452 while ((i = ordered_hashmap_first(c->will_install))) {
9f03ee51 1453 assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
83096483 1454
aedd4012 1455 q = unit_file_search(c, i, paths, root_dir, false, true, NULL);
02b9e969
ZJS
1456 if (q == -ENOENT) {
1457 /* do nothing */
1458 } else if (q < 0) {
83096483
LP
1459 if (r >= 0)
1460 r = q;
1461
1462 return r;
1463 } else if (r >= 0)
1464 r += q;
1465
7410616c 1466 if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
6c5a2825
ZJS
1467 char *unit_file;
1468
1469 if (i->path) {
2b6bf07d 1470 unit_file = basename(i->path);
6c5a2825 1471
7410616c 1472 if (unit_name_is_valid(unit_file, UNIT_NAME_INSTANCE))
6c5a2825
ZJS
1473 /* unit file named as instance exists, thus all symlinks
1474 * pointing to it will be removed */
1475 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1476 else
1477 /* does not exist, thus we will mark for removal symlinks
1478 * to template unit file */
1479 q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
1480 } else {
1481 /* If i->path is not set, it means that we didn't actually find
1482 * the unit file. But we can still remove symlinks to the
1483 * nonexistent template. */
7410616c
LP
1484 r = unit_name_template(i->name, &unit_file);
1485 if (r < 0)
1486 return r;
29283ea4 1487
29283ea4 1488 q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
6c5a2825
ZJS
1489 free(unit_file);
1490 }
29283ea4
MS
1491 } else
1492 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1493
83096483
LP
1494 if (r >= 0 && q < 0)
1495 r = q;
1496 }
1497
1498 return r;
1499}
1500
e94937df
LN
1501int unit_file_add_dependency(
1502 UnitFileScope scope,
1503 bool runtime,
1504 const char *root_dir,
1505 char **files,
1506 char *target,
1507 UnitDependency dep,
1508 bool force,
1509 UnitFileChange **changes,
1510 unsigned *n_changes) {
1511
1512 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1513 _cleanup_(install_context_done) InstallContext c = {};
1514 _cleanup_free_ char *config_path = NULL;
1515 char **i;
1516 int r;
cab6235f 1517 UnitFileInstallInfo *info;
e94937df
LN
1518
1519 assert(scope >= 0);
1520 assert(scope < _UNIT_FILE_SCOPE_MAX);
1521
1522 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
1523 if (r < 0)
1524 return r;
1525
1526 r = get_config_path(scope, runtime, root_dir, &config_path);
1527 if (r < 0)
1528 return r;
1529
1530 STRV_FOREACH(i, files) {
1531 UnitFileState state;
1532
1533 state = unit_file_get_state(scope, root_dir, *i);
f647962d
MS
1534 if (state < 0)
1535 return log_error_errno(state, "Failed to get unit file state for %s: %m", *i);
e94937df
LN
1536
1537 if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
1538 log_error("Failed to enable unit: Unit %s is masked", *i);
15411c0c 1539 return -EOPNOTSUPP;
e94937df
LN
1540 }
1541
1542 r = install_info_add_auto(&c, *i);
1543 if (r < 0)
1544 return r;
1545 }
1546
2d5c93c7 1547 if (!ordered_hashmap_isempty(c.will_install)) {
9f03ee51 1548 r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops);
e94937df
LN
1549 if (r < 0)
1550 return r;
1551
2d5c93c7
MS
1552 r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install));
1553 if (r < 0)
1554 return r;
1555 }
1556
1557 while ((info = ordered_hashmap_first(c.will_install))) {
9f03ee51 1558 assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0);
e94937df 1559
aedd4012 1560 r = unit_file_search(&c, info, &paths, root_dir, false, false, NULL);
e94937df
LN
1561 if (r < 0)
1562 return r;
1563
1564 if (dep == UNIT_WANTS)
1565 r = strv_extend(&info->wanted_by, target);
1566 else if (dep == UNIT_REQUIRES)
1567 r = strv_extend(&info->required_by, target);
1568 else
1569 r = -EINVAL;
1570
1571 if (r < 0)
1572 return r;
1573
1574 r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes);
1575 if (r < 0)
1576 return r;
1577 }
1578
1579 return 0;
1580}
1581
83096483
LP
1582int unit_file_enable(
1583 UnitFileScope scope,
1584 bool runtime,
1585 const char *root_dir,
7195aa42 1586 char **files,
83096483
LP
1587 bool force,
1588 UnitFileChange **changes,
1589 unsigned *n_changes) {
1590
7fd1b19b 1591 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 1592 _cleanup_(install_context_done) InstallContext c = {};
d9e5e694 1593 char **i;
7fd1b19b 1594 _cleanup_free_ char *config_path = NULL;
83096483
LP
1595 int r;
1596
1597 assert(scope >= 0);
1598 assert(scope < _UNIT_FILE_SCOPE_MAX);
1599
12ed81d9 1600 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
83096483
LP
1601 if (r < 0)
1602 return r;
1603
1604 r = get_config_path(scope, runtime, root_dir, &config_path);
1605 if (r < 0)
d9e5e694 1606 return r;
83096483
LP
1607
1608 STRV_FOREACH(i, files) {
f7101b73
JS
1609 UnitFileState state;
1610
0ffce503
DR
1611 /* We only want to know if this unit is masked, so we ignore
1612 * errors from unit_file_get_state, deferring other checks.
1613 * This allows templated units to be enabled on the fly. */
f7101b73 1614 state = unit_file_get_state(scope, root_dir, *i);
f7101b73
JS
1615 if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
1616 log_error("Failed to enable unit: Unit %s is masked", *i);
15411c0c 1617 return -EOPNOTSUPP;
f7101b73
JS
1618 }
1619
83096483
LP
1620 r = install_info_add_auto(&c, *i);
1621 if (r < 0)
d9e5e694 1622 return r;
83096483
LP
1623 }
1624
729e3769
LP
1625 /* This will return the number of symlink rules that were
1626 supposed to be created, not the ones actually created. This is
20f59e42 1627 useful to determine whether the passed files had any
729e3769 1628 installation data at all. */
b91a3b02
LP
1629
1630 return install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
83096483
LP
1631}
1632
1633int unit_file_disable(
1634 UnitFileScope scope,
1635 bool runtime,
1636 const char *root_dir,
7195aa42 1637 char **files,
83096483
LP
1638 UnitFileChange **changes,
1639 unsigned *n_changes) {
1640
7fd1b19b 1641 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 1642 _cleanup_(install_context_done) InstallContext c = {};
d9e5e694 1643 char **i;
7fd1b19b
HH
1644 _cleanup_free_ char *config_path = NULL;
1645 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
83096483
LP
1646 int r, q;
1647
1648 assert(scope >= 0);
1649 assert(scope < _UNIT_FILE_SCOPE_MAX);
1650
12ed81d9 1651 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
83096483
LP
1652 if (r < 0)
1653 return r;
1654
1655 r = get_config_path(scope, runtime, root_dir, &config_path);
1656 if (r < 0)
d9e5e694 1657 return r;
83096483
LP
1658
1659 STRV_FOREACH(i, files) {
1660 r = install_info_add_auto(&c, *i);
1661 if (r < 0)
d9e5e694 1662 return r;
83096483
LP
1663 }
1664
1665 r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
1666
29283ea4 1667 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
e95c9837 1668 if (r >= 0)
034a2a52 1669 r = q;
83096483 1670
83096483
LP
1671 return r;
1672}
1673
1674int unit_file_reenable(
1675 UnitFileScope scope,
1676 bool runtime,
1677 const char *root_dir,
7195aa42 1678 char **files,
83096483
LP
1679 bool force,
1680 UnitFileChange **changes,
1681 unsigned *n_changes) {
92d430a9 1682 int r;
83096483 1683
92d430a9
RL
1684 r = unit_file_disable(scope, runtime, root_dir, files,
1685 changes, n_changes);
83096483 1686 if (r < 0)
d9e5e694 1687 return r;
83096483 1688
92d430a9
RL
1689 return unit_file_enable(scope, runtime, root_dir, files, force,
1690 changes, n_changes);
83096483
LP
1691}
1692
99504dd4
VP
1693int unit_file_set_default(
1694 UnitFileScope scope,
1695 const char *root_dir,
16ed0233 1696 const char *file,
718db961 1697 bool force,
99504dd4
VP
1698 UnitFileChange **changes,
1699 unsigned *n_changes) {
1700
1701 _cleanup_lookup_paths_free_ LookupPaths paths = {};
59ccf93d 1702 _cleanup_(install_context_done) InstallContext c = {};
99504dd4
VP
1703 _cleanup_free_ char *config_path = NULL;
1704 char *path;
1705 int r;
cab6235f 1706 UnitFileInstallInfo *i = NULL;
99504dd4
VP
1707
1708 assert(scope >= 0);
1709 assert(scope < _UNIT_FILE_SCOPE_MAX);
16ed0233 1710 assert(file);
99504dd4
VP
1711
1712 if (unit_name_to_type(file) != UNIT_TARGET)
1713 return -EINVAL;
1714
12ed81d9 1715 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
99504dd4
VP
1716 if (r < 0)
1717 return r;
1718
1719 r = get_config_path(scope, false, root_dir, &config_path);
1720 if (r < 0)
1721 return r;
1722
1723 r = install_info_add_auto(&c, file);
1724 if (r < 0)
1725 return r;
1726
9f03ee51 1727 assert_se(i = ordered_hashmap_first(c.will_install));
99504dd4 1728
aedd4012 1729 r = unit_file_search(&c, i, &paths, root_dir, false, true, NULL);
99504dd4
VP
1730 if (r < 0)
1731 return r;
1732
63c372cb 1733 path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET);
16ed0233 1734
718db961 1735 r = create_symlink(i->path, path, force, changes, n_changes);
99504dd4
VP
1736 if (r < 0)
1737 return r;
1738
1739 return 0;
1740}
1741
1742int unit_file_get_default(
1743 UnitFileScope scope,
1744 const char *root_dir,
1745 char **name) {
1746
1747 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1748 char **p;
1749 int r;
1750
16ed0233
LP
1751 assert(scope >= 0);
1752 assert(scope < _UNIT_FILE_SCOPE_MAX);
1753 assert(name);
1754
12ed81d9 1755 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
99504dd4
VP
1756 if (r < 0)
1757 return r;
1758
1759 STRV_FOREACH(p, paths.unit_path) {
1760 _cleanup_free_ char *path = NULL, *tmp = NULL;
97137ecc 1761 char *n;
99504dd4 1762
0c6ea3a4 1763 path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET);
99504dd4
VP
1764 if (!path)
1765 return -ENOMEM;
1766
1767 r = readlink_malloc(path, &tmp);
1768 if (r == -ENOENT)
1769 continue;
e8372f7e
LP
1770 else if (r == -EINVAL)
1771 /* not a symlink */
1772 n = strdup(SPECIAL_DEFAULT_TARGET);
1773 else if (r < 0)
99504dd4 1774 return r;
e8372f7e 1775 else
2b6bf07d 1776 n = strdup(basename(tmp));
99504dd4 1777
97137ecc 1778 if (!n)
99504dd4
VP
1779 return -ENOMEM;
1780
97137ecc 1781 *name = n;
99504dd4
VP
1782 return 0;
1783 }
1784
1785 return -ENOENT;
1786}
1787
a8ffe6fb 1788UnitFileState unit_file_lookup_state(
83096483
LP
1789 UnitFileScope scope,
1790 const char *root_dir,
a8ffe6fb 1791 const LookupPaths *paths,
83096483
LP
1792 const char *name) {
1793
83096483 1794 UnitFileState state = _UNIT_FILE_STATE_INVALID;
d9e5e694 1795 char **i;
7fd1b19b 1796 _cleanup_free_ char *path = NULL;
a7f7d1bd 1797 int r = 0;
83096483 1798
a8ffe6fb 1799 assert(paths);
83096483 1800
7410616c 1801 if (!unit_name_is_valid(name, UNIT_NAME_ANY))
83096483
LP
1802 return -EINVAL;
1803
a8ffe6fb 1804 STRV_FOREACH(i, paths->unit_path) {
83096483 1805 struct stat st;
12ed81d9 1806 char *partial;
aedd4012 1807 bool also = false;
83096483
LP
1808
1809 free(path);
0c6ea3a4 1810 path = path_join(root_dir, *i, name);
d9e5e694
ZJS
1811 if (!path)
1812 return -ENOMEM;
83096483 1813
12ed81d9 1814 if (root_dir)
0c6ea3a4 1815 partial = path + strlen(root_dir);
12ed81d9
ZJS
1816 else
1817 partial = path;
1818
67820a0c
MT
1819 /*
1820 * Search for a unit file in our default paths, to
1821 * be sure, that there are no broken symlinks.
1822 */
83096483 1823 if (lstat(path, &st) < 0) {
81006b8a 1824 r = -errno;
67820a0c
MT
1825 if (errno != ENOENT)
1826 return r;
83096483 1827
7410616c 1828 if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE))
67820a0c
MT
1829 continue;
1830 } else {
1831 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
1832 return -ENOENT;
1833
1834 r = null_or_empty_path(path);
1835 if (r < 0 && r != -ENOENT)
1836 return r;
1837 else if (r > 0) {
7410616c 1838 state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
67820a0c
MT
1839 return state;
1840 }
83096483
LP
1841 }
1842
1843 r = find_symlinks_in_scope(scope, root_dir, name, &state);
d9e5e694
ZJS
1844 if (r < 0)
1845 return r;
1846 else if (r > 0)
1847 return state;
83096483 1848
a8ffe6fb 1849 r = unit_file_can_install(paths, root_dir, partial, true, &also);
bcb161b0 1850 if (r < 0 && errno != ENOENT)
d9e5e694
ZJS
1851 return r;
1852 else if (r > 0)
1853 return UNIT_FILE_DISABLED;
aedd4012
JS
1854 else if (r == 0) {
1855 if (also)
1856 return UNIT_FILE_INDIRECT;
d9e5e694 1857 return UNIT_FILE_STATIC;
aedd4012 1858 }
83096483
LP
1859 }
1860
83096483
LP
1861 return r < 0 ? r : state;
1862}
1863
a8ffe6fb
ZJS
1864UnitFileState unit_file_get_state(
1865 UnitFileScope scope,
1866 const char *root_dir,
1867 const char *name) {
1868
1869 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1870 int r;
1871
1872 assert(scope >= 0);
1873 assert(scope < _UNIT_FILE_SCOPE_MAX);
1874 assert(name);
1875
1876 if (root_dir && scope != UNIT_FILE_SYSTEM)
1877 return -EINVAL;
1878
1879 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
1880 if (r < 0)
1881 return r;
1882
1883 return unit_file_lookup_state(scope, root_dir, &paths, name);
1884}
1885
c2a8d7b0 1886int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
7fd1b19b 1887 _cleanup_strv_free_ char **files = NULL;
cba2ef02 1888 char **p;
83096483
LP
1889 int r;
1890
1891 assert(scope >= 0);
1892 assert(scope < _UNIT_FILE_SCOPE_MAX);
1893 assert(name);
1894
1895 if (scope == UNIT_FILE_SYSTEM)
c2a8d7b0 1896 r = conf_files_list(&files, ".preset", root_dir,
a7480dba
LP
1897 "/etc/systemd/system-preset",
1898 "/usr/local/lib/systemd/system-preset",
1899 "/usr/lib/systemd/system-preset",
b4bdfefa 1900#ifdef HAVE_SPLIT_USR
a7480dba 1901 "/lib/systemd/system-preset",
b4bdfefa 1902#endif
83096483
LP
1903 NULL);
1904 else if (scope == UNIT_FILE_GLOBAL)
c2a8d7b0 1905 r = conf_files_list(&files, ".preset", root_dir,
a7480dba
LP
1906 "/etc/systemd/user-preset",
1907 "/usr/local/lib/systemd/user-preset",
1908 "/usr/lib/systemd/user-preset",
83096483
LP
1909 NULL);
1910 else
1911 return 1;
1912
1913 if (r < 0)
1914 return r;
1915
cba2ef02 1916 STRV_FOREACH(p, files) {
7fd1b19b 1917 _cleanup_fclose_ FILE *f;
83096483 1918
cba2ef02 1919 f = fopen(*p, "re");
83096483
LP
1920 if (!f) {
1921 if (errno == ENOENT)
1922 continue;
1923
d9e5e694 1924 return -errno;
83096483
LP
1925 }
1926
1927 for (;;) {
1928 char line[LINE_MAX], *l;
1929
1930 if (!fgets(line, sizeof(line), f))
1931 break;
1932
1933 l = strstrip(line);
1934 if (!*l)
1935 continue;
1936
d3b6d0c2 1937 if (strchr(COMMENTS "\n", *l))
83096483
LP
1938 continue;
1939
1940 if (first_word(l, "enable")) {
1941 l += 6;
1942 l += strspn(l, WHITESPACE);
1943
c2a8d7b0
LP
1944 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
1945 log_debug("Preset file says enable %s.", name);
d9e5e694 1946 return 1;
c2a8d7b0 1947 }
d9e5e694 1948
83096483
LP
1949 } else if (first_word(l, "disable")) {
1950 l += 7;
1951 l += strspn(l, WHITESPACE);
1952
c2a8d7b0
LP
1953 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
1954 log_debug("Preset file says disable %s.", name);
d9e5e694 1955 return 0;
c2a8d7b0 1956 }
d9e5e694 1957
83096483
LP
1958 } else
1959 log_debug("Couldn't parse line '%s'", l);
1960 }
83096483
LP
1961 }
1962
1963 /* Default is "enable" */
c2a8d7b0 1964 log_debug("Preset file doesn't say anything about %s, enabling.", name);
d9e5e694 1965 return 1;
83096483
LP
1966}
1967
1968int unit_file_preset(
1969 UnitFileScope scope,
1970 bool runtime,
1971 const char *root_dir,
7195aa42 1972 char **files,
d309c1c3 1973 UnitFilePresetMode mode,
83096483
LP
1974 bool force,
1975 UnitFileChange **changes,
1976 unsigned *n_changes) {
1977
59ccf93d 1978 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
da39f6a6
LP
1979 _cleanup_lookup_paths_free_ LookupPaths paths = {};
1980 _cleanup_free_ char *config_path = NULL;
1981 char **i;
83096483
LP
1982 int r, q;
1983
1984 assert(scope >= 0);
1985 assert(scope < _UNIT_FILE_SCOPE_MAX);
86bbe5bf 1986 assert(mode < _UNIT_FILE_PRESET_MAX);
83096483 1987
12ed81d9 1988 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
83096483
LP
1989 if (r < 0)
1990 return r;
1991
1992 r = get_config_path(scope, runtime, root_dir, &config_path);
1993 if (r < 0)
d9e5e694 1994 return r;
83096483
LP
1995
1996 STRV_FOREACH(i, files) {
1997
7410616c 1998 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
d9e5e694 1999 return -EINVAL;
83096483 2000
c2a8d7b0 2001 r = unit_file_query_preset(scope, root_dir, *i);
83096483 2002 if (r < 0)
d9e5e694 2003 return r;
83096483 2004
d309c1c3 2005 if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
83096483 2006 r = install_info_add_auto(&plus, *i);
d309c1c3 2007 else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
83096483 2008 r = install_info_add_auto(&minus, *i);
d309c1c3
LP
2009 else
2010 r = 0;
83096483 2011 if (r < 0)
d9e5e694 2012 return r;
83096483
LP
2013 }
2014
d309c1c3 2015 r = 0;
83096483 2016
d309c1c3
LP
2017 if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
2018 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
83096483 2019
d309c1c3
LP
2020 r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
2021
2022 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
2023 if (r == 0)
2024 r = q;
2025 }
2026
2027 if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
2028 /* Returns number of symlinks that where supposed to be installed. */
2029 q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
2030 if (r == 0)
2031 r = q;
2032 }
2033
2034 return r;
2035}
2036
2037int unit_file_preset_all(
2038 UnitFileScope scope,
2039 bool runtime,
2040 const char *root_dir,
2041 UnitFilePresetMode mode,
2042 bool force,
2043 UnitFileChange **changes,
2044 unsigned *n_changes) {
2045
59ccf93d 2046 _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
d309c1c3
LP
2047 _cleanup_lookup_paths_free_ LookupPaths paths = {};
2048 _cleanup_free_ char *config_path = NULL;
2049 char **i;
2050 int r, q;
2051
2052 assert(scope >= 0);
2053 assert(scope < _UNIT_FILE_SCOPE_MAX);
86bbe5bf 2054 assert(mode < _UNIT_FILE_PRESET_MAX);
d309c1c3
LP
2055
2056 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
2057 if (r < 0)
2058 return r;
2059
2060 r = get_config_path(scope, runtime, root_dir, &config_path);
2061 if (r < 0)
2062 return r;
2063
2064 STRV_FOREACH(i, paths.unit_path) {
2065 _cleanup_closedir_ DIR *d = NULL;
0c6ea3a4 2066 _cleanup_free_ char *units_dir;
d309c1c3 2067
0c6ea3a4
ZJS
2068 units_dir = path_join(root_dir, *i, NULL);
2069 if (!units_dir)
2070 return -ENOMEM;
d309c1c3
LP
2071
2072 d = opendir(units_dir);
2073 if (!d) {
2074 if (errno == ENOENT)
2075 continue;
2076
2077 return -errno;
2078 }
2079
2080 for (;;) {
2081 struct dirent *de;
2082
2083 errno = 0;
2084 de = readdir(d);
2085 if (!de && errno != 0)
2086 return -errno;
2087
2088 if (!de)
2089 break;
2090
a34bf9db 2091 if (hidden_file(de->d_name))
d309c1c3
LP
2092 continue;
2093
7410616c 2094 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
d309c1c3
LP
2095 continue;
2096
2097 dirent_ensure_type(d, de);
2098
2099 if (de->d_type != DT_REG)
2100 continue;
2101
c2a8d7b0 2102 r = unit_file_query_preset(scope, root_dir, de->d_name);
d309c1c3
LP
2103 if (r < 0)
2104 return r;
2105
2106 if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
2107 r = install_info_add_auto(&plus, de->d_name);
2108 else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
2109 r = install_info_add_auto(&minus, de->d_name);
2110 else
2111 r = 0;
2112 if (r < 0)
2113 return r;
2114 }
2115 }
2116
2117 r = 0;
2118
2119 if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
2120 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
2121
2122 r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
2123
2124 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL);
2125 if (r == 0)
2126 r = q;
2127 }
2128
2129 if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
2130 q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
2131 if (r == 0)
2132 r = q;
2133 }
83096483 2134
83096483
LP
2135 return r;
2136}
2137
59ccf93d
LP
2138static void unit_file_list_free_one(UnitFileList *f) {
2139 if (!f)
d9e5e694
ZJS
2140 return;
2141
59ccf93d
LP
2142 free(f->path);
2143 free(f);
d9e5e694 2144}
59ccf93d
LP
2145
2146DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
d9e5e694 2147
83096483
LP
2148int unit_file_get_list(
2149 UnitFileScope scope,
2150 const char *root_dir,
2151 Hashmap *h) {
2152
7fd1b19b 2153 _cleanup_lookup_paths_free_ LookupPaths paths = {};
d9e5e694 2154 char **i;
83096483
LP
2155 int r;
2156
2157 assert(scope >= 0);
2158 assert(scope < _UNIT_FILE_SCOPE_MAX);
2159 assert(h);
2160
83096483
LP
2161 if (root_dir && scope != UNIT_FILE_SYSTEM)
2162 return -EINVAL;
2163
fdbdf6ec
LN
2164 if (root_dir) {
2165 r = access(root_dir, F_OK);
2166 if (r < 0)
2167 return -errno;
2168 }
2169
12ed81d9 2170 r = lookup_paths_init_from_scope(&paths, scope, root_dir);
83096483
LP
2171 if (r < 0)
2172 return r;
2173
2174 STRV_FOREACH(i, paths.unit_path) {
da39f6a6 2175 _cleanup_closedir_ DIR *d = NULL;
0c6ea3a4 2176 _cleanup_free_ char *units_dir;
83096483 2177
0c6ea3a4
ZJS
2178 units_dir = path_join(root_dir, *i, NULL);
2179 if (!units_dir)
2180 return -ENOMEM;
83096483 2181
83096483
LP
2182 d = opendir(units_dir);
2183 if (!d) {
2184 if (errno == ENOENT)
2185 continue;
2186
d9e5e694 2187 return -errno;
83096483
LP
2188 }
2189
2190 for (;;) {
59ccf93d 2191 _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
d309c1c3 2192 struct dirent *de;
81fc054d 2193 _cleanup_free_ char *path = NULL;
8508ea9d 2194 bool also = false;
83096483 2195
4d993c8c
FW
2196 errno = 0;
2197 de = readdir(d);
2198 if (!de && errno != 0)
2199 return -errno;
83096483
LP
2200
2201 if (!de)
2202 break;
2203
a34bf9db 2204 if (hidden_file(de->d_name))
83096483
LP
2205 continue;
2206
7410616c 2207 if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
83096483
LP
2208 continue;
2209
2210 if (hashmap_get(h, de->d_name))
2211 continue;
2212
da39f6a6 2213 dirent_ensure_type(d, de);
83096483 2214
da39f6a6 2215 if (!IN_SET(de->d_type, DT_LNK, DT_REG))
83096483
LP
2216 continue;
2217
2218 f = new0(UnitFileList, 1);
d9e5e694
ZJS
2219 if (!f)
2220 return -ENOMEM;
83096483 2221
4fc13f52 2222 f->path = path_make_absolute(de->d_name, units_dir);
d9e5e694
ZJS
2223 if (!f->path)
2224 return -ENOMEM;
83096483
LP
2225
2226 r = null_or_empty_path(f->path);
d9e5e694
ZJS
2227 if (r < 0 && r != -ENOENT)
2228 return r;
2229 else if (r > 0) {
83096483
LP
2230 f->state =
2231 path_startswith(*i, "/run") ?
2232 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
2233 goto found;
2234 }
2235
2236 r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
d9e5e694
ZJS
2237 if (r < 0)
2238 return r;
2239 else if (r > 0) {
b5b46d59 2240 f->state = UNIT_FILE_ENABLED;
83096483 2241 goto found;
b5b46d59 2242 }
83096483 2243
81fc054d
LN
2244 path = path_make_absolute(de->d_name, *i);
2245 if (!path)
2246 return -ENOMEM;
2247
8508ea9d 2248 r = unit_file_can_install(&paths, root_dir, path, true, &also);
b5b46d59
LP
2249 if (r == -EINVAL || /* Invalid setting? */
2250 r == -EBADMSG || /* Invalid format? */
2251 r == -ENOENT /* Included file not found? */)
2252 f->state = UNIT_FILE_INVALID;
d9e5e694
ZJS
2253 else if (r < 0)
2254 return r;
2255 else if (r > 0)
83096483 2256 f->state = UNIT_FILE_DISABLED;
b5b46d59 2257 else
8508ea9d 2258 f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC;
83096483
LP
2259
2260 found:
2b6bf07d 2261 r = hashmap_put(h, basename(f->path), f);
d9e5e694
ZJS
2262 if (r < 0)
2263 return r;
2264 f = NULL; /* prevent cleanup */
83096483
LP
2265 }
2266 }
2267
77cd2c87 2268 return 0;
83096483
LP
2269}
2270
2271static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
2272 [UNIT_FILE_ENABLED] = "enabled",
771faa9a 2273 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
83096483
LP
2274 [UNIT_FILE_LINKED] = "linked",
2275 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
2276 [UNIT_FILE_MASKED] = "masked",
2277 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
2278 [UNIT_FILE_STATIC] = "static",
b5b46d59 2279 [UNIT_FILE_DISABLED] = "disabled",
aedd4012 2280 [UNIT_FILE_INDIRECT] = "indirect",
b5b46d59 2281 [UNIT_FILE_INVALID] = "invalid",
83096483
LP
2282};
2283
2284DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
c0576cd6
LP
2285
2286static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
2287 [UNIT_FILE_SYMLINK] = "symlink",
2288 [UNIT_FILE_UNLINK] = "unlink",
2289};
2290
2291DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
d309c1c3 2292
86bbe5bf 2293static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
d309c1c3
LP
2294 [UNIT_FILE_PRESET_FULL] = "full",
2295 [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
2296 [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
2297};
2298
2299DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);