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