]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-unit-file.c
44f0c3fb0294a2909fef0d22d3fb129841f76677
[thirdparty/systemd.git] / src / test / test-unit-file.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "argv-util.h"
6 #include "fileio.h"
7 #include "initrd-util.h"
8 #include "path-lookup.h"
9 #include "path-util.h"
10 #include "random-util.h"
11 #include "rm-rf.h"
12 #include "set.h"
13 #include "special.h"
14 #include "strv.h"
15 #include "tests.h"
16 #include "time-util.h"
17 #include "unit-file.h"
18
19 TEST(unit_validate_alias_symlink_and_warn) {
20 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.service") == 0);
21 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.socket") == -EXDEV);
22 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.foobar") == -EXDEV);
23 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.service") == 0);
24 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.socket") == -EXDEV);
25 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.service") == -EXDEV);
26 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.socket") == -EXDEV);
27 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@YYY.service") == -EXDEV);
28 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@XXX.service") == 0);
29 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@.service") == 0);
30 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b.service") == -EXDEV);
31 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b@.service") == -EXDEV);
32 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.slice", "/other/b.slice") == -EINVAL);
33 assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.slice", "/other/b.slice") == -EINVAL);
34 }
35
36 TEST(unit_file_build_name_map) {
37 _cleanup_(lookup_paths_done) LookupPaths lp = {};
38 _cleanup_hashmap_free_ Hashmap *unit_ids = NULL;
39 _cleanup_hashmap_free_ Hashmap *unit_names = NULL;
40 const char *k, *dst;
41 char **v, **ids;
42 usec_t mtime = 0;
43 int r;
44
45 ids = strv_skip(saved_argv, 1);
46
47 assert_se(lookup_paths_init(&lp, RUNTIME_SCOPE_SYSTEM, 0, NULL) >= 0);
48
49 assert_se(unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_names, NULL) == 1);
50
51 HASHMAP_FOREACH_KEY(dst, k, unit_ids)
52 log_info("ids: %s → %s", k, dst);
53
54 HASHMAP_FOREACH_KEY(v, k, unit_names) {
55 _cleanup_free_ char *j = strv_join(v, ", ");
56 log_info("aliases: %s ← %s", k, j);
57 }
58
59 char buf[FORMAT_TIMESTAMP_MAX];
60 log_debug("Last modification time: %s", format_timestamp(buf, sizeof buf, mtime));
61
62 r = unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_names, NULL);
63 assert_se(IN_SET(r, 0, 1));
64 if (r == 0)
65 log_debug("Cache rebuild skipped based on mtime.");
66
67 STRV_FOREACH(id, ids) {
68 const char *fragment, *name;
69 _cleanup_set_free_ Set *names = NULL;
70 log_info("*** %s ***", *id);
71 r = unit_file_find_fragment(unit_ids,
72 unit_names,
73 *id,
74 &fragment,
75 &names);
76 assert_se(r == 0);
77 log_info("fragment: %s", fragment);
78 log_info("names:");
79 SET_FOREACH(name, names)
80 log_info(" %s", name);
81 }
82
83 /* Make sure everything still works if we don't collect names. */
84 STRV_FOREACH(id, ids) {
85 const char *fragment;
86 log_info("*** %s ***", *id);
87 r = unit_file_find_fragment(unit_ids,
88 unit_names,
89 *id,
90 &fragment,
91 NULL);
92 assert_se(r == 0);
93 log_info("fragment: %s", fragment);
94 }
95 }
96
97 static bool test_unit_file_remove_from_name_map_trail(const LookupPaths *lp, size_t trial) {
98 int r;
99
100 log_debug("/* %s(trial=%zu) */", __func__, trial);
101
102 _cleanup_hashmap_free_ Hashmap *unit_ids = NULL, *unit_names = NULL;
103 _cleanup_set_free_ Set *path_cache = NULL;
104 ASSERT_OK_POSITIVE(unit_file_build_name_map(lp, NULL, &unit_ids, &unit_names, &path_cache));
105
106 _cleanup_free_ char *name = NULL;
107 for (size_t i = 0; i < 100; i++) {
108 ASSERT_OK(asprintf(&name, "test-unit-file-%"PRIx64".service", random_u64()));
109 if (!hashmap_contains(unit_ids, name))
110 break;
111 name = mfree(name);
112 }
113 ASSERT_NOT_NULL(name);
114
115 _cleanup_free_ char *path = path_join(lp->transient, name);
116 ASSERT_NOT_NULL(path);
117 ASSERT_OK(write_string_file(path, "[Unit]\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755));
118
119 uint64_t cache_timestamp_hash = 0;
120 ASSERT_OK_POSITIVE(unit_file_build_name_map(lp, &cache_timestamp_hash, &unit_ids, &unit_names, &path_cache));
121
122 ASSERT_STREQ(hashmap_get(unit_ids, name), path);
123 ASSERT_TRUE(strv_equal(hashmap_get(unit_names, name), STRV_MAKE(name)));
124 ASSERT_TRUE(set_contains(path_cache, path));
125
126 ASSERT_OK_ERRNO(unlink(path));
127
128 ASSERT_OK(r = unit_file_remove_from_name_map(lp, &cache_timestamp_hash, &unit_ids, &unit_names, &path_cache, path));
129 if (r > 0)
130 return false; /* someone touches unit files. Retrying. */
131
132 ASSERT_FALSE(hashmap_contains(unit_ids, name));
133 ASSERT_FALSE(hashmap_contains(unit_names, path));
134 ASSERT_FALSE(set_contains(path_cache, path));
135
136 _cleanup_hashmap_free_ Hashmap *unit_ids_2 = NULL, *unit_names_2 = NULL;
137 _cleanup_set_free_ Set *path_cache_2 = NULL;
138 ASSERT_OK_POSITIVE(unit_file_build_name_map(lp, NULL, &unit_ids_2, &unit_names_2, &path_cache_2));
139
140 if (hashmap_size(unit_ids) != hashmap_size(unit_ids_2) ||
141 hashmap_size(unit_names) != hashmap_size(unit_names_2) ||
142 !set_equal(path_cache, path_cache_2))
143 return false;
144
145 const char *k, *v;
146 HASHMAP_FOREACH_KEY(v, k, unit_ids)
147 if (!streq_ptr(hashmap_get(unit_ids_2, k), v))
148 return false;
149
150 char **l;
151 HASHMAP_FOREACH_KEY(l, k, unit_names)
152 if (!strv_equal_ignore_order(hashmap_get(unit_names_2, k), l))
153 return false;
154
155 return true;
156 }
157
158
159 TEST(unit_file_remove_from_name_map) {
160 _cleanup_(rm_rf_physical_and_freep) char *d = NULL;
161
162 _cleanup_(lookup_paths_done) LookupPaths lp = {};
163 ASSERT_OK(lookup_paths_init(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_TEMPORARY_GENERATED, NULL));
164 ASSERT_NOT_NULL((d = strdup(lp.temporary_dir)));
165
166 for (size_t i = 0; i < 10; i++)
167 if (test_unit_file_remove_from_name_map_trail(&lp, i))
168 return;
169
170 assert_not_reached();
171 }
172
173 TEST(runlevel_to_target) {
174 in_initrd_force(false);
175 ASSERT_STREQ(runlevel_to_target(NULL), NULL);
176 ASSERT_STREQ(runlevel_to_target("unknown-runlevel"), NULL);
177 ASSERT_STREQ(runlevel_to_target("rd.unknown-runlevel"), NULL);
178 ASSERT_STREQ(runlevel_to_target("3"), SPECIAL_MULTI_USER_TARGET);
179 ASSERT_STREQ(runlevel_to_target("rd.rescue"), NULL);
180
181 in_initrd_force(true);
182 ASSERT_STREQ(runlevel_to_target(NULL), NULL);
183 ASSERT_STREQ(runlevel_to_target("unknown-runlevel"), NULL);
184 ASSERT_STREQ(runlevel_to_target("rd.unknown-runlevel"), NULL);
185 ASSERT_STREQ(runlevel_to_target("3"), NULL);
186 ASSERT_STREQ(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET);
187 }
188
189 static int intro(void) {
190 log_show_color(true);
191 return EXIT_SUCCESS;
192 }
193
194 DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);