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