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(!changes == !n_changes);
135 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
143 c[i].path = strdup(path);
148 c[i].source = strdup(source);
160 static int mark_symlink_for_removal(
161 Set **remove_symlinks_to,
169 r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func);
177 path_kill_slashes(n);
179 r = set_put(*remove_symlinks_to, n);
182 return r == -EEXIST ? 0 : r;
188 static int remove_marked_symlinks_fd(
189 Set *remove_symlinks_to,
192 const char *config_path,
194 UnitFileChange **changes,
195 unsigned *n_changes) {
199 struct dirent buffer, *de;
201 assert(remove_symlinks_to);
209 close_nointr_nofail(fd);
218 k = readdir_r(d, &buffer, &de);
227 if (ignore_file(de->d_name))
230 dirent_ensure_type(d, de);
232 if (de->d_type == DT_DIR) {
236 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
246 p = path_make_absolute(de->d_name, path);
248 close_nointr_nofail(nfd);
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);
260 } else if (de->d_type == DT_LNK) {
265 p = path_make_absolute(de->d_name, path);
271 q = readlink_and_canonicalize(p, &dest);
284 set_get(remove_symlinks_to, dest) ||
285 set_get(remove_symlinks_to, file_name_from_path(dest));
289 if (unlink(p) < 0 && errno != ENOENT) {
294 rmdir_parents(p, config_path);
295 path_kill_slashes(p);
297 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
299 if (!set_get(remove_symlinks_to, p)) {
301 q = mark_symlink_for_removal(&remove_symlinks_to, p);
321 static int remove_marked_symlinks(
322 Set *remove_symlinks_to,
323 const char *config_path,
324 UnitFileChange **changes,
325 unsigned *n_changes) {
332 if (set_size(remove_symlinks_to) <= 0)
335 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
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);
355 close_nointr_nofail(fd);
360 static int find_symlinks_fd(
364 const char *config_path,
365 bool *same_name_link) {
369 struct dirent buffer, *de;
375 assert(same_name_link);
379 close_nointr_nofail(fd);
386 k = readdir_r(d, &buffer, &de);
395 if (ignore_file(de->d_name))
398 dirent_ensure_type(d, de);
400 if (de->d_type == DT_DIR) {
404 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
414 p = path_make_absolute(de->d_name, path);
416 close_nointr_nofail(nfd);
421 /* This will close nfd, regardless whether it succeeds or not */
422 q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
433 } else if (de->d_type == DT_LNK) {
435 bool found_path, found_dest, b = false;
438 /* Acquire symlink name */
439 p = path_make_absolute(de->d_name, path);
445 /* Acquire symlink destination */
446 q = readlink_and_canonicalize(p, &dest);
458 /* Check if the symlink itself matches what we
460 if (path_is_absolute(name))
461 found_path = path_equal(p, name);
463 found_path = streq(de->d_name, name);
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);
470 found_dest = streq(file_name_from_path(dest), name);
474 if (found_path && found_dest) {
477 /* Filter out same name links in the main
479 t = path_make_absolute(name, config_path);
486 b = path_equal(t, p);
493 *same_name_link = true;
494 else if (found_path || found_dest) {
506 static int find_symlinks(
508 const char *config_path,
509 bool *same_name_link) {
515 assert(same_name_link);
517 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
521 /* This takes possession of fd and closes it */
522 return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
525 static int find_symlinks_in_scope(
527 const char *root_dir,
529 UnitFileState *state) {
533 bool same_name_link_runtime = false, same_name_link = false;
536 assert(scope < _UNIT_FILE_SCOPE_MAX);
539 if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) {
541 /* First look in runtime config path */
542 r = get_config_path(scope, true, root_dir, &path);
546 r = find_symlinks(name, path, &same_name_link_runtime);
552 *state = UNIT_FILE_ENABLED_RUNTIME;
557 /* Then look in the normal config path */
558 r = get_config_path(scope, false, root_dir, &path);
562 r = find_symlinks(name, path, &same_name_link);
568 *state = UNIT_FILE_ENABLED;
572 /* Hmm, we didn't find it, but maybe we found the same name
574 if (same_name_link_runtime) {
575 *state = UNIT_FILE_LINKED_RUNTIME;
577 } else if (same_name_link) {
578 *state = UNIT_FILE_LINKED;
588 const char *root_dir,
591 UnitFileChange **changes,
592 unsigned *n_changes) {
598 assert(scope < _UNIT_FILE_SCOPE_MAX);
600 r = get_config_path(scope, runtime, root_dir, &prefix);
604 STRV_FOREACH(i, files) {
607 if (!unit_name_is_valid_no_type(*i, true)) {
613 path = path_make_absolute(*i, prefix);
619 if (symlink("/dev/null", path) >= 0) {
620 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
626 if (errno == EEXIST) {
628 if (null_or_empty_path(path) > 0) {
636 if (symlink("/dev/null", path) >= 0) {
638 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
639 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
661 int unit_file_unmask(
664 const char *root_dir,
666 UnitFileChange **changes,
667 unsigned *n_changes) {
669 char **i, *config_path = NULL;
671 Set *remove_symlinks_to = NULL;
674 assert(scope < _UNIT_FILE_SCOPE_MAX);
676 r = get_config_path(scope, runtime, root_dir, &config_path);
680 STRV_FOREACH(i, files) {
683 if (!unit_name_is_valid_no_type(*i, true)) {
689 path = path_make_absolute(*i, config_path);
695 q = null_or_empty_path(path);
697 if (unlink(path) >= 0) {
698 mark_symlink_for_removal(&remove_symlinks_to, path);
699 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
708 if (q != -ENOENT && r == 0)
716 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
720 set_free_free(remove_symlinks_to);
729 const char *root_dir,
732 UnitFileChange **changes,
733 unsigned *n_changes) {
736 char **i, *config_path = NULL;
740 assert(scope < _UNIT_FILE_SCOPE_MAX);
744 r = lookup_paths_init_from_scope(&paths, scope);
748 r = get_config_path(scope, runtime, root_dir, &config_path);
752 STRV_FOREACH(i, files) {
756 fn = file_name_from_path(*i);
758 if (!path_is_absolute(*i) ||
759 !unit_name_is_valid_no_type(fn, true)) {
765 if (lstat(*i, &st) < 0) {
771 if (!S_ISREG(st.st_mode)) {
776 q = in_search_path(*i, paths.unit_path);
785 path = path_make_absolute(fn, config_path);
791 if (symlink(*i, path) >= 0) {
792 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
798 if (errno == EEXIST) {
801 q = readlink_and_make_absolute(path, &dest);
803 if (q < 0 && errno != ENOENT) {
812 if (q >= 0 && path_equal(dest, *i)) {
823 if (symlink(*i, path) >= 0) {
825 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
826 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
844 lookup_paths_free(&paths);
850 void unit_file_list_free(Hashmap *h) {
853 while ((i = hashmap_steal_first(h))) {
861 void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
864 assert(changes || n_changes == 0);
869 for (i = 0; i < n_changes; i++) {
870 free(changes[i].path);
871 free(changes[i].source);
877 static void install_info_free(InstallInfo *i) {
882 strv_free(i->aliases);
883 strv_free(i->wanted_by);
887 static void install_info_hashmap_free(Hashmap *m) {
893 while ((i = hashmap_steal_first(m)))
894 install_info_free(i);
899 static void install_context_done(InstallContext *c) {
902 install_info_hashmap_free(c->will_install);
903 install_info_hashmap_free(c->have_installed);
905 c->will_install = c->have_installed = NULL;
908 static int install_info_add(
912 InstallInfo *i = NULL;
916 assert(name || path);
919 name = file_name_from_path(path);
921 if (!unit_name_is_valid_no_type(name, true))
924 if (hashmap_get(c->have_installed, name) ||
925 hashmap_get(c->will_install, name))
928 r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func);
932 i = new0(InstallInfo, 1);
936 i->name = strdup(name);
943 i->path = strdup(path);
950 r = hashmap_put(c->will_install, i->name, i);
958 install_info_free(i);
963 static int install_info_add_auto(
965 const char *name_or_path) {
968 assert(name_or_path);
970 if (path_is_absolute(name_or_path))
971 return install_info_add(c, NULL, name_or_path);
973 return install_info_add(c, name_or_path, NULL);
976 static int config_parse_also(
977 const char *filename,
989 InstallContext *c = data;
995 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
1003 r = install_info_add(c, n, NULL);
1015 static int unit_file_load(
1019 bool allow_symlink) {
1021 const ConfigTableItem items[] = {
1022 { "Install", "Alias", config_parse_strv, 0, &info->aliases },
1023 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
1024 { "Install", "Also", config_parse_also, 0, c },
1025 { NULL, NULL, NULL, 0, NULL }
1036 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
1040 f = fdopen(fd, "re");
1042 close_nointr_nofail(fd);
1046 r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info);
1051 return strv_length(info->aliases) + strv_length(info->wanted_by);
1054 static int unit_file_search(
1058 const char *root_dir,
1059 bool allow_symlink) {
1069 return unit_file_load(c, info, info->path, allow_symlink);
1073 STRV_FOREACH(p, paths->unit_path) {
1076 if (isempty(root_dir))
1077 asprintf(&path, "%s/%s", *p, info->name);
1079 asprintf(&path, "%s/%s/%s", root_dir, *p, info->name);
1084 r = unit_file_load(c, info, path, allow_symlink);
1091 if (r != -ENOENT && r != -ELOOP)
1098 static int unit_file_can_install(
1100 const char *root_dir,
1102 bool allow_symlink) {
1113 r = install_info_add_auto(&c, name);
1117 assert_se(i = hashmap_first(c.will_install));
1119 r = unit_file_search(&c, i, paths, root_dir, allow_symlink);
1122 r = strv_length(i->aliases) + strv_length(i->wanted_by);
1124 install_context_done(&c);
1129 static int create_symlink(
1130 const char *old_path,
1131 const char *new_path,
1133 UnitFileChange **changes,
1134 unsigned *n_changes) {
1142 mkdir_parents(new_path, 0755);
1144 if (symlink(old_path, new_path) >= 0) {
1145 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1149 if (errno != EEXIST)
1152 r = readlink_and_make_absolute(new_path, &dest);
1156 if (path_equal(dest, old_path)) {
1168 if (symlink(old_path, new_path) >= 0) {
1169 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
1170 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1177 static int install_info_symlink_alias(
1179 const char *config_path,
1181 UnitFileChange **changes,
1182 unsigned *n_changes) {
1188 assert(config_path);
1190 STRV_FOREACH(s, i->aliases) {
1193 alias_path = path_make_absolute(*s, config_path);
1198 q = create_symlink(i->path, alias_path, force, changes, n_changes);
1208 static int install_info_symlink_wants(
1210 const char *config_path,
1212 UnitFileChange **changes,
1213 unsigned *n_changes) {
1219 assert(config_path);
1221 STRV_FOREACH(s, i->wanted_by) {
1224 if (!unit_name_is_valid_no_type(*s, true)) {
1229 if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0)
1232 q = create_symlink(i->path, path, force, changes, n_changes);
1242 static int install_info_symlink_link(
1245 const char *config_path,
1247 UnitFileChange **changes,
1248 unsigned *n_changes) {
1255 assert(config_path);
1258 r = in_search_path(i->path, paths->unit_path);
1262 if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
1265 r = create_symlink(i->path, path, force, changes, n_changes);
1271 static int install_info_apply(
1274 const char *config_path,
1276 UnitFileChange **changes,
1277 unsigned *n_changes) {
1283 assert(config_path);
1285 r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
1287 q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
1291 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1298 static int install_context_apply(
1301 const char *config_path,
1302 const char *root_dir,
1304 UnitFileChange **changes,
1305 unsigned *n_changes) {
1312 assert(config_path);
1314 while ((i = hashmap_first(c->will_install))) {
1316 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1320 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1322 q = unit_file_search(c, i, paths, root_dir, false);
1331 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1332 if (r >= 0 && q < 0)
1339 static int install_context_mark_for_removal(
1342 Set **remove_symlinks_to,
1343 const char *config_path,
1344 const char *root_dir) {
1351 assert(config_path);
1353 /* Marks all items for removal */
1355 while ((i = hashmap_first(c->will_install))) {
1357 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1361 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1363 q = unit_file_search(c, i, paths, root_dir, false);
1372 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1373 if (r >= 0 && q < 0)
1380 int unit_file_enable(
1381 UnitFileScope scope,
1383 const char *root_dir,
1386 UnitFileChange **changes,
1387 unsigned *n_changes) {
1391 char **i, *config_path = NULL;
1395 assert(scope < _UNIT_FILE_SCOPE_MAX);
1400 r = lookup_paths_init_from_scope(&paths, scope);
1404 r = get_config_path(scope, runtime, root_dir, &config_path);
1408 STRV_FOREACH(i, files) {
1409 r = install_info_add_auto(&c, *i);
1414 /* This will return the number of symlink rules that were
1415 supposed to be created, not the ones actually created. This is
1416 useful to determine whether the passed files hat any
1417 installation data at all. */
1418 r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1421 install_context_done(&c);
1422 lookup_paths_free(&paths);
1428 int unit_file_disable(
1429 UnitFileScope scope,
1431 const char *root_dir,
1433 UnitFileChange **changes,
1434 unsigned *n_changes) {
1438 char **i, *config_path = NULL;
1439 Set *remove_symlinks_to = NULL;
1443 assert(scope < _UNIT_FILE_SCOPE_MAX);
1448 r = lookup_paths_init_from_scope(&paths, scope);
1452 r = get_config_path(scope, runtime, root_dir, &config_path);
1456 STRV_FOREACH(i, files) {
1457 r = install_info_add_auto(&c, *i);
1462 r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
1464 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
1469 install_context_done(&c);
1470 lookup_paths_free(&paths);
1471 set_free_free(remove_symlinks_to);
1477 int unit_file_reenable(
1478 UnitFileScope scope,
1480 const char *root_dir,
1483 UnitFileChange **changes,
1484 unsigned *n_changes) {
1488 char **i, *config_path = NULL;
1489 Set *remove_symlinks_to = NULL;
1493 assert(scope < _UNIT_FILE_SCOPE_MAX);
1498 r = lookup_paths_init_from_scope(&paths, scope);
1502 r = get_config_path(scope, runtime, root_dir, &config_path);
1506 STRV_FOREACH(i, files) {
1507 r = mark_symlink_for_removal(&remove_symlinks_to, *i);
1511 r = install_info_add_auto(&c, *i);
1516 r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
1518 /* Returns number of symlinks that where supposed to be installed. */
1519 q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1524 lookup_paths_free(&paths);
1525 install_context_done(&c);
1526 set_free_free(remove_symlinks_to);
1532 UnitFileState unit_file_get_state(
1533 UnitFileScope scope,
1534 const char *root_dir,
1538 UnitFileState state = _UNIT_FILE_STATE_INVALID;
1539 char **i, *path = NULL;
1543 assert(scope < _UNIT_FILE_SCOPE_MAX);
1548 if (root_dir && scope != UNIT_FILE_SYSTEM)
1551 if (!unit_name_is_valid_no_type(name, true))
1554 r = lookup_paths_init_from_scope(&paths, scope);
1558 STRV_FOREACH(i, paths.unit_path) {
1565 asprintf(&path, "%s/%s/%s", root_dir, *i, name);
1567 asprintf(&path, "%s/%s", *i, name);
1574 if (lstat(path, &st) < 0) {
1575 if (errno == ENOENT)
1582 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
1587 r = null_or_empty_path(path);
1588 if (r < 0 && r != -ENOENT)
1591 state = path_startswith(*i, "/run") ?
1592 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1597 r = find_symlinks_in_scope(scope, root_dir, name, &state);
1605 r = unit_file_can_install(&paths, root_dir, path, true);
1606 if (r < 0 && errno != -ENOENT)
1609 state = UNIT_FILE_DISABLED;
1612 } else if (r == 0) {
1613 state = UNIT_FILE_STATIC;
1620 lookup_paths_free(&paths);
1623 return r < 0 ? r : state;
1626 int unit_file_query_preset(UnitFileScope scope, const char *name) {
1631 assert(scope < _UNIT_FILE_SCOPE_MAX);
1634 if (scope == UNIT_FILE_SYSTEM)
1635 r = conf_files_list(&files, ".preset",
1636 "/etc/systemd/system.preset",
1637 "/usr/local/lib/systemd/system.preset",
1638 "/usr/lib/systemd/system.preset",
1639 "/lib/systemd/system.preset",
1641 else if (scope == UNIT_FILE_GLOBAL)
1642 r = conf_files_list(&files, ".preset",
1643 "/etc/systemd/user.preset",
1644 "/usr/local/lib/systemd/user.preset",
1645 "/usr/lib/systemd/user.preset",
1653 STRV_FOREACH(i, files) {
1656 f = fopen(*i, "re");
1658 if (errno == ENOENT)
1666 char line[LINE_MAX], *l;
1668 if (!fgets(line, sizeof(line), f))
1675 if (strchr(COMMENTS, *l))
1678 if (first_word(l, "enable")) {
1680 l += strspn(l, WHITESPACE);
1682 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
1687 } else if (first_word(l, "disable")) {
1689 l += strspn(l, WHITESPACE);
1691 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
1697 log_debug("Couldn't parse line '%s'", l);
1703 /* Default is "enable" */
1712 int unit_file_preset(
1713 UnitFileScope scope,
1715 const char *root_dir,
1718 UnitFileChange **changes,
1719 unsigned *n_changes) {
1722 InstallContext plus, minus;
1723 char **i, *config_path = NULL;
1724 Set *remove_symlinks_to = NULL;
1728 assert(scope < _UNIT_FILE_SCOPE_MAX);
1734 r = lookup_paths_init_from_scope(&paths, scope);
1738 r = get_config_path(scope, runtime, root_dir, &config_path);
1742 STRV_FOREACH(i, files) {
1744 if (!unit_name_is_valid_no_type(*i, true)) {
1749 r = unit_file_query_preset(scope, *i);
1754 r = install_info_add_auto(&plus, *i);
1756 r = install_info_add_auto(&minus, *i);
1762 r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
1764 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
1768 /* Returns number of symlinks that where supposed to be installed. */
1769 q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
1774 lookup_paths_free(&paths);
1775 install_context_done(&plus);
1776 install_context_done(&minus);
1777 set_free_free(remove_symlinks_to);
1783 int unit_file_get_list(
1784 UnitFileScope scope,
1785 const char *root_dir,
1789 char **i, *buf = NULL;
1794 assert(scope < _UNIT_FILE_SCOPE_MAX);
1799 if (root_dir && scope != UNIT_FILE_SYSTEM)
1802 r = lookup_paths_init_from_scope(&paths, scope);
1806 STRV_FOREACH(i, paths.unit_path) {
1807 struct dirent buffer, *de;
1808 const char *units_dir;
1814 if (asprintf(&buf, "%s/%s", root_dir, *i) < 0) {
1825 d = opendir(units_dir);
1827 if (errno == ENOENT)
1837 r = readdir_r(d, &buffer, &de);
1846 if (ignore_file(de->d_name))
1849 if (!unit_name_is_valid_no_type(de->d_name, true))
1852 if (hashmap_get(h, de->d_name))
1855 r = dirent_ensure_type(d, de);
1857 if (errno == ENOENT)
1863 if (de->d_type != DT_LNK && de->d_type != DT_REG)
1866 f = new0(UnitFileList, 1);
1872 f->path = path_make_absolute(de->d_name, units_dir);
1879 r = null_or_empty_path(f->path);
1880 if (r < 0 && r != -ENOENT) {
1886 path_startswith(*i, "/run") ?
1887 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1891 r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
1899 r = unit_file_can_install(&paths, root_dir, f->path, true);
1905 f->state = UNIT_FILE_DISABLED;
1908 f->state = UNIT_FILE_STATIC;
1917 r = hashmap_put(h, file_name_from_path(f->path), f);
1927 lookup_paths_free(&paths);
1936 static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
1937 [UNIT_FILE_ENABLED] = "enabled",
1938 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtie",
1939 [UNIT_FILE_LINKED] = "linked",
1940 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
1941 [UNIT_FILE_MASKED] = "masked",
1942 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
1943 [UNIT_FILE_STATIC] = "static",
1944 [UNIT_FILE_DISABLED] = "disabled"
1947 DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
1949 static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
1950 [UNIT_FILE_SYMLINK] = "symlink",
1951 [UNIT_FILE_UNLINK] = "unlink",
1954 DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);