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