chiark / gitweb /
pid1: do not initialize join_controllers by default
[elogind.git] / src / test / test-conf-parser.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2015 Ronny Chevalier
6
7   systemd is free software; you can redistribute it and/or modify it
8   under the terms of the GNU Lesser General Public License as published by
9   the Free Software Foundation; either version 2.1 of the License, or
10   (at your option) any later version.
11
12   systemd is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public License
18   along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include "conf-parser.h"
22 #include "fd-util.h"
23 #include "fileio.h"
24 #include "log.h"
25 #include "macro.h"
26 #include "string-util.h"
27 #include "strv.h"
28 #include "util.h"
29
30 /// Additional includes needed by elogind
31 #include "fd-util.h"
32 #include "fileio.h"
33
34 static void test_config_parse_path_one(const char *rvalue, const char *expected) {
35         _cleanup_free_ char *path = NULL;
36
37         assert_se(config_parse_path("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &path, NULL) >= 0);
38         assert_se(streq_ptr(expected, path));
39 }
40
41 static void test_config_parse_log_level_one(const char *rvalue, int expected) {
42         int log_level = 0;
43
44         assert_se(config_parse_log_level("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_level, NULL) >= 0);
45         assert_se(expected == log_level);
46 }
47
48 #if 0 /// UNNEEDED by elogind
49 static void test_config_parse_log_facility_one(const char *rvalue, int expected) {
50         int log_facility = 0;
51
52         assert_se(config_parse_log_facility("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_facility, NULL) >= 0);
53         assert_se(expected == log_facility);
54 }
55 #endif // 0
56
57 static void test_config_parse_iec_size_one(const char *rvalue, size_t expected) {
58         size_t iec_size = 0;
59
60         assert_se(config_parse_iec_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &iec_size, NULL) >= 0);
61         assert_se(expected == iec_size);
62 }
63
64 #if 0 /// UNNEEDED by elogind
65 static void test_config_parse_si_size_one(const char *rvalue, size_t expected) {
66         size_t si_size = 0;
67
68         assert_se(config_parse_si_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_size, NULL) >= 0);
69         assert_se(expected == si_size);
70 }
71 #endif // 0
72
73 static void test_config_parse_int_one(const char *rvalue, int expected) {
74         int v = -1;
75
76         assert_se(config_parse_int("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
77         assert_se(expected == v);
78 }
79
80 static void test_config_parse_unsigned_one(const char *rvalue, unsigned expected) {
81         unsigned v = 0;
82
83         assert_se(config_parse_unsigned("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
84         assert_se(expected == v);
85 }
86
87 static void test_config_parse_strv_one(const char *rvalue, char **expected) {
88         _cleanup_strv_free_ char **strv = NULL;
89
90         assert_se(config_parse_strv("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &strv, NULL) >= 0);
91         assert_se(strv_equal(expected, strv));
92 }
93
94 static void test_config_parse_mode_one(const char *rvalue, mode_t expected) {
95         mode_t v = 0;
96
97         assert_se(config_parse_mode("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
98         assert_se(expected == v);
99 }
100
101 static void test_config_parse_sec_one(const char *rvalue, usec_t expected) {
102         usec_t v = 0;
103
104         assert_se(config_parse_sec("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
105         assert_se(expected == v);
106 }
107
108 #if 0 /// UNNEEDED by elogind
109 static void test_config_parse_nsec_one(const char *rvalue, nsec_t expected) {
110         nsec_t v = 0;
111
112         assert_se(config_parse_nsec("unit", "filename", 1, "nsection", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
113         assert_se(expected == v);
114 }
115 #endif // 0
116
117 static void test_config_parse_path(void) {
118         test_config_parse_path_one("/path", "/path");
119         test_config_parse_path_one("/path//////////", "/path");
120         test_config_parse_path_one("///path/foo///bar////bar//", "/path/foo/bar/bar");
121         test_config_parse_path_one("/path/\xc3\x80", "/path/\xc3\x80");
122
123         test_config_parse_path_one("not_absolute/path", NULL);
124         test_config_parse_path_one("/path/\xc3\x7f", NULL);
125 }
126
127 static void test_config_parse_log_level(void) {
128         test_config_parse_log_level_one("debug", LOG_DEBUG);
129         test_config_parse_log_level_one("info", LOG_INFO);
130
131         test_config_parse_log_level_one("garbage", 0);
132 }
133
134 #if 0 /// UNNEEDED by elogind
135 static void test_config_parse_log_facility(void) {
136         test_config_parse_log_facility_one("mail", LOG_MAIL);
137         test_config_parse_log_facility_one("user", LOG_USER);
138
139         test_config_parse_log_facility_one("garbage", 0);
140 }
141 #endif // 0
142
143 static void test_config_parse_iec_size(void) {
144         test_config_parse_iec_size_one("1024", 1024);
145         test_config_parse_iec_size_one("2K", 2048);
146         test_config_parse_iec_size_one("10M", 10 * 1024 * 1024);
147         test_config_parse_iec_size_one("1G", 1 * 1024 * 1024 * 1024);
148         test_config_parse_iec_size_one("0G", 0);
149         test_config_parse_iec_size_one("0", 0);
150
151         test_config_parse_iec_size_one("-982", 0);
152         test_config_parse_iec_size_one("49874444198739873000000G", 0);
153         test_config_parse_iec_size_one("garbage", 0);
154 }
155
156 #if 0 /// UNNEEDED by elogind
157 static void test_config_parse_si_size(void) {
158         test_config_parse_si_size_one("1024", 1024);
159         test_config_parse_si_size_one("2K", 2000);
160         test_config_parse_si_size_one("10M", 10 * 1000 * 1000);
161         test_config_parse_si_size_one("1G", 1 * 1000 * 1000 * 1000);
162         test_config_parse_si_size_one("0G", 0);
163         test_config_parse_si_size_one("0", 0);
164
165         test_config_parse_si_size_one("-982", 0);
166         test_config_parse_si_size_one("49874444198739873000000G", 0);
167         test_config_parse_si_size_one("garbage", 0);
168 }
169 #endif // 0
170
171 static void test_config_parse_int(void) {
172         test_config_parse_int_one("1024", 1024);
173         test_config_parse_int_one("-1024", -1024);
174         test_config_parse_int_one("0", 0);
175
176         test_config_parse_int_one("99999999999999999999999999999999999999999999999999999999", -1);
177         test_config_parse_int_one("-99999999999999999999999999999999999999999999999999999999", -1);
178         test_config_parse_int_one("1G", -1);
179         test_config_parse_int_one("garbage", -1);
180 }
181
182 static void test_config_parse_unsigned(void) {
183         test_config_parse_unsigned_one("10241024", 10241024);
184         test_config_parse_unsigned_one("1024", 1024);
185         test_config_parse_unsigned_one("0", 0);
186
187         test_config_parse_unsigned_one("99999999999999999999999999999999999999999999999999999999", 0);
188         test_config_parse_unsigned_one("1G", 0);
189         test_config_parse_unsigned_one("garbage", 0);
190         test_config_parse_unsigned_one("1000garbage", 0);
191 }
192
193 static void test_config_parse_strv(void) {
194         test_config_parse_strv_one("", STRV_MAKE_EMPTY);
195         test_config_parse_strv_one("foo", STRV_MAKE("foo"));
196         test_config_parse_strv_one("foo bar foo", STRV_MAKE("foo", "bar", "foo"));
197         test_config_parse_strv_one("\"foo bar\" foo", STRV_MAKE("foo bar", "foo"));
198         test_config_parse_strv_one("\xc3\x80", STRV_MAKE("\xc3\x80"));
199         test_config_parse_strv_one("\xc3\x7f", STRV_MAKE_EMPTY);
200 }
201
202 static void test_config_parse_mode(void) {
203         test_config_parse_mode_one("777", 0777);
204         test_config_parse_mode_one("644", 0644);
205
206         test_config_parse_mode_one("-777", 0);
207         test_config_parse_mode_one("999", 0);
208         test_config_parse_mode_one("garbage", 0);
209         test_config_parse_mode_one("777garbage", 0);
210         test_config_parse_mode_one("777 garbage", 0);
211 }
212
213 static void test_config_parse_sec(void) {
214         test_config_parse_sec_one("1", 1 * USEC_PER_SEC);
215         test_config_parse_sec_one("1s", 1 * USEC_PER_SEC);
216         test_config_parse_sec_one("100ms", 100 * USEC_PER_MSEC);
217         test_config_parse_sec_one("5min 20s", 5 * 60 * USEC_PER_SEC + 20 * USEC_PER_SEC);
218
219         test_config_parse_sec_one("-1", 0);
220         test_config_parse_sec_one("10foo", 0);
221         test_config_parse_sec_one("garbage", 0);
222 }
223
224 #if 0 /// UNNEEDED by elogind
225 static void test_config_parse_nsec(void) {
226         test_config_parse_nsec_one("1", 1);
227         test_config_parse_nsec_one("1s", 1 * NSEC_PER_SEC);
228         test_config_parse_nsec_one("100ms", 100 * NSEC_PER_MSEC);
229         test_config_parse_nsec_one("5min 20s", 5 * 60 * NSEC_PER_SEC + 20 * NSEC_PER_SEC);
230
231         test_config_parse_nsec_one("-1", 0);
232         test_config_parse_nsec_one("10foo", 0);
233         test_config_parse_nsec_one("garbage", 0);
234 }
235
236 static void test_config_parse_iec_uint64(void) {
237         uint64_t offset = 0;
238         assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4M", &offset, NULL) == 0);
239         assert_se(offset == 4 * 1024 * 1024);
240
241         assert_se(config_parse_iec_uint64(NULL, "/this/file", 11, "Section", 22, "Size", 0, "4.5M", &offset, NULL) == 0);
242 }
243 #endif // 0
244
245 static void test_config_parse_join_controllers(void) {
246         int r;
247         _cleanup_(strv_free_freep) char ***c = NULL;
248         char ***c2;
249
250         /* Test normal operation */
251         r = config_parse_join_controllers(NULL, "example.conf", 11, "Section", 10, "JoinControllers", 0, "cpu,cpuacct net_cls,netprio", &c, NULL);
252         assert_se(r == 0);
253         assert_se(c);
254         assert_se(strv_length(c[0]) == 2);
255         assert_se(strv_equal(c[0], STRV_MAKE("cpu", "cpuacct")));
256         assert_se(strv_length(c[1]) == 2);
257         assert_se(strv_equal(c[1], STRV_MAKE("net_cls", "netprio")));
258         assert_se(c[2] == NULL);
259
260         /* Test special case of no mounted controllers */
261         r = config_parse_join_controllers(NULL, "example.conf", 12, "Section", 10, "JoinControllers", 0, "", &c, NULL);
262         assert_se(r == 0);
263         assert_se(c);
264         assert_se(strv_equal(c[0], STRV_MAKE_EMPTY));
265         assert_se(c[1] == NULL);
266
267         /* Test merging of overlapping lists */
268         r = config_parse_join_controllers(NULL, "example.conf", 13, "Section", 10, "JoinControllers", 0, "a,b b,c", &c, NULL);
269         assert_se(r == 0);
270         assert_se(c);
271         assert_se(strv_length(c[0]) == 3);
272         assert_se(strv_contains(c[0], "a"));
273         assert_se(strv_contains(c[0], "b"));
274         assert_se(strv_contains(c[0], "c"));
275         assert_se(c[1] == NULL);
276
277         /* Test ignoring of bad lines */
278         c2 = c;
279         r = config_parse_join_controllers(NULL, "example.conf", 14, "Section", 10, "JoinControllers", 0, "a,\"b ", &c, NULL);
280         assert_se(r < 0);
281         assert_se(c == c2);
282 }
283
284 #define x10(x) x x x x x x x x x x
285 #define x100(x) x10(x10(x))
286 #define x1000(x) x10(x100(x))
287
288 static const char* const config_file[] = {
289         "[Section]\n"
290         "setting1=1\n",
291
292         "[Section]\n"
293         "setting1=1",        /* no terminating newline */
294
295         "\n\n\n\n[Section]\n\n\n"
296         "setting1=1",        /* some whitespace, no terminating newline */
297
298         "[Section]\n"
299         "[Section]\n"
300         "setting1=1\n"
301         "setting1=2\n"
302         "setting1=1\n",      /* repeated settings */
303
304         "[Section]\n"
305         "setting1=1\\\n"     /* normal continuation */
306         "2\\\n"
307         "3\n",
308
309         "[Section]\n"
310         "setting1=1\\\\\\\n" /* continuation with trailing escape symbols */
311         "\\\\2\n",           /* note that C requires one level of escaping, so the
312                               * parser gets "…1 BS BS BS NL BS BS 2 NL", which
313                               * it translates into "…1 BS BS SP BS BS 2" */
314
315         "\n[Section]\n\n"
316         "setting1="          /* a line above LINE_MAX length */
317         x1000("ABCD")
318         "\n",
319
320         "[Section]\n"
321         "setting1="          /* a line above LINE_MAX length, with continuation */
322         x1000("ABCD") "\\\n"
323         "foobar",
324
325         "[Section]\n"
326         "setting1="          /* a line above the allowed limit: 9 + 1050000 + 1 */
327         x1000(x1000("x") x10("abcde")) "\n",
328
329         "[Section]\n"
330         "setting1="          /* many continuation lines, together above the limit */
331         x1000(x1000("x") x10("abcde") "\\\n") "xxx",
332 };
333
334 static void test_config_parse(unsigned i, const char *s) {
335         char name[] = "/tmp/test-conf-parser.XXXXXX";
336         int fd, r;
337         _cleanup_fclose_ FILE *f = NULL;
338         _cleanup_free_ char *setting1 = NULL;
339
340         const ConfigTableItem items[] = {
341                 { "Section", "setting1",  config_parse_string,   0, &setting1},
342                 {}
343         };
344
345         log_info("== %s[%i] ==", __func__, i);
346
347         fd = mkostemp_safe(name);
348         assert_se(fd >= 0);
349         assert_se((size_t) write(fd, s, strlen(s)) == strlen(s));
350
351         assert_se(lseek(fd, 0, SEEK_SET) == 0);
352         assert_se(f = fdopen(fd, "r"));
353
354         /*
355         int config_parse(const char *unit,
356                          const char *filename,
357                          FILE *f,
358                          const char *sections,
359                          ConfigItemLookup lookup,
360                          const void *table,
361                          bool relaxed,
362                          bool allow_include,
363                          bool warn,
364                          void *userdata)
365         */
366
367         r = config_parse(NULL, name, f,
368                          "Section\0",
369                          config_item_table_lookup, items,
370                          CONFIG_PARSE_WARN, NULL);
371
372         switch (i) {
373         case 0 ... 3:
374                 assert_se(r == 0);
375                 assert_se(streq(setting1, "1"));
376                 break;
377
378         case 4:
379                 assert_se(r == 0);
380                 assert_se(streq(setting1, "1 2 3"));
381                 break;
382
383         case 5:
384                 assert_se(r == 0);
385                 assert_se(streq(setting1, "1\\\\ \\\\2"));
386                 break;
387
388         case 6:
389                 assert_se(r == 0);
390                 assert_se(streq(setting1, x1000("ABCD")));
391                 break;
392
393         case 7:
394                 assert_se(r == 0);
395                 assert_se(streq(setting1, x1000("ABCD") " foobar"));
396                 break;
397
398         case 8 ... 9:
399                 assert_se(r == -ENOBUFS);
400                 assert_se(setting1 == NULL);
401                 break;
402         }
403 }
404
405 int main(int argc, char **argv) {
406         unsigned i;
407
408         log_parse_environment();
409         log_open();
410
411         test_config_parse_path();
412         test_config_parse_log_level();
413 #if 0 /// UNNEEDED by elogind
414         test_config_parse_log_facility();
415 #endif // 0
416         test_config_parse_iec_size();
417 #if 0 /// UNNEEDED by elogind
418         test_config_parse_si_size();
419 #endif // 0
420         test_config_parse_int();
421         test_config_parse_unsigned();
422         test_config_parse_strv();
423         test_config_parse_mode();
424         test_config_parse_sec();
425 #if 0 /// UNNEEDED by elogind
426         test_config_parse_nsec();
427         test_config_parse_iec_uint64();
428 #endif // 0
429         test_config_parse_join_controllers();
430
431         for (i = 0; i < ELEMENTSOF(config_file); i++)
432                 test_config_parse(i, config_file[i]);
433
434         return 0;
435 }