]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-install-root.c
Merge pull request #31648 from neighbourhoodie/review-content
[thirdparty/systemd.git] / src / test / test-install-root.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <unistd.h>
4
5 #include "alloc-util.h"
6 #include "fileio.h"
7 #include "fs-util.h"
8 #include "install.h"
9 #include "mkdir.h"
10 #include "rm-rf.h"
11 #include "special.h"
12 #include "string-util.h"
13 #include "tests.h"
14 #include "tmpfile-util.h"
15
16 static char *root = NULL;
17
18 STATIC_DESTRUCTOR_REGISTER(root, rm_rf_physical_and_freep);
19
20 TEST(basic_mask_and_enable) {
21 const char *p;
22 UnitFileState state;
23 InstallChange *changes = NULL;
24 size_t n_changes = 0;
25
26 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", NULL) == -ENOENT);
27 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", NULL) == -ENOENT);
28 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", NULL) == -ENOENT);
29 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", NULL) == -ENOENT);
30 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "e.service", NULL) == -ENOENT);
31 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "f.service", NULL) == -ENOENT);
32
33 p = strjoina(root, "/usr/lib/systemd/system/a.service");
34 assert_se(write_string_file(p,
35 "[Install]\n"
36 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
37
38 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", NULL) >= 0);
39 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
40
41 p = strjoina(root, "/usr/lib/systemd/system/b.service");
42 assert_se(symlink("a.service", p) >= 0);
43
44 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", NULL) >= 0);
45 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
46
47 p = strjoina(root, "/usr/lib/systemd/system/c.service");
48 assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0);
49
50 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", NULL) >= 0);
51 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
52
53 p = strjoina(root, "/usr/lib/systemd/system/d.service");
54 assert_se(symlink("c.service", p) >= 0);
55
56 /* This one is interesting, as d follows a relative, then an absolute symlink */
57 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", NULL) >= 0);
58 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
59
60 assert_se(unit_file_mask(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
61 assert_se(n_changes == 1);
62 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
63 ASSERT_STREQ(changes[0].source, "/dev/null");
64 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
65 ASSERT_STREQ(changes[0].path, p);
66
67 install_changes_free(changes, n_changes);
68 changes = NULL; n_changes = 0;
69
70 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED);
71 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED);
72 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED);
73 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED);
74
75 /* Enabling a masked unit should fail! */
76 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == -ERFKILL);
77 install_changes_free(changes, n_changes);
78 changes = NULL; n_changes = 0;
79
80 assert_se(unit_file_unmask(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
81 assert_se(n_changes == 1);
82 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
83 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service");
84 ASSERT_STREQ(changes[0].path, p);
85 install_changes_free(changes, n_changes);
86 changes = NULL; n_changes = 0;
87
88 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == 1);
89 assert_se(n_changes == 1);
90 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
91 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/a.service");
92 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
93 ASSERT_STREQ(changes[0].path, p);
94 install_changes_free(changes, n_changes);
95 changes = NULL; n_changes = 0;
96
97 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
98 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
99 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
100 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
101
102 /* Enabling it again should succeed but be a NOP */
103 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
104 assert_se(n_changes == 0);
105 install_changes_free(changes, n_changes);
106 changes = NULL; n_changes = 0;
107
108 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
109 assert_se(n_changes == 1);
110 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
111 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
112 ASSERT_STREQ(changes[0].path, p);
113 install_changes_free(changes, n_changes);
114 changes = NULL; n_changes = 0;
115
116 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
117 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
118 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
119 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
120
121 /* Disabling a disabled unit must succeed but be a NOP */
122 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0);
123 assert_se(n_changes == 0);
124 install_changes_free(changes, n_changes);
125 changes = NULL; n_changes = 0;
126
127 /* Let's enable this indirectly via a symlink */
128 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("d.service"), &changes, &n_changes) >= 0);
129 assert_se(n_changes == 1);
130 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
131 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/a.service");
132 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
133 ASSERT_STREQ(changes[0].path, p);
134 install_changes_free(changes, n_changes);
135 changes = NULL; n_changes = 0;
136
137 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
138 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
139 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
140 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
141
142 /* Let's try to reenable */
143
144 assert_se(unit_file_reenable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("b.service"), &changes, &n_changes) >= 0);
145 assert_se(n_changes == 2);
146 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
147 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service");
148 ASSERT_STREQ(changes[0].path, p);
149 assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
150 ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/a.service");
151 ASSERT_STREQ(changes[1].path, p);
152 install_changes_free(changes, n_changes);
153 changes = NULL; n_changes = 0;
154
155 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
156 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
157 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
158 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
159
160 /* Test masking with relative symlinks */
161
162 p = strjoina(root, "/usr/lib/systemd/system/e.service");
163 assert_se(symlink("../../../../../../dev/null", p) >= 0);
164
165 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "e.service", NULL) >= 0);
166 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED);
167
168 assert_se(unlink(p) == 0);
169 assert_se(symlink("/usr/../dev/null", p) >= 0);
170
171 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "e.service", NULL) >= 0);
172 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED);
173
174 assert_se(unlink(p) == 0);
175
176 /* Test enabling with unknown dependency target */
177
178 p = strjoina(root, "/usr/lib/systemd/system/f.service");
179 assert_se(write_string_file(p,
180 "[Install]\n"
181 "WantedBy=x.target\n", WRITE_STRING_FILE_CREATE) >= 0);
182
183 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "f.service", NULL) >= 0);
184 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
185
186 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("f.service"), &changes, &n_changes) == 1);
187 assert_se(n_changes == 2);
188 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
189 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/f.service");
190 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/x.target.wants/f.service");
191 ASSERT_STREQ(changes[0].path, p);
192 assert_se(changes[1].type == INSTALL_CHANGE_DESTINATION_NOT_PRESENT);
193 p = strjoina(root, "/usr/lib/systemd/system/f.service");
194 ASSERT_STREQ(changes[1].source, p);
195 ASSERT_STREQ(changes[1].path, "x.target");
196 install_changes_free(changes, n_changes);
197 changes = NULL; n_changes = 0;
198
199 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
200 }
201
202 TEST(linked_units) {
203 const char *p, *q, *s;
204 UnitFileState state;
205 InstallChange *changes = NULL;
206 size_t n_changes = 0, i;
207
208 /*
209 * We'll test three cases here:
210 *
211 * a) a unit file in /opt, that we use "systemctl link" and
212 * "systemctl enable" on to make it available to the system
213 *
214 * b) a unit file in /opt, that is statically linked into
215 * /usr/lib/systemd/system, that "enable" should work on
216 * correctly.
217 *
218 * c) a unit file in /opt, that is linked into
219 * /etc/systemd/system, and where "enable" should result in
220 * -ELOOP, since using information from /etc to generate
221 * information in /etc should not be allowed.
222 */
223
224 p = strjoina(root, "/opt/linked.service");
225 assert_se(write_string_file(p,
226 "[Install]\n"
227 "Alias=linked-alias.service\n"
228 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
229
230 p = strjoina(root, "/opt/linked2.service");
231 assert_se(write_string_file(p,
232 "[Install]\n"
233 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
234
235 p = strjoina(root, "/opt/linked3.service");
236 assert_se(write_string_file(p,
237 "[Install]\n"
238 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
239
240 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
241 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked2.service", NULL) == -ENOENT);
242 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked3.service", NULL) == -ENOENT);
243
244 p = strjoina(root, "/usr/lib/systemd/system/linked2.service");
245 assert_se(symlink("/opt/linked2.service", p) >= 0);
246
247 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked3.service");
248 assert_se(symlink("/opt/linked3.service", p) >= 0);
249
250 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked.service", &state) == -ENOENT);
251 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
252 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED);
253
254 /* First, let's link the unit into the search path */
255 assert_se(unit_file_link(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0);
256 assert_se(n_changes == 1);
257 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
258 ASSERT_STREQ(changes[0].source, "/opt/linked.service");
259 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
260 ASSERT_STREQ(changes[0].path, p);
261 install_changes_free(changes, n_changes);
262 changes = NULL; n_changes = 0;
263
264 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED);
265
266 /* Let's unlink it from the search path again */
267 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
268 assert_se(n_changes == 1);
269 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
270 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
271 ASSERT_STREQ(changes[0].path, p);
272 install_changes_free(changes, n_changes);
273 changes = NULL; n_changes = 0;
274
275 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
276
277 /* Now, let's not just link it, but also enable it */
278 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0);
279 assert_se(n_changes == 3);
280 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service");
281 q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
282 s = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked-alias.service");
283 for (i = 0 ; i < n_changes; i++) {
284 assert_se(changes[i].type == INSTALL_CHANGE_SYMLINK);
285
286 if (s && streq(changes[i].path, s))
287 /* The alias symlink should point within the search path. */
288 ASSERT_STREQ(changes[i].source, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
289 else
290 ASSERT_STREQ(changes[i].source, "/opt/linked.service");
291
292 if (p && streq(changes[i].path, p))
293 p = NULL;
294 else if (q && streq(changes[i].path, q))
295 q = NULL;
296 else if (s && streq(changes[i].path, s))
297 s = NULL;
298 else
299 assert_not_reached();
300 }
301 assert_se(!p && !q && !s);
302 install_changes_free(changes, n_changes);
303 changes = NULL; n_changes = 0;
304
305 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
306 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked-alias.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
307
308 /* And let's unlink it again */
309 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0);
310 assert_se(n_changes == 3);
311 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service");
312 q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service");
313 s = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked-alias.service");
314 for (i = 0; i < n_changes; i++) {
315 assert_se(changes[i].type == INSTALL_CHANGE_UNLINK);
316
317 if (p && streq(changes[i].path, p))
318 p = NULL;
319 else if (q && streq(changes[i].path, q))
320 q = NULL;
321 else if (s && streq(changes[i].path, s))
322 s = NULL;
323 else
324 assert_not_reached();
325 }
326 assert_se(!p && !q && !s);
327 install_changes_free(changes, n_changes);
328 changes = NULL; n_changes = 0;
329
330 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT);
331
332 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked2.service"), &changes, &n_changes) >= 0);
333 assert_se(n_changes == 2);
334 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked2.service");
335 q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked2.service");
336 for (i = 0 ; i < n_changes; i++) {
337 assert_se(changes[i].type == INSTALL_CHANGE_SYMLINK);
338 ASSERT_STREQ(changes[i].source, "/opt/linked2.service");
339
340 if (p && streq(changes[i].path, p))
341 p = NULL;
342 else if (q && streq(changes[i].path, q))
343 q = NULL;
344 else
345 assert_not_reached();
346 }
347 assert_se(!p && !q);
348 install_changes_free(changes, n_changes);
349 changes = NULL; n_changes = 0;
350
351 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked3.service"), &changes, &n_changes) >= 0);
352 assert_se(n_changes == 1);
353 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
354 assert_se(startswith(changes[0].path, root));
355 assert_se(endswith(changes[0].path, "linked3.service"));
356 ASSERT_STREQ(changes[0].source, "/opt/linked3.service");
357 install_changes_free(changes, n_changes);
358 changes = NULL; n_changes = 0;
359 }
360
361 TEST(default) {
362 _cleanup_free_ char *def = NULL;
363 InstallChange *changes = NULL;
364 size_t n_changes = 0;
365 const char *p;
366
367 p = strjoina(root, "/usr/lib/systemd/system/test-default-real.target");
368 assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
369
370 p = strjoina(root, "/usr/lib/systemd/system/test-default.target");
371 assert_se(symlink("test-default-real.target", p) >= 0);
372
373 assert_se(unit_file_get_default(RUNTIME_SCOPE_SYSTEM, root, &def) == -ENOENT);
374
375 assert_se(unit_file_set_default(RUNTIME_SCOPE_SYSTEM, 0, root, "idontexist.target", &changes, &n_changes) == -ENOENT);
376 assert_se(n_changes == 1);
377 assert_se(changes[0].type == -ENOENT);
378 ASSERT_STREQ(changes[0].path, "idontexist.target");
379 install_changes_free(changes, n_changes);
380 changes = NULL; n_changes = 0;
381
382 assert_se(unit_file_get_default(RUNTIME_SCOPE_SYSTEM, root, &def) == -ENOENT);
383
384 assert_se(unit_file_set_default(RUNTIME_SCOPE_SYSTEM, 0, root, "test-default.target", &changes, &n_changes) >= 0);
385 assert_se(n_changes == 1);
386 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
387 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/test-default-real.target");
388 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR "/" SPECIAL_DEFAULT_TARGET);
389 ASSERT_STREQ(changes[0].path, p);
390 install_changes_free(changes, n_changes);
391 changes = NULL; n_changes = 0;
392
393 assert_se(unit_file_get_default(RUNTIME_SCOPE_SYSTEM, root, &def) >= 0);
394 ASSERT_STREQ(def, "test-default-real.target");
395 }
396
397 TEST(add_dependency) {
398 InstallChange *changes = NULL;
399 size_t n_changes = 0;
400 const char *p;
401
402 p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-target.target");
403 assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
404
405 p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-target.target");
406 assert_se(symlink("real-add-dependency-test-target.target", p) >= 0);
407
408 p = strjoina(root, "/usr/lib/systemd/system/real-add-dependency-test-service.service");
409 assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
410
411 p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service");
412 assert_se(symlink("real-add-dependency-test-service.service", p) >= 0);
413
414 assert_se(unit_file_add_dependency(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, &changes, &n_changes) >= 0);
415 assert_se(n_changes == 1);
416 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
417 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service");
418 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service");
419 ASSERT_STREQ(changes[0].path, p);
420 install_changes_free(changes, n_changes);
421 changes = NULL; n_changes = 0;
422 }
423
424 TEST(template_enable) {
425 InstallChange *changes = NULL;
426 size_t n_changes = 0;
427 UnitFileState state;
428 const char *p;
429
430 log_info("== %s ==", __func__);
431
432 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@.service", &state) == -ENOENT);
433 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@def.service", &state) == -ENOENT);
434 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@foo.service", &state) == -ENOENT);
435 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT);
436
437 p = strjoina(root, "/usr/lib/systemd/system/template@.service");
438 assert_se(write_string_file(p,
439 "[Install]\n"
440 "DefaultInstance=def\n"
441 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
442
443 p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service");
444 assert_se(symlink("template@.service", p) >= 0);
445
446 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
447 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
448 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
449 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
450 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
451 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
452
453 log_info("== %s with template@.service enabled ==", __func__);
454
455 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
456 assert_se(n_changes == 1);
457 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
458 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
459 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@def.service");
460 ASSERT_STREQ(changes[0].path, p);
461 install_changes_free(changes, n_changes);
462 changes = NULL; n_changes = 0;
463
464 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
465 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
466 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
467 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
468 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
469 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
470
471 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0);
472 assert_se(n_changes == 1);
473 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
474 ASSERT_STREQ(changes[0].path, p);
475 install_changes_free(changes, n_changes);
476 changes = NULL; n_changes = 0;
477
478 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
479 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
480 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
481 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
482 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
483 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
484
485 log_info("== %s with template@foo.service enabled ==", __func__);
486
487 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
488 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
489 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
490 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@foo.service");
491 ASSERT_STREQ(changes[0].path, p);
492 install_changes_free(changes, n_changes);
493 changes = NULL; n_changes = 0;
494
495 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
496 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
497 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
498 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
499 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
500 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
501
502 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0);
503 assert_se(n_changes == 1);
504 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
505 ASSERT_STREQ(changes[0].path, p);
506 install_changes_free(changes, n_changes);
507 changes = NULL; n_changes = 0;
508
509 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
510 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
511 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
512 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
513 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
514 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
515 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
516 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
517
518 log_info("== %s with template-symlink@quux.service enabled ==", __func__);
519
520 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0);
521 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
522 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/template@.service");
523 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@quux.service");
524 ASSERT_STREQ(changes[0].path, p);
525 install_changes_free(changes, n_changes);
526 changes = NULL; n_changes = 0;
527
528 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
529 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
530 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
531 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
532 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
533 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
534 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
535 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
536 }
537
538 TEST(indirect) {
539 InstallChange *changes = NULL;
540 size_t n_changes = 0;
541 UnitFileState state;
542 const char *p;
543
544 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirecta.service", &state) == -ENOENT);
545 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirectb.service", &state) == -ENOENT);
546 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirectc.service", &state) == -ENOENT);
547
548 p = strjoina(root, "/usr/lib/systemd/system/indirecta.service");
549 assert_se(write_string_file(p,
550 "[Install]\n"
551 "Also=indirectb.service\n", WRITE_STRING_FILE_CREATE) >= 0);
552
553 p = strjoina(root, "/usr/lib/systemd/system/indirectb.service");
554 assert_se(write_string_file(p,
555 "[Install]\n"
556 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
557
558 p = strjoina(root, "/usr/lib/systemd/system/indirectc.service");
559 assert_se(symlink("indirecta.service", p) >= 0);
560
561 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
562 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
563 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
564
565 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
566 assert_se(n_changes == 1);
567 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
568 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/indirectb.service");
569 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
570 ASSERT_STREQ(changes[0].path, p);
571 install_changes_free(changes, n_changes);
572 changes = NULL; n_changes = 0;
573
574 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT);
575 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
576 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS);
577
578 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0);
579 assert_se(n_changes == 1);
580 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
581 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service");
582 ASSERT_STREQ(changes[0].path, p);
583 install_changes_free(changes, n_changes);
584 changes = NULL; n_changes = 0;
585 }
586
587 TEST(preset_and_list) {
588 InstallChange *changes = NULL;
589 size_t n_changes = 0, i;
590 const char *p, *q;
591 UnitFileState state;
592 bool got_yes = false, got_no = false;
593 UnitFileList *fl;
594 _cleanup_hashmap_free_ Hashmap *h = NULL;
595
596 CLEANUP_ARRAY(changes, n_changes, install_changes_free);
597
598 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT);
599 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) == -ENOENT);
600 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) == -ENOENT);
601
602 p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
603 assert_se(write_string_file(p,
604 "[Install]\n"
605 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
606
607 p = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
608 assert_se(write_string_file(p,
609 "[Install]\n"
610 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
611
612 p = strjoina(root, "/usr/lib/systemd/system/preset-ignore.service");
613 assert_se(write_string_file(p,
614 "[Install]\n"
615 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
616
617 p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
618 assert_se(write_string_file(p,
619 "enable *-yes.*\n"
620 "ignore *-ignore.*\n"
621 "disable *\n", WRITE_STRING_FILE_CREATE) >= 0);
622
623 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
624 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
625 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
626
627 assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
628 assert_se(n_changes == 1);
629 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
630 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/preset-yes.service");
631 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
632 ASSERT_STREQ(changes[0].path, p);
633 install_changes_free(changes, n_changes);
634 changes = NULL; n_changes = 0;
635
636 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
637 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
638 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
639
640 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0);
641 assert_se(n_changes == 1);
642 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
643 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
644 ASSERT_STREQ(changes[0].path, p);
645 install_changes_free(changes, n_changes);
646 changes = NULL; n_changes = 0;
647
648 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
649 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
650 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
651
652 assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
653 assert_se(n_changes == 0);
654 install_changes_free(changes, n_changes);
655 changes = NULL; n_changes = 0;
656
657 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
658 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
659 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
660
661 assert_se(unit_file_preset_all(RUNTIME_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
662
663 assert_se(n_changes > 0);
664
665 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service");
666
667 for (i = 0; i < n_changes; i++) {
668
669 if (changes[i].type == INSTALL_CHANGE_SYMLINK) {
670 ASSERT_STREQ(changes[i].source, "/usr/lib/systemd/system/preset-yes.service");
671 ASSERT_STREQ(changes[i].path, p);
672 } else
673 assert_se(changes[i].type == INSTALL_CHANGE_UNLINK);
674 }
675
676 install_changes_free(changes, n_changes);
677 changes = NULL; n_changes = 0;
678
679 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
680 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
681 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
682
683 assert_se(h = hashmap_new(&unit_file_list_hash_ops_free));
684 assert_se(unit_file_get_list(RUNTIME_SCOPE_SYSTEM, root, h, NULL, NULL) >= 0);
685
686 p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
687 q = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
688
689 HASHMAP_FOREACH(fl, h) {
690 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, basename(fl->path), &state) >= 0);
691 assert_se(fl->state == state);
692
693 if (streq(fl->path, p)) {
694 got_yes = true;
695 assert_se(fl->state == UNIT_FILE_ENABLED);
696 } else if (streq(fl->path, q)) {
697 got_no = true;
698 assert_se(fl->state == UNIT_FILE_DISABLED);
699 } else
700 assert_se(IN_SET(fl->state, UNIT_FILE_DISABLED, UNIT_FILE_STATIC, UNIT_FILE_INDIRECT, UNIT_FILE_ALIAS));
701 }
702
703 assert_se(got_yes && got_no);
704
705 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), &changes, &n_changes) >= 0);
706 assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-ignore.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
707 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "preset-ignore.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
708 }
709
710 TEST(revert) {
711 const char *p;
712 UnitFileState state;
713 InstallChange *changes = NULL;
714 size_t n_changes = 0;
715
716 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "xx.service", NULL) == -ENOENT);
717 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "yy.service", NULL) == -ENOENT);
718
719 p = strjoina(root, "/usr/lib/systemd/system/xx.service");
720 assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0);
721
722 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "xx.service", NULL) >= 0);
723 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC);
724
725 /* Initially there's nothing to revert */
726 assert_se(unit_file_revert(RUNTIME_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
727 assert_se(n_changes == 0);
728 install_changes_free(changes, n_changes);
729 changes = NULL; n_changes = 0;
730
731 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service");
732 assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0);
733
734 /* Revert the override file */
735 assert_se(unit_file_revert(RUNTIME_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
736 assert_se(n_changes == 1);
737 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
738 ASSERT_STREQ(changes[0].path, p);
739 install_changes_free(changes, n_changes);
740 changes = NULL; n_changes = 0;
741
742 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d/dropin.conf");
743 assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
744
745 /* Revert the dropin file */
746 assert_se(unit_file_revert(RUNTIME_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0);
747 assert_se(n_changes == 2);
748 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
749 ASSERT_STREQ(changes[0].path, p);
750
751 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/xx.service.d");
752 assert_se(changes[1].type == INSTALL_CHANGE_UNLINK);
753 ASSERT_STREQ(changes[1].path, p);
754 install_changes_free(changes, n_changes);
755 changes = NULL; n_changes = 0;
756 }
757
758 TEST(preset_order) {
759 InstallChange *changes = NULL;
760 size_t n_changes = 0;
761 const char *p;
762 UnitFileState state;
763
764 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT);
765 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT);
766
767 p = strjoina(root, "/usr/lib/systemd/system/prefix-1.service");
768 assert_se(write_string_file(p,
769 "[Install]\n"
770 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
771
772 p = strjoina(root, "/usr/lib/systemd/system/prefix-2.service");
773 assert_se(write_string_file(p,
774 "[Install]\n"
775 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
776
777 p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
778 assert_se(write_string_file(p,
779 "enable prefix-1.service\n"
780 "disable prefix-*.service\n"
781 "enable prefix-2.service\n", WRITE_STRING_FILE_CREATE) >= 0);
782
783 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
784 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
785
786 assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
787 assert_se(n_changes == 1);
788 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
789 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/prefix-1.service");
790 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/prefix-1.service");
791 ASSERT_STREQ(changes[0].path, p);
792 install_changes_free(changes, n_changes);
793 changes = NULL; n_changes = 0;
794
795 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
796 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
797
798 assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
799 assert_se(n_changes == 0);
800
801 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
802 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
803 }
804
805 TEST(static_instance) {
806 UnitFileState state;
807 const char *p;
808
809 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT);
810 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT);
811
812 p = strjoina(root, "/usr/lib/systemd/system/static-instance@.service");
813 assert_se(write_string_file(p,
814 "[Install]\n"
815 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
816
817 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
818 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
819
820 p = strjoina(root, "/usr/lib/systemd/system/static-instance@foo.service");
821 assert_se(symlink("static-instance@.service", p) >= 0);
822
823 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
824 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC);
825 }
826
827 TEST(with_dropin) {
828 const char *p;
829 UnitFileState state;
830 InstallChange *changes = NULL;
831 size_t n_changes = 0;
832
833 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) == -ENOENT);
834 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) == -ENOENT);
835 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) == -ENOENT);
836 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) == -ENOENT);
837 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) == -ENOENT);
838
839 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1.service");
840 assert_se(write_string_file(p,
841 "[Install]\n"
842 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
843
844 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1.service.d/dropin.conf");
845 assert_se(write_string_file(p,
846 "[Install]\n"
847 "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
848
849 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
850
851 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
852 assert_se(write_string_file(p,
853 "[Install]\n"
854 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
855
856 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2.service.d/dropin.conf");
857 assert_se(write_string_file(p,
858 "[Install]\n"
859 "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
860
861 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
862
863 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3.service");
864 assert_se(write_string_file(p,
865 "[Install]\n"
866 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
867
868 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-3.service.d/dropin.conf");
869 assert_se(write_string_file(p,
870 "[Install]\n"
871 "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
872
873 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
874
875 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4a.service");
876 assert_se(write_string_file(p,
877 "[Install]\n"
878 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
879
880 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-4a.service.d/dropin.conf");
881 assert_se(write_string_file(p,
882 "[Install]\n"
883 "Also=with-dropin-4b.service\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
884
885 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
886
887 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4b.service");
888 assert_se(write_string_file(p,
889 "[Install]\n"
890 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
891
892 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
893
894 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1.service"), &changes, &n_changes) == 1);
895 assert_se(n_changes == 2);
896 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
897 assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
898 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service");
899 ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service");
900 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1.service");
901 ASSERT_STREQ(changes[0].path, p);
902 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1.service");
903 ASSERT_STREQ(changes[1].path, p);
904 install_changes_free(changes, n_changes);
905 changes = NULL; n_changes = 0;
906
907 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2.service"), &changes, &n_changes) == 1);
908 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
909 assert_se(n_changes == 2);
910 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
911 assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
912 ASSERT_STREQ(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
913 ASSERT_STREQ(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service");
914 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2.service");
915 ASSERT_STREQ(changes[0].path, p);
916 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2.service");
917 ASSERT_STREQ(changes[1].path, p);
918 install_changes_free(changes, n_changes);
919 changes = NULL; n_changes = 0;
920
921 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3.service"), &changes, &n_changes) == 1);
922 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
923 assert_se(n_changes == 2);
924 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
925 assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
926 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service");
927 ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service");
928 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3.service");
929 ASSERT_STREQ(changes[0].path, p);
930 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-3.service");
931 ASSERT_STREQ(changes[1].path, p);
932 install_changes_free(changes, n_changes);
933 changes = NULL; n_changes = 0;
934
935 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 2);
936 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
937 assert_se(n_changes == 2);
938 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
939 assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
940 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service");
941 ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service");
942 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4a.service");
943 ASSERT_STREQ(changes[0].path, p);
944 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4b.service");
945 ASSERT_STREQ(changes[1].path, p);
946 install_changes_free(changes, n_changes);
947 changes = NULL; n_changes = 0;
948
949 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
950 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
951 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
952 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
953 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
954 }
955
956 TEST(with_dropin_template) {
957 const char *p;
958 UnitFileState state;
959 InstallChange *changes = NULL;
960 size_t n_changes = 0;
961
962 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-1@.service", &state) == -ENOENT);
963 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2@.service", &state) == -ENOENT);
964 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3@.service", &state) == -ENOENT);
965
966 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1@.service");
967 assert_se(write_string_file(p,
968 "[Install]\n"
969 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
970
971 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1@.service.d/dropin.conf");
972 assert_se(write_string_file(p,
973 "[Install]\n"
974 "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
975
976 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-1@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
977
978 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2@.service");
979 assert_se(write_string_file(p,
980 "[Install]\n"
981 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
982
983 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2@instance-1.service.d/dropin.conf");
984 assert_se(write_string_file(p,
985 "[Install]\n"
986 "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
987
988 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
989
990 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3@.service");
991 assert_se(write_string_file(p,
992 "[Install]\n"
993 "DefaultInstance=instance-1\n"
994 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
995
996 p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3@.service.d/dropin.conf");
997 assert_se(write_string_file(p,
998 "[Install]\n"
999 "DefaultInstance=instance-2\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0);
1000
1001 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1002
1003 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1@instance-1.service"), &changes, &n_changes) == 1);
1004 assert_se(n_changes == 2);
1005 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
1006 assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
1007 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service");
1008 ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service");
1009 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1@instance-1.service");
1010 ASSERT_STREQ(changes[0].path, p);
1011 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1@instance-1.service");
1012 ASSERT_STREQ(changes[1].path, p);
1013 install_changes_free(changes, n_changes);
1014 changes = NULL; n_changes = 0;
1015
1016 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-1.service"), &changes, &n_changes) == 1);
1017 assert_se(n_changes == 2);
1018 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
1019 assert_se(changes[1].type == INSTALL_CHANGE_SYMLINK);
1020 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service");
1021 ASSERT_STREQ(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service");
1022 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-1.service");
1023 ASSERT_STREQ(changes[0].path, p);
1024 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2@instance-1.service");
1025 ASSERT_STREQ(changes[1].path, p);
1026 install_changes_free(changes, n_changes);
1027 changes = NULL; n_changes = 0;
1028
1029 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1);
1030 assert_se(n_changes == 1);
1031 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
1032 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service");
1033 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-2.service");
1034 ASSERT_STREQ(changes[0].path, p);
1035 install_changes_free(changes, n_changes);
1036 changes = NULL; n_changes = 0;
1037
1038 assert_se(unit_file_enable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1);
1039 assert_se(n_changes == 1);
1040 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
1041 ASSERT_STREQ(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service");
1042 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3@instance-2.service");
1043 ASSERT_STREQ(changes[0].path, p);
1044 install_changes_free(changes, n_changes);
1045 changes = NULL; n_changes = 0;
1046
1047 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-1@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1048 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1049 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-2@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1050 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3@instance-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1051 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1052 }
1053
1054 TEST(preset_multiple_instances) {
1055 InstallChange *changes = NULL;
1056 size_t n_changes = 0;
1057 const char *p;
1058 UnitFileState state;
1059
1060 /* Set up template service files and preset file */
1061 p = strjoina(root, "/usr/lib/systemd/system/foo@.service");
1062 assert_se(write_string_file(p,
1063 "[Install]\n"
1064 "DefaultInstance=def\n"
1065 "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0);
1066
1067 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1068
1069 p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset");
1070 assert_se(write_string_file(p,
1071 "enable foo@.service bar0 bar1 bartest\n"
1072 "enable emptylist@.service\n" /* This line ensures the old functionality for templated unit still works */
1073 "disable *\n" , WRITE_STRING_FILE_CREATE) >= 0);
1074
1075 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1076
1077 /* Preset a single instantiated unit specified in the list */
1078 assert_se(unit_file_preset(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
1079 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1080 assert_se(n_changes == 1);
1081 assert_se(changes[0].type == INSTALL_CHANGE_SYMLINK);
1082 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
1083 ASSERT_STREQ(changes[0].path, p);
1084 install_changes_free(changes, n_changes);
1085 changes = NULL; n_changes = 0;
1086
1087 assert_se(unit_file_disable(RUNTIME_SCOPE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0);
1088 assert_se(n_changes == 1);
1089 assert_se(changes[0].type == INSTALL_CHANGE_UNLINK);
1090 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service");
1091 ASSERT_STREQ(changes[0].path, p);
1092 install_changes_free(changes, n_changes);
1093 changes = NULL; n_changes = 0;
1094
1095 /* Check for preset-all case, only instances on the list should be enabled, not including the default instance */
1096 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1097 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1098 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1099
1100 assert_se(unit_file_preset_all(RUNTIME_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0);
1101 assert_se(n_changes > 0);
1102
1103 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
1104 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1105 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1106 assert_se(unit_file_get_state(RUNTIME_SCOPE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED);
1107
1108 install_changes_free(changes, n_changes);
1109 }
1110
1111 static void verify_one(
1112 const InstallInfo *i,
1113 const char *alias,
1114 int expected,
1115 const char *updated_name) {
1116 int r;
1117 static const InstallInfo *last_info = NULL;
1118 _cleanup_free_ char *alias2 = NULL;
1119
1120 if (i != last_info)
1121 log_info("-- %s --", (last_info = i)->name);
1122
1123 r = unit_file_verify_alias(i, alias, &alias2, NULL, NULL);
1124 log_info_errno(r, "alias %s ← %s: %d/%m (expected %d)%s%s%s",
1125 i->name, alias, r, expected,
1126 alias2 ? " [" : "", strempty(alias2),
1127 alias2 ? "]" : "");
1128 assert_se(r == expected);
1129
1130 /* This is test for "instance propagation". This propagation matters mostly for WantedBy= and
1131 * RequiredBy= settings, and less so for Alias=. The only case where it should happen is when we have
1132 * an Alias=alias@.service an instantiated template template@instance. In that case the instance name
1133 * should be propagated into the alias as alias@instance. */
1134 ASSERT_STREQ(alias2, updated_name);
1135 }
1136
1137 TEST(verify_alias) {
1138 const InstallInfo
1139 plain_service = { .name = (char*) "plain.service" },
1140 bare_template = { .name = (char*) "template1@.service" },
1141 di_template = { .name = (char*) "template2@.service",
1142 .default_instance = (char*) "di" },
1143 inst_template = { .name = (char*) "template3@inst.service" },
1144 di_inst_template = { .name = (char*) "template4@inst.service",
1145 .default_instance = (char*) "di" };
1146
1147 verify_one(&plain_service, "alias.service", 0, NULL);
1148 verify_one(&plain_service, "alias.socket", -EXDEV, NULL);
1149 verify_one(&plain_service, "alias@.service", -EXDEV, NULL);
1150 verify_one(&plain_service, "alias@inst.service", -EXDEV, NULL);
1151
1152 /* Setting WantedBy= and RequiredBy= through Alias= is supported for the sake of backwards
1153 * compatibility. */
1154 verify_one(&plain_service, "foo.target.wants/plain.service", 0, NULL);
1155 verify_one(&plain_service, "foo.target.wants/plain.socket", -EXDEV, NULL);
1156 verify_one(&plain_service, "foo.target.wants/plain@.service", -EXDEV, NULL);
1157 verify_one(&plain_service, "foo.target.wants/service", -EXDEV, NULL);
1158 verify_one(&plain_service, "foo.target.requires/plain.service", 0, NULL);
1159 verify_one(&plain_service, "foo.target.requires/plain.socket", -EXDEV, NULL);
1160 verify_one(&plain_service, "foo.target.requires/plain@.service", -EXDEV, NULL);
1161 verify_one(&plain_service, "foo.target.requires/service", -EXDEV, NULL);
1162 verify_one(&plain_service, "asdf.requires/plain.service", -EXDEV, NULL); /* invalid unit name component */
1163 /* The newly-added UpheldBy= (.upholds/) and other suffixes should be rejected */
1164 verify_one(&plain_service, "foo.target.upholds/plain.service", -EXDEV, NULL);
1165 verify_one(&plain_service, "foo.target.upholds/plain.socket", -EXDEV, NULL);
1166 verify_one(&plain_service, "foo.target.upholds/plain@.service", -EXDEV, NULL);
1167 verify_one(&plain_service, "foo.target.upholds/service", -EXDEV, NULL);
1168 verify_one(&plain_service, "foo.service/plain.service", -EXDEV, NULL); /* missing dir suffix */
1169 verify_one(&plain_service, "foo.target.conf/plain.service", -EXDEV, NULL);
1170
1171 verify_one(&bare_template, "alias.service", -EXDEV, NULL);
1172 verify_one(&bare_template, "alias.socket", -EXDEV, NULL);
1173 verify_one(&bare_template, "alias@.socket", -EXDEV, NULL);
1174 verify_one(&bare_template, "alias@inst.socket", -EXDEV, NULL);
1175 /* A general alias alias@.service → template1@.service. */
1176 verify_one(&bare_template, "alias@.service", 0, NULL);
1177 /* Only a specific instance is aliased, see the discussion in https://github.com/systemd/systemd/pull/13119. */
1178 verify_one(&bare_template, "alias@inst.service", 0, NULL);
1179 verify_one(&bare_template, "foo.target.wants/plain.service", -EXDEV, NULL);
1180 verify_one(&bare_template, "foo.target.wants/plain.socket", -EXDEV, NULL);
1181 verify_one(&bare_template, "foo.target.wants/plain@.service", -EXDEV, NULL);
1182 /* Name mismatch: we cannot allow this, because plain@foo.service would be pulled in by foo.target,
1183 * but would not be resolveable on its own, since systemd doesn't know how to load the fragment. */
1184 verify_one(&bare_template, "foo.target.wants/plain@foo.service", -EXDEV, NULL);
1185 verify_one(&bare_template, "foo.target.wants/template1@foo.service", 0, NULL);
1186 verify_one(&bare_template, "foo.target.wants/service", -EXDEV, NULL);
1187 verify_one(&bare_template, "foo.target.requires/plain.service", -EXDEV, NULL);
1188 verify_one(&bare_template, "foo.target.requires/plain.socket", -EXDEV, NULL);
1189 verify_one(&bare_template, "foo.target.requires/plain@.service", -EXDEV, NULL); /* instance missing */
1190 verify_one(&bare_template, "foo.target.requires/template1@inst.service", 0, NULL);
1191 verify_one(&bare_template, "foo.target.requires/service", -EXDEV, NULL);
1192 verify_one(&bare_template, "foo.target.conf/plain.service", -EXDEV, NULL);
1193 verify_one(&bare_template, "FOO@.target.requires/plain@.service", -EXDEV, NULL); /* template name mismatch */
1194 verify_one(&bare_template, "FOO@inst.target.requires/plain@.service", -EXDEV, NULL);
1195 verify_one(&bare_template, "FOO@inst.target.requires/plain@inst.service", -EXDEV, NULL);
1196 verify_one(&bare_template, "FOO@.target.requires/template1@.service", 0, NULL); /* instance propagated */
1197 verify_one(&bare_template, "FOO@inst.target.requires/template1@.service", -EXDEV, NULL); /* instance missing */
1198 verify_one(&bare_template, "FOO@inst.target.requires/template1@inst.service", 0, NULL); /* instance provided */
1199
1200 verify_one(&di_template, "alias.service", -EXDEV, NULL);
1201 verify_one(&di_template, "alias.socket", -EXDEV, NULL);
1202 verify_one(&di_template, "alias@.socket", -EXDEV, NULL);
1203 verify_one(&di_template, "alias@inst.socket", -EXDEV, NULL);
1204 verify_one(&di_template, "alias@inst.service", 0, NULL);
1205 verify_one(&di_template, "alias@.service", 0, NULL);
1206 verify_one(&di_template, "alias@di.service", 0, NULL);
1207 verify_one(&di_template, "foo.target.wants/plain.service", -EXDEV, NULL);
1208 verify_one(&di_template, "foo.target.wants/plain.socket", -EXDEV, NULL);
1209 verify_one(&di_template, "foo.target.wants/plain@.service", -EXDEV, NULL);
1210 verify_one(&di_template, "foo.target.wants/plain@di.service", -EXDEV, NULL);
1211 verify_one(&di_template, "foo.target.wants/template2@di.service", 0, NULL);
1212 verify_one(&di_template, "foo.target.wants/service", -EXDEV, NULL);
1213 verify_one(&di_template, "foo.target.requires/plain.service", -EXDEV, NULL);
1214 verify_one(&di_template, "foo.target.requires/plain.socket", -EXDEV, NULL);
1215 verify_one(&di_template, "foo.target.requires/plain@.service", -EXDEV, NULL);
1216 verify_one(&di_template, "foo.target.requires/plain@di.service", -EXDEV, NULL);
1217 verify_one(&di_template, "foo.target.requires/plain@foo.service", -EXDEV, NULL);
1218 verify_one(&di_template, "foo.target.requires/template2@.service", -EXDEV, NULL); /* instance missing */
1219 verify_one(&di_template, "foo.target.requires/template2@di.service", 0, NULL);
1220 verify_one(&di_template, "foo.target.requires/service", -EXDEV, NULL);
1221 verify_one(&di_template, "foo.target.conf/plain.service", -EXDEV, NULL);
1222
1223 verify_one(&inst_template, "alias.service", -EXDEV, NULL);
1224 verify_one(&inst_template, "alias.socket", -EXDEV, NULL);
1225 verify_one(&inst_template, "alias@.socket", -EXDEV, NULL);
1226 verify_one(&inst_template, "alias@inst.socket", -EXDEV, NULL);
1227 verify_one(&inst_template, "alias@inst.service", 0, NULL);
1228 verify_one(&inst_template, "alias@.service", 0, "alias@inst.service");
1229 verify_one(&inst_template, "alias@di.service", -EXDEV, NULL);
1230 verify_one(&inst_template, "bar.target.wants/plain.service", -EXDEV, NULL);
1231 verify_one(&inst_template, "bar.target.wants/plain.socket", -EXDEV, NULL);
1232 verify_one(&inst_template, "bar.target.wants/plain@.service", -EXDEV, NULL);
1233 verify_one(&inst_template, "bar.target.wants/plain@di.service", -EXDEV, NULL);
1234 verify_one(&inst_template, "bar.target.wants/plain@inst.service", -EXDEV, NULL);
1235 verify_one(&inst_template, "bar.target.wants/template3@foo.service", -EXDEV, NULL);
1236 verify_one(&inst_template, "bar.target.wants/template3@inst.service", 0, NULL);
1237 verify_one(&inst_template, "bar.target.wants/service", -EXDEV, NULL);
1238 verify_one(&inst_template, "bar.target.requires/plain.service", -EXDEV, NULL);
1239 verify_one(&inst_template, "bar.target.requires/plain.socket", -EXDEV, NULL);
1240 verify_one(&inst_template, "bar.target.requires/plain@.service", -EXDEV, NULL);
1241 verify_one(&inst_template, "bar.target.requires/plain@di.service", -EXDEV, NULL);
1242 verify_one(&inst_template, "bar.target.requires/plain@inst.service", -EXDEV, NULL);
1243 verify_one(&inst_template, "bar.target.requires/template3@foo.service", -EXDEV, NULL);
1244 verify_one(&inst_template, "bar.target.requires/template3@inst.service", 0, NULL);
1245 verify_one(&inst_template, "bar.target.requires/service", -EXDEV, NULL);
1246 verify_one(&inst_template, "bar.target.conf/plain.service", -EXDEV, NULL);
1247 verify_one(&inst_template, "BAR@.target.requires/plain@.service", -EXDEV, NULL); /* template name mismatch */
1248 verify_one(&inst_template, "BAR@inst.target.requires/plain@.service", -EXDEV, NULL);
1249 verify_one(&inst_template, "BAR@inst.target.requires/plain@inst.service", -EXDEV, NULL);
1250 verify_one(&inst_template, "BAR@.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */
1251 verify_one(&inst_template, "BAR@inst.target.requires/template3@.service", -EXDEV, NULL); /* instance missing */
1252 verify_one(&inst_template, "BAR@inst.target.requires/template3@inst.service", 0, NULL); /* instance provided */
1253 verify_one(&inst_template, "BAR@inst.target.requires/template3@ins2.service", -EXDEV, NULL); /* instance mismatch */
1254
1255 /* explicit alias overrides DefaultInstance */
1256 verify_one(&di_inst_template, "alias.service", -EXDEV, NULL);
1257 verify_one(&di_inst_template, "alias.socket", -EXDEV, NULL);
1258 verify_one(&di_inst_template, "alias@.socket", -EXDEV, NULL);
1259 verify_one(&di_inst_template, "alias@inst.socket", -EXDEV, NULL);
1260 verify_one(&di_inst_template, "alias@inst.service", 0, NULL);
1261 verify_one(&di_inst_template, "alias@.service", 0, "alias@inst.service");
1262 verify_one(&di_inst_template, "alias@di.service", -EXDEV, NULL);
1263 verify_one(&di_inst_template, "goo.target.wants/plain.service", -EXDEV, NULL);
1264 verify_one(&di_inst_template, "goo.target.wants/plain.socket", -EXDEV, NULL);
1265 verify_one(&di_inst_template, "goo.target.wants/plain@.service", -EXDEV, NULL);
1266 verify_one(&di_inst_template, "goo.target.wants/plain@di.service", -EXDEV, NULL);
1267 verify_one(&di_inst_template, "goo.target.wants/template4@foo.service", -EXDEV, NULL);
1268 verify_one(&di_inst_template, "goo.target.wants/template4@inst.service", 0, NULL);
1269 verify_one(&di_inst_template, "goo.target.wants/template4@di.service", -EXDEV, NULL);
1270 verify_one(&di_inst_template, "goo.target.wants/service", -EXDEV, NULL);
1271 verify_one(&di_inst_template, "goo.target.requires/plain.service", -EXDEV, NULL);
1272 verify_one(&di_inst_template, "goo.target.requires/plain.socket", -EXDEV, NULL);
1273 verify_one(&di_inst_template, "goo.target.requires/plain@.service", -EXDEV, NULL);
1274 verify_one(&di_inst_template, "goo.target.requires/plain@di.service", -EXDEV, NULL);
1275 verify_one(&di_inst_template, "goo.target.requires/plain@inst.service", -EXDEV, NULL);
1276 verify_one(&di_inst_template, "goo.target.requires/template4@foo.service", -EXDEV, NULL);
1277 verify_one(&di_inst_template, "goo.target.requires/template4@inst.service", 0, NULL);
1278 verify_one(&di_inst_template, "goo.target.requires/service", -EXDEV, NULL);
1279 verify_one(&di_inst_template, "goo.target.conf/plain.service", -EXDEV, NULL);
1280 }
1281
1282 static int intro(void) {
1283 const char *p;
1284
1285 assert_se(mkdtemp_malloc("/tmp/rootXXXXXX", &root) >= 0);
1286
1287 p = strjoina(root, "/usr/lib/systemd/system/");
1288 assert_se(mkdir_p(p, 0755) >= 0);
1289
1290 p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/");
1291 assert_se(mkdir_p(p, 0755) >= 0);
1292
1293 p = strjoina(root, "/run/systemd/system/");
1294 assert_se(mkdir_p(p, 0755) >= 0);
1295
1296 p = strjoina(root, "/opt/");
1297 assert_se(mkdir_p(p, 0755) >= 0);
1298
1299 p = strjoina(root, "/usr/lib/systemd/system-preset/");
1300 assert_se(mkdir_p(p, 0755) >= 0);
1301
1302 p = strjoina(root, "/usr/lib/systemd/system/multi-user.target");
1303 assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
1304
1305 p = strjoina(root, "/usr/lib/systemd/system/graphical.target");
1306 assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0);
1307
1308 return EXIT_SUCCESS;
1309 }
1310
1311
1312 DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro);