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