chiark / gitweb /
test-unit-file: add test for semicolon escaping
[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         assert_se(c);
77         log_info("%s %s %s %s",
78                  c->path, c->argv[0], c->argv[1], c->argv[2]);
79         assert_se(streq(c->path, path));
80         assert_se(streq(c->argv[0], argv0));
81         assert_se(streq(c->argv[1], argv1));
82         assert_se(streq_ptr(c->argv[2], argv2));
83         assert_se(c->ignore == ignore);
84 }
85
86 static void test_config_parse_exec(void) {
87         /* int config_parse_exec( */
88         /*         const char *filename, */
89         /*         unsigned line, */
90         /*         const char *section, */
91         /*         unsigned section_line, */
92         /*         const char *lvalue, */
93         /*         int ltype, */
94         /*         const char *rvalue, */
95         /*         void *data, */
96         /*         void *userdata) */
97         int r;
98
99         ExecCommand *c = NULL, *c1;
100
101         /* basic test */
102         r = config_parse_exec(NULL, "fake", 1, "section", 1,
103                               "LValue", 0, "/RValue r1",
104                               &c, NULL);
105         assert_se(r >= 0);
106         check_execcommand(c, "/RValue", "/RValue", "r1", NULL, false);
107
108         r = config_parse_exec(NULL, "fake", 2, "section", 1,
109                               "LValue", 0, "/RValue///slashes/// r1",
110                               &c, NULL);
111        /* test slashes */
112         assert_se(r >= 0);
113         c1 = c->command_next;
114         check_execcommand(c1, "/RValue/slashes", "/RValue///slashes///", "r1", NULL, false);
115
116         /* honour_argv0 */
117         r = config_parse_exec(NULL, "fake", 3, "section", 1,
118                               "LValue", 0, "@/RValue///slashes2/// argv0 r1",
119                               &c, NULL);
120         assert_se(r >= 0);
121         c1 = c1->command_next;
122         check_execcommand(c1, "/RValue/slashes2", "argv0", "r1", NULL, false);
123
124         /* ignore && honour_argv0 */
125         r = config_parse_exec(NULL, "fake", 4, "section", 1,
126                               "LValue", 0, "-@/RValue///slashes3/// argv0a r1",
127                               &c, NULL);
128         assert_se(r >= 0);
129         c1 = c1->command_next;
130         check_execcommand(c1, "/RValue/slashes3", "argv0a", "r1", NULL, true);
131
132         /* ignore && honour_argv0 */
133         r = config_parse_exec(NULL, "fake", 4, "section", 1,
134                               "LValue", 0, "@-/RValue///slashes4/// argv0b r1",
135                               &c, NULL);
136         assert_se(r >= 0);
137         c1 = c1->command_next;
138         check_execcommand(c1, "/RValue/slashes4", "argv0b", "r1", NULL, true);
139
140         /* ignore && ignore */
141         r = config_parse_exec(NULL, "fake", 4, "section", 1,
142                               "LValue", 0, "--/RValue argv0 r1",
143                               &c, NULL);
144         assert_se(r == 0);
145         assert_se(c1->command_next == NULL);
146
147         /* ignore && ignore */
148         r = config_parse_exec(NULL, "fake", 4, "section", 1,
149                               "LValue", 0, "-@-/RValue argv0 r1",
150                               &c, NULL);
151         assert_se(r == 0);
152         assert_se(c1->command_next == NULL);
153
154         /* semicolon */
155         r = config_parse_exec(NULL, "fake", 5, "section", 1,
156                               "LValue", 0,
157                               "-@/RValue argv0 r1 ; "
158                               "/goo/goo boo",
159                               &c, NULL);
160         assert_se(r >= 0);
161         c1 = c1->command_next;
162         check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
163
164         c1 = c1->command_next;
165         check_execcommand(c1, "/goo/goo", "/goo/goo", "boo", NULL, false);
166
167         /* trailing semicolon */
168         r = config_parse_exec(NULL, "fake", 5, "section", 1,
169                               "LValue", 0,
170                               "-@/RValue argv0 r1 ; ",
171                               &c, NULL);
172         assert_se(r >= 0);
173         c1 = c1->command_next;
174         check_execcommand(c1, "/RValue", "argv0", "r1", NULL, true);
175
176         assert_se(c1->command_next == NULL);
177
178         /* escaped semicolon */
179         r = config_parse_exec(NULL, "fake", 5, "section", 1,
180                               "LValue", 0,
181                               "/bin/find \\;",
182                               &c, NULL);
183         assert_se(r >= 0);
184         c1 = c1->command_next;
185         check_execcommand(c1, "/bin/find", "/bin/find", ";", NULL, false);
186
187         /* escaped semicolon with following arg */
188         r = config_parse_exec(NULL, "fake", 5, "section", 1,
189                               "LValue", 0,
190                               "/sbin/find \\; x",
191                               &c, NULL);
192         assert_se(r >= 0);
193         c1 = c1->command_next;
194         check_execcommand(c1,
195                           "/sbin/find", "/sbin/find", ";", "x", false);
196
197         exec_command_free_list(c);
198 }
199
200 #define env_file_1                              \
201         "a=a\n"                                 \
202         "b=b\\\n"                               \
203         "c\n"                                   \
204         "d=d\\\n"                               \
205         "e\\\n"                                 \
206         "f\n"                                   \
207         "g=g\\ \n"                              \
208         "h=h\n"                                 \
209         "i=i\\"
210
211 #define env_file_2                              \
212         "a=a\\\n"
213
214 #define env_file_3 \
215         "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \
216         "#--nouser-config                                     \\\n" \
217         "normal=line"
218
219 #define env_file_4 \
220        "# Generated\n" \
221        "\n" \
222        "HWMON_MODULES=\"coretemp f71882fg\"\n" \
223        "\n" \
224        "# For compatibility reasons\n" \
225        "\n" \
226        "MODULE_0=coretemp\n" \
227        "MODULE_1=f71882fg"
228
229 #define env_file_5                              \
230         "a=\n"                                 \
231         "b="
232
233 static void test_load_env_file_1(void) {
234         _cleanup_strv_free_ char **data = NULL;
235         int r;
236
237         char name[] = "/tmp/test-load-env-file.XXXXXX";
238         _cleanup_close_ int fd;
239
240         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
241         assert_se(fd >= 0);
242         assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1));
243
244         r = load_env_file(NULL, name, NULL, &data);
245         assert_se(r == 0);
246         assert_se(streq(data[0], "a=a"));
247         assert_se(streq(data[1], "b=bc"));
248         assert_se(streq(data[2], "d=def"));
249         assert_se(streq(data[3], "g=g "));
250         assert_se(streq(data[4], "h=h"));
251         assert_se(streq(data[5], "i=i"));
252         assert_se(data[6] == NULL);
253         unlink(name);
254 }
255
256 static void test_load_env_file_2(void) {
257         _cleanup_strv_free_ char **data = NULL;
258         int r;
259
260         char name[] = "/tmp/test-load-env-file.XXXXXX";
261         _cleanup_close_ int fd;
262
263         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
264         assert_se(fd >= 0);
265         assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2));
266
267         r = load_env_file(NULL, name, NULL, &data);
268         assert_se(r == 0);
269         assert_se(streq(data[0], "a=a"));
270         assert_se(data[1] == NULL);
271         unlink(name);
272 }
273
274 static void test_load_env_file_3(void) {
275         _cleanup_strv_free_ char **data = NULL;
276         int r;
277
278         char name[] = "/tmp/test-load-env-file.XXXXXX";
279         _cleanup_close_ int fd;
280
281         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
282         assert_se(fd >= 0);
283         assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3));
284
285         r = load_env_file(NULL, name, NULL, &data);
286         assert_se(r == 0);
287         assert_se(data == NULL);
288         unlink(name);
289 }
290
291 static void test_load_env_file_4(void) {
292         _cleanup_strv_free_ char **data = NULL;
293         char name[] = "/tmp/test-load-env-file.XXXXXX";
294         _cleanup_close_ int fd;
295         int r;
296
297         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
298         assert_se(fd >= 0);
299         assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4));
300
301         r = load_env_file(NULL, name, NULL, &data);
302         assert_se(r == 0);
303         assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg"));
304         assert_se(streq(data[1], "MODULE_0=coretemp"));
305         assert_se(streq(data[2], "MODULE_1=f71882fg"));
306         assert_se(data[3] == NULL);
307         unlink(name);
308 }
309
310 static void test_load_env_file_5(void) {
311         _cleanup_strv_free_ char **data = NULL;
312         int r;
313
314         char name[] = "/tmp/test-load-env-file.XXXXXX";
315         _cleanup_close_ int fd;
316
317         fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
318         assert_se(fd >= 0);
319         assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5));
320
321         r = load_env_file(NULL, name, NULL, &data);
322         assert_se(r == 0);
323         assert_se(streq(data[0], "a="));
324         assert_se(streq(data[1], "b="));
325         assert_se(data[2] == NULL);
326         unlink(name);
327 }
328
329 static void test_install_printf(void) {
330         char    name[] = "name.service",
331                 path[] = "/run/systemd/system/name.service",
332                 user[] = "xxxx-no-such-user";
333         InstallInfo i = {name, path, user};
334         InstallInfo i2 = {name, path, NULL};
335         char    name3[] = "name@inst.service",
336                 path3[] = "/run/systemd/system/name.service";
337         InstallInfo i3 = {name3, path3, user};
338         InstallInfo i4 = {name3, path3, NULL};
339
340         _cleanup_free_ char *mid, *bid, *host;
341
342         assert_se(specifier_machine_id('m', NULL, NULL, &mid) >= 0 && mid);
343         assert_se(specifier_boot_id('b', NULL, NULL, &bid) >= 0 && bid);
344         assert_se((host = gethostname_malloc()));
345
346 #define expect(src, pattern, result)                                    \
347         do {                                                            \
348                 _cleanup_free_ char *t = NULL;                          \
349                 _cleanup_free_ char                                     \
350                         *d1 = strdup(i.name),                           \
351                         *d2 = strdup(i.path),                           \
352                         *d3 = strdup(i.user);                           \
353                 assert_se(install_full_printf(&src, pattern, &t) >= 0 || !result); \
354                 memzero(i.name, strlen(i.name));                        \
355                 memzero(i.path, strlen(i.path));                        \
356                 memzero(i.user, strlen(i.user));                        \
357                 assert_se(d1 && d2 && d3);                                 \
358                 if (result) {                                           \
359                         printf("%s\n", t);                              \
360                         assert_se(streq(t, result));                       \
361                 } else assert_se(t == NULL);                               \
362                 strcpy(i.name, d1);                                     \
363                 strcpy(i.path, d2);                                     \
364                 strcpy(i.user, d3);                                     \
365         } while(false)
366
367         assert_se(setenv("USER", "root", 1) == 0);
368
369         expect(i, "%n", "name.service");
370         expect(i, "%N", "name");
371         expect(i, "%p", "name");
372         expect(i, "%i", "");
373         expect(i, "%u", "xxxx-no-such-user");
374
375         DISABLE_WARNING_NONNULL;
376         expect(i, "%U", NULL);
377         REENABLE_WARNING;
378
379         expect(i, "%m", mid);
380         expect(i, "%b", bid);
381         expect(i, "%H", host);
382
383         expect(i2, "%u", "root");
384         expect(i2, "%U", "0");
385
386         expect(i3, "%n", "name@inst.service");
387         expect(i3, "%N", "name@inst");
388         expect(i3, "%p", "name");
389         expect(i3, "%u", "xxxx-no-such-user");
390
391         DISABLE_WARNING_NONNULL;
392         expect(i3, "%U", NULL);
393         REENABLE_WARNING;
394
395         expect(i3, "%m", mid);
396         expect(i3, "%b", bid);
397         expect(i3, "%H", host);
398
399         expect(i4, "%u", "root");
400         expect(i4, "%U", "0");
401 }
402
403 int main(int argc, char *argv[]) {
404         int r;
405
406         log_parse_environment();
407         log_open();
408
409         r = test_unit_file_get_set();
410         test_config_parse_exec();
411         test_load_env_file_1();
412         test_load_env_file_2();
413         test_load_env_file_3();
414         test_load_env_file_4();
415         test_load_env_file_5();
416         TEST_REQ_RUNNING_SYSTEMD(test_install_printf());
417
418         return r;
419 }