1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
7 Copyright 2013 Zbigniew Jędrzejewski-Szmek
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.
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.
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/>.
27 #include <sys/capability.h>
30 #include "alloc-util.h"
34 #include "hostname-util.h"
35 #include "install-printf.h"
37 #include "load-fragment.h"
39 #include "specifier.h"
40 #include "string-util.h"
42 #include "test-helper.h"
45 static int test_unit_file_get_set(void) {
51 h
= hashmap_new(&string_hash_ops
);
54 r
= unit_file_get_list(UNIT_FILE_SYSTEM
, NULL
, h
);
56 if (r
== -EPERM
|| r
== -EACCES
) {
57 printf("Skipping test: unit_file_get_list: %s", strerror(-r
));
58 return EXIT_TEST_SKIP
;
61 log_full(r
== 0 ? LOG_INFO
: LOG_ERR
,
62 "unit_file_get_list: %s", strerror(-r
));
66 HASHMAP_FOREACH(p
, h
, i
)
67 printf("%s = %s\n", p
->path
, unit_file_state_to_string(p
->state
));
69 unit_file_list_free(h
);
74 static void check_execcommand(ExecCommand
*c
,
83 log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
84 path
, argv0
?: path
, argv1
, argv2
);
85 n
= strv_length(c
->argv
);
86 log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
87 c
->path
, c
->argv
[0], n
> 0 ? c
->argv
[1] : NULL
, n
> 1 ? c
->argv
[2] : NULL
);
88 assert_se(streq(c
->path
, path
));
89 assert_se(streq(c
->argv
[0], argv0
?: path
));
91 assert_se(streq_ptr(c
->argv
[1], argv1
));
93 assert_se(streq_ptr(c
->argv
[2], argv2
));
94 assert_se(c
->ignore
== ignore
);
97 static void test_config_parse_exec(void) {
98 /* int config_parse_exec(
100 const char *filename,
103 unsigned section_line,
111 ExecCommand
*c
= NULL
, *c1
;
114 log_info("/* basic test */");
115 r
= config_parse_exec(NULL
, "fake", 1, "section", 1,
116 "LValue", 0, "/RValue r1",
119 check_execcommand(c
, "/RValue", "/RValue", "r1", NULL
, false);
121 r
= config_parse_exec(NULL
, "fake", 2, "section", 1,
122 "LValue", 0, "/RValue///slashes r1///",
125 log_info("/* test slashes */");
127 c1
= c
->command_next
;
128 check_execcommand(c1
, "/RValue/slashes", "/RValue///slashes", "r1///", NULL
, false);
130 log_info("/* trailing slash */");
131 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
132 "LValue", 0, "/RValue/ argv0 r1",
135 assert_se(c1
->command_next
== NULL
);
137 log_info("/* honour_argv0 */");
138 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
139 "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
142 c1
= c1
->command_next
;
143 check_execcommand(c1
, "/RValue/slashes2", "///argv0", "r1", NULL
, false);
145 log_info("/* honour_argv0, no args */");
146 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
147 "LValue", 0, "@/RValue",
150 assert_se(c1
->command_next
== NULL
);
152 log_info("/* no command, whitespace only, reset */");
153 r
= config_parse_exec(NULL
, "fake", 3, "section", 1,
157 assert_se(c
== NULL
);
159 log_info("/* ignore && honour_argv0 */");
160 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
161 "LValue", 0, "-@/RValue///slashes3 argv0a r1",
165 check_execcommand(c1
, "/RValue/slashes3", "argv0a", "r1", NULL
, true);
167 log_info("/* ignore && honour_argv0 */");
168 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
169 "LValue", 0, "@-/RValue///slashes4 argv0b r1",
172 c1
= c1
->command_next
;
173 check_execcommand(c1
, "/RValue/slashes4", "argv0b", "r1", NULL
, true);
175 log_info("/* ignore && ignore */");
176 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
177 "LValue", 0, "--/RValue argv0 r1",
180 assert_se(c1
->command_next
== NULL
);
182 log_info("/* ignore && ignore (2) */");
183 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
184 "LValue", 0, "-@-/RValue argv0 r1",
187 assert_se(c1
->command_next
== NULL
);
189 log_info("/* semicolon */");
190 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
192 "-@/RValue argv0 r1 ; "
196 c1
= c1
->command_next
;
197 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
199 c1
= c1
->command_next
;
200 check_execcommand(c1
, "/goo/goo", NULL
, "boo", NULL
, false);
202 log_info("/* two semicolons in a row */");
203 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
205 "-@/RValue argv0 r1 ; ; "
209 c1
= c1
->command_next
;
210 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
212 /* second command fails because the executable name is ";" */
213 assert_se(c1
->command_next
== NULL
);
215 log_info("/* trailing semicolon */");
216 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
218 "-@/RValue argv0 r1 ; ",
221 c1
= c1
->command_next
;
222 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
224 assert_se(c1
->command_next
== NULL
);
226 log_info("/* trailing semicolon, no whitespace */");
227 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
229 "-@/RValue argv0 r1 ;",
232 c1
= c1
->command_next
;
233 check_execcommand(c1
, "/RValue", "argv0", "r1", NULL
, true);
235 assert_se(c1
->command_next
== NULL
);
237 log_info("/* trailing semicolon in single quotes */");
238 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
240 "-@/RValue argv0 r1 ';'",
243 c1
= c1
->command_next
;
244 check_execcommand(c1
, "/RValue", "argv0", "r1", ";", true);
246 log_info("/* escaped semicolon */");
247 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
252 c1
= c1
->command_next
;
253 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
255 log_info("/* escaped semicolon with following arg */");
256 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
261 c1
= c1
->command_next
;
262 check_execcommand(c1
,
263 "/sbin/find", NULL
, ";", "/x", false);
265 log_info("/* escaped semicolon as part of an expression */");
266 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
271 c1
= c1
->command_next
;
272 check_execcommand(c1
,
273 "/sbin/find", NULL
, "\\;x", NULL
, false);
275 log_info("/* encoded semicolon */");
276 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
281 c1
= c1
->command_next
;
282 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
284 log_info("/* quoted semicolon */");
285 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
290 c1
= c1
->command_next
;
291 check_execcommand(c1
, "/bin/find", NULL
, ";", NULL
, false);
293 log_info("/* quoted semicolon with following arg */");
294 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
296 "/sbin/find \";\" /x",
299 c1
= c1
->command_next
;
300 check_execcommand(c1
,
301 "/sbin/find", NULL
, ";", "/x", false);
303 log_info("/* spaces in the filename */");
304 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
306 "\"/PATH WITH SPACES/daemon\" -1 -2",
309 c1
= c1
->command_next
;
310 check_execcommand(c1
,
311 "/PATH WITH SPACES/daemon", NULL
, "-1", "-2", false);
313 log_info("/* spaces in the filename, no args */");
314 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
316 "\"/PATH WITH SPACES/daemon -1 -2\"",
319 c1
= c1
->command_next
;
320 check_execcommand(c1
,
321 "/PATH WITH SPACES/daemon -1 -2", NULL
, NULL
, NULL
, false);
323 log_info("/* spaces in the filename, everything quoted */");
324 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
326 "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
329 c1
= c1
->command_next
;
330 check_execcommand(c1
,
331 "/PATH WITH SPACES/daemon", NULL
, "-1", "-2", false);
333 log_info("/* escaped spaces in the filename */");
334 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
336 "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
339 c1
= c1
->command_next
;
340 check_execcommand(c1
,
341 "/PATH WITH SPACES/daemon", NULL
, "-1 -2", NULL
, false);
343 log_info("/* escaped spaces in the filename (2) */");
344 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
346 "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
349 c1
= c1
->command_next
;
350 check_execcommand(c1
,
351 "/PATH WITH SPACES/daemon", NULL
, "-1 -2", NULL
, false);
353 for (ccc
= "abfnrtv\\\'\"x"; *ccc
; ccc
++) {
354 /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
355 char path
[] = "/path\\X";
356 path
[sizeof(path
) - 2] = *ccc
;
358 log_info("/* invalid character: \\%c */", *ccc
);
359 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
363 assert_se(c1
->command_next
== NULL
);
366 log_info("/* valid character: \\s */");
367 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
368 "LValue", 0, "/path\\s",
371 c1
= c1
->command_next
;
372 check_execcommand(c1
, "/path ", NULL
, NULL
, NULL
, false);
374 log_info("/* quoted backslashes */");
375 r
= config_parse_exec(NULL
, "fake", 5, "section", 1,
377 "/bin/grep '\\w+\\K'",
380 c1
= c1
->command_next
;
381 check_execcommand(c1
, "/bin/grep", NULL
, "\\w+\\K", NULL
, false);
384 log_info("/* trailing backslash: \\ */");
385 /* backslash is invalid */
386 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
387 "LValue", 0, "/path\\",
390 assert_se(c1
->command_next
== NULL
);
392 log_info("/* missing ending ' */");
393 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
394 "LValue", 0, "/path 'foo",
397 assert_se(c1
->command_next
== NULL
);
399 log_info("/* missing ending ' with trailing backslash */");
400 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
401 "LValue", 0, "/path 'foo\\",
404 assert_se(c1
->command_next
== NULL
);
406 log_info("/* invalid space between modifiers */");
407 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
408 "LValue", 0, "- /path",
411 assert_se(c1
->command_next
== NULL
);
413 log_info("/* only modifiers, no path */");
414 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
418 assert_se(c1
->command_next
== NULL
);
420 log_info("/* empty argument, reset */");
421 r
= config_parse_exec(NULL
, "fake", 4, "section", 1,
425 assert_se(c
== NULL
);
427 exec_command_free_list(c
);
445 "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
446 "#--nouser-config \\\n" \
452 "HWMON_MODULES=\"coretemp f71882fg\"\n" \
454 "# For compatibility reasons\n" \
456 "MODULE_0=coretemp\n" \
463 static void test_load_env_file_1(void) {
464 _cleanup_strv_free_
char **data
= NULL
;
467 char name
[] = "/tmp/test-load-env-file.XXXXXX";
468 _cleanup_close_
int fd
;
470 fd
= mkostemp_safe(name
, O_RDWR
|O_CLOEXEC
);
472 assert_se(write(fd
, env_file_1
, sizeof(env_file_1
)) == sizeof(env_file_1
));
474 r
= load_env_file(NULL
, name
, NULL
, &data
);
476 assert_se(streq(data
[0], "a=a"));
477 assert_se(streq(data
[1], "b=bc"));
478 assert_se(streq(data
[2], "d=def"));
479 assert_se(streq(data
[3], "g=g "));
480 assert_se(streq(data
[4], "h=h"));
481 assert_se(streq(data
[5], "i=i"));
482 assert_se(data
[6] == NULL
);
486 static void test_load_env_file_2(void) {
487 _cleanup_strv_free_
char **data
= NULL
;
490 char name
[] = "/tmp/test-load-env-file.XXXXXX";
491 _cleanup_close_
int fd
;
493 fd
= mkostemp_safe(name
, O_RDWR
|O_CLOEXEC
);
495 assert_se(write(fd
, env_file_2
, sizeof(env_file_2
)) == sizeof(env_file_2
));
497 r
= load_env_file(NULL
, name
, NULL
, &data
);
499 assert_se(streq(data
[0], "a=a"));
500 assert_se(data
[1] == NULL
);
504 static void test_load_env_file_3(void) {
505 _cleanup_strv_free_
char **data
= NULL
;
508 char name
[] = "/tmp/test-load-env-file.XXXXXX";
509 _cleanup_close_
int fd
;
511 fd
= mkostemp_safe(name
, O_RDWR
|O_CLOEXEC
);
513 assert_se(write(fd
, env_file_3
, sizeof(env_file_3
)) == sizeof(env_file_3
));
515 r
= load_env_file(NULL
, name
, NULL
, &data
);
517 assert_se(data
== NULL
);
521 static void test_load_env_file_4(void) {
522 _cleanup_strv_free_
char **data
= NULL
;
523 char name
[] = "/tmp/test-load-env-file.XXXXXX";
524 _cleanup_close_
int fd
;
527 fd
= mkostemp_safe(name
, O_RDWR
|O_CLOEXEC
);
529 assert_se(write(fd
, env_file_4
, sizeof(env_file_4
)) == sizeof(env_file_4
));
531 r
= load_env_file(NULL
, name
, NULL
, &data
);
533 assert_se(streq(data
[0], "HWMON_MODULES=coretemp f71882fg"));
534 assert_se(streq(data
[1], "MODULE_0=coretemp"));
535 assert_se(streq(data
[2], "MODULE_1=f71882fg"));
536 assert_se(data
[3] == NULL
);
540 static void test_load_env_file_5(void) {
541 _cleanup_strv_free_
char **data
= NULL
;
544 char name
[] = "/tmp/test-load-env-file.XXXXXX";
545 _cleanup_close_
int fd
;
547 fd
= mkostemp_safe(name
, O_RDWR
|O_CLOEXEC
);
549 assert_se(write(fd
, env_file_5
, sizeof(env_file_5
)) == sizeof(env_file_5
));
551 r
= load_env_file(NULL
, name
, NULL
, &data
);
553 assert_se(streq(data
[0], "a="));
554 assert_se(streq(data
[1], "b="));
555 assert_se(data
[2] == NULL
);
559 static void test_install_printf(void) {
560 char name
[] = "name.service",
561 path
[] = "/run/systemd/system/name.service",
562 user
[] = "xxxx-no-such-user";
563 UnitFileInstallInfo i
= {name
, path
, user
};
564 UnitFileInstallInfo i2
= {name
, path
, NULL
};
565 char name3
[] = "name@inst.service",
566 path3
[] = "/run/systemd/system/name.service";
567 UnitFileInstallInfo i3
= {name3
, path3
, user
};
568 UnitFileInstallInfo i4
= {name3
, path3
, NULL
};
570 _cleanup_free_
char *mid
, *bid
, *host
;
572 assert_se(specifier_machine_id('m', NULL
, NULL
, &mid
) >= 0 && mid
);
573 assert_se(specifier_boot_id('b', NULL
, NULL
, &bid
) >= 0 && bid
);
574 assert_se((host
= gethostname_malloc()));
576 #define expect(src, pattern, result) \
578 _cleanup_free_ char *t = NULL; \
579 _cleanup_free_ char \
580 *d1 = strdup(i.name), \
581 *d2 = strdup(i.path), \
582 *d3 = strdup(i.user); \
583 assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
584 memzero(i.name, strlen(i.name)); \
585 memzero(i.path, strlen(i.path)); \
586 memzero(i.user, strlen(i.user)); \
587 assert_se(d1 && d2 && d3); \
590 assert_se(streq(t, result)); \
591 } else assert_se(t == NULL); \
592 strcpy(i.name, d1); \
593 strcpy(i.path, d2); \
594 strcpy(i.user, d3); \
597 assert_se(setenv("USER", "root", 1) == 0);
599 expect(i
, "%n", "name.service");
600 expect(i
, "%N", "name");
601 expect(i
, "%p", "name");
603 expect(i
, "%u", "xxxx-no-such-user");
605 DISABLE_WARNING_NONNULL
;
606 expect(i
, "%U", NULL
);
609 expect(i
, "%m", mid
);
610 expect(i
, "%b", bid
);
611 expect(i
, "%H", host
);
613 expect(i2
, "%u", "root");
614 expect(i2
, "%U", "0");
616 expect(i3
, "%n", "name@inst.service");
617 expect(i3
, "%N", "name@inst");
618 expect(i3
, "%p", "name");
619 expect(i3
, "%u", "xxxx-no-such-user");
621 DISABLE_WARNING_NONNULL
;
622 expect(i3
, "%U", NULL
);
625 expect(i3
, "%m", mid
);
626 expect(i3
, "%b", bid
);
627 expect(i3
, "%H", host
);
629 expect(i4
, "%u", "root");
630 expect(i4
, "%U", "0");
633 static uint64_t make_cap(int cap
) {
634 return ((uint64_t) 1ULL << (uint64_t) cap
);
637 static void test_config_parse_bounding_set(void) {
638 /* int config_parse_bounding_set(
640 const char *filename,
643 unsigned section_line,
650 uint64_t capability_bounding_set_drop
= 0;
652 r
= config_parse_bounding_set(NULL
, "fake", 1, "section", 1,
653 "CapabilityBoundingSet", 0, "CAP_NET_RAW",
654 &capability_bounding_set_drop
, NULL
);
656 assert_se(capability_bounding_set_drop
== ~make_cap(CAP_NET_RAW
));
658 r
= config_parse_bounding_set(NULL
, "fake", 1, "section", 1,
659 "CapabilityBoundingSet", 0, "CAP_NET_ADMIN",
660 &capability_bounding_set_drop
, NULL
);
662 assert_se(capability_bounding_set_drop
== ~(make_cap(CAP_NET_RAW
) | make_cap(CAP_NET_ADMIN
)));
664 r
= config_parse_bounding_set(NULL
, "fake", 1, "section", 1,
665 "CapabilityBoundingSet", 0, "",
666 &capability_bounding_set_drop
, NULL
);
668 assert_se(capability_bounding_set_drop
== ~((uint64_t) 0ULL));
670 r
= config_parse_bounding_set(NULL
, "fake", 1, "section", 1,
671 "CapabilityBoundingSet", 0, "~",
672 &capability_bounding_set_drop
, NULL
);
674 assert_se(capability_bounding_set_drop
== (uint64_t) 0ULL);
676 capability_bounding_set_drop
= 0;
677 r
= config_parse_bounding_set(NULL
, "fake", 1, "section", 1,
678 "CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage",
679 &capability_bounding_set_drop
, NULL
);
681 assert_se(capability_bounding_set_drop
== ~(make_cap(CAP_NET_RAW
) | make_cap(CAP_NET_ADMIN
)));
684 int main(int argc
, char *argv
[]) {
687 log_parse_environment();
690 r
= test_unit_file_get_set();
691 test_config_parse_exec();
692 test_config_parse_bounding_set();
693 test_load_env_file_1();
694 test_load_env_file_2();
695 test_load_env_file_3();
696 test_load_env_file_4();
697 test_load_env_file_5();
698 TEST_REQ_RUNNING_SYSTEMD(test_install_printf());