chiark / gitweb /
e517f571d6901c7799b91327801976279644bb08
[elogind.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 }