]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/install.c
Use initalization instead of explicit zeroing
[thirdparty/systemd.git] / src / shared / install.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <fnmatch.h>
27
28 #include "util.h"
29 #include "mkdir.h"
30 #include "hashmap.h"
31 #include "set.h"
32 #include "path-util.h"
33 #include "path-lookup.h"
34 #include "strv.h"
35 #include "unit-name.h"
36 #include "install.h"
37 #include "conf-parser.h"
38 #include "conf-files.h"
39 #include "specifier.h"
40 #include "install-printf.h"
41
42 typedef struct {
43 Hashmap *will_install;
44 Hashmap *have_installed;
45 } InstallContext;
46
47 #define _cleanup_lookup_paths_free_ \
48 __attribute__((cleanup(lookup_paths_free)))
49 #define _cleanup_install_context_done_ \
50 __attribute__((cleanup(install_context_done)))
51
52 static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
53 assert(paths);
54 assert(scope >= 0);
55 assert(scope < _UNIT_FILE_SCOPE_MAX);
56
57 zero(*paths);
58
59 return lookup_paths_init(paths,
60 scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
61 scope == UNIT_FILE_USER,
62 NULL, NULL, NULL);
63 }
64
65 static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
66 char *p = NULL;
67 int r;
68
69 assert(scope >= 0);
70 assert(scope < _UNIT_FILE_SCOPE_MAX);
71 assert(ret);
72
73 switch (scope) {
74
75 case UNIT_FILE_SYSTEM:
76
77 if (root_dir && runtime)
78 asprintf(&p, "%s/run/systemd/system", root_dir);
79 else if (runtime)
80 p = strdup("/run/systemd/system");
81 else if (root_dir)
82 asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH);
83 else
84 p = strdup(SYSTEM_CONFIG_UNIT_PATH);
85
86 break;
87
88 case UNIT_FILE_GLOBAL:
89
90 if (root_dir)
91 return -EINVAL;
92
93 if (runtime)
94 p = strdup("/run/systemd/user");
95 else
96 p = strdup(USER_CONFIG_UNIT_PATH);
97 break;
98
99 case UNIT_FILE_USER:
100
101 if (root_dir || runtime)
102 return -EINVAL;
103
104 r = user_config_home(&p);
105 if (r <= 0)
106 return r < 0 ? r : -ENOENT;
107
108 break;
109
110 default:
111 assert_not_reached("Bad scope");
112 }
113
114 if (!p)
115 return -ENOMEM;
116
117 *ret = p;
118 return 0;
119 }
120
121 static int add_file_change(
122 UnitFileChange **changes,
123 unsigned *n_changes,
124 UnitFileChangeType type,
125 const char *path,
126 const char *source) {
127
128 UnitFileChange *c;
129 unsigned i;
130
131 assert(path);
132 assert(!changes == !n_changes);
133
134 if (!changes)
135 return 0;
136
137 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
138 if (!c)
139 return -ENOMEM;
140
141 *changes = c;
142 i = *n_changes;
143
144 c[i].type = type;
145 c[i].path = strdup(path);
146 if (!c[i].path)
147 return -ENOMEM;
148
149 if (source) {
150 c[i].source = strdup(source);
151 if (!c[i].source) {
152 free(c[i].path);
153 return -ENOMEM;
154 }
155 } else
156 c[i].source = NULL;
157
158 *n_changes = i+1;
159 return 0;
160 }
161
162 static int mark_symlink_for_removal(
163 Set **remove_symlinks_to,
164 const char *p) {
165
166 char *n;
167 int r;
168
169 assert(p);
170
171 r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func);
172 if (r < 0)
173 return r;
174
175 n = strdup(p);
176 if (!n)
177 return -ENOMEM;
178
179 path_kill_slashes(n);
180
181 r = set_put(*remove_symlinks_to, n);
182 if (r < 0) {
183 free(n);
184 return r == -EEXIST ? 0 : r;
185 }
186
187 return 0;
188 }
189
190 static int remove_marked_symlinks_fd(
191 Set *remove_symlinks_to,
192 int fd,
193 const char *path,
194 const char *config_path,
195 bool *deleted,
196 UnitFileChange **changes,
197 unsigned *n_changes,
198 char** files) {
199
200 int r = 0;
201 DIR _cleanup_closedir_ *d = NULL;
202
203 assert(remove_symlinks_to);
204 assert(fd >= 0);
205 assert(path);
206 assert(config_path);
207 assert(deleted);
208
209 d = fdopendir(fd);
210 if (!d) {
211 close_nointr_nofail(fd);
212 return -errno;
213 }
214
215 rewinddir(d);
216
217 for (;;) {
218 struct dirent *de;
219 union dirent_storage buf;
220 int k;
221
222 k = readdir_r(d, &buf.de, &de);
223 if (k != 0) {
224 r = -errno;
225 break;
226 }
227
228 if (!de)
229 break;
230
231 if (ignore_file(de->d_name))
232 continue;
233
234 dirent_ensure_type(d, de);
235
236 if (de->d_type == DT_DIR) {
237 int nfd, q;
238 char _cleanup_free_ *p = NULL;
239
240 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
241 if (nfd < 0) {
242 if (errno == ENOENT)
243 continue;
244
245 if (r == 0)
246 r = -errno;
247 continue;
248 }
249
250 p = path_make_absolute(de->d_name, path);
251 if (!p) {
252 close_nointr_nofail(nfd);
253 return -ENOMEM;
254 }
255
256 /* This will close nfd, regardless whether it succeeds or not */
257 q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, files);
258
259 if (r == 0)
260 r = q;
261
262 } else if (de->d_type == DT_LNK) {
263 char _cleanup_free_ *p = NULL, *dest = NULL;
264 int q;
265 bool found;
266
267 p = path_make_absolute(de->d_name, path);
268 if (!p)
269 return -ENOMEM;
270
271 q = readlink_and_canonicalize(p, &dest);
272 if (q < 0) {
273 if (q == -ENOENT)
274 continue;
275
276 if (r == 0)
277 r = q;
278 continue;
279 }
280
281 found =
282 set_get(remove_symlinks_to, dest) ||
283 set_get(remove_symlinks_to, path_get_file_name(dest));
284
285 if (unit_name_is_instance(p))
286 found = found && strv_contains(files, path_get_file_name(p));
287
288 if (found) {
289
290 if (unlink(p) < 0 && errno != ENOENT) {
291
292 if (r == 0)
293 r = -errno;
294 } else {
295 rmdir_parents(p, config_path);
296 path_kill_slashes(p);
297
298 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
299
300 if (!set_get(remove_symlinks_to, p)) {
301
302 q = mark_symlink_for_removal(&remove_symlinks_to, p);
303 if (q < 0) {
304 if (r == 0)
305 r = q;
306 } else
307 *deleted = true;
308 }
309 }
310 }
311 }
312 }
313
314 return r;
315 }
316
317 static int remove_marked_symlinks(
318 Set *remove_symlinks_to,
319 const char *config_path,
320 UnitFileChange **changes,
321 unsigned *n_changes,
322 char** files) {
323
324 int fd, r = 0;
325 bool deleted;
326
327 assert(config_path);
328
329 if (set_size(remove_symlinks_to) <= 0)
330 return 0;
331
332 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
333 if (fd < 0)
334 return -errno;
335
336 do {
337 int q, cfd;
338 deleted = false;
339
340 cfd = dup(fd);
341 if (cfd < 0) {
342 r = -errno;
343 break;
344 }
345
346 /* This takes possession of cfd and closes it */
347 q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, files);
348 if (r == 0)
349 r = q;
350 } while (deleted);
351
352 close_nointr_nofail(fd);
353
354 return r;
355 }
356
357 static int find_symlinks_fd(
358 const char *name,
359 int fd,
360 const char *path,
361 const char *config_path,
362 bool *same_name_link) {
363
364 int r = 0;
365 DIR _cleanup_closedir_ *d = NULL;
366
367 assert(name);
368 assert(fd >= 0);
369 assert(path);
370 assert(config_path);
371 assert(same_name_link);
372
373 d = fdopendir(fd);
374 if (!d) {
375 close_nointr_nofail(fd);
376 return -errno;
377 }
378
379 for (;;) {
380 int k;
381 struct dirent *de;
382 union dirent_storage buf;
383
384 k = readdir_r(d, &buf.de, &de);
385 if (k != 0)
386 return -errno;
387
388 if (!de)
389 return r;
390
391 if (ignore_file(de->d_name))
392 continue;
393
394 dirent_ensure_type(d, de);
395
396 if (de->d_type == DT_DIR) {
397 int nfd, q;
398 char _cleanup_free_ *p = NULL;
399
400 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
401 if (nfd < 0) {
402 if (errno == ENOENT)
403 continue;
404
405 if (r == 0)
406 r = -errno;
407 continue;
408 }
409
410 p = path_make_absolute(de->d_name, path);
411 if (!p) {
412 close_nointr_nofail(nfd);
413 return -ENOMEM;
414 }
415
416 /* This will close nfd, regardless whether it succeeds or not */
417 q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
418
419 if (q > 0)
420 return 1;
421
422 if (r == 0)
423 r = q;
424
425 } else if (de->d_type == DT_LNK) {
426 char _cleanup_free_ *p = NULL, *dest = NULL;
427 bool found_path, found_dest, b = false;
428 int q;
429
430 /* Acquire symlink name */
431 p = path_make_absolute(de->d_name, path);
432 if (!p)
433 return -ENOMEM;
434
435 /* Acquire symlink destination */
436 q = readlink_and_canonicalize(p, &dest);
437 if (q < 0) {
438 if (q == -ENOENT)
439 continue;
440
441 if (r == 0)
442 r = q;
443 continue;
444 }
445
446 /* Check if the symlink itself matches what we
447 * are looking for */
448 if (path_is_absolute(name))
449 found_path = path_equal(p, name);
450 else
451 found_path = streq(de->d_name, name);
452
453 /* Check if what the symlink points to
454 * matches what we are looking for */
455 if (path_is_absolute(name))
456 found_dest = path_equal(dest, name);
457 else
458 found_dest = streq(path_get_file_name(dest), name);
459
460 if (found_path && found_dest) {
461 char _cleanup_free_ *t = NULL;
462
463 /* Filter out same name links in the main
464 * config path */
465 t = path_make_absolute(name, config_path);
466 if (!t)
467 return -ENOMEM;
468
469 b = path_equal(t, p);
470 }
471
472 if (b)
473 *same_name_link = true;
474 else if (found_path || found_dest)
475 return 1;
476 }
477 }
478
479 return r;
480 }
481
482 static int find_symlinks(
483 const char *name,
484 const char *config_path,
485 bool *same_name_link) {
486
487 int fd;
488
489 assert(name);
490 assert(config_path);
491 assert(same_name_link);
492
493 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
494 if (fd < 0) {
495 if (errno == ENOENT)
496 return 0;
497 return -errno;
498 }
499
500 /* This takes possession of fd and closes it */
501 return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
502 }
503
504 static int find_symlinks_in_scope(
505 UnitFileScope scope,
506 const char *root_dir,
507 const char *name,
508 UnitFileState *state) {
509
510 int r;
511 char _cleanup_free_ *path = NULL;
512 bool same_name_link_runtime = false, same_name_link = false;
513
514 assert(scope >= 0);
515 assert(scope < _UNIT_FILE_SCOPE_MAX);
516 assert(name);
517
518 if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) {
519
520 /* First look in runtime config path */
521 r = get_config_path(scope, true, root_dir, &path);
522 if (r < 0)
523 return r;
524
525 r = find_symlinks(name, path, &same_name_link_runtime);
526 if (r < 0)
527 return r;
528 else if (r > 0) {
529 *state = UNIT_FILE_ENABLED_RUNTIME;
530 return r;
531 }
532 }
533
534 /* Then look in the normal config path */
535 r = get_config_path(scope, false, root_dir, &path);
536 if (r < 0)
537 return r;
538
539 r = find_symlinks(name, path, &same_name_link);
540 if (r < 0)
541 return r;
542 else if (r > 0) {
543 *state = UNIT_FILE_ENABLED;
544 return r;
545 }
546
547 /* Hmm, we didn't find it, but maybe we found the same name
548 * link? */
549 if (same_name_link_runtime) {
550 *state = UNIT_FILE_LINKED_RUNTIME;
551 return 1;
552 } else if (same_name_link) {
553 *state = UNIT_FILE_LINKED;
554 return 1;
555 }
556
557 return 0;
558 }
559
560 int unit_file_mask(
561 UnitFileScope scope,
562 bool runtime,
563 const char *root_dir,
564 char *files[],
565 bool force,
566 UnitFileChange **changes,
567 unsigned *n_changes) {
568
569 char **i;
570 char _cleanup_free_ *prefix;
571 int r;
572
573 assert(scope >= 0);
574 assert(scope < _UNIT_FILE_SCOPE_MAX);
575
576 r = get_config_path(scope, runtime, root_dir, &prefix);
577 if (r < 0)
578 return r;
579
580 STRV_FOREACH(i, files) {
581 char _cleanup_free_ *path = NULL;
582
583 if (!unit_name_is_valid(*i, true)) {
584 if (r == 0)
585 r = -EINVAL;
586 continue;
587 }
588
589 path = path_make_absolute(*i, prefix);
590 if (!path) {
591 r = -ENOMEM;
592 break;
593 }
594
595 if (symlink("/dev/null", path) >= 0) {
596 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
597
598 continue;
599 }
600
601 if (errno == EEXIST) {
602
603 if (null_or_empty_path(path) > 0)
604 continue;
605
606 if (force) {
607 unlink(path);
608
609 if (symlink("/dev/null", path) >= 0) {
610
611 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
612 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
613
614 continue;
615 }
616 }
617
618 if (r == 0)
619 r = -EEXIST;
620 } else {
621 if (r == 0)
622 r = -errno;
623 }
624 }
625
626 return r;
627 }
628
629 int unit_file_unmask(
630 UnitFileScope scope,
631 bool runtime,
632 const char *root_dir,
633 char *files[],
634 UnitFileChange **changes,
635 unsigned *n_changes) {
636
637 char **i, *config_path = NULL;
638 int r, q;
639 Set *remove_symlinks_to = NULL;
640
641 assert(scope >= 0);
642 assert(scope < _UNIT_FILE_SCOPE_MAX);
643
644 r = get_config_path(scope, runtime, root_dir, &config_path);
645 if (r < 0)
646 goto finish;
647
648 STRV_FOREACH(i, files) {
649 char *path;
650
651 if (!unit_name_is_valid(*i, true)) {
652 if (r == 0)
653 r = -EINVAL;
654 continue;
655 }
656
657 path = path_make_absolute(*i, config_path);
658 if (!path) {
659 r = -ENOMEM;
660 break;
661 }
662
663 q = null_or_empty_path(path);
664 if (q > 0) {
665 if (unlink(path) >= 0) {
666 mark_symlink_for_removal(&remove_symlinks_to, path);
667 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
668
669 free(path);
670 continue;
671 }
672
673 q = -errno;
674 }
675
676 if (q != -ENOENT && r == 0)
677 r = q;
678
679 free(path);
680 }
681
682
683 finish:
684 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
685 if (r == 0)
686 r = q;
687
688 set_free_free(remove_symlinks_to);
689 free(config_path);
690
691 return r;
692 }
693
694 int unit_file_link(
695 UnitFileScope scope,
696 bool runtime,
697 const char *root_dir,
698 char *files[],
699 bool force,
700 UnitFileChange **changes,
701 unsigned *n_changes) {
702
703 LookupPaths _cleanup_lookup_paths_free_ paths = {};
704 char **i;
705 char _cleanup_free_ *config_path = NULL;
706 int r, q;
707
708 assert(scope >= 0);
709 assert(scope < _UNIT_FILE_SCOPE_MAX);
710
711 r = lookup_paths_init_from_scope(&paths, scope);
712 if (r < 0)
713 return r;
714
715 r = get_config_path(scope, runtime, root_dir, &config_path);
716 if (r < 0)
717 return r;
718
719 STRV_FOREACH(i, files) {
720 char _cleanup_free_ *path = NULL;
721 char *fn;
722 struct stat st;
723
724 fn = path_get_file_name(*i);
725
726 if (!path_is_absolute(*i) ||
727 !unit_name_is_valid(fn, true)) {
728 if (r == 0)
729 r = -EINVAL;
730 continue;
731 }
732
733 if (lstat(*i, &st) < 0) {
734 if (r == 0)
735 r = -errno;
736 continue;
737 }
738
739 if (!S_ISREG(st.st_mode)) {
740 r = -ENOENT;
741 continue;
742 }
743
744 q = in_search_path(*i, paths.unit_path);
745 if (q < 0)
746 return q;
747
748 if (q > 0)
749 continue;
750
751 path = path_make_absolute(fn, config_path);
752 if (!path)
753 return -ENOMEM;
754
755 if (symlink(*i, path) >= 0) {
756 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
757 continue;
758 }
759
760 if (errno == EEXIST) {
761 char _cleanup_free_ *dest = NULL;
762
763 q = readlink_and_make_absolute(path, &dest);
764
765 if (q < 0 && errno != ENOENT) {
766 if (r == 0)
767 r = q;
768 continue;
769 }
770
771 if (q >= 0 && path_equal(dest, *i))
772 continue;
773
774 if (force) {
775 unlink(path);
776
777 if (symlink(*i, path) >= 0) {
778
779 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
780 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
781
782 continue;
783 }
784 }
785
786 if (r == 0)
787 r = -EEXIST;
788 } else {
789 if (r == 0)
790 r = -errno;
791 }
792 }
793
794 return r;
795 }
796
797 void unit_file_list_free(Hashmap *h) {
798 UnitFileList *i;
799
800 while ((i = hashmap_steal_first(h))) {
801 free(i->path);
802 free(i);
803 }
804
805 hashmap_free(h);
806 }
807
808 void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
809 unsigned i;
810
811 assert(changes || n_changes == 0);
812
813 if (!changes)
814 return;
815
816 for (i = 0; i < n_changes; i++) {
817 free(changes[i].path);
818 free(changes[i].source);
819 }
820
821 free(changes);
822 }
823
824 static void install_info_free(InstallInfo *i) {
825 assert(i);
826
827 free(i->name);
828 free(i->path);
829 strv_free(i->aliases);
830 strv_free(i->wanted_by);
831 strv_free(i->required_by);
832 free(i);
833 }
834
835 static void install_info_hashmap_free(Hashmap *m) {
836 InstallInfo *i;
837
838 if (!m)
839 return;
840
841 while ((i = hashmap_steal_first(m)))
842 install_info_free(i);
843
844 hashmap_free(m);
845 }
846
847 static void install_context_done(InstallContext *c) {
848 assert(c);
849
850 install_info_hashmap_free(c->will_install);
851 install_info_hashmap_free(c->have_installed);
852
853 c->will_install = c->have_installed = NULL;
854 }
855
856 static int install_info_add(
857 InstallContext *c,
858 const char *name,
859 const char *path) {
860 InstallInfo *i = NULL;
861 int r;
862
863 assert(c);
864 assert(name || path);
865
866 if (!name)
867 name = path_get_file_name(path);
868
869 if (!unit_name_is_valid(name, true))
870 return -EINVAL;
871
872 if (hashmap_get(c->have_installed, name) ||
873 hashmap_get(c->will_install, name))
874 return 0;
875
876 r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func);
877 if (r < 0)
878 return r;
879
880 i = new0(InstallInfo, 1);
881 if (!i)
882 return -ENOMEM;
883
884 i->name = strdup(name);
885 if (!i->name) {
886 r = -ENOMEM;
887 goto fail;
888 }
889
890 if (path) {
891 i->path = strdup(path);
892 if (!i->path) {
893 r = -ENOMEM;
894 goto fail;
895 }
896 }
897
898 r = hashmap_put(c->will_install, i->name, i);
899 if (r < 0)
900 goto fail;
901
902 return 0;
903
904 fail:
905 if (i)
906 install_info_free(i);
907
908 return r;
909 }
910
911 static int install_info_add_auto(
912 InstallContext *c,
913 const char *name_or_path) {
914
915 assert(c);
916 assert(name_or_path);
917
918 if (path_is_absolute(name_or_path))
919 return install_info_add(c, NULL, name_or_path);
920 else
921 return install_info_add(c, name_or_path, NULL);
922 }
923
924 static int config_parse_also(
925 const char *filename,
926 unsigned line,
927 const char *section,
928 const char *lvalue,
929 int ltype,
930 const char *rvalue,
931 void *data,
932 void *userdata) {
933
934 char *w;
935 size_t l;
936 char *state;
937 InstallContext *c = data;
938
939 assert(filename);
940 assert(lvalue);
941 assert(rvalue);
942
943 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
944 char _cleanup_free_ *n;
945 int r;
946
947 n = strndup(w, l);
948 if (!n)
949 return -ENOMEM;
950
951 r = install_info_add(c, n, NULL);
952 if (r < 0)
953 return r;
954 }
955
956 return 0;
957 }
958
959 static int config_parse_user(
960 const char *filename,
961 unsigned line,
962 const char *section,
963 const char *lvalue,
964 int ltype,
965 const char *rvalue,
966 void *data,
967 void *userdata) {
968
969 InstallInfo *i = data;
970 char* printed;
971
972 assert(filename);
973 assert(lvalue);
974 assert(rvalue);
975
976 printed = install_full_printf(i, rvalue);
977 if (!printed)
978 return -ENOMEM;
979
980 free(i->user);
981 i->user = printed;
982
983 return 0;
984 }
985
986 static int unit_file_load(
987 InstallContext *c,
988 InstallInfo *info,
989 const char *path,
990 bool allow_symlink) {
991
992 const ConfigTableItem items[] = {
993 { "Install", "Alias", config_parse_strv, 0, &info->aliases },
994 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
995 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
996 { "Install", "Also", config_parse_also, 0, c },
997 { "Exec", "User", config_parse_user, 0, info },
998 { NULL, NULL, NULL, 0, NULL }
999 };
1000
1001 int fd;
1002 FILE _cleanup_fclose_ *f = NULL;
1003 int r;
1004
1005 assert(c);
1006 assert(info);
1007 assert(path);
1008
1009 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
1010 if (fd < 0)
1011 return -errno;
1012
1013 f = fdopen(fd, "re");
1014 if (!f) {
1015 close_nointr_nofail(fd);
1016 return -ENOMEM;
1017 }
1018
1019 r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info);
1020 if (r < 0)
1021 return r;
1022
1023 return
1024 strv_length(info->aliases) +
1025 strv_length(info->wanted_by) +
1026 strv_length(info->required_by);
1027 }
1028
1029 static int unit_file_search(
1030 InstallContext *c,
1031 InstallInfo *info,
1032 LookupPaths *paths,
1033 const char *root_dir,
1034 bool allow_symlink) {
1035
1036 char **p;
1037 int r;
1038
1039 assert(c);
1040 assert(info);
1041 assert(paths);
1042
1043 if (info->path)
1044 return unit_file_load(c, info, info->path, allow_symlink);
1045
1046 assert(info->name);
1047
1048 STRV_FOREACH(p, paths->unit_path) {
1049 char *path = NULL;
1050
1051 if (isempty(root_dir))
1052 asprintf(&path, "%s/%s", *p, info->name);
1053 else
1054 asprintf(&path, "%s/%s/%s", root_dir, *p, info->name);
1055
1056 if (!path)
1057 return -ENOMEM;
1058
1059 r = unit_file_load(c, info, path, allow_symlink);
1060
1061 if (r >= 0)
1062 info->path = path;
1063 else {
1064 if (r == -ENOENT && unit_name_is_instance(info->name)) {
1065 /* unit file doesn't exist, however instance enablement was request */
1066 /* we will check if it is possible to load template unit file */
1067 char *template = NULL,
1068 *template_path = NULL,
1069 *template_dir = NULL;
1070
1071 template = unit_name_template(info->name);
1072 if (!template) {
1073 free(path);
1074 return -ENOMEM;
1075 }
1076
1077 /* we will reuse path variable since we don't need it anymore */
1078 template_dir = path;
1079 *(strrchr(path, '/') + 1) = '\0';
1080
1081 template_path = strjoin(template_dir, template, NULL);
1082 if (!template_path) {
1083 free(path);
1084 free(template);
1085 return -ENOMEM;
1086 }
1087
1088 /* let's try to load template unit */
1089 r = unit_file_load(c, info, template_path, allow_symlink);
1090 if (r >= 0) {
1091 info->path = strdup(template_path);
1092 if (!info->path) {
1093 free(path);
1094 free(template);
1095 free(template_path);
1096 return -ENOMEM;
1097 }
1098 }
1099
1100 free(template);
1101 free(template_path);
1102 }
1103 free(path);
1104 }
1105
1106 if (r != -ENOENT && r != -ELOOP)
1107 return r;
1108 }
1109
1110 return -ENOENT;
1111 }
1112
1113 static int unit_file_can_install(
1114 LookupPaths *paths,
1115 const char *root_dir,
1116 const char *name,
1117 bool allow_symlink) {
1118
1119 InstallContext _cleanup_install_context_done_ c = {};
1120 InstallInfo *i;
1121 int r;
1122
1123 assert(paths);
1124 assert(name);
1125
1126 r = install_info_add_auto(&c, name);
1127 if (r < 0)
1128 return r;
1129
1130 assert_se(i = hashmap_first(c.will_install));
1131
1132 r = unit_file_search(&c, i, paths, root_dir, allow_symlink);
1133
1134 if (r >= 0)
1135 r =
1136 strv_length(i->aliases) +
1137 strv_length(i->wanted_by) +
1138 strv_length(i->required_by);
1139
1140 return r;
1141 }
1142
1143 static int create_symlink(
1144 const char *old_path,
1145 const char *new_path,
1146 bool force,
1147 UnitFileChange **changes,
1148 unsigned *n_changes) {
1149
1150 char _cleanup_free_ *dest = NULL;
1151 int r;
1152
1153 assert(old_path);
1154 assert(new_path);
1155
1156 mkdir_parents_label(new_path, 0755);
1157
1158 if (symlink(old_path, new_path) >= 0) {
1159 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1160 return 0;
1161 }
1162
1163 if (errno != EEXIST)
1164 return -errno;
1165
1166 r = readlink_and_make_absolute(new_path, &dest);
1167 if (r < 0)
1168 return r;
1169
1170 if (path_equal(dest, old_path))
1171 return 0;
1172
1173 if (!force)
1174 return -EEXIST;
1175
1176 unlink(new_path);
1177
1178 if (symlink(old_path, new_path) >= 0) {
1179 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
1180 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1181 return 0;
1182 }
1183
1184 return -errno;
1185 }
1186
1187 static int install_info_symlink_alias(
1188 InstallInfo *i,
1189 const char *config_path,
1190 bool force,
1191 UnitFileChange **changes,
1192 unsigned *n_changes) {
1193
1194 char **s;
1195 int r = 0, q;
1196
1197 assert(i);
1198 assert(config_path);
1199
1200 STRV_FOREACH(s, i->aliases) {
1201 char _cleanup_free_ *alias_path = NULL, *dst = NULL;
1202
1203 dst = install_full_printf(i, *s);
1204 if (!dst)
1205 return -ENOMEM;
1206
1207 alias_path = path_make_absolute(dst, config_path);
1208 if (!alias_path)
1209 return -ENOMEM;
1210
1211 q = create_symlink(i->path, alias_path, force, changes, n_changes);
1212 if (r == 0)
1213 r = q;
1214 }
1215
1216 return r;
1217 }
1218
1219 static int install_info_symlink_wants(
1220 InstallInfo *i,
1221 const char *config_path,
1222 bool force,
1223 UnitFileChange **changes,
1224 unsigned *n_changes) {
1225
1226 char **s;
1227 int r = 0, q;
1228
1229 assert(i);
1230 assert(config_path);
1231
1232 STRV_FOREACH(s, i->wanted_by) {
1233 char _cleanup_free_ *path = NULL, *dst = NULL;
1234
1235 dst = install_full_printf(i, *s);
1236 if (!dst)
1237 return -ENOMEM;
1238
1239 if (!unit_name_is_valid(dst, true)) {
1240 r = -EINVAL;
1241 continue;
1242 }
1243
1244 if (asprintf(&path, "%s/%s.wants/%s", config_path, dst, i->name) < 0)
1245 return -ENOMEM;
1246
1247 q = create_symlink(i->path, path, force, changes, n_changes);
1248
1249 if (r == 0)
1250 r = q;
1251 }
1252
1253 return r;
1254 }
1255
1256 static int install_info_symlink_requires(
1257 InstallInfo *i,
1258 const char *config_path,
1259 bool force,
1260 UnitFileChange **changes,
1261 unsigned *n_changes) {
1262
1263 char **s;
1264 int r = 0, q;
1265
1266 assert(i);
1267 assert(config_path);
1268
1269 STRV_FOREACH(s, i->required_by) {
1270 char _cleanup_free_ *path = NULL, *dst = NULL;
1271
1272 dst = install_full_printf(i, *s);
1273 if (!dst)
1274 return -ENOMEM;
1275
1276 if (!unit_name_is_valid(dst, true)) {
1277 r = -EINVAL;
1278 continue;
1279 }
1280
1281 if (asprintf(&path, "%s/%s.requires/%s", config_path, dst, i->name) < 0)
1282 return -ENOMEM;
1283
1284 q = create_symlink(i->path, path, force, changes, n_changes);
1285
1286 if (r == 0)
1287 r = q;
1288 }
1289
1290 return r;
1291 }
1292
1293 static int install_info_symlink_link(
1294 InstallInfo *i,
1295 LookupPaths *paths,
1296 const char *config_path,
1297 bool force,
1298 UnitFileChange **changes,
1299 unsigned *n_changes) {
1300
1301 int r;
1302 char _cleanup_free_ *path = NULL;
1303
1304 assert(i);
1305 assert(paths);
1306 assert(config_path);
1307 assert(i->path);
1308
1309 r = in_search_path(i->path, paths->unit_path);
1310 if (r != 0)
1311 return r;
1312
1313 if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
1314 return -ENOMEM;
1315
1316 r = create_symlink(i->path, path, force, changes, n_changes);
1317 return r;
1318 }
1319
1320 static int install_info_apply(
1321 InstallInfo *i,
1322 LookupPaths *paths,
1323 const char *config_path,
1324 bool force,
1325 UnitFileChange **changes,
1326 unsigned *n_changes) {
1327
1328 int r, q;
1329
1330 assert(i);
1331 assert(paths);
1332 assert(config_path);
1333
1334 r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
1335
1336 q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
1337 if (r == 0)
1338 r = q;
1339
1340 q = install_info_symlink_requires(i, config_path, force, changes, n_changes);
1341 if (r == 0)
1342 r = q;
1343
1344 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1345 if (r == 0)
1346 r = q;
1347
1348 return r;
1349 }
1350
1351 static int install_context_apply(
1352 InstallContext *c,
1353 LookupPaths *paths,
1354 const char *config_path,
1355 const char *root_dir,
1356 bool force,
1357 UnitFileChange **changes,
1358 unsigned *n_changes) {
1359
1360 InstallInfo *i;
1361 int r = 0, q;
1362
1363 assert(c);
1364 assert(paths);
1365 assert(config_path);
1366
1367 while ((i = hashmap_first(c->will_install))) {
1368
1369 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1370 if (q < 0)
1371 return q;
1372
1373 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1374
1375 q = unit_file_search(c, i, paths, root_dir, false);
1376 if (q < 0) {
1377 if (r >= 0)
1378 r = q;
1379
1380 return r;
1381 } else if (r >= 0)
1382 r += q;
1383
1384 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1385 if (r >= 0 && q < 0)
1386 r = q;
1387 }
1388
1389 return r;
1390 }
1391
1392 static int install_context_mark_for_removal(
1393 InstallContext *c,
1394 LookupPaths *paths,
1395 Set **remove_symlinks_to,
1396 const char *config_path,
1397 const char *root_dir) {
1398
1399 InstallInfo *i;
1400 int r = 0, q;
1401
1402 assert(c);
1403 assert(paths);
1404 assert(config_path);
1405
1406 /* Marks all items for removal */
1407
1408 while ((i = hashmap_first(c->will_install))) {
1409
1410 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1411 if (q < 0)
1412 return q;
1413
1414 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1415
1416 q = unit_file_search(c, i, paths, root_dir, false);
1417 if (q < 0) {
1418 if (r >= 0)
1419 r = q;
1420
1421 return r;
1422 } else if (r >= 0)
1423 r += q;
1424
1425 if (unit_name_is_instance(i->name)) {
1426 char *unit_file = NULL;
1427
1428 unit_file = path_get_file_name(i->path);
1429
1430 if (unit_name_is_instance(unit_file))
1431 /* unit file named as instance exists, thus all symlinks pointing to it, will be removed */
1432 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1433 else
1434 /* does not exist, thus we will mark for removal symlinks to template unit file */
1435 q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
1436 } else
1437 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1438
1439 if (r >= 0 && q < 0)
1440 r = q;
1441 }
1442
1443 return r;
1444 }
1445
1446 int unit_file_enable(
1447 UnitFileScope scope,
1448 bool runtime,
1449 const char *root_dir,
1450 char *files[],
1451 bool force,
1452 UnitFileChange **changes,
1453 unsigned *n_changes) {
1454
1455 LookupPaths _cleanup_lookup_paths_free_ paths = {};
1456 InstallContext _cleanup_install_context_done_ c = {};
1457 char **i;
1458 char _cleanup_free_ *config_path = NULL;
1459 int r;
1460
1461 assert(scope >= 0);
1462 assert(scope < _UNIT_FILE_SCOPE_MAX);
1463
1464 r = lookup_paths_init_from_scope(&paths, scope);
1465 if (r < 0)
1466 return r;
1467
1468 r = get_config_path(scope, runtime, root_dir, &config_path);
1469 if (r < 0)
1470 return r;
1471
1472 STRV_FOREACH(i, files) {
1473 r = install_info_add_auto(&c, *i);
1474 if (r < 0)
1475 return r;
1476 }
1477
1478 /* This will return the number of symlink rules that were
1479 supposed to be created, not the ones actually created. This is
1480 useful to determine whether the passed files had any
1481 installation data at all. */
1482 r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1483 return r;
1484 }
1485
1486 int unit_file_disable(
1487 UnitFileScope scope,
1488 bool runtime,
1489 const char *root_dir,
1490 char *files[],
1491 UnitFileChange **changes,
1492 unsigned *n_changes) {
1493
1494 LookupPaths _cleanup_lookup_paths_free_ paths = {};
1495 InstallContext _cleanup_install_context_done_ c = {};
1496 char **i;
1497 char _cleanup_free_ *config_path = NULL;
1498 Set _cleanup_set_free_free_ *remove_symlinks_to = NULL;
1499 int r, q;
1500
1501 assert(scope >= 0);
1502 assert(scope < _UNIT_FILE_SCOPE_MAX);
1503
1504 r = lookup_paths_init_from_scope(&paths, scope);
1505 if (r < 0)
1506 return r;
1507
1508 r = get_config_path(scope, runtime, root_dir, &config_path);
1509 if (r < 0)
1510 return r;
1511
1512 STRV_FOREACH(i, files) {
1513 r = install_info_add_auto(&c, *i);
1514 if (r < 0)
1515 return r;
1516 }
1517
1518 r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
1519
1520 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
1521 if (r == 0)
1522 r = q;
1523
1524 return r;
1525 }
1526
1527 int unit_file_reenable(
1528 UnitFileScope scope,
1529 bool runtime,
1530 const char *root_dir,
1531 char *files[],
1532 bool force,
1533 UnitFileChange **changes,
1534 unsigned *n_changes) {
1535
1536 LookupPaths _cleanup_lookup_paths_free_ paths = {};
1537 InstallContext _cleanup_install_context_done_ c = {};
1538 char **i;
1539 char _cleanup_free_ *config_path = NULL;
1540 Set _cleanup_set_free_free_ *remove_symlinks_to = NULL;
1541 int r, q;
1542
1543 assert(scope >= 0);
1544 assert(scope < _UNIT_FILE_SCOPE_MAX);
1545
1546 r = lookup_paths_init_from_scope(&paths, scope);
1547 if (r < 0)
1548 return r;
1549
1550 r = get_config_path(scope, runtime, root_dir, &config_path);
1551 if (r < 0)
1552 return r;
1553
1554 STRV_FOREACH(i, files) {
1555 r = mark_symlink_for_removal(&remove_symlinks_to, *i);
1556 if (r < 0)
1557 return r;
1558
1559 r = install_info_add_auto(&c, *i);
1560 if (r < 0)
1561 return r;
1562 }
1563
1564 r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
1565
1566 /* Returns number of symlinks that where supposed to be installed. */
1567 q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1568 if (r == 0)
1569 r = q;
1570
1571 return r;
1572 }
1573
1574 UnitFileState unit_file_get_state(
1575 UnitFileScope scope,
1576 const char *root_dir,
1577 const char *name) {
1578
1579 LookupPaths _cleanup_lookup_paths_free_ paths = {};
1580 UnitFileState state = _UNIT_FILE_STATE_INVALID;
1581 char **i;
1582 char _cleanup_free_ *path = NULL;
1583 int r;
1584
1585 assert(scope >= 0);
1586 assert(scope < _UNIT_FILE_SCOPE_MAX);
1587 assert(name);
1588
1589 if (root_dir && scope != UNIT_FILE_SYSTEM)
1590 return -EINVAL;
1591
1592 if (!unit_name_is_valid(name, true))
1593 return -EINVAL;
1594
1595 r = lookup_paths_init_from_scope(&paths, scope);
1596 if (r < 0)
1597 return r;
1598
1599 STRV_FOREACH(i, paths.unit_path) {
1600 struct stat st;
1601
1602 free(path);
1603 path = NULL;
1604
1605 if (root_dir)
1606 asprintf(&path, "%s/%s/%s", root_dir, *i, name);
1607 else
1608 asprintf(&path, "%s/%s", *i, name);
1609
1610 if (!path)
1611 return -ENOMEM;
1612
1613 if (lstat(path, &st) < 0) {
1614 r = -errno;
1615 if (errno == ENOENT)
1616 continue;
1617
1618 return -errno;
1619 }
1620
1621 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
1622 return -ENOENT;
1623
1624 r = null_or_empty_path(path);
1625 if (r < 0 && r != -ENOENT)
1626 return r;
1627 else if (r > 0) {
1628 state = path_startswith(*i, "/run") ?
1629 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1630 return state;
1631 }
1632
1633 r = find_symlinks_in_scope(scope, root_dir, name, &state);
1634 if (r < 0)
1635 return r;
1636 else if (r > 0)
1637 return state;
1638
1639 r = unit_file_can_install(&paths, root_dir, path, true);
1640 if (r < 0 && errno != -ENOENT)
1641 return r;
1642 else if (r > 0)
1643 return UNIT_FILE_DISABLED;
1644 else if (r == 0)
1645 return UNIT_FILE_STATIC;
1646 }
1647
1648 return r < 0 ? r : state;
1649 }
1650
1651 int unit_file_query_preset(UnitFileScope scope, const char *name) {
1652 char _cleanup_strv_free_ **files = NULL;
1653 char **i;
1654 int r;
1655
1656 assert(scope >= 0);
1657 assert(scope < _UNIT_FILE_SCOPE_MAX);
1658 assert(name);
1659
1660 if (scope == UNIT_FILE_SYSTEM)
1661 r = conf_files_list(&files, ".preset", NULL,
1662 "/etc/systemd/system-preset",
1663 "/usr/local/lib/systemd/system-preset",
1664 "/usr/lib/systemd/system-preset",
1665 #ifdef HAVE_SPLIT_USR
1666 "/lib/systemd/system-preset",
1667 #endif
1668 NULL);
1669 else if (scope == UNIT_FILE_GLOBAL)
1670 r = conf_files_list(&files, ".preset", NULL,
1671 "/etc/systemd/user-preset",
1672 "/usr/local/lib/systemd/user-preset",
1673 "/usr/lib/systemd/user-preset",
1674 NULL);
1675 else
1676 return 1;
1677
1678 if (r < 0)
1679 return r;
1680
1681 STRV_FOREACH(i, files) {
1682 FILE _cleanup_fclose_ *f;
1683
1684 f = fopen(*i, "re");
1685 if (!f) {
1686 if (errno == ENOENT)
1687 continue;
1688
1689 return -errno;
1690 }
1691
1692 for (;;) {
1693 char line[LINE_MAX], *l;
1694
1695 if (!fgets(line, sizeof(line), f))
1696 break;
1697
1698 l = strstrip(line);
1699 if (!*l)
1700 continue;
1701
1702 if (strchr(COMMENTS, *l))
1703 continue;
1704
1705 if (first_word(l, "enable")) {
1706 l += 6;
1707 l += strspn(l, WHITESPACE);
1708
1709 if (fnmatch(l, name, FNM_NOESCAPE) == 0)
1710 return 1;
1711
1712 } else if (first_word(l, "disable")) {
1713 l += 7;
1714 l += strspn(l, WHITESPACE);
1715
1716 if (fnmatch(l, name, FNM_NOESCAPE) == 0)
1717 return 0;
1718
1719 } else
1720 log_debug("Couldn't parse line '%s'", l);
1721 }
1722 }
1723
1724 /* Default is "enable" */
1725 return 1;
1726 }
1727
1728 int unit_file_preset(
1729 UnitFileScope scope,
1730 bool runtime,
1731 const char *root_dir,
1732 char *files[],
1733 bool force,
1734 UnitFileChange **changes,
1735 unsigned *n_changes) {
1736
1737 LookupPaths _cleanup_lookup_paths_free_ paths = {};
1738 InstallContext _cleanup_install_context_done_ plus = {}, minus = {NULL};
1739 char **i;
1740 char _cleanup_free_ *config_path = NULL;
1741 Set _cleanup_set_free_free_ *remove_symlinks_to = NULL;
1742 int r, q;
1743
1744 assert(scope >= 0);
1745 assert(scope < _UNIT_FILE_SCOPE_MAX);
1746
1747 r = lookup_paths_init_from_scope(&paths, scope);
1748 if (r < 0)
1749 return r;
1750
1751 r = get_config_path(scope, runtime, root_dir, &config_path);
1752 if (r < 0)
1753 return r;
1754
1755 STRV_FOREACH(i, files) {
1756
1757 if (!unit_name_is_valid(*i, true))
1758 return -EINVAL;
1759
1760 r = unit_file_query_preset(scope, *i);
1761 if (r < 0)
1762 return r;
1763
1764 if (r)
1765 r = install_info_add_auto(&plus, *i);
1766 else
1767 r = install_info_add_auto(&minus, *i);
1768
1769 if (r < 0)
1770 return r;
1771 }
1772
1773 r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to,
1774 config_path, root_dir);
1775
1776 q = remove_marked_symlinks(remove_symlinks_to, config_path,
1777 changes, n_changes, files);
1778 if (r == 0)
1779 r = q;
1780
1781 /* Returns number of symlinks that where supposed to be installed. */
1782 q = install_context_apply(&plus, &paths, config_path, root_dir, force,
1783 changes, n_changes);
1784 if (r == 0)
1785 r = q;
1786
1787 return r;
1788 }
1789
1790 static void unitfilelist_free(UnitFileList **f) {
1791 if (!*f)
1792 return;
1793
1794 free((*f)->path);
1795 free(*f);
1796 }
1797
1798 int unit_file_get_list(
1799 UnitFileScope scope,
1800 const char *root_dir,
1801 Hashmap *h) {
1802
1803 LookupPaths _cleanup_lookup_paths_free_ paths = {};
1804 char **i;
1805 char _cleanup_free_ *buf = NULL;
1806 DIR _cleanup_closedir_ *d = NULL;
1807 int r;
1808
1809 assert(scope >= 0);
1810 assert(scope < _UNIT_FILE_SCOPE_MAX);
1811 assert(h);
1812
1813 if (root_dir && scope != UNIT_FILE_SYSTEM)
1814 return -EINVAL;
1815
1816 r = lookup_paths_init_from_scope(&paths, scope);
1817 if (r < 0)
1818 return r;
1819
1820 STRV_FOREACH(i, paths.unit_path) {
1821 const char *units_dir;
1822
1823 free(buf);
1824 buf = NULL;
1825
1826 if (root_dir) {
1827 if (asprintf(&buf, "%s/%s", root_dir, *i) < 0)
1828 return -ENOMEM;
1829
1830 units_dir = buf;
1831 } else
1832 units_dir = *i;
1833
1834 if (d)
1835 closedir(d);
1836
1837 d = opendir(units_dir);
1838 if (!d) {
1839 if (errno == ENOENT)
1840 continue;
1841
1842 return -errno;
1843 }
1844
1845 for (;;) {
1846 struct dirent *de;
1847 union dirent_storage buffer;
1848 UnitFileList __attribute__((cleanup(unitfilelist_free)))
1849 *f = NULL;
1850
1851 r = readdir_r(d, &buffer.de, &de);
1852 if (r != 0)
1853 return -r;
1854
1855 if (!de)
1856 break;
1857
1858 if (ignore_file(de->d_name))
1859 continue;
1860
1861 if (!unit_name_is_valid(de->d_name, true))
1862 continue;
1863
1864 if (hashmap_get(h, de->d_name))
1865 continue;
1866
1867 r = dirent_ensure_type(d, de);
1868 if (r < 0) {
1869 if (r == -ENOENT)
1870 continue;
1871
1872 return r;
1873 }
1874
1875 if (de->d_type != DT_LNK && de->d_type != DT_REG)
1876 continue;
1877
1878 f = new0(UnitFileList, 1);
1879 if (!f)
1880 return -ENOMEM;
1881
1882 f->path = path_make_absolute(de->d_name, units_dir);
1883 if (!f->path)
1884 return -ENOMEM;
1885
1886 r = null_or_empty_path(f->path);
1887 if (r < 0 && r != -ENOENT)
1888 return r;
1889 else if (r > 0) {
1890 f->state =
1891 path_startswith(*i, "/run") ?
1892 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1893 goto found;
1894 }
1895
1896 r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
1897 if (r < 0)
1898 return r;
1899 else if (r > 0) {
1900 f->state = UNIT_FILE_ENABLED;
1901 goto found;
1902 }
1903
1904 r = unit_file_can_install(&paths, root_dir, f->path, true);
1905 if (r == -EINVAL || /* Invalid setting? */
1906 r == -EBADMSG || /* Invalid format? */
1907 r == -ENOENT /* Included file not found? */)
1908 f->state = UNIT_FILE_INVALID;
1909 else if (r < 0)
1910 return r;
1911 else if (r > 0)
1912 f->state = UNIT_FILE_DISABLED;
1913 else
1914 f->state = UNIT_FILE_STATIC;
1915
1916 found:
1917 r = hashmap_put(h, path_get_file_name(f->path), f);
1918 if (r < 0)
1919 return r;
1920 f = NULL; /* prevent cleanup */
1921 }
1922 }
1923
1924 return r;
1925 }
1926
1927 static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
1928 [UNIT_FILE_ENABLED] = "enabled",
1929 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
1930 [UNIT_FILE_LINKED] = "linked",
1931 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
1932 [UNIT_FILE_MASKED] = "masked",
1933 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
1934 [UNIT_FILE_STATIC] = "static",
1935 [UNIT_FILE_DISABLED] = "disabled",
1936 [UNIT_FILE_INVALID] = "invalid",
1937 };
1938
1939 DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
1940
1941 static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
1942 [UNIT_FILE_SYMLINK] = "symlink",
1943 [UNIT_FILE_UNLINK] = "unlink",
1944 };
1945
1946 DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);