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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty <of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
32 #include "path-util.h"
33 #include "path-lookup.h"
35 #include "unit-name.h"
37 #include "conf-parser.h"
38 #include "conf-files.h"
50 Hashmap *will_install;
51 Hashmap *have_installed;
54 #define _cleanup_lookup_paths_free_ \
55 __attribute__((cleanup(lookup_paths_free)))
56 #define _cleanup_install_context_done_ \
57 __attribute__((cleanup(install_context_done)))
59 static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
62 assert(scope < _UNIT_FILE_SCOPE_MAX);
66 return lookup_paths_init(paths,
67 scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
68 scope == UNIT_FILE_USER,
72 static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
77 assert(scope < _UNIT_FILE_SCOPE_MAX);
82 case UNIT_FILE_SYSTEM:
84 if (root_dir && runtime)
85 asprintf(&p, "%s/run/systemd/system", root_dir);
87 p = strdup("/run/systemd/system");
89 asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH);
91 p = strdup(SYSTEM_CONFIG_UNIT_PATH);
95 case UNIT_FILE_GLOBAL:
101 p = strdup("/run/systemd/user");
103 p = strdup(USER_CONFIG_UNIT_PATH);
108 if (root_dir || runtime)
111 r = user_config_home(&p);
113 return r < 0 ? r : -ENOENT;
118 assert_not_reached("Bad scope");
128 static int add_file_change(
129 UnitFileChange **changes,
131 UnitFileChangeType type,
133 const char *source) {
139 assert(!changes == !n_changes);
144 c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
152 c[i].path = strdup(path);
157 c[i].source = strdup(source);
169 static int mark_symlink_for_removal(
170 Set **remove_symlinks_to,
178 r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func);
186 path_kill_slashes(n);
188 r = set_put(*remove_symlinks_to, n);
191 return r == -EEXIST ? 0 : r;
197 static int remove_marked_symlinks_fd(
198 Set *remove_symlinks_to,
201 const char *config_path,
203 UnitFileChange **changes,
208 DIR _cleanup_closedir_ *d = NULL;
210 assert(remove_symlinks_to);
218 close_nointr_nofail(fd);
226 union dirent_storage buf;
229 k = readdir_r(d, &buf.de, &de);
238 if (ignore_file(de->d_name))
241 dirent_ensure_type(d, de);
243 if (de->d_type == DT_DIR) {
245 char _cleanup_free_ *p = NULL;
247 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
257 p = path_make_absolute(de->d_name, path);
259 close_nointr_nofail(nfd);
263 /* This will close nfd, regardless whether it succeeds or not */
264 q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, files);
269 } else if (de->d_type == DT_LNK) {
270 char _cleanup_free_ *p = NULL, *dest = NULL;
274 p = path_make_absolute(de->d_name, path);
278 q = readlink_and_canonicalize(p, &dest);
289 set_get(remove_symlinks_to, dest) ||
290 set_get(remove_symlinks_to, path_get_file_name(dest));
292 if (unit_name_is_instance(p))
293 found = found && strv_contains(files, path_get_file_name(p));
297 if (unlink(p) < 0 && errno != ENOENT) {
302 rmdir_parents(p, config_path);
303 path_kill_slashes(p);
305 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
307 if (!set_get(remove_symlinks_to, p)) {
309 q = mark_symlink_for_removal(&remove_symlinks_to, p);
324 static int remove_marked_symlinks(
325 Set *remove_symlinks_to,
326 const char *config_path,
327 UnitFileChange **changes,
336 if (set_size(remove_symlinks_to) <= 0)
339 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
353 /* This takes possession of cfd and closes it */
354 q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, files);
359 close_nointr_nofail(fd);
364 static int find_symlinks_fd(
368 const char *config_path,
369 bool *same_name_link) {
372 DIR _cleanup_closedir_ *d = NULL;
378 assert(same_name_link);
382 close_nointr_nofail(fd);
389 union dirent_storage buf;
391 k = readdir_r(d, &buf.de, &de);
398 if (ignore_file(de->d_name))
401 dirent_ensure_type(d, de);
403 if (de->d_type == DT_DIR) {
405 char _cleanup_free_ *p = NULL;
407 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
417 p = path_make_absolute(de->d_name, path);
419 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);
432 } else if (de->d_type == DT_LNK) {
433 char _cleanup_free_ *p = NULL, *dest = NULL;
434 bool found_path, found_dest, b = false;
437 /* Acquire symlink name */
438 p = path_make_absolute(de->d_name, path);
442 /* Acquire symlink destination */
443 q = readlink_and_canonicalize(p, &dest);
453 /* Check if the symlink itself matches what we
455 if (path_is_absolute(name))
456 found_path = path_equal(p, name);
458 found_path = streq(de->d_name, name);
460 /* Check if what the symlink points to
461 * matches what we are looking for */
462 if (path_is_absolute(name))
463 found_dest = path_equal(dest, name);
465 found_dest = streq(path_get_file_name(dest), name);
467 if (found_path && found_dest) {
468 char _cleanup_free_ *t = NULL;
470 /* Filter out same name links in the main
472 t = path_make_absolute(name, config_path);
476 b = path_equal(t, p);
480 *same_name_link = true;
481 else if (found_path || found_dest)
489 static int find_symlinks(
491 const char *config_path,
492 bool *same_name_link) {
498 assert(same_name_link);
500 fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
507 /* This takes possession of fd and closes it */
508 return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
511 static int find_symlinks_in_scope(
513 const char *root_dir,
515 UnitFileState *state) {
518 char _cleanup_free_ *path = NULL;
519 bool same_name_link_runtime = false, same_name_link = false;
522 assert(scope < _UNIT_FILE_SCOPE_MAX);
525 if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) {
527 /* First look in runtime config path */
528 r = get_config_path(scope, true, root_dir, &path);
532 r = find_symlinks(name, path, &same_name_link_runtime);
536 *state = UNIT_FILE_ENABLED_RUNTIME;
541 /* Then look in the normal config path */
542 r = get_config_path(scope, false, root_dir, &path);
546 r = find_symlinks(name, path, &same_name_link);
550 *state = UNIT_FILE_ENABLED;
554 /* Hmm, we didn't find it, but maybe we found the same name
556 if (same_name_link_runtime) {
557 *state = UNIT_FILE_LINKED_RUNTIME;
559 } else if (same_name_link) {
560 *state = UNIT_FILE_LINKED;
570 const char *root_dir,
573 UnitFileChange **changes,
574 unsigned *n_changes) {
577 char _cleanup_free_ *prefix;
581 assert(scope < _UNIT_FILE_SCOPE_MAX);
583 r = get_config_path(scope, runtime, root_dir, &prefix);
587 STRV_FOREACH(i, files) {
588 char _cleanup_free_ *path = NULL;
590 if (!unit_name_is_valid(*i, true)) {
596 path = path_make_absolute(*i, prefix);
602 if (symlink("/dev/null", path) >= 0) {
603 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
608 if (errno == EEXIST) {
610 if (null_or_empty_path(path) > 0)
616 if (symlink("/dev/null", path) >= 0) {
618 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
619 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
636 int unit_file_unmask(
639 const char *root_dir,
641 UnitFileChange **changes,
642 unsigned *n_changes) {
644 char **i, *config_path = NULL;
646 Set *remove_symlinks_to = NULL;
649 assert(scope < _UNIT_FILE_SCOPE_MAX);
651 r = get_config_path(scope, runtime, root_dir, &config_path);
655 STRV_FOREACH(i, files) {
658 if (!unit_name_is_valid(*i, true)) {
664 path = path_make_absolute(*i, config_path);
670 q = null_or_empty_path(path);
672 if (unlink(path) >= 0) {
673 mark_symlink_for_removal(&remove_symlinks_to, path);
674 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
683 if (q != -ENOENT && r == 0)
691 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
695 set_free_free(remove_symlinks_to);
704 const char *root_dir,
707 UnitFileChange **changes,
708 unsigned *n_changes) {
710 LookupPaths _cleanup_lookup_paths_free_ paths = {NULL};
712 char _cleanup_free_ *config_path = NULL;
716 assert(scope < _UNIT_FILE_SCOPE_MAX);
718 r = lookup_paths_init_from_scope(&paths, scope);
722 r = get_config_path(scope, runtime, root_dir, &config_path);
726 STRV_FOREACH(i, files) {
727 char _cleanup_free_ *path = NULL;
731 fn = path_get_file_name(*i);
733 if (!path_is_absolute(*i) ||
734 !unit_name_is_valid(fn, true)) {
740 if (lstat(*i, &st) < 0) {
746 if (!S_ISREG(st.st_mode)) {
751 q = in_search_path(*i, paths.unit_path);
758 path = path_make_absolute(fn, config_path);
762 if (symlink(*i, path) >= 0) {
763 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
767 if (errno == EEXIST) {
768 char _cleanup_free_ *dest = NULL;
770 q = readlink_and_make_absolute(path, &dest);
772 if (q < 0 && errno != ENOENT) {
778 if (q >= 0 && path_equal(dest, *i))
784 if (symlink(*i, path) >= 0) {
786 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
787 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
804 void unit_file_list_free(Hashmap *h) {
807 while ((i = hashmap_steal_first(h))) {
815 void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
818 assert(changes || n_changes == 0);
823 for (i = 0; i < n_changes; i++) {
824 free(changes[i].path);
825 free(changes[i].source);
831 static void install_info_free(InstallInfo *i) {
836 strv_free(i->aliases);
837 strv_free(i->wanted_by);
838 strv_free(i->required_by);
842 static void install_info_hashmap_free(Hashmap *m) {
848 while ((i = hashmap_steal_first(m)))
849 install_info_free(i);
854 static void install_context_done(InstallContext *c) {
857 install_info_hashmap_free(c->will_install);
858 install_info_hashmap_free(c->have_installed);
860 c->will_install = c->have_installed = NULL;
863 static int install_info_add(
867 InstallInfo *i = NULL;
871 assert(name || path);
874 name = path_get_file_name(path);
876 if (!unit_name_is_valid(name, true))
879 if (hashmap_get(c->have_installed, name) ||
880 hashmap_get(c->will_install, name))
883 r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func);
887 i = new0(InstallInfo, 1);
891 i->name = strdup(name);
898 i->path = strdup(path);
905 r = hashmap_put(c->will_install, i->name, i);
913 install_info_free(i);
918 static int install_info_add_auto(
920 const char *name_or_path) {
923 assert(name_or_path);
925 if (path_is_absolute(name_or_path))
926 return install_info_add(c, NULL, name_or_path);
928 return install_info_add(c, name_or_path, NULL);
931 static int config_parse_also(
932 const char *filename,
944 InstallContext *c = data;
950 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
951 char _cleanup_free_ *n;
958 r = install_info_add(c, n, NULL);
966 static int unit_file_load(
970 bool allow_symlink) {
972 const ConfigTableItem items[] = {
973 { "Install", "Alias", config_parse_strv, 0, &info->aliases },
974 { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
975 { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
976 { "Install", "Also", config_parse_also, 0, c },
977 { NULL, NULL, NULL, 0, NULL }
981 FILE _cleanup_fclose_ *f = NULL;
988 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
992 f = fdopen(fd, "re");
994 close_nointr_nofail(fd);
998 r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info);
1003 strv_length(info->aliases) +
1004 strv_length(info->wanted_by) +
1005 strv_length(info->required_by);
1008 static int unit_file_search(
1012 const char *root_dir,
1013 bool allow_symlink) {
1023 return unit_file_load(c, info, info->path, allow_symlink);
1027 STRV_FOREACH(p, paths->unit_path) {
1030 if (isempty(root_dir))
1031 asprintf(&path, "%s/%s", *p, info->name);
1033 asprintf(&path, "%s/%s/%s", root_dir, *p, info->name);
1038 r = unit_file_load(c, info, path, allow_symlink);
1043 if (r == -ENOENT && unit_name_is_instance(info->name)) {
1044 /* unit file doesn't exist, however instance enablement was request */
1045 /* we will check if it is possible to load template unit file */
1046 char *template = NULL,
1047 *template_path = NULL,
1048 *template_dir = NULL;
1050 template = unit_name_template(info->name);
1056 /* we will reuse path variable since we don't need it anymore */
1057 template_dir = path;
1058 *(strrchr(path, '/') + 1) = '\0';
1060 template_path = strjoin(template_dir, template, NULL);
1061 if (!template_path) {
1067 /* let's try to load template unit */
1068 r = unit_file_load(c, info, template_path, allow_symlink);
1070 info->path = strdup(template_path);
1074 free(template_path);
1080 free(template_path);
1085 if (r != -ENOENT && r != -ELOOP)
1092 static int unit_file_can_install(
1094 const char *root_dir,
1096 bool allow_symlink) {
1098 InstallContext _cleanup_install_context_done_ c = {NULL};
1105 r = install_info_add_auto(&c, name);
1109 assert_se(i = hashmap_first(c.will_install));
1111 r = unit_file_search(&c, i, paths, root_dir, allow_symlink);
1115 strv_length(i->aliases) +
1116 strv_length(i->wanted_by) +
1117 strv_length(i->required_by);
1122 static int create_symlink(
1123 const char *old_path,
1124 const char *new_path,
1126 UnitFileChange **changes,
1127 unsigned *n_changes) {
1129 char _cleanup_free_ *dest = NULL;
1135 mkdir_parents_label(new_path, 0755);
1137 if (symlink(old_path, new_path) >= 0) {
1138 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1142 if (errno != EEXIST)
1145 r = readlink_and_make_absolute(new_path, &dest);
1149 if (path_equal(dest, old_path))
1157 if (symlink(old_path, new_path) >= 0) {
1158 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
1159 add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
1166 static int install_info_symlink_alias(
1168 const char *config_path,
1170 UnitFileChange **changes,
1171 unsigned *n_changes) {
1177 assert(config_path);
1179 STRV_FOREACH(s, i->aliases) {
1182 alias_path = path_make_absolute(*s, config_path);
1187 q = create_symlink(i->path, alias_path, force, changes, n_changes);
1197 static int install_info_symlink_wants(
1199 const char *config_path,
1201 UnitFileChange **changes,
1202 unsigned *n_changes) {
1208 assert(config_path);
1210 STRV_FOREACH(s, i->wanted_by) {
1213 if (!unit_name_is_valid(*s, true)) {
1218 if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0)
1221 q = create_symlink(i->path, path, force, changes, n_changes);
1231 static int install_info_symlink_requires(
1233 const char *config_path,
1235 UnitFileChange **changes,
1236 unsigned *n_changes) {
1242 assert(config_path);
1244 STRV_FOREACH(s, i->required_by) {
1247 if (!unit_name_is_valid(*s, true)) {
1252 if (asprintf(&path, "%s/%s.requires/%s", config_path, *s, i->name) < 0)
1255 q = create_symlink(i->path, path, force, changes, n_changes);
1265 static int install_info_symlink_link(
1268 const char *config_path,
1270 UnitFileChange **changes,
1271 unsigned *n_changes) {
1274 char _cleanup_free_ *path = NULL;
1278 assert(config_path);
1281 r = in_search_path(i->path, paths->unit_path);
1285 if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
1288 r = create_symlink(i->path, path, force, changes, n_changes);
1292 static int install_info_apply(
1295 const char *config_path,
1297 UnitFileChange **changes,
1298 unsigned *n_changes) {
1304 assert(config_path);
1306 r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
1308 q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
1312 q = install_info_symlink_requires(i, config_path, force, changes, n_changes);
1316 q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
1323 static int install_context_apply(
1326 const char *config_path,
1327 const char *root_dir,
1329 UnitFileChange **changes,
1330 unsigned *n_changes) {
1337 assert(config_path);
1339 while ((i = hashmap_first(c->will_install))) {
1341 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1345 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1347 q = unit_file_search(c, i, paths, root_dir, false);
1356 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
1357 if (r >= 0 && q < 0)
1364 static int install_context_mark_for_removal(
1367 Set **remove_symlinks_to,
1368 const char *config_path,
1369 const char *root_dir) {
1376 assert(config_path);
1378 /* Marks all items for removal */
1380 while ((i = hashmap_first(c->will_install))) {
1382 q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
1386 assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
1388 q = unit_file_search(c, i, paths, root_dir, false);
1397 if (unit_name_is_instance(i->name)) {
1398 char *unit_file = NULL;
1400 unit_file = path_get_file_name(i->path);
1402 if (unit_name_is_instance(unit_file))
1403 /* unit file named as instance exists, thus all symlinks pointing to it, will be removed */
1404 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1406 /* does not exist, thus we will mark for removal symlinks to template unit file */
1407 q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
1409 q = mark_symlink_for_removal(remove_symlinks_to, i->name);
1411 if (r >= 0 && q < 0)
1418 int unit_file_enable(
1419 UnitFileScope scope,
1421 const char *root_dir,
1424 UnitFileChange **changes,
1425 unsigned *n_changes) {
1427 LookupPaths _cleanup_lookup_paths_free_ paths = {NULL};
1428 InstallContext _cleanup_install_context_done_ c = {NULL};
1430 char _cleanup_free_ *config_path = NULL;
1434 assert(scope < _UNIT_FILE_SCOPE_MAX);
1436 r = lookup_paths_init_from_scope(&paths, scope);
1440 r = get_config_path(scope, runtime, root_dir, &config_path);
1444 STRV_FOREACH(i, files) {
1445 r = install_info_add_auto(&c, *i);
1450 /* This will return the number of symlink rules that were
1451 supposed to be created, not the ones actually created. This is
1452 useful to determine whether the passed files had any
1453 installation data at all. */
1454 r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1458 int unit_file_disable(
1459 UnitFileScope scope,
1461 const char *root_dir,
1463 UnitFileChange **changes,
1464 unsigned *n_changes) {
1466 LookupPaths _cleanup_lookup_paths_free_ paths = {NULL};
1467 InstallContext _cleanup_install_context_done_ c = {NULL};
1469 char _cleanup_free_ *config_path = NULL;
1470 Set _cleanup_set_free_free_ *remove_symlinks_to = NULL;
1474 assert(scope < _UNIT_FILE_SCOPE_MAX);
1476 r = lookup_paths_init_from_scope(&paths, scope);
1480 r = get_config_path(scope, runtime, root_dir, &config_path);
1484 STRV_FOREACH(i, files) {
1485 r = install_info_add_auto(&c, *i);
1490 r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
1492 q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
1499 int unit_file_reenable(
1500 UnitFileScope scope,
1502 const char *root_dir,
1505 UnitFileChange **changes,
1506 unsigned *n_changes) {
1508 LookupPaths _cleanup_lookup_paths_free_ paths = {NULL};
1509 InstallContext _cleanup_install_context_done_ c = {NULL};
1511 char _cleanup_free_ *config_path = NULL;
1512 Set _cleanup_set_free_free_ *remove_symlinks_to = NULL;
1516 assert(scope < _UNIT_FILE_SCOPE_MAX);
1518 r = lookup_paths_init_from_scope(&paths, scope);
1522 r = get_config_path(scope, runtime, root_dir, &config_path);
1526 STRV_FOREACH(i, files) {
1527 r = mark_symlink_for_removal(&remove_symlinks_to, *i);
1531 r = install_info_add_auto(&c, *i);
1536 r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
1538 /* Returns number of symlinks that where supposed to be installed. */
1539 q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
1546 UnitFileState unit_file_get_state(
1547 UnitFileScope scope,
1548 const char *root_dir,
1551 LookupPaths _cleanup_lookup_paths_free_ paths = {NULL};
1552 UnitFileState state = _UNIT_FILE_STATE_INVALID;
1554 char _cleanup_free_ *path = NULL;
1558 assert(scope < _UNIT_FILE_SCOPE_MAX);
1561 if (root_dir && scope != UNIT_FILE_SYSTEM)
1564 if (!unit_name_is_valid(name, true))
1567 r = lookup_paths_init_from_scope(&paths, scope);
1571 STRV_FOREACH(i, paths.unit_path) {
1578 asprintf(&path, "%s/%s/%s", root_dir, *i, name);
1580 asprintf(&path, "%s/%s", *i, name);
1585 if (lstat(path, &st) < 0) {
1587 if (errno == ENOENT)
1593 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
1596 r = null_or_empty_path(path);
1597 if (r < 0 && r != -ENOENT)
1600 state = path_startswith(*i, "/run") ?
1601 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1605 r = find_symlinks_in_scope(scope, root_dir, name, &state);
1611 r = unit_file_can_install(&paths, root_dir, path, true);
1612 if (r < 0 && errno != -ENOENT)
1615 return UNIT_FILE_DISABLED;
1617 return UNIT_FILE_STATIC;
1620 return r < 0 ? r : state;
1623 int unit_file_query_preset(UnitFileScope scope, const char *name) {
1624 char _cleanup_strv_free_ **files = NULL;
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 #ifdef HAVE_SPLIT_USR
1638 "/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) {
1654 FILE _cleanup_fclose_ *f;
1656 f = fopen(*i, "re");
1658 if (errno == ENOENT)
1665 char line[LINE_MAX], *l;
1667 if (!fgets(line, sizeof(line), f))
1674 if (strchr(COMMENTS, *l))
1677 if (first_word(l, "enable")) {
1679 l += strspn(l, WHITESPACE);
1681 if (fnmatch(l, name, FNM_NOESCAPE) == 0)
1684 } else if (first_word(l, "disable")) {
1686 l += strspn(l, WHITESPACE);
1688 if (fnmatch(l, name, FNM_NOESCAPE) == 0)
1692 log_debug("Couldn't parse line '%s'", l);
1696 /* Default is "enable" */
1700 int unit_file_preset(
1701 UnitFileScope scope,
1703 const char *root_dir,
1706 UnitFileChange **changes,
1707 unsigned *n_changes) {
1709 LookupPaths _cleanup_lookup_paths_free_ paths = {NULL};
1710 InstallContext _cleanup_install_context_done_ plus = {NULL}, minus = {NULL};
1712 char _cleanup_free_ *config_path = NULL;
1713 Set _cleanup_set_free_free_ *remove_symlinks_to = NULL;
1717 assert(scope < _UNIT_FILE_SCOPE_MAX);
1719 r = lookup_paths_init_from_scope(&paths, scope);
1723 r = get_config_path(scope, runtime, root_dir, &config_path);
1727 STRV_FOREACH(i, files) {
1729 if (!unit_name_is_valid(*i, true))
1732 r = unit_file_query_preset(scope, *i);
1737 r = install_info_add_auto(&plus, *i);
1739 r = install_info_add_auto(&minus, *i);
1745 r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to,
1746 config_path, root_dir);
1748 q = remove_marked_symlinks(remove_symlinks_to, config_path,
1749 changes, n_changes, files);
1753 /* Returns number of symlinks that where supposed to be installed. */
1754 q = install_context_apply(&plus, &paths, config_path, root_dir, force,
1755 changes, n_changes);
1762 static void unitfilelist_free(UnitFileList **f) {
1770 int unit_file_get_list(
1771 UnitFileScope scope,
1772 const char *root_dir,
1775 LookupPaths _cleanup_lookup_paths_free_ paths = {NULL};
1777 char _cleanup_free_ *buf = NULL;
1778 DIR _cleanup_closedir_ *d = NULL;
1782 assert(scope < _UNIT_FILE_SCOPE_MAX);
1785 if (root_dir && scope != UNIT_FILE_SYSTEM)
1788 r = lookup_paths_init_from_scope(&paths, scope);
1792 STRV_FOREACH(i, paths.unit_path) {
1793 const char *units_dir;
1799 if (asprintf(&buf, "%s/%s", root_dir, *i) < 0)
1809 d = opendir(units_dir);
1811 if (errno == ENOENT)
1819 union dirent_storage buffer;
1820 UnitFileList __attribute__((cleanup(unitfilelist_free)))
1823 r = readdir_r(d, &buffer.de, &de);
1830 if (ignore_file(de->d_name))
1833 if (!unit_name_is_valid(de->d_name, true))
1836 if (hashmap_get(h, de->d_name))
1839 r = dirent_ensure_type(d, de);
1847 if (de->d_type != DT_LNK && de->d_type != DT_REG)
1850 f = new0(UnitFileList, 1);
1854 f->path = path_make_absolute(de->d_name, units_dir);
1858 r = null_or_empty_path(f->path);
1859 if (r < 0 && r != -ENOENT)
1863 path_startswith(*i, "/run") ?
1864 UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
1868 r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
1872 f->state = UNIT_FILE_ENABLED;
1876 r = unit_file_can_install(&paths, root_dir, f->path, true);
1877 if (r == -EINVAL || /* Invalid setting? */
1878 r == -EBADMSG || /* Invalid format? */
1879 r == -ENOENT /* Included file not found? */)
1880 f->state = UNIT_FILE_INVALID;
1884 f->state = UNIT_FILE_DISABLED;
1886 f->state = UNIT_FILE_STATIC;
1889 r = hashmap_put(h, path_get_file_name(f->path), f);
1892 f = NULL; /* prevent cleanup */
1899 static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
1900 [UNIT_FILE_ENABLED] = "enabled",
1901 [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
1902 [UNIT_FILE_LINKED] = "linked",
1903 [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
1904 [UNIT_FILE_MASKED] = "masked",
1905 [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
1906 [UNIT_FILE_STATIC] = "static",
1907 [UNIT_FILE_DISABLED] = "disabled",
1908 [UNIT_FILE_INVALID] = "invalid",
1911 DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
1913 static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
1914 [UNIT_FILE_SYMLINK] = "symlink",
1915 [UNIT_FILE_UNLINK] = "unlink",
1918 DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);