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