]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-unit-file.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / test / test-unit-file.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7 Copyright 2013 Zbigniew Jędrzejewski-Szmek
8
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <fcntl.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include "fileio.h"
30 #include "hashmap.h"
31 #include "hostname-util.h"
32 #include "install-printf.h"
33 #include "install.h"
34 #include "load-fragment.h"
35 #include "macro.h"
36 #include "specifier.h"
37 #include "string-util.h"
38 #include "strv.h"
39 #include "test-helper.h"
40 #include "util.h"
41
42 static int test_unit_file_get_set(void) {
43 int r;
44 Hashmap *h;
45 Iterator i;
46 UnitFileList *p;
47
48 h = hashmap_new(&string_hash_ops);
49 assert_se(h);
50
51 r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
52
53 if (r == -EPERM || r == -EACCES) {
54 printf("Skipping test: unit_file_get_list: %s", strerror(-r));
55 return EXIT_TEST_SKIP;
56 }
57
58 log_full(r == 0 ? LOG_INFO : LOG_ERR,
59 "unit_file_get_list: %s", strerror(-r));
60 if (r < 0)
61 return EXIT_FAILURE;
62
63 HASHMAP_FOREACH(p, h, i)
64 printf("%s = %s\n", p->path, unit_file_state_to_string(p->state));
65
66 unit_file_list_free(h);
67
68 return 0;
69 }
70
71 static void check_execcommand(ExecCommand *c,
72 const char* path,
73 const char* argv0,
74 const char* argv1,
75 const char* argv2,
76 bool ignore) {
77 size_t n;
78
79 assert_se(c);
80 log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
81 path, argv0 ?: path, argv1, argv2);
82 n = strv_length(c->argv);
83 log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
84 c->path, c->argv[0], n > 0 ? c->argv[1] : NULL, n > 1 ? c->argv[2] : NULL);
85 assert_se(streq(c->path, path));
86 assert_se(streq(c->argv[0], argv0 ?: path));
87 if (n > 0)
88 assert_se(streq_ptr(c->argv[1], argv1));
89 if (n > 1)
90 assert_se(streq_ptr(c->argv[2], argv2));
91 assert_se(c->ignore == ignore);
92 }
93
94 static void test_config_parse_exec(void) {
95 /* int config_parse_exec(
96 const char *unit,
97 const char *filename,
98 unsigned line,
99 const char *section,
100 unsigned section_line,
101 const char *lvalue,
102 int ltype,
103 const char *rvalue,
104 void *data,
105 void *userdata) */
106 int r;
107
108 ExecCommand *c = NULL, *c1;
109 const char *ccc;
110
111 log_info("/* basic test */");
112 r = config_parse_exec(NULL, "fake", 1, "section", 1,
113 "LValue", 0, "/RValue r1",
114 &c, NULL);
115 assert_se(r >= 0);
116 check_execcommand(c, "/RValue", "/RValue", "r1", NULL, false);
117
118 r = config_parse_exec(NULL, "fake", 2, "section", 1,
119 "LValue", 0, "/RValue///slashes r1///",
120 &c, NULL);
121
122 log_info("/* test slashes */");
123 assert_se(r >= 0);
124 c1 = c->command_next;
125 check_execcommand(c1, "/RValue/slashes", "/RValue///slashes", "r1///", NULL, false);
126
127 log_info("/* trailing slash */");
128 r = config_parse_exec(NULL, "fake", 4, "section", 1,
129 "LValue", 0, "/RValue/ argv0 r1",
130 &c, NULL);
131 assert_se(r == 0);
132 assert_se(c1->command_next == NULL);
133
134 log_info("/* honour_argv0 */");
135 r = config_parse_exec(NULL, "fake", 3, "section", 1,
136 "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
137 &c, NULL);
138 assert_se(r >= 0);
139 c1 = c1->command_next;
140 check_execcommand(c1, "/RValue/slashes2", "///argv0", "r1", NULL, false);
141
142 log_info("/* honour_argv0, no args */");
143 r = config_parse_exec(NULL, "fake", 3, "section", 1,
144 "LValue", 0, "@/RValue",
145 &c, NULL);
146 assert_se(r == 0);
147 assert_se(c1->command_next == NULL);
148
149 log_info("/* no command, whitespace only, reset */");
150 r = config_parse_exec(NULL, "fake", 3, "section", 1,
151 "LValue", 0, " ",
152 &c, NULL);
153 assert_se(r == 0);
154 assert_se(c == NULL);
155
156 log_info("/* ignore && honour_argv0 */");
157 r = config_parse_exec(NULL, "fake", 4, "section", 1,
158 "LValue", 0, "-@/RValue///slashes3 argv0a r1",
159 &c, NULL);
160 assert_se(r >= 0);
161 c1 = c;
162 check_execcommand(c1, "/RValue/slashes3", "argv0a", "r1", NULL, true);
163
164 log_info("/* ignore && honour_argv0 */");
165 r = config_parse_exec(NULL, "fake", 4, "section", 1,
166 "LValue", 0, "@-/RValue///slashes4 argv0b r1",
167 &c, NULL);
168 assert_se(r >= 0);
169 c1 = c1->command_next;
170 check_execcommand(c1, "/RValue/slashes4", "argv0b", "r1", NULL, true);
171
172 log_info("/* ignore && ignore */");
173 r = config_parse_exec(NULL, "fake", 4, "section", 1,
174 "LValue", 0, "--/RValue argv0 r1",
175 &c, NULL);
176 assert_se(r == 0);
177 assert_se(c1->command_next == NULL);
178
179 log_info("/* ignore && ignore (2) */");
180 r = config_parse_exec(NULL, "fake", 4, "section", 1,
181 "LValue", 0, "-@-/RValue argv0 r1",
182 &c, NULL);
183 assert_se(r == 0);
184 assert_se(c1->command_next == NULL);
185
186 log_info("/* semicolon */");
187 r = config_parse_exec(NULL, "fake", 5, "section", 1,
188 "LValue", 0,
189 "-@/RValue argv0 r1 ; "
190 "/goo/goo boo",
191 &c, NULL);
192 assert_se(r >= 0);
193 c1 = c1->command_next;
194 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
195
196 c1 = c1->command_next;
197 check_execcommand(c1, "/goo/goo", NULL, "boo", NULL, false);
198
199 log_info("/* two semicolons in a row */");
200 r = config_parse_exec(NULL, "fake", 5, "section", 1,
201 "LValue", 0,
202 "-@/RValue argv0 r1 ; ; "
203 "/goo/goo boo",
204 &c, NULL);
205 assert_se(r >= 0);
206 c1 = c1->command_next;
207 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
208
209 /* second command fails because the executable name is ";" */
210 assert_se(c1->command_next == NULL);
211
212 log_info("/* trailing semicolon */");
213 r = config_parse_exec(NULL, "fake", 5, "section", 1,
214 "LValue", 0,
215 "-@/RValue argv0 r1 ; ",
216 &c, NULL);
217 assert_se(r >= 0);
218 c1 = c1->command_next;
219 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
220
221 assert_se(c1->command_next == NULL);
222
223 log_info("/* trailing semicolon, no whitespace */");
224 r = config_parse_exec(NULL, "fake", 5, "section", 1,
225 "LValue", 0,
226 "-@/RValue argv0 r1 ;",
227 &c, NULL);
228 assert_se(r >= 0);
229 c1 = c1->command_next;
230 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
231
232 assert_se(c1->command_next == NULL);
233
234 log_info("/* trailing semicolon in single quotes */");
235 r = config_parse_exec(NULL, "fake", 5, "section", 1,
236 "LValue", 0,
237 "-@/RValue argv0 r1 ';'",
238 &c, NULL);
239 assert_se(r >= 0);
240 c1 = c1->command_next;
241 check_execcommand(c1, "/RValue", "argv0", "r1", ";", true);
242
243 log_info("/* escaped semicolon */");
244 r = config_parse_exec(NULL, "fake", 5, "section", 1,
245 "LValue", 0,
246 "/bin/find \\;",
247 &c, NULL);
248 assert_se(r >= 0);
249 c1 = c1->command_next;
250 check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
251
252 log_info("/* escaped semicolon with following arg */");
253 r = config_parse_exec(NULL, "fake", 5, "section", 1,
254 "LValue", 0,
255 "/sbin/find \\; /x",
256 &c, NULL);
257 assert_se(r >= 0);
258 c1 = c1->command_next;
259 check_execcommand(c1,
260 "/sbin/find", NULL, ";", "/x", false);
261
262 log_info("/* escaped semicolon as part of an expression */");
263 r = config_parse_exec(NULL, "fake", 5, "section", 1,
264 "LValue", 0,
265 "/sbin/find \\;x",
266 &c, NULL);
267 assert_se(r >= 0);
268 c1 = c1->command_next;
269 check_execcommand(c1,
270 "/sbin/find", NULL, "\\;x", NULL, false);
271
272 log_info("/* encoded semicolon */");
273 r = config_parse_exec(NULL, "fake", 5, "section", 1,
274 "LValue", 0,
275 "/bin/find \\073",
276 &c, NULL);
277 assert_se(r >= 0);
278 c1 = c1->command_next;
279 check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
280
281 log_info("/* quoted semicolon */");
282 r = config_parse_exec(NULL, "fake", 5, "section", 1,
283 "LValue", 0,
284 "/bin/find \";\"",
285 &c, NULL);
286 assert_se(r >= 0);
287 c1 = c1->command_next;
288 check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
289
290 log_info("/* quoted semicolon with following arg */");
291 r = config_parse_exec(NULL, "fake", 5, "section", 1,
292 "LValue", 0,
293 "/sbin/find \";\" /x",
294 &c, NULL);
295 assert_se(r >= 0);
296 c1 = c1->command_next;
297 check_execcommand(c1,
298 "/sbin/find", NULL, ";", "/x", false);
299
300 log_info("/* spaces in the filename */");
301 r = config_parse_exec(NULL, "fake", 5, "section", 1,
302 "LValue", 0,
303 "\"/PATH WITH SPACES/daemon\" -1 -2",
304 &c, NULL);
305 assert_se(r >= 0);
306 c1 = c1->command_next;
307 check_execcommand(c1,
308 "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
309
310 log_info("/* spaces in the filename, no args */");
311 r = config_parse_exec(NULL, "fake", 5, "section", 1,
312 "LValue", 0,
313 "\"/PATH WITH SPACES/daemon -1 -2\"",
314 &c, NULL);
315 assert_se(r >= 0);
316 c1 = c1->command_next;
317 check_execcommand(c1,
318 "/PATH WITH SPACES/daemon -1 -2", NULL, NULL, NULL, false);
319
320 log_info("/* spaces in the filename, everything quoted */");
321 r = config_parse_exec(NULL, "fake", 5, "section", 1,
322 "LValue", 0,
323 "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
324 &c, NULL);
325 assert_se(r >= 0);
326 c1 = c1->command_next;
327 check_execcommand(c1,
328 "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
329
330 log_info("/* escaped spaces in the filename */");
331 r = config_parse_exec(NULL, "fake", 5, "section", 1,
332 "LValue", 0,
333 "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
334 &c, NULL);
335 assert_se(r >= 0);
336 c1 = c1->command_next;
337 check_execcommand(c1,
338 "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
339
340 log_info("/* escaped spaces in the filename (2) */");
341 r = config_parse_exec(NULL, "fake", 5, "section", 1,
342 "LValue", 0,
343 "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
344 &c, NULL);
345 assert_se(r >= 0);
346 c1 = c1->command_next;
347 check_execcommand(c1,
348 "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
349
350 for (ccc = "abfnrtv\\\'\"x"; *ccc; ccc++) {
351 /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
352 char path[] = "/path\\X";
353 path[sizeof(path) - 2] = *ccc;
354
355 log_info("/* invalid character: \\%c */", *ccc);
356 r = config_parse_exec(NULL, "fake", 4, "section", 1,
357 "LValue", 0, path,
358 &c, NULL);
359 assert_se(r == 0);
360 assert_se(c1->command_next == NULL);
361 }
362
363 log_info("/* valid character: \\s */");
364 r = config_parse_exec(NULL, "fake", 4, "section", 1,
365 "LValue", 0, "/path\\s",
366 &c, NULL);
367 assert_se(r >= 0);
368 c1 = c1->command_next;
369 check_execcommand(c1, "/path ", NULL, NULL, NULL, false);
370
371 log_info("/* quoted backslashes */");
372 r = config_parse_exec(NULL, "fake", 5, "section", 1,
373 "LValue", 0,
374 "/bin/grep '\\w+\\K'",
375 &c, NULL);
376 assert_se(r >= 0);
377 c1 = c1->command_next;
378 check_execcommand(c1, "/bin/grep", NULL, "\\w+\\K", NULL, false);
379
380
381 log_info("/* trailing backslash: \\ */");
382 /* backslash is invalid */
383 r = config_parse_exec(NULL, "fake", 4, "section", 1,
384 "LValue", 0, "/path\\",
385 &c, NULL);
386 assert_se(r == 0);
387 assert_se(c1->command_next == NULL);
388
389 log_info("/* missing ending ' */");
390 r = config_parse_exec(NULL, "fake", 4, "section", 1,
391 "LValue", 0, "/path 'foo",
392 &c, NULL);
393 assert_se(r == 0);
394 assert_se(c1->command_next == NULL);
395
396 log_info("/* missing ending ' with trailing backslash */");
397 r = config_parse_exec(NULL, "fake", 4, "section", 1,
398 "LValue", 0, "/path 'foo\\",
399 &c, NULL);
400 assert_se(r == 0);
401 assert_se(c1->command_next == NULL);
402
403 log_info("/* invalid space between modifiers */");
404 r = config_parse_exec(NULL, "fake", 4, "section", 1,
405 "LValue", 0, "- /path",
406 &c, NULL);
407 assert_se(r == 0);
408 assert_se(c1->command_next == NULL);
409
410 log_info("/* only modifiers, no path */");
411 r = config_parse_exec(NULL, "fake", 4, "section", 1,
412 "LValue", 0, "-",
413 &c, NULL);
414 assert_se(r == 0);
415 assert_se(c1->command_next == NULL);
416
417 log_info("/* empty argument, reset */");
418 r = config_parse_exec(NULL, "fake", 4, "section", 1,
419 "LValue", 0, "",
420 &c, NULL);
421 assert_se(r == 0);
422 assert_se(c == NULL);
423
424 exec_command_free_list(c);
425 }
426
427 #define env_file_1 \
428 "a=a\n" \
429 "b=b\\\n" \
430 "c\n" \
431 "d=d\\\n" \
432 "e\\\n" \
433 "f\n" \
434 "g=g\\ \n" \
435 "h=h\n" \
436 "i=i\\"
437
438 #define env_file_2 \
439 "a=a\\\n"
440
441 #define env_file_3 \
442 "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
443 "#--nouser-config \\\n" \
444 "normal=line"
445
446 #define env_file_4 \
447 "# Generated\n" \
448 "\n" \
449 "HWMON_MODULES=\"coretemp f71882fg\"\n" \
450 "\n" \
451 "# For compatibility reasons\n" \
452 "\n" \
453 "MODULE_0=coretemp\n" \
454 "MODULE_1=f71882fg"
455
456 #define env_file_5 \
457 "a=\n" \
458 "b="
459
460 static void test_load_env_file_1(void) {
461 _cleanup_strv_free_ char **data = NULL;
462 int r;
463
464 char name[] = "/tmp/test-load-env-file.XXXXXX";
465 _cleanup_close_ int fd;
466
467 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
468 assert_se(fd >= 0);
469 assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
470
471 r = load_env_file(NULL, name, NULL, &data);
472 assert_se(r == 0);
473 assert_se(streq(data[0], "a=a"));
474 assert_se(streq(data[1], "b=bc"));
475 assert_se(streq(data[2], "d=def"));
476 assert_se(streq(data[3], "g=g "));
477 assert_se(streq(data[4], "h=h"));
478 assert_se(streq(data[5], "i=i"));
479 assert_se(data[6] == NULL);
480 unlink(name);
481 }
482
483 static void test_load_env_file_2(void) {
484 _cleanup_strv_free_ char **data = NULL;
485 int r;
486
487 char name[] = "/tmp/test-load-env-file.XXXXXX";
488 _cleanup_close_ int fd;
489
490 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
491 assert_se(fd >= 0);
492 assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
493
494 r = load_env_file(NULL, name, NULL, &data);
495 assert_se(r == 0);
496 assert_se(streq(data[0], "a=a"));
497 assert_se(data[1] == NULL);
498 unlink(name);
499 }
500
501 static void test_load_env_file_3(void) {
502 _cleanup_strv_free_ char **data = NULL;
503 int r;
504
505 char name[] = "/tmp/test-load-env-file.XXXXXX";
506 _cleanup_close_ int fd;
507
508 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
509 assert_se(fd >= 0);
510 assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
511
512 r = load_env_file(NULL, name, NULL, &data);
513 assert_se(r == 0);
514 assert_se(data == NULL);
515 unlink(name);
516 }
517
518 static void test_load_env_file_4(void) {
519 _cleanup_strv_free_ char **data = NULL;
520 char name[] = "/tmp/test-load-env-file.XXXXXX";
521 _cleanup_close_ int fd;
522 int r;
523
524 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
525 assert_se(fd >= 0);
526 assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
527
528 r = load_env_file(NULL, name, NULL, &data);
529 assert_se(r == 0);
530 assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
531 assert_se(streq(data[1], "MODULE_0=coretemp"));
532 assert_se(streq(data[2], "MODULE_1=f71882fg"));
533 assert_se(data[3] == NULL);
534 unlink(name);
535 }
536
537 static void test_load_env_file_5(void) {
538 _cleanup_strv_free_ char **data = NULL;
539 int r;
540
541 char name[] = "/tmp/test-load-env-file.XXXXXX";
542 _cleanup_close_ int fd;
543
544 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
545 assert_se(fd >= 0);
546 assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5));
547
548 r = load_env_file(NULL, name, NULL, &data);
549 assert_se(r == 0);
550 assert_se(streq(data[0], "a="));
551 assert_se(streq(data[1], "b="));
552 assert_se(data[2] == NULL);
553 unlink(name);
554 }
555
556 static void test_install_printf(void) {
557 char name[] = "name.service",
558 path[] = "/run/systemd/system/name.service",
559 user[] = "xxxx-no-such-user";
560 UnitFileInstallInfo i = {name, path, user};
561 UnitFileInstallInfo i2 = {name, path, NULL};
562 char name3[] = "name@inst.service",
563 path3[] = "/run/systemd/system/name.service";
564 UnitFileInstallInfo i3 = {name3, path3, user};
565 UnitFileInstallInfo i4 = {name3, path3, NULL};
566
567 _cleanup_free_ char *mid, *bid, *host;
568
569 assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
570 assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
571 assert_se((host = gethostname_malloc()));
572
573 #define expect(src, pattern, result) \
574 do { \
575 _cleanup_free_ char *t = NULL; \
576 _cleanup_free_ char \
577 *d1 = strdup(i.name), \
578 *d2 = strdup(i.path), \
579 *d3 = strdup(i.user); \
580 assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
581 memzero(i.name, strlen(i.name)); \
582 memzero(i.path, strlen(i.path)); \
583 memzero(i.user, strlen(i.user)); \
584 assert_se(d1 && d2 && d3); \
585 if (result) { \
586 printf("%s\n", t); \
587 assert_se(streq(t, result)); \
588 } else assert_se(t == NULL); \
589 strcpy(i.name, d1); \
590 strcpy(i.path, d2); \
591 strcpy(i.user, d3); \
592 } while(false)
593
594 assert_se(setenv("USER", "root", 1) == 0);
595
596 expect(i, "%n", "name.service");
597 expect(i, "%N", "name");
598 expect(i, "%p", "name");
599 expect(i, "%i", "");
600 expect(i, "%u", "xxxx-no-such-user");
601
602 DISABLE_WARNING_NONNULL;
603 expect(i, "%U", NULL);
604 REENABLE_WARNING;
605
606 expect(i, "%m", mid);
607 expect(i, "%b", bid);
608 expect(i, "%H", host);
609
610 expect(i2, "%u", "root");
611 expect(i2, "%U", "0");
612
613 expect(i3, "%n", "name@inst.service");
614 expect(i3, "%N", "name@inst");
615 expect(i3, "%p", "name");
616 expect(i3, "%u", "xxxx-no-such-user");
617
618 DISABLE_WARNING_NONNULL;
619 expect(i3, "%U", NULL);
620 REENABLE_WARNING;
621
622 expect(i3, "%m", mid);
623 expect(i3, "%b", bid);
624 expect(i3, "%H", host);
625
626 expect(i4, "%u", "root");
627 expect(i4, "%U", "0");
628 }
629
630 int main(int argc, char *argv[]) {
631 int r;
632
633 log_parse_environment();
634 log_open();
635
636 r = test_unit_file_get_set();
637 test_config_parse_exec();
638 test_load_env_file_1();
639 test_load_env_file_2();
640 test_load_env_file_3();
641 test_load_env_file_4();
642 test_load_env_file_5();
643 TEST_REQ_RUNNING_SYSTEMD(test_install_printf());
644
645 return r;
646 }