chiark / gitweb /
d6a7d439150be1e0c4168f0447c7bb7ac1367ae1
[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("/* ignore && honour_argv0 */");
141         r = config_parse_exec(NULL, "fake", 4, "section", 1,
142                               "LValue", 0, "-@/RValue///slashes3 argv0a r1",
143                               &c, NULL);
144         assert_se(r >= 0);
145         c1 = c1->command_next;
146         check_execcommand(c1, "/RValue/slashes3", "argv0a", "r1", NULL, true);
147
148         log_info("/* ignore && honour_argv0 */");
149         r = config_parse_exec(NULL, "fake", 4, "section", 1,
150                               "LValue", 0, "@-/RValue///slashes4 argv0b r1",
151                               &c, NULL);
152         assert_se(r >= 0);
153         c1 = c1->command_next;
154         check_execcommand(c1, "/RValue/slashes4", "argv0b", "r1", NULL, true);
155
156         log_info("/* ignore && ignore */");
157         r = config_parse_exec(NULL, "fake", 4, "section", 1,
158                               "LValue", 0, "--/RValue argv0 r1",
159                               &c, NULL);
160         assert_se(r == 0);
161         assert_se(c1->command_next == NULL);
162
163         log_info("/* ignore && ignore (2) */");
164         r = config_parse_exec(NULL, "fake", 4, "section", 1,
165                               "LValue", 0, "-@-/RValue argv0 r1",
166                               &c, NULL);
167         assert_se(r == 0);
168         assert_se(c1->command_next == NULL);
169
170         log_info("/* semicolon */");
171         r = config_parse_exec(NULL, "fake", 5, "section", 1,
172                               "LValue", 0,
173                               "-@/RValue argv0 r1 ; "
174                               "/goo/goo boo",
175                               &c, NULL);
176         assert_se(r >= 0);
177         c1 = c1->command_next;
178         check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
179
180         c1 = c1->command_next;
181         check_execcommand(c1, "/goo/goo", NULL, "boo", NULL, false);
182
183         log_info("/* trailing semicolon */");
184         r = config_parse_exec(NULL, "fake", 5, "section", 1,
185                               "LValue", 0,
186                               "-@/RValue argv0 r1 ; ",
187                               &c, NULL);
188         assert_se(r >= 0);
189         c1 = c1->command_next;
190         check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
191
192         assert_se(c1->command_next == NULL);
193
194         log_info("/* escaped semicolon */");
195         r = config_parse_exec(NULL, "fake", 5, "section", 1,
196                               "LValue", 0,
197                               "/bin/find \\;",
198                               &c, NULL);
199         assert_se(r >= 0);
200         c1 = c1->command_next;
201         check_execcommand(c1, "/bin/find", NULL, ";", NULL, false);
202
203         log_info("/* escaped semicolon with following arg */");
204         r = config_parse_exec(NULL, "fake", 5, "section", 1,
205                               "LValue", 0,
206                               "/sbin/find \\; x",
207                               &c, NULL);
208         assert_se(r >= 0);
209         c1 = c1->command_next;
210         check_execcommand(c1,
211                           "/sbin/find", NULL, ";", "x", false);
212
213         log_info("/* spaces in the filename */");
214         r = config_parse_exec(NULL, "fake", 5, "section", 1,
215                               "LValue", 0,
216                               "\"/PATH WITH SPACES/daemon\" -1 -2",
217                               &c, NULL);
218         assert_se(r >= 0);
219         c1 = c1->command_next;
220         check_execcommand(c1,
221                           "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
222
223         log_info("/* spaces in the filename, no args */");
224         r = config_parse_exec(NULL, "fake", 5, "section", 1,
225                               "LValue", 0,
226                               "\"/PATH WITH SPACES/daemon -1 -2\"",
227                               &c, NULL);
228         assert_se(r >= 0);
229         c1 = c1->command_next;
230         check_execcommand(c1,
231                           "/PATH WITH SPACES/daemon -1 -2", NULL, NULL, NULL, false);
232
233         log_info("/* spaces in the filename, everything quoted */");
234         r = config_parse_exec(NULL, "fake", 5, "section", 1,
235                               "LValue", 0,
236                               "\"/PATH WITH SPACES/daemon\" \"-1\" '-2'",
237                               &c, NULL);
238         assert_se(r >= 0);
239         c1 = c1->command_next;
240         check_execcommand(c1,
241                           "/PATH WITH SPACES/daemon", NULL, "-1", "-2", false);
242
243         log_info("/* escaped spaces in the filename */");
244         r = config_parse_exec(NULL, "fake", 5, "section", 1,
245                               "LValue", 0,
246                               "\"/PATH\\sWITH\\sSPACES/daemon\" '-1 -2'",
247                               &c, NULL);
248         assert_se(r >= 0);
249         c1 = c1->command_next;
250         check_execcommand(c1,
251                           "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
252
253         log_info("/* escaped spaces in the filename (2) */");
254         r = config_parse_exec(NULL, "fake", 5, "section", 1,
255                               "LValue", 0,
256                               "\"/PATH\\x20WITH\\x20SPACES/daemon\" \"-1 -2\"",
257                               &c, NULL);
258         assert_se(r >= 0);
259         c1 = c1->command_next;
260         check_execcommand(c1,
261                           "/PATH WITH SPACES/daemon", NULL, "-1 -2", NULL, false);
262
263         for (ccc = "abfnrtv\\\'\"x"; *ccc; ccc++) {
264                 /* \\x is an incomplete hexadecimal sequence, invalid because of the slash */
265                 char path[] = "/path\\X";
266                 path[sizeof(path) - 2] = *ccc;
267
268                 log_info("/* invalid character: \\%c */", *ccc);
269                 r = config_parse_exec(NULL, "fake", 4, "section", 1,
270                                       "LValue", 0, path,
271                                       &c, NULL);
272                 assert_se(r == 0);
273                 assert_se(c1->command_next == NULL);
274         }
275
276         log_info("/* valid character: \\s */");
277         r = config_parse_exec(NULL, "fake", 4, "section", 1,
278                               "LValue", 0, "/path\\s",
279                               &c, NULL);
280         assert_se(r >= 0);
281         c1 = c1->command_next;
282         check_execcommand(c1, "/path ", NULL, NULL, NULL, false);
283
284         log_info("/* trailing backslash: \\ */");
285         /* backslash is invalid */
286         r = config_parse_exec(NULL, "fake", 4, "section", 1,
287                               "LValue", 0, "/path\\",
288                               &c, NULL);
289         assert_se(r == 0);
290         assert_se(c1->command_next == NULL);
291
292         exec_command_free_list(c);
293 }
294
295 #define env_file_1                              \
296         "a=a\n"                                 \
297         "b=b\\\n"                               \
298         "c\n"                                   \
299         "d=d\\\n"                               \
300         "e\\\n"                                 \
301         "f\n"                                   \
302         "g=g\\ \n"                              \
303         "h=h\n"                                 \
304         "i=i\\"
305
306 #define env_file_2                              \
307         "a=a\\\n"
308
309 #define env_file_3 \
310         "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
311         "#--nouser-config                                     \\\n" \
312         "normal=line"
313
314 #define env_file_4 \
315        "# Generated\n" \
316        "\n" \
317        "HWMON_MODULES=\"coretemp f71882fg\"\n" \
318        "\n" \
319        "# For compatibility reasons\n" \
320        "\n" \
321        "MODULE_0=coretemp\n" \
322        "MODULE_1=f71882fg"
323
324 #define env_file_5                              \
325         "a=\n"                                 \
326         "b="
327
328 static void test_load_env_file_1(void) {
329         _cleanup_strv_free_ char **data = NULL;
330         int r;
331
332         char name[] = "/tmp/test-load-env-file.XXXXXX";
333         _cleanup_close_ int fd;
334
335         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
336         assert_se(fd >= 0);
337         assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
338
339         r = load_env_file(NULL, name, NULL, &data);
340         assert_se(r == 0);
341         assert_se(streq(data[0], "a=a"));
342         assert_se(streq(data[1], "b=bc"));
343         assert_se(streq(data[2], "d=def"));
344         assert_se(streq(data[3], "g=g "));
345         assert_se(streq(data[4], "h=h"));
346         assert_se(streq(data[5], "i=i"));
347         assert_se(data[6] == NULL);
348         unlink(name);
349 }
350
351 static void test_load_env_file_2(void) {
352         _cleanup_strv_free_ char **data = NULL;
353         int r;
354
355         char name[] = "/tmp/test-load-env-file.XXXXXX";
356         _cleanup_close_ int fd;
357
358         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
359         assert_se(fd >= 0);
360         assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
361
362         r = load_env_file(NULL, name, NULL, &data);
363         assert_se(r == 0);
364         assert_se(streq(data[0], "a=a"));
365         assert_se(data[1] == NULL);
366         unlink(name);
367 }
368
369 static void test_load_env_file_3(void) {
370         _cleanup_strv_free_ char **data = NULL;
371         int r;
372
373         char name[] = "/tmp/test-load-env-file.XXXXXX";
374         _cleanup_close_ int fd;
375
376         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
377         assert_se(fd >= 0);
378         assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
379
380         r = load_env_file(NULL, name, NULL, &data);
381         assert_se(r == 0);
382         assert_se(data == NULL);
383         unlink(name);
384 }
385
386 static void test_load_env_file_4(void) {
387         _cleanup_strv_free_ char **data = NULL;
388         char name[] = "/tmp/test-load-env-file.XXXXXX";
389         _cleanup_close_ int fd;
390         int r;
391
392         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
393         assert_se(fd >= 0);
394         assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
395
396         r = load_env_file(NULL, name, NULL, &data);
397         assert_se(r == 0);
398         assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
399         assert_se(streq(data[1], "MODULE_0=coretemp"));
400         assert_se(streq(data[2], "MODULE_1=f71882fg"));
401         assert_se(data[3] == NULL);
402         unlink(name);
403 }
404
405 static void test_load_env_file_5(void) {
406         _cleanup_strv_free_ char **data = NULL;
407         int r;
408
409         char name[] = "/tmp/test-load-env-file.XXXXXX";
410         _cleanup_close_ int fd;
411
412         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
413         assert_se(fd >= 0);
414         assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5));
415
416         r = load_env_file(NULL, name, NULL, &data);
417         assert_se(r == 0);
418         assert_se(streq(data[0], "a="));
419         assert_se(streq(data[1], "b="));
420         assert_se(data[2] == NULL);
421         unlink(name);
422 }
423
424 static void test_install_printf(void) {
425         char    name[] = "name.service",
426                 path[] = "/run/systemd/system/name.service",
427                 user[] = "xxxx-no-such-user";
428         InstallInfo i = {name, path, user};
429         InstallInfo i2 = {name, path, NULL};
430         char    name3[] = "name@inst.service",
431                 path3[] = "/run/systemd/system/name.service";
432         InstallInfo i3 = {name3, path3, user};
433         InstallInfo i4 = {name3, path3, NULL};
434
435         _cleanup_free_ char *mid, *bid, *host;
436
437         assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
438         assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
439         assert_se((host = gethostname_malloc()));
440
441 #define expect(src, pattern, result)                                    \
442         do {                                                            \
443                 _cleanup_free_ char *t = NULL;                          \
444                 _cleanup_free_ char                                     \
445                         *d1 = strdup(i.name),                           \
446                         *d2 = strdup(i.path),                           \
447                         *d3 = strdup(i.user);                           \
448                 assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
449                 memzero(i.name, strlen(i.name));                        \
450                 memzero(i.path, strlen(i.path));                        \
451                 memzero(i.user, strlen(i.user));                        \
452                 assert_se(d1 && d2 && d3);                                 \
453                 if (result) {                                           \
454                         printf("%s\n", t);                              \
455                         assert_se(streq(t, result));                       \
456                 } else assert_se(t == NULL);                               \
457                 strcpy(i.name, d1);                                     \
458                 strcpy(i.path, d2);                                     \
459                 strcpy(i.user, d3);                                     \
460         } while(false)
461
462         assert_se(setenv("USER", "root", 1) == 0);
463
464         expect(i, "%n", "name.service");
465         expect(i, "%N", "name");
466         expect(i, "%p", "name");
467         expect(i, "%i", "");
468         expect(i, "%u", "xxxx-no-such-user");
469
470         DISABLE_WARNING_NONNULL;
471         expect(i, "%U", NULL);
472         REENABLE_WARNING;
473
474         expect(i, "%m", mid);
475         expect(i, "%b", bid);
476         expect(i, "%H", host);
477
478         expect(i2, "%u", "root");
479         expect(i2, "%U", "0");
480
481         expect(i3, "%n", "name@inst.service");
482         expect(i3, "%N", "name@inst");
483         expect(i3, "%p", "name");
484         expect(i3, "%u", "xxxx-no-such-user");
485
486         DISABLE_WARNING_NONNULL;
487         expect(i3, "%U", NULL);
488         REENABLE_WARNING;
489
490         expect(i3, "%m", mid);
491         expect(i3, "%b", bid);
492         expect(i3, "%H", host);
493
494         expect(i4, "%u", "root");
495         expect(i4, "%U", "0");
496 }
497
498 int main(int argc, char *argv[]) {
499         int r;
500
501         log_parse_environment();
502         log_open();
503
504         r = test_unit_file_get_set();
505         test_config_parse_exec();
506         test_load_env_file_1();
507         test_load_env_file_2();
508         test_load_env_file_3();
509         test_load_env_file_4();
510         test_load_env_file_5();
511         TEST_REQ_RUNNING_SYSTEMD(test_install_printf());
512
513         return r;
514 }