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