]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
CommitLineData
b5b46d59
LP
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
b9893505 7 Copyright 2013 Zbigniew Jędrzejewski-Szmek
b5b46d59
LP
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>
b9893505 27#include <unistd.h>
2d5bdf5b 28#include <fcntl.h>
b5b46d59
LP
29
30#include "install.h"
7742f7e9
ZJS
31#include "install-printf.h"
32#include "specifier.h"
b5b46d59
LP
33#include "util.h"
34#include "macro.h"
35#include "hashmap.h"
2c5417ad 36#include "load-fragment.h"
b9893505 37#include "strv.h"
a5c32cff 38#include "fileio.h"
143bfdaf 39#include "test-helper.h"
b5b46d59 40
751e7576 41static int test_unit_file_get_set(void) {
b5b46d59
LP
42 int r;
43 Hashmap *h;
44 Iterator i;
45 UnitFileList *p;
46
d5099efc 47 h = hashmap_new(&string_hash_ops);
bdf7026e 48 assert_se(h);
b5b46d59
LP
49
50 r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
552c693e
CR
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
751e7576
CH
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;
b5b46d59
LP
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);
751e7576
CH
66
67 return 0;
2c5417ad
ZJS
68}
69
70static void check_execcommand(ExecCommand *c,
71 const char* path,
72 const char* argv0,
73 const char* argv1,
503dbda6 74 const char* argv2,
2c5417ad 75 bool ignore) {
4d8629de
ZJS
76 size_t n;
77
2c5417ad 78 assert_se(c);
c8539536
ZJS
79 log_info("expect: \"%s\" [\"%s\" \"%s\" \"%s\"]",
80 path, argv0 ?: path, argv1, argv2);
4d8629de 81 n = strv_length(c->argv);
c8539536 82 log_info("actual: \"%s\" [\"%s\" \"%s\" \"%s\"]",
4d8629de 83 c->path, c->argv[0], n > 0 ? c->argv[1] : NULL, n > 1 ? c->argv[2] : NULL);
2c5417ad 84 assert_se(streq(c->path, path));
c8539536 85 assert_se(streq(c->argv[0], argv0 ?: path));
4d8629de
ZJS
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));
2c5417ad
ZJS
90 assert_se(c->ignore == ignore);
91}
92
93static void test_config_parse_exec(void) {
c8539536
ZJS
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) */
2c5417ad
ZJS
104 int r;
105
106 ExecCommand *c = NULL, *c1;
c8539536 107 const char *ccc;
2c5417ad 108
c8539536 109 log_info("/* basic test */");
71a61510 110 r = config_parse_exec(NULL, "fake", 1, "section", 1,
2c5417ad
ZJS
111 "LValue", 0, "/RValue r1",
112 &c, NULL);
113 assert_se(r >= 0);
503dbda6 114 check_execcommand(c, "/RValue", "/RValue", "r1", NULL, false);
2c5417ad 115
71a61510 116 r = config_parse_exec(NULL, "fake", 2, "section", 1,
c8539536 117 "LValue", 0, "/RValue///slashes r1///",
2c5417ad 118 &c, NULL);
c8539536
ZJS
119
120 log_info("/* test slashes */");
2c5417ad
ZJS
121 assert_se(r >= 0);
122 c1 = c->command_next;
c8539536 123 check_execcommand(c1, "/RValue/slashes", "/RValue///slashes", "r1///", NULL, false);
2c5417ad 124
c8539536
ZJS
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 */");
71a61510 133 r = config_parse_exec(NULL, "fake", 3, "section", 1,
c8539536 134 "LValue", 0, "@/RValue///slashes2 ///argv0 r1",
2c5417ad
ZJS
135 &c, NULL);
136 assert_se(r >= 0);
137 c1 = c1->command_next;
c8539536 138 check_execcommand(c1, "/RValue/slashes2", "///argv0", "r1", NULL, false);
2c5417ad 139
e01ff428
ZJS
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
c8539536 154 log_info("/* ignore && honour_argv0 */");
71a61510 155 r = config_parse_exec(NULL, "fake", 4, "section", 1,
c8539536 156 "LValue", 0, "-@/RValue///slashes3 argv0a r1",
2c5417ad
ZJS
157 &c, NULL);
158 assert_se(r >= 0);
159 c1 = c1->command_next;
503dbda6 160 check_execcommand(c1, "/RValue/slashes3", "argv0a", "r1", NULL, true);
2c5417ad 161
c8539536 162 log_info("/* ignore && honour_argv0 */");
71a61510 163 r = config_parse_exec(NULL, "fake", 4, "section", 1,
c8539536 164 "LValue", 0, "@-/RValue///slashes4 argv0b r1",
0f67f1ef
ZJS
165 &c, NULL);
166 assert_se(r >= 0);
167 c1 = c1->command_next;
503dbda6 168 check_execcommand(c1, "/RValue/slashes4", "argv0b", "r1", NULL, true);
0f67f1ef 169
c8539536 170 log_info("/* ignore && ignore */");
71a61510 171 r = config_parse_exec(NULL, "fake", 4, "section", 1,
0f67f1ef
ZJS
172 "LValue", 0, "--/RValue argv0 r1",
173 &c, NULL);
174 assert_se(r == 0);
175 assert_se(c1->command_next == NULL);
176
c8539536 177 log_info("/* ignore && ignore (2) */");
71a61510 178 r = config_parse_exec(NULL, "fake", 4, "section", 1,
0f67f1ef
ZJS
179 "LValue", 0, "-@-/RValue argv0 r1",
180 &c, NULL);
181 assert_se(r == 0);
182 assert_se(c1->command_next == NULL);
183
c8539536 184 log_info("/* semicolon */");
71a61510 185 r = config_parse_exec(NULL, "fake", 5, "section", 1,
2c5417ad
ZJS
186 "LValue", 0,
187 "-@/RValue argv0 r1 ; "
188 "/goo/goo boo",
189 &c, NULL);
190 assert_se(r >= 0);
191 c1 = c1->command_next;
503dbda6 192 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
2c5417ad
ZJS
193
194 c1 = c1->command_next;
c8539536 195 check_execcommand(c1, "/goo/goo", NULL, "boo", NULL, false);
2c5417ad 196
c8539536 197 log_info("/* trailing semicolon */");
71a61510 198 r = config_parse_exec(NULL, "fake", 5, "section", 1,
2c5417ad
ZJS
199 "LValue", 0,
200 "-@/RValue argv0 r1 ; ",
201 &c, NULL);
202 assert_se(r >= 0);
203 c1 = c1->command_next;
503dbda6 204 check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
2c5417ad
ZJS
205
206 assert_se(c1->command_next == NULL);
207
c8539536 208 log_info("/* escaped semicolon */");
71a61510 209 r = config_parse_exec(NULL, "fake", 5, "section", 1,
7e1a84f5 210 "LValue", 0,
503dbda6
ZJS
211 "/bin/find \\;",
212 &c, NULL);
213 assert_se(r >= 0);
214 c1 = c1->command_next;
c8539536 215 check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
503dbda6 216
c8539536 217 log_info("/* escaped semicolon with following arg */");
503dbda6
ZJS
218 r = config_parse_exec(NULL, "fake", 5, "section", 1,
219 "LValue", 0,
220 "/sbin/find \\; x",
7e1a84f5
OS
221 &c, NULL);
222 assert_se(r >= 0);
223 c1 = c1->command_next;
224 check_execcommand(c1,
c8539536
ZJS
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);
7e1a84f5 305
2c5417ad
ZJS
306 exec_command_free_list(c);
307}
308
f73141d7
LP
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"
b9893505 322
a3aa7ee6
ZJS
323#define env_file_3 \
324 "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
325 "#--nouser-config \\\n" \
326 "normal=line"
327
d3b6d0c2
ZJS
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
58f10d40
ILG
338#define env_file_5 \
339 "a=\n" \
340 "b="
d3b6d0c2 341
b9893505 342static void test_load_env_file_1(void) {
7fd1b19b 343 _cleanup_strv_free_ char **data = NULL;
b9893505
ZJS
344 int r;
345
346 char name[] = "/tmp/test-load-env-file.XXXXXX";
2d5bdf5b
LP
347 _cleanup_close_ int fd;
348
349 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
bdf7026e 350 assert_se(fd >= 0);
b9893505
ZJS
351 assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
352
717603e3 353 r = load_env_file(NULL, name, NULL, &data);
bdf7026e
TA
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);
b9893505
ZJS
362 unlink(name);
363}
364
365static void test_load_env_file_2(void) {
7fd1b19b 366 _cleanup_strv_free_ char **data = NULL;
b9893505
ZJS
367 int r;
368
369 char name[] = "/tmp/test-load-env-file.XXXXXX";
2d5bdf5b
LP
370 _cleanup_close_ int fd;
371
372 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
bdf7026e 373 assert_se(fd >= 0);
b9893505
ZJS
374 assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
375
717603e3 376 r = load_env_file(NULL, name, NULL, &data);
bdf7026e
TA
377 assert_se(r == 0);
378 assert_se(streq(data[0], "a=a"));
379 assert_se(data[1] == NULL);
b9893505
ZJS
380 unlink(name);
381}
382
a3aa7ee6 383static void test_load_env_file_3(void) {
7fd1b19b 384 _cleanup_strv_free_ char **data = NULL;
a3aa7ee6
ZJS
385 int r;
386
387 char name[] = "/tmp/test-load-env-file.XXXXXX";
2d5bdf5b
LP
388 _cleanup_close_ int fd;
389
390 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
bdf7026e 391 assert_se(fd >= 0);
a3aa7ee6
ZJS
392 assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
393
717603e3 394 r = load_env_file(NULL, name, NULL, &data);
bdf7026e
TA
395 assert_se(r == 0);
396 assert_se(data == NULL);
a3aa7ee6
ZJS
397 unlink(name);
398}
399
d3b6d0c2 400static void test_load_env_file_4(void) {
7fd1b19b 401 _cleanup_strv_free_ char **data = NULL;
2d5bdf5b
LP
402 char name[] = "/tmp/test-load-env-file.XXXXXX";
403 _cleanup_close_ int fd;
d3b6d0c2
ZJS
404 int r;
405
2d5bdf5b 406 fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
bdf7026e 407 assert_se(fd >= 0);
d3b6d0c2
ZJS
408 assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
409
717603e3 410 r = load_env_file(NULL, name, NULL, &data);
bdf7026e
TA
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);
d3b6d0c2
ZJS
416 unlink(name);
417}
418
58f10d40
ILG
419static 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}
d3b6d0c2 437
7742f7e9
ZJS
438static void test_install_printf(void) {
439 char name[] = "name.service",
ad88e758 440 path[] = "/run/systemd/system/name.service",
7742f7e9
ZJS
441 user[] = "xxxx-no-such-user";
442 InstallInfo i = {name, path, user};
443 InstallInfo i2 = {name, path, NULL};
444 char name3[] = "name@inst.service",
ad88e758 445 path3[] = "/run/systemd/system/name.service";
7742f7e9
ZJS
446 InstallInfo i3 = {name3, path3, user};
447 InstallInfo i4 = {name3, path3, NULL};
448
7fd1b19b 449 _cleanup_free_ char *mid, *bid, *host;
7742f7e9 450
19f6d710
LP
451 assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
452 assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
7742f7e9
ZJS
453 assert_se((host = gethostname_malloc()));
454
455#define expect(src, pattern, result) \
f73141d7 456 do { \
19f6d710 457 _cleanup_free_ char *t = NULL; \
7fd1b19b 458 _cleanup_free_ char \
7742f7e9
ZJS
459 *d1 = strdup(i.name), \
460 *d2 = strdup(i.path), \
461 *d3 = strdup(i.user); \
19f6d710 462 assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
7742f7e9
ZJS
463 memzero(i.name, strlen(i.name)); \
464 memzero(i.path, strlen(i.path)); \
465 memzero(i.user, strlen(i.user)); \
bdf7026e 466 assert_se(d1 && d2 && d3); \
7742f7e9
ZJS
467 if (result) { \
468 printf("%s\n", t); \
bdf7026e
TA
469 assert_se(streq(t, result)); \
470 } else assert_se(t == NULL); \
7742f7e9
ZJS
471 strcpy(i.name, d1); \
472 strcpy(i.path, d2); \
473 strcpy(i.user, d3); \
f73141d7 474 } while(false)
7742f7e9
ZJS
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");
8fca4e30
LP
483
484 DISABLE_WARNING_NONNULL;
7742f7e9 485 expect(i, "%U", NULL);
8fca4e30
LP
486 REENABLE_WARNING;
487
7742f7e9
ZJS
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");
8fca4e30
LP
499
500 DISABLE_WARNING_NONNULL;
7742f7e9 501 expect(i3, "%U", NULL);
8fca4e30
LP
502 REENABLE_WARNING;
503
7742f7e9
ZJS
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}
b9893505 511
2c5417ad 512int main(int argc, char *argv[]) {
751e7576 513 int r;
2c5417ad 514
c1b6628d
ZJS
515 log_parse_environment();
516 log_open();
517
751e7576 518 r = test_unit_file_get_set();
2c5417ad 519 test_config_parse_exec();
b9893505
ZJS
520 test_load_env_file_1();
521 test_load_env_file_2();
a3aa7ee6 522 test_load_env_file_3();
d3b6d0c2 523 test_load_env_file_4();
58f10d40 524 test_load_env_file_5();
143bfdaf 525 TEST_REQ_RUNNING_SYSTEMD(test_install_printf());
b5b46d59 526
751e7576 527 return r;
b5b46d59 528}