]>
Commit | Line | Data |
---|---|---|
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); |