]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-unit-file.c
core/load-fragment: avoid allocating 0 bytes when given an invalid command
[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 <assert.h>
24 #include <stdio.h>
25 #include <stddef.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29
30 #include "install.h"
31 #include "install-printf.h"
32 #include "specifier.h"
33 #include "util.h"
34 #include "macro.h"
35 #include "hashmap.h"
36 #include "load-fragment.h"
37 #include "strv.h"
38 #include "fileio.h"
39 #include "test-helper.h"
40
41 static int test_unit_file_get_set(void) {
42 int r;
43 Hashmap *h;
44 Iterator i;
45 UnitFileList *p;
46
47 h = hashmap_new(&string_hash_ops);
48 assert_se(h);
49
50 r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
51
52 if (r == -EPERM || r == -EACCES) {
53 printf("Skipping test: unit_file_get_list: %s", strerror(-r));
54 return EXIT_TEST_SKIP;
55 }
56
57 log_full(r == 0 ? LOG_INFO : LOG_ERR,
58 "unit_file_get_list: %s", strerror(-r));
59 if (r < 0)
60 return EXIT_FAILURE;
61
62 HASHMAP_FOREACH(p, h, i)
63 printf("%s = %s\n", p->path, unit_file_state_to_string(p->state));
64
65 unit_file_list_free(h);
66
67 return 0;
68 }
69
70 static void check_execcommand(ExecCommand *c,
71 const char* path,
72 const char* argv0,
73 const char* argv1,
74 const char* argv2,
75 bool ignore) {
76 size_t n;
77
78 assert_se(c);
79 log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
80 path, argv0 ?: path, argv1, argv2);
81 n = strv_length(c->argv);
82 log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
83 c->path, c->argv[0], n > 0 ? c->argv[1] : NULL, n > 1 ? c->argv[2] : NULL);
84 assert_se(streq(c->path, path));
85 assert_se(streq(c->argv[0], argv0 ?: path));
86 if (n > 0)
87 assert_se(streq_ptr(c->argv[1], argv1));
88 if (n > 1)
89 assert_se(streq_ptr(c->argv[2], argv2));
90 assert_se(c->ignore == ignore);
91 }
92
93 static void test_config_parse_exec(void) {
94 /* int config_parse_exec(
95 const char *filename,
96 unsigned line,
97 const char *section,
98 unsigned section_line,
99 const char *lvalue,
100 int ltype,
101 const char *rvalue,
102 void *data,
103 void *userdata) */
104 int r;
105
106 ExecCommand *c = NULL, *c1;
107 const char *ccc;
108
109 log_info("/* basic test */");
110 r = config_parse_exec(NULL, "fake", 1, "section", 1,
111 "LValue", 0, "/RValue r1",
112 &c, NULL);
113 assert_se(r >= 0);
114 check_execcommand(c, "/RValue", "/RValue", "r1", NULL, false);
115
116 r = config_parse_exec(NULL, "fake", 2, "section", 1,
117 "LValue", 0, "/RValue///slashes r1///",
118 &c, NULL);
119
120 log_info("/* test slashes */");
121 assert_se(r >= 0);
122 c1 = c->command_next;
123 check_execcommand(c1, "/RValue/slashes", "/RValue///slashes", "r1///", NULL, false);
124
125 log_info("/* trailing slash */");
126 r = config_parse_exec(NULL, "fake", 4, "section", 1,
127 "LValue", 0, "/RValue/ argv0 r1",
128 &c, NULL);
129 assert_se(r == 0);
130 assert_se(c1->command_next == NULL);
131
132 log_info("/* honour_argv0 */");
133 r = config_parse_exec(NULL, "fake", 3, "section", 1,
134 "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
135 &c, NULL);
136 assert_se(r >= 0);
137 c1 = c1->command_next;
138 check_execcommand(c1, "/RValue/slashes2", "///argv0", "r1", NULL, false);
139
140 log_info("/* honour_argv0, no args */");
141 r = config_parse_exec(NULL, "fake", 3, "section", 1,
142 "LValue", 0, "@/RValue",
143 &c, NULL);
144 assert_se(r == 0);
145 assert_se(c1->command_next == NULL);
146
147 log_info("/* no command, check for bad memory access */");
148 r = config_parse_exec(NULL, "fake", 3, "section", 1,
149 "LValue", 0, " ",
150 &c, NULL);
151 assert_se(r == 0);
152 assert_se(c1->command_next == NULL);
153
154 log_info("/* ignore && honour_argv0 */");
155 r = config_parse_exec(NULL, "fake", 4, "section", 1,
156 "LValue", 0, "-@/RValue///slashes3 argv0a r1",
157 &c, NULL);
158 assert_se(r >= 0);
159 c1 = c1->command_next;
160 check_execcommand(c1, "/RValue/slashes3", "argv0a", "r1", NULL, true);
161
162 log_info("/* ignore && honour_argv0 */");
163 r = config_parse_exec(NULL, "fake", 4, "section", 1,
164 "LValue", 0, "@-/RValue///slashes4 argv0b r1",
165 &c, NULL);
166 assert_se(r >= 0);
167 c1 = c1->command_next;
168 check_execcommand(c1, "/RValue/slashes4", "argv0b", "r1", NULL, true);
169
170 log_info("/* ignore && ignore */");
171 r = config_parse_exec(NULL, "fake", 4, "section", 1,
172 "LValue", 0, "--/RValue argv0 r1",
173 &c, NULL);
174 assert_se(r == 0);
175 assert_se(c1->command_next == NULL);
176
177 log_info("/* ignore && ignore (2) */");
178 r = config_parse_exec(NULL, "fake", 4, "section", 1,
179 "LValue", 0, "-@-/RValue argv0 r1",
180 &c, NULL);
181 assert_se(r == 0);
182 assert_se(c1->command_next == NULL);
183
184 log_info("/* semicolon */");
185 r = config_parse_exec(NULL, "fake", 5, "section", 1,
186 "LValue", 0,
187 "-@/RValue argv0 r1 ; "
188 "/goo/goo boo",
189 &c, NULL);
190 assert_se(r >= 0);
191 c1 = c1->command_next;
192 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
193
194 c1 = c1->command_next;
195 check_execcommand(c1, "/goo/goo", NULL, "boo", NULL, false);
196
197 log_info("/* trailing semicolon */");
198 r = config_parse_exec(NULL, "fake", 5, "section", 1,
199 "LValue", 0,
200 "-@/RValue argv0 r1 ; ",
201 &c, NULL);
202 assert_se(r >= 0);
203 c1 = c1->command_next;
204 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
205
206 assert_se(c1->command_next == NULL);
207
208 log_info("/* escaped semicolon */");
209 r = config_parse_exec(NULL, "fake", 5, "section", 1,
210 "LValue", 0,
211 "/bin/find \\;",
212 &c, NULL);
213 assert_se(r >= 0);
214 c1 = c1->command_next;
215 check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
216
217 log_info("/* escaped semicolon with following arg */");
218 r = config_parse_exec(NULL, "fake", 5, "section", 1,
219 "LValue", 0,
220 "/sbin/find \\; x",
221 &c, NULL);
222 assert_se(r >= 0);
223 c1 = c1->command_next;
224 check_execcommand(c1,
225 "/sbin/find", NULL, ";", "x", false);
226
227 log_info("/* spaces in the filename */");
228 r = config_parse_exec(NULL, "fake", 5, "section", 1,
229 "LValue", 0,
230 "\"/PATH WITH SPACES/daemon\" -1 -2",
231 &c, NULL);
232 assert_se(r >= 0);
233 c1 = c1->command_next;
234 check_execcommand(c1,
235 "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
236
237 log_info("/* spaces in the filename, no args */");
238 r = config_parse_exec(NULL, "fake", 5, "section", 1,
239 "LValue", 0,
240 "\"/PATH WITH SPACES/daemon -1 -2\"",
241 &c, NULL);
242 assert_se(r >= 0);
243 c1 = c1->command_next;
244 check_execcommand(c1,
245 "/PATH WITH SPACES/daemon -1 -2", NULL, NULL, NULL, false);
246
247 log_info("/* spaces in the filename, everything quoted */");
248 r = config_parse_exec(NULL, "fake", 5, "section", 1,
249 "LValue", 0,
250 "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
251 &c, NULL);
252 assert_se(r >= 0);
253 c1 = c1->command_next;
254 check_execcommand(c1,
255 "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
256
257 log_info("/* escaped spaces in the filename */");
258 r = config_parse_exec(NULL, "fake", 5, "section", 1,
259 "LValue", 0,
260 "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
261 &c, NULL);
262 assert_se(r >= 0);
263 c1 = c1->command_next;
264 check_execcommand(c1,
265 "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
266
267 log_info("/* escaped spaces in the filename (2) */");
268 r = config_parse_exec(NULL, "fake", 5, "section", 1,
269 "LValue", 0,
270 "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
271 &c, NULL);
272 assert_se(r >= 0);
273 c1 = c1->command_next;
274 check_execcommand(c1,
275 "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
276
277 for (ccc = "abfnrtv\\\'\"x"; *ccc; ccc++) {
278 /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
279 char path[] = "/path\\X";
280 path[sizeof(path) - 2] = *ccc;
281
282 log_info("/* invalid character: \\%c */", *ccc);
283 r = config_parse_exec(NULL, "fake", 4, "section", 1,
284 "LValue", 0, path,
285 &c, NULL);
286 assert_se(r == 0);
287 assert_se(c1->command_next == NULL);
288 }
289
290 log_info("/* valid character: \\s */");
291 r = config_parse_exec(NULL, "fake", 4, "section", 1,
292 "LValue", 0, "/path\\s",
293 &c, NULL);
294 assert_se(r >= 0);
295 c1 = c1->command_next;
296 check_execcommand(c1, "/path ", NULL, NULL, NULL, false);
297
298 log_info("/* trailing backslash: \\ */");
299 /* backslash is invalid */
300 r = config_parse_exec(NULL, "fake", 4, "section", 1,
301 "LValue", 0, "/path\\",
302 &c, NULL);
303 assert_se(r == 0);
304 assert_se(c1->command_next == NULL);
305
306 exec_command_free_list(c);
307 }
308
309 #define env_file_1 \
310 "a=a\n" \
311 "b=b\\\n" \
312 "c\n" \
313 "d=d\\\n" \
314 "e\\\n" \
315 "f\n" \
316 "g=g\\ \n" \
317 "h=h\n" \
318 "i=i\\"
319
320 #define env_file_2 \
321 "a=a\\\n"
322
323 #define env_file_3 \
324 "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
325 "#--nouser-config \\\n" \
326 "normal=line"
327
328 #define env_file_4 \
329 "# Generated\n" \
330 "\n" \
331 "HWMON_MODULES=\"coretemp f71882fg\"\n" \
332 "\n" \
333 "# For compatibility reasons\n" \
334 "\n" \
335 "MODULE_0=coretemp\n" \
336 "MODULE_1=f71882fg"
337
338 #define env_file_5 \
339 "a=\n" \
340 "b="
341
342 static void test_load_env_file_1(void) {
343 _cleanup_strv_free_ char **data = NULL;
344 int r;
345
346 char name[] = "/tmp/test-load-env-file.XXXXXX";
347 _cleanup_close_ int fd;
348
349 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
350 assert_se(fd >= 0);
351 assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
352
353 r = load_env_file(NULL, name, NULL, &data);
354 assert_se(r == 0);
355 assert_se(streq(data[0], "a=a"));
356 assert_se(streq(data[1], "b=bc"));
357 assert_se(streq(data[2], "d=def"));
358 assert_se(streq(data[3], "g=g "));
359 assert_se(streq(data[4], "h=h"));
360 assert_se(streq(data[5], "i=i"));
361 assert_se(data[6] == NULL);
362 unlink(name);
363 }
364
365 static void test_load_env_file_2(void) {
366 _cleanup_strv_free_ char **data = NULL;
367 int r;
368
369 char name[] = "/tmp/test-load-env-file.XXXXXX";
370 _cleanup_close_ int fd;
371
372 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
373 assert_se(fd >= 0);
374 assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
375
376 r = load_env_file(NULL, name, NULL, &data);
377 assert_se(r == 0);
378 assert_se(streq(data[0], "a=a"));
379 assert_se(data[1] == NULL);
380 unlink(name);
381 }
382
383 static void test_load_env_file_3(void) {
384 _cleanup_strv_free_ char **data = NULL;
385 int r;
386
387 char name[] = "/tmp/test-load-env-file.XXXXXX";
388 _cleanup_close_ int fd;
389
390 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
391 assert_se(fd >= 0);
392 assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
393
394 r = load_env_file(NULL, name, NULL, &data);
395 assert_se(r == 0);
396 assert_se(data == NULL);
397 unlink(name);
398 }
399
400 static void test_load_env_file_4(void) {
401 _cleanup_strv_free_ char **data = NULL;
402 char name[] = "/tmp/test-load-env-file.XXXXXX";
403 _cleanup_close_ int fd;
404 int r;
405
406 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
407 assert_se(fd >= 0);
408 assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
409
410 r = load_env_file(NULL, name, NULL, &data);
411 assert_se(r == 0);
412 assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
413 assert_se(streq(data[1], "MODULE_0=coretemp"));
414 assert_se(streq(data[2], "MODULE_1=f71882fg"));
415 assert_se(data[3] == NULL);
416 unlink(name);
417 }
418
419 static void test_load_env_file_5(void) {
420 _cleanup_strv_free_ char **data = NULL;
421 int r;
422
423 char name[] = "/tmp/test-load-env-file.XXXXXX";
424 _cleanup_close_ int fd;
425
426 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
427 assert_se(fd >= 0);
428 assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5));
429
430 r = load_env_file(NULL, name, NULL, &data);
431 assert_se(r == 0);
432 assert_se(streq(data[0], "a="));
433 assert_se(streq(data[1], "b="));
434 assert_se(data[2] == NULL);
435 unlink(name);
436 }
437
438 static void test_install_printf(void) {
439 char name[] = "name.service",
440 path[] = "/run/systemd/system/name.service",
441 user[] = "xxxx-no-such-user";
442 InstallInfo i = {name, path, user};
443 InstallInfo i2 = {name, path, NULL};
444 char name3[] = "name@inst.service",
445 path3[] = "/run/systemd/system/name.service";
446 InstallInfo i3 = {name3, path3, user};
447 InstallInfo i4 = {name3, path3, NULL};
448
449 _cleanup_free_ char *mid, *bid, *host;
450
451 assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
452 assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
453 assert_se((host = gethostname_malloc()));
454
455 #define expect(src, pattern, result) \
456 do { \
457 _cleanup_free_ char *t = NULL; \
458 _cleanup_free_ char \
459 *d1 = strdup(i.name), \
460 *d2 = strdup(i.path), \
461 *d3 = strdup(i.user); \
462 assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
463 memzero(i.name, strlen(i.name)); \
464 memzero(i.path, strlen(i.path)); \
465 memzero(i.user, strlen(i.user)); \
466 assert_se(d1 && d2 && d3); \
467 if (result) { \
468 printf("%s\n", t); \
469 assert_se(streq(t, result)); \
470 } else assert_se(t == NULL); \
471 strcpy(i.name, d1); \
472 strcpy(i.path, d2); \
473 strcpy(i.user, d3); \
474 } while(false)
475
476 assert_se(setenv("USER", "root", 1) == 0);
477
478 expect(i, "%n", "name.service");
479 expect(i, "%N", "name");
480 expect(i, "%p", "name");
481 expect(i, "%i", "");
482 expect(i, "%u", "xxxx-no-such-user");
483
484 DISABLE_WARNING_NONNULL;
485 expect(i, "%U", NULL);
486 REENABLE_WARNING;
487
488 expect(i, "%m", mid);
489 expect(i, "%b", bid);
490 expect(i, "%H", host);
491
492 expect(i2, "%u", "root");
493 expect(i2, "%U", "0");
494
495 expect(i3, "%n", "name@inst.service");
496 expect(i3, "%N", "name@inst");
497 expect(i3, "%p", "name");
498 expect(i3, "%u", "xxxx-no-such-user");
499
500 DISABLE_WARNING_NONNULL;
501 expect(i3, "%U", NULL);
502 REENABLE_WARNING;
503
504 expect(i3, "%m", mid);
505 expect(i3, "%b", bid);
506 expect(i3, "%H", host);
507
508 expect(i4, "%u", "root");
509 expect(i4, "%U", "0");
510 }
511
512 int main(int argc, char *argv[]) {
513 int r;
514
515 log_parse_environment();
516 log_open();
517
518 r = test_unit_file_get_set();
519 test_config_parse_exec();
520 test_load_env_file_1();
521 test_load_env_file_2();
522 test_load_env_file_3();
523 test_load_env_file_4();
524 test_load_env_file_5();
525 TEST_REQ_RUNNING_SYSTEMD(test_install_printf());
526
527 return r;
528 }