1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
31 #include "path-lookup.h"
33 #include "unit-name.h"
35 #include "conf-parser.h"
46 Hashmap *will_install;
47 Hashmap *have_installed;
50 static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
53 assert(scope < _UNIT_FILE_SCOPE_MAX);
57 return lookup_paths_init(paths,
58 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
59 scope == UNIT_FILE_USER);
62 static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
67 assert(scope < _UNIT_FILE_SCOPE_MAX);
72 case UNIT_FILE_SYSTEM:
74 if (root_dir && runtime)
78 p = strdup("/run/systemd/system");
80 asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH);
82 p = strdup(SYSTEM_CONFIG_UNIT_PATH);
86 case UNIT_FILE_GLOBAL:
92 p = strdup("/run/systemd/user");
94 p = strdup(USER_CONFIG_UNIT_PATH);
99 if (root_dir || runtime)
102 r = user_config_home(&p);
104 return r < 0 ? r : -ENOENT;
109 assert_not_reached("Bad scope");
119 static int add_file_change(
120 UnitFileChange **changes,
122 UnitFileChangeType type,
124 const char *source) {
130 assert(type < _UNIT_FILE_CHANGE_TYPE_MAX);
132 assert(!changes == !n_changes);
137 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
145 c[i].path = strdup(path);
150 c[i].source = strdup(source);
162 static int mark_symlink_for_removal(
163 Set **remove_symlinks_to,
171 r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func);
179 path_kill_slashes(n);
181 r = set_put(*remove_symlinks_to, n);
184 return r == -EEXIST ? 0 : r;
190 static int remove_marked_symlinks_fd(
191 Set *remove_symlinks_to,
194 const char *config_path,
196 UnitFileChange **changes,
197 unsigned *n_changes) {
201 struct dirent buffer, *de;
203 assert(remove_symlinks_to);
211 close_nointr_nofail(fd);
220 k = readdir_r(d, &buffer, &de);
229 if (ignore_file(de->d_name))
232 dirent_ensure_type(d, de);
234 if (de->d_type == DT_DIR) {
238 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
248 p = path_make_absolute(de->d_name, path);
250 close_nointr_nofail(nfd);
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);
262 } else if (de->d_type == DT_LNK) {
267 p = path_make_absolute(de->d_name, path);
273 q = readlink_and_canonicalize(p, &dest);
286 set_get(remove_symlinks_to, dest) ||
287 set_get(remove_symlinks_to, file_name_from_path(dest));
291 if (unlink(p) < 0 && errno != ENOENT) {
296 rmdir_parents(p, config_path);
297 path_kill_slashes(p);
299 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
301 if (!set_get(remove_symlinks_to, p)) {
303 q = mark_symlink_for_removal(&remove_symlinks_to, p);
323 static int remove_marked_symlinks(
324 Set *remove_symlinks_to,
325 const char *config_path,
326 UnitFileChange **changes,
327 unsigned *n_changes) {
334 if (set_size(remove_symlinks_to) <= 0)
337 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
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);
357 close_nointr_nofail(fd);
362 static int find_symlinks_fd(
366 const char *config_path,
367 bool *same_name_link) {
371 struct dirent buffer, *de;
377 assert(same_name_link);
381 close_nointr_nofail(fd);
388 k = readdir_r(d, &buffer, &de);
397 if (ignore_file(de->d_name))
400 dirent_ensure_type(d, de);
402 if (de->d_type == DT_DIR) {
406 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
416 p = path_make_absolute(de->d_name, path);
418 close_nointr_nofail(nfd);
423 /* This will close nfd, regardless whether it succeeds or not */
424 q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
435 } else if (de->d_type == DT_LNK) {
437 bool found_path, found_dest, b = false;
440 /* Acquire symlink name */
441 p = path_make_absolute(de->d_name, path);
447 /* Acquire symlink destination */
448 q = readlink_and_canonicalize(p, &dest);
460 /* Check if the symlink itself matches what we
462 if (path_is_absolute(name))
463 found_path = path_equal(p, name);
465 found_path = streq(de->d_name, name);
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);
472 found_dest = streq(file_name_from_path(dest), name);
476 if (found_path && found_dest) {
479 /* Filter out same name links in the main
481 t = path_make_absolute(name, config_path);
489 b = path_equal(t, p);
496 *same_name_link = true;
497 else if (found_path || found_dest) {
509 static int find_symlinks(
511 const char *config_path,
512 bool *same_name_link) {
518 assert(same_name_link);
520 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
524 /* This takes possession of fd and closes it */
525 return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
528 static int find_symlinks_in_scope(
530 const char *root_dir,
532 UnitFileState *state) {
536 bool same_name_link_runtime = false, same_name_link = false;
539 assert(scope < _UNIT_FILE_SCOPE_MAX);
542 if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) {
544 /* First look in runtime config path */
545 r = get_config_path(scope, true, root_dir, &path);
549 r = find_symlinks(name, path, &same_name_link_runtime);
555 *state = UNIT_FILE_ENABLED_RUNTIME;
560 /* Then look in the normal config path */
561 r = get_config_path(scope, false, root_dir, &path);
565 r = find_symlinks(name, path, &same_name_link);
571 *state = UNIT_FILE_ENABLED;
575 /* Hmm, we didn't find it, but maybe we found the same name
577 if (same_name_link_runtime) {
578 *state = UNIT_FILE_LINKED_RUNTIME;
580 } else if (same_name_link) {
581 *state = UNIT_FILE_LINKED;
591 const char *root_dir,
594 UnitFileChange **changes,
595 unsigned *n_changes) {
601 assert(scope < _UNIT_FILE_SCOPE_MAX);
603 r = get_config_path(scope, runtime, root_dir, &prefix);
607 STRV_FOREACH(i, files) {
610 if (!unit_name_is_valid_no_type(*i, true)) {
616 path = path_make_absolute(*i, prefix);
622 if (symlink("/dev/null", path) >= 0) {
623 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
629 if (errno == EEXIST) {
631 if (null_or_empty_path(path) > 0) {
639 if (symlink("/dev/null", path) >= 0) {
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");
664 int unit_file_unmask(
667 const char *root_dir,
669 UnitFileChange **changes,
670 unsigned *n_changes) {
672 char **i, *config_path = NULL;
674 Set *remove_symlinks_to = NULL;
677 assert(scope < _UNIT_FILE_SCOPE_MAX);
679 r = get_config_path(scope, runtime, root_dir, &config_path);
683 STRV_FOREACH(i, files) {
686 if (!unit_name_is_valid_no_type(*i, true)) {
692 path = path_make_absolute(*i, config_path);
698 q = null_or_empty_path(path);
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);
711 if (q != -ENOENT && r == 0)
719 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
723 set_free_free(remove_symlinks_to);
732 const char *root_dir,
735 UnitFileChange **changes,
736 unsigned *n_changes) {
739 char **i, *config_path = NULL;
743 assert(scope < _UNIT_FILE_SCOPE_MAX);
747 r = lookup_paths_init_from_scope(&paths, scope);
751 r = get_config_path(scope, runtime, root_dir, &config_path);
755 STRV_FOREACH(i, files) {
759 fn = file_name_from_path(*i);
761 if (!path_is_absolute(*i) ||
762 !unit_name_is_valid_no_type(fn, true)) {
768 if (lstat(*i, &st) < 0) {
774 if (!S_ISREG(st.st_mode)) {
779 q = in_search_path(*i, paths.unit_path);
788 path = path_make_absolute(fn, config_path);
794 if (symlink(*i, path) >= 0) {
795 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
801 if (errno == EEXIST) {
804 q = readlink_and_make_absolute(path, &dest);
806 if (q < 0 && errno != ENOENT) {
815 if (q >= 0 && path_equal(dest, *i)) {
826 if (symlink(*i, path) >= 0) {
828 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
829 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
847 lookup_paths_free(&paths);
853 void unit_file_list_free(Hashmap *h) {
856 while ((i = hashmap_steal_first(h))) {
864 void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
867 assert(changes || n_changes == 0);
872 for (i = 0; i < n_changes; i++) {
873 free(changes[i].path);
874 free(changes[i].source);
880 static void install_info_free(InstallInfo *i) {
885 strv_free(i->aliases);
886 strv_free(i->wanted_by);
890 static void install_info_hashmap_free(Hashmap *m) {
896 while ((i = hashmap_steal_first(m)))
897 install_info_free(i);
902 static void install_context_done(InstallContext *c) {
905 install_info_hashmap_free(c->will_install);
906 install_info_hashmap_free(c->have_installed);
908 c->will_install = c->have_installed = NULL;
911 static int install_info_add(
915 InstallInfo *i = NULL;
919 assert(name || path);
922 name = file_name_from_path(path);
924 if (!unit_name_is_valid_no_type(name, true))
927 if (hashmap_get(c->have_installed, name) ||
928 hashmap_get(c->will_install, name))
931 r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func);
935 i = new0(InstallInfo, 1);
939 i->name = strdup(name);
946 i->path = strdup(path);
953 r = hashmap_put(c->will_install, i->name, i);
961 install_info_free(i);
966 static int install_info_add_auto(
968 const char *name_or_path) {
971 assert(name_or_path);
973 if (path_is_absolute(name_or_path))
974 return install_info_add(c, NULL, name_or_path);
976 return install_info_add(c, name_or_path, NULL);
979 static int config_parse_also(
980 const char *filename,
992 InstallContext *c = data;
998 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1006 r = install_info_add(c, n, NULL);
1018 static int unit_file_load(
1022 bool allow_symlink) {
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 }
1039 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
1043 f = fdopen(fd, "re");
1045 close_nointr_nofail(fd);
1049 r = config_parse(path, f, NULL, items, true, info);
1054 return strv_length(info->aliases) + strv_length(info->wanted_by);
1057 static int unit_file_search(
1061 const char *root_dir,
1062 bool allow_symlink) {
1072 return unit_file_load(c, info, info->path, allow_symlink);
1076 STRV_FOREACH(p, paths->unit_path) {
1079 if (isempty(root_dir))
1080 asprintf(&path, "%s/%s", *p, info->name);
1082 asprintf(&path, "%s/%s/%s", root_dir, *p, info->name);
1087 r = unit_file_load(c, info, path, allow_symlink);
1094 if (r != -ENOENT && r != -ELOOP)
1101 static int unit_file_can_install(
1103 const char *root_dir,
1105 bool allow_symlink) {
1116 r = install_info_add_auto(&c, name);
1120 assert_se(i = hashmap_first(c.will_install));
1122 r = unit_file_search(&c, i, paths, root_dir, allow_symlink);
1125 r = strv_length(i->aliases) + strv_length(i->wanted_by);
1127 install_context_done(&c);
1132 static int create_symlink(
1133 const char *old_path,
1134 const char *new_path,
1136 UnitFileChange **changes,
1137 unsigned *n_changes) {
1145 mkdir_parents(new_path, 0755);
1147 if (symlink(old_path, new_path) >= 0) {
1148 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1152 if (errno != EEXIST)
1155 r = readlink_and_make_absolute(new_path, &dest);
1159 if (path_equal(dest, old_path)) {
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);
1180 static int install_info_symlink_alias(
1182 const char *config_path,
1184 UnitFileChange **changes,
1185 unsigned *n_changes) {
1191 assert(config_path);
1193 STRV_FOREACH(s, i->aliases) {
1196 alias_path = path_make_absolute(*s, config_path);
1201 q = create_symlink(i->path, alias_path, force, changes, n_changes);
1211 static int install_info_symlink_wants(
1213 const char *config_path,
1215 UnitFileChange **changes,
1216 unsigned *n_changes) {
1222 assert(config_path);
1224 STRV_FOREACH(s, i->wanted_by) {
1227 if (!unit_name_is_valid_no_type(*s, true)) {
1232 if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0)
1235 q = create_symlink(i->path, path, force, changes, n_changes);
1245 static int install_info_symlink_link(
1248 const char *config_path,
1250 UnitFileChange **changes,
1251 unsigned *n_changes) {
1258 assert(config_path);
1261 r = in_search_path(i->path, paths->unit_path);
1265 if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
1268 r = create_symlink(i->path, path, force, changes, n_changes);
1274 static int install_info_apply(
1277 const char *config_path,
1279 UnitFileChange **changes,
1280 unsigned *n_changes) {
1286 assert(config_path);
1288 r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
1290 q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
1294 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1301 static int install_context_apply(
1304 const char *config_path,
1305 const char *root_dir,
1307 UnitFileChange **changes,
1308 unsigned *n_changes) {
1315 assert(config_path);
1317 while ((i = hashmap_first(c->will_install))) {
1319 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1323 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1325 q = unit_file_search(c, i, paths, root_dir, false);
1334 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1335 if (r >= 0 && q < 0)
1342 static int install_context_mark_for_removal(
1345 Set **remove_symlinks_to,
1346 const char *config_path,
1347 const char *root_dir) {
1354 assert(config_path);
1356 /* Marks all items for removal */
1358 while ((i = hashmap_first(c->will_install))) {
1360 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1364 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1366 q = unit_file_search(c, i, paths, root_dir, false);
1375 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1376 if (r >= 0 && q < 0)
1383 int unit_file_enable(
1384 UnitFileScope scope,
1386 const char *root_dir,
1389 UnitFileChange **changes,
1390 unsigned *n_changes) {
1394 char **i, *config_path = NULL;
1398 assert(scope < _UNIT_FILE_SCOPE_MAX);
1403 r = lookup_paths_init_from_scope(&paths, scope);
1407 r = get_config_path(scope, runtime, root_dir, &config_path);
1411 STRV_FOREACH(i, files) {
1412 r = install_info_add_auto(&c, *i);
1417 r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1420 install_context_done(&c);
1421 lookup_paths_free(&paths);
1427 int unit_file_disable(
1428 UnitFileScope scope,
1430 const char *root_dir,
1432 UnitFileChange **changes,
1433 unsigned *n_changes) {
1437 char **i, *config_path = NULL;
1438 Set *remove_symlinks_to = NULL;
1442 assert(scope < _UNIT_FILE_SCOPE_MAX);
1447 r = lookup_paths_init_from_scope(&paths, scope);
1451 r = get_config_path(scope, runtime, root_dir, &config_path);
1455 STRV_FOREACH(i, files) {
1456 r = install_info_add_auto(&c, *i);
1461 r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
1463 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
1468 install_context_done(&c);
1469 lookup_paths_free(&paths);
1470 set_free_free(remove_symlinks_to);
1476 int unit_file_reenable(
1477 UnitFileScope scope,
1479 const char *root_dir,
1482 UnitFileChange **changes,
1483 unsigned *n_changes) {
1487 char **i, *config_path = NULL;
1488 Set *remove_symlinks_to = NULL;
1492 assert(scope < _UNIT_FILE_SCOPE_MAX);
1497 r = lookup_paths_init_from_scope(&paths, scope);
1501 r = get_config_path(scope, runtime, root_dir, &config_path);
1505 STRV_FOREACH(i, files) {
1506 r = mark_symlink_for_removal(&remove_symlinks_to, *i);
1510 r = install_info_add_auto(&c, *i);
1515 r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
1517 q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1522 lookup_paths_free(&paths);
1523 install_context_done(&c);
1524 set_free_free(remove_symlinks_to);
1530 UnitFileState unit_file_get_state(
1531 UnitFileScope scope,
1532 const char *root_dir,
1536 UnitFileState state = _UNIT_FILE_STATE_INVALID;
1537 char **i, *path = NULL;
1541 assert(scope < _UNIT_FILE_SCOPE_MAX);
1546 if (root_dir && scope != UNIT_FILE_SYSTEM)
1549 if (!unit_name_is_valid_no_type(name, true))
1552 r = lookup_paths_init_from_scope(&paths, scope);
1556 STRV_FOREACH(i, paths.unit_path) {
1563 asprintf(&path, "%s/%s/%s", root_dir, *i, name);
1565 asprintf(&path, "%s/%s", *i, name);
1572 if (lstat(path, &st) < 0) {
1573 if (errno == ENOENT)
1580 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
1585 r = null_or_empty_path(path);
1586 if (r < 0 && r != -ENOENT)
1589 state = path_startswith(*i, "/run") ?
1590 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1595 r = find_symlinks_in_scope(scope, root_dir, name, &state);
1603 r = unit_file_can_install(&paths, root_dir, path, true);
1604 if (r < 0 && errno != -ENOENT)
1607 state = UNIT_FILE_DISABLED;
1610 } else if (r == 0) {
1611 state = UNIT_FILE_STATIC;
1618 lookup_paths_free(&paths);
1621 return r < 0 ? r : state;
1624 int unit_file_query_preset(UnitFileScope scope, const char *name) {
1629 assert(scope < _UNIT_FILE_SCOPE_MAX);
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",
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",
1651 STRV_FOREACH(i, files) {
1654 f = fopen(*i, "re");
1656 if (errno == ENOENT)
1664 char line[LINE_MAX], *l;
1666 if (!fgets(line, sizeof(line), f))
1673 if (strchr(COMMENTS, *l))
1676 if (first_word(l, "enable")) {
1678 l += strspn(l, WHITESPACE);
1680 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
1685 } else if (first_word(l, "disable")) {
1687 l += strspn(l, WHITESPACE);
1689 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
1695 log_debug("Couldn't parse line '%s'", l);
1701 /* Default is "enable" */
1710 int unit_file_preset(
1711 UnitFileScope scope,
1713 const char *root_dir,
1716 UnitFileChange **changes,
1717 unsigned *n_changes) {
1720 InstallContext plus, minus;
1721 char **i, *config_path = NULL;
1722 Set *remove_symlinks_to = NULL;
1726 assert(scope < _UNIT_FILE_SCOPE_MAX);
1732 r = lookup_paths_init_from_scope(&paths, scope);
1736 r = get_config_path(scope, runtime, root_dir, &config_path);
1740 STRV_FOREACH(i, files) {
1742 if (!unit_name_is_valid_no_type(*i, true)) {
1747 r = unit_file_query_preset(scope, *i);
1752 r = install_info_add_auto(&plus, *i);
1754 r = install_info_add_auto(&minus, *i);
1760 r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
1762 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
1766 q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
1771 lookup_paths_free(&paths);
1772 install_context_done(&plus);
1773 install_context_done(&minus);
1774 set_free_free(remove_symlinks_to);
1780 int unit_file_get_list(
1781 UnitFileScope scope,
1782 const char *root_dir,
1786 char **i, *buf = NULL;
1791 assert(scope < _UNIT_FILE_SCOPE_MAX);
1796 if (root_dir && scope != UNIT_FILE_SYSTEM)
1799 r = lookup_paths_init_from_scope(&paths, scope);
1803 STRV_FOREACH(i, paths.unit_path) {
1804 struct dirent buffer, *de;
1805 const char *units_dir;
1811 if (asprintf(&buf, "%s/%s", root_dir, *i) < 0) {
1822 d = opendir(units_dir);
1824 if (errno == ENOENT)
1834 r = readdir_r(d, &buffer, &de);
1843 if (ignore_file(de->d_name))
1846 if (!unit_name_is_valid_no_type(de->d_name, true))
1849 if (hashmap_get(h, de->d_name))
1852 r = dirent_ensure_type(d, de);
1854 if (errno == ENOENT)
1860 if (de->d_type != DT_LNK && de->d_type != DT_REG)
1863 f = new0(UnitFileList, 1);
1869 f->path = path_make_absolute(de->d_name, units_dir);
1876 r = null_or_empty_path(f->path);
1883 path_startswith(*i, "/run") ?
1884 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1888 r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
1896 r = unit_file_can_install(&paths, root_dir, f->path, true);
1902 f->state = UNIT_FILE_DISABLED;
1904 } else if (r == 0) {
1905 f->state = UNIT_FILE_STATIC;
1914 r = hashmap_put(h, file_name_from_path(f->path), f);
1924 lookup_paths_free(&paths);
1933 static 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"
1944 DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);