]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/test/test-fileio.c
Merge pull request #1989 from keszybz/filetriggers-v2
[thirdparty/systemd.git] / src / test / test-fileio.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <unistd.h>
25
26 #include "alloc-util.h"
27 #include "ctype.h"
28 #include "def.h"
29 #include "env-util.h"
30 #include "fd-util.h"
31 #include "fileio.h"
32 #include "parse-util.h"
33 #include "process-util.h"
34 #include "string-util.h"
35 #include "strv.h"
36 #include "util.h"
37
38 static void test_parse_env_file(void) {
39 char t[] = "/tmp/test-fileio-in-XXXXXX",
40 p[] = "/tmp/test-fileio-out-XXXXXX";
41 int fd, r;
42 FILE *f;
43 _cleanup_free_ char *one = NULL, *two = NULL, *three = NULL, *four = NULL, *five = NULL,
44 *six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL;
45 _cleanup_strv_free_ char **a = NULL, **b = NULL;
46 char **i;
47 unsigned k;
48
49 fd = mkostemp_safe(p, O_RDWR|O_CLOEXEC);
50 assert_se(fd >= 0);
51 close(fd);
52
53 fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC);
54 assert_se(fd >= 0);
55
56 f = fdopen(fd, "w");
57 assert_se(f);
58
59 fputs("one=BAR \n"
60 "# comment\n"
61 " # comment \n"
62 " ; comment \n"
63 " two = bar \n"
64 "invalid line\n"
65 "invalid line #comment\n"
66 "three = \"333\n"
67 "xxxx\"\n"
68 "four = \'44\\\"44\'\n"
69 "five = \'55\\\'55\' \"FIVE\" cinco \n"
70 "six = seis sechs\\\n"
71 " sis\n"
72 "seven=\"sevenval\" #nocomment\n"
73 "eight=eightval #nocomment\n"
74 "export nine=nineval\n"
75 "ten=", f);
76
77 fflush(f);
78 fclose(f);
79
80 r = load_env_file(NULL, t, NULL, &a);
81 assert_se(r >= 0);
82
83 STRV_FOREACH(i, a)
84 log_info("Got: <%s>", *i);
85
86 assert_se(streq_ptr(a[0], "one=BAR"));
87 assert_se(streq_ptr(a[1], "two=bar"));
88 assert_se(streq_ptr(a[2], "three=333\nxxxx"));
89 assert_se(streq_ptr(a[3], "four=44\"44"));
90 assert_se(streq_ptr(a[4], "five=55\'55FIVEcinco"));
91 assert_se(streq_ptr(a[5], "six=seis sechs sis"));
92 assert_se(streq_ptr(a[6], "seven=sevenval#nocomment"));
93 assert_se(streq_ptr(a[7], "eight=eightval #nocomment"));
94 assert_se(streq_ptr(a[8], "export nine=nineval"));
95 assert_se(streq_ptr(a[9], "ten="));
96 assert_se(a[10] == NULL);
97
98 strv_env_clean(a);
99
100 k = 0;
101 STRV_FOREACH(i, b) {
102 log_info("Got2: <%s>", *i);
103 assert_se(streq(*i, a[k++]));
104 }
105
106 r = parse_env_file(
107 t, NULL,
108 "one", &one,
109 "two", &two,
110 "three", &three,
111 "four", &four,
112 "five", &five,
113 "six", &six,
114 "seven", &seven,
115 "eight", &eight,
116 "export nine", &nine,
117 "ten", &ten,
118 NULL);
119
120 assert_se(r >= 0);
121
122 log_info("one=[%s]", strna(one));
123 log_info("two=[%s]", strna(two));
124 log_info("three=[%s]", strna(three));
125 log_info("four=[%s]", strna(four));
126 log_info("five=[%s]", strna(five));
127 log_info("six=[%s]", strna(six));
128 log_info("seven=[%s]", strna(seven));
129 log_info("eight=[%s]", strna(eight));
130 log_info("export nine=[%s]", strna(nine));
131 log_info("ten=[%s]", strna(nine));
132
133 assert_se(streq(one, "BAR"));
134 assert_se(streq(two, "bar"));
135 assert_se(streq(three, "333\nxxxx"));
136 assert_se(streq(four, "44\"44"));
137 assert_se(streq(five, "55\'55FIVEcinco"));
138 assert_se(streq(six, "seis sechs sis"));
139 assert_se(streq(seven, "sevenval#nocomment"));
140 assert_se(streq(eight, "eightval #nocomment"));
141 assert_se(streq(nine, "nineval"));
142 assert_se(ten == NULL);
143
144 r = write_env_file(p, a);
145 assert_se(r >= 0);
146
147 r = load_env_file(NULL, p, NULL, &b);
148 assert_se(r >= 0);
149
150 unlink(t);
151 unlink(p);
152 }
153
154 static void test_parse_multiline_env_file(void) {
155 char t[] = "/tmp/test-fileio-in-XXXXXX",
156 p[] = "/tmp/test-fileio-out-XXXXXX";
157 int fd, r;
158 FILE *f;
159 _cleanup_strv_free_ char **a = NULL, **b = NULL;
160 char **i;
161
162 fd = mkostemp_safe(p, O_RDWR|O_CLOEXEC);
163 assert_se(fd >= 0);
164 close(fd);
165
166 fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC);
167 assert_se(fd >= 0);
168
169 f = fdopen(fd, "w");
170 assert_se(f);
171
172 fputs("one=BAR\\\n"
173 " VAR\\\n"
174 "\tGAR\n"
175 "#comment\n"
176 "two=\"bar\\\n"
177 " var\\\n"
178 "\tgar\"\n"
179 "#comment\n"
180 "tri=\"bar \\\n"
181 " var \\\n"
182 "\tgar \"\n", f);
183
184 fflush(f);
185 fclose(f);
186
187 r = load_env_file(NULL, t, NULL, &a);
188 assert_se(r >= 0);
189
190 STRV_FOREACH(i, a)
191 log_info("Got: <%s>", *i);
192
193 assert_se(streq_ptr(a[0], "one=BAR VAR\tGAR"));
194 assert_se(streq_ptr(a[1], "two=bar var\tgar"));
195 assert_se(streq_ptr(a[2], "tri=bar var \tgar "));
196 assert_se(a[3] == NULL);
197
198 r = write_env_file(p, a);
199 assert_se(r >= 0);
200
201 r = load_env_file(NULL, p, NULL, &b);
202 assert_se(r >= 0);
203
204 unlink(t);
205 unlink(p);
206 }
207
208
209 static void test_executable_is_script(void) {
210 char t[] = "/tmp/test-executable-XXXXXX";
211 int fd, r;
212 FILE *f;
213 char *command;
214
215 fd = mkostemp_safe(t, O_RDWR|O_CLOEXEC);
216 assert_se(fd >= 0);
217
218 f = fdopen(fd, "w");
219 assert_se(f);
220
221 fputs("#! /bin/script -a -b \ngoo goo", f);
222 fflush(f);
223
224 r = executable_is_script(t, &command);
225 assert_se(r > 0);
226 assert_se(streq(command, "/bin/script"));
227 free(command);
228
229 r = executable_is_script("/bin/sh", &command);
230 assert_se(r == 0);
231
232 r = executable_is_script("/usr/bin/yum", &command);
233 assert_se(r > 0 || r == -ENOENT);
234 if (r > 0) {
235 assert_se(startswith(command, "/"));
236 free(command);
237 }
238
239 fclose(f);
240 unlink(t);
241 }
242
243 static void test_status_field(void) {
244 _cleanup_free_ char *t = NULL, *p = NULL, *s = NULL, *z = NULL;
245 unsigned long long total = 0, buffers = 0;
246 int r;
247
248 assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0);
249 puts(t);
250 assert_se(streq(t, "1"));
251
252 r = get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE, &p);
253 if (r != -ENOENT) {
254 assert_se(r == 0);
255 puts(p);
256 assert_se(safe_atollu(p, &total) == 0);
257 }
258
259 r = get_proc_field("/proc/meminfo", "Buffers", WHITESPACE, &s);
260 if (r != -ENOENT) {
261 assert_se(r == 0);
262 puts(s);
263 assert_se(safe_atollu(s, &buffers) == 0);
264 }
265
266 if (p)
267 assert_se(buffers < total);
268
269 /* Seccomp should be a good test for field full of zeros. */
270 r = get_proc_field("/proc/meminfo", "Seccomp", WHITESPACE, &z);
271 if (r != -ENOENT) {
272 assert_se(r == 0);
273 puts(z);
274 assert_se(safe_atollu(z, &buffers) == 0);
275 }
276 }
277
278 static void test_capeff(void) {
279 int pid, p;
280
281 for (pid = 0; pid < 2; pid++) {
282 _cleanup_free_ char *capeff = NULL;
283 int r;
284
285 r = get_process_capeff(0, &capeff);
286 log_info("capeff: '%s' (r=%d)", capeff, r);
287
288 if (r == -ENOENT || r == -EPERM)
289 return;
290
291 assert_se(r == 0);
292 assert_se(*capeff);
293 p = capeff[strspn(capeff, DIGITS "abcdefABCDEF")];
294 assert_se(!p || isspace(p));
295 }
296 }
297
298 static void test_write_string_stream(void) {
299 char fn[] = "/tmp/test-write_string_stream-XXXXXX";
300 _cleanup_fclose_ FILE *f = NULL;
301 int fd;
302 char buf[64];
303
304 fd = mkostemp_safe(fn, O_RDWR);
305 assert_se(fd >= 0);
306
307 f = fdopen(fd, "r");
308 assert_se(f);
309 assert_se(write_string_stream(f, "boohoo", true) < 0);
310
311 f = freopen(fn, "r+", f);
312 assert_se(f);
313
314 assert_se(write_string_stream(f, "boohoo", true) == 0);
315 rewind(f);
316
317 assert_se(fgets(buf, sizeof(buf), f));
318 assert_se(streq(buf, "boohoo\n"));
319
320 f = freopen(fn, "w+", f);
321 assert_se(f);
322
323 assert_se(write_string_stream(f, "boohoo", false) == 0);
324 rewind(f);
325
326 assert_se(fgets(buf, sizeof(buf), f));
327 printf(">%s<", buf);
328 assert_se(streq(buf, "boohoo"));
329
330 unlink(fn);
331 }
332
333 static void test_write_string_file(void) {
334 char fn[] = "/tmp/test-write_string_file-XXXXXX";
335 char buf[64] = {};
336 _cleanup_close_ int fd;
337
338 fd = mkostemp_safe(fn, O_RDWR);
339 assert_se(fd >= 0);
340
341 assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0);
342
343 assert_se(read(fd, buf, sizeof(buf)) == 7);
344 assert_se(streq(buf, "boohoo\n"));
345
346 unlink(fn);
347 }
348
349 static void test_write_string_file_no_create(void) {
350 char fn[] = "/tmp/test-write_string_file_no_create-XXXXXX";
351 _cleanup_close_ int fd;
352 char buf[64] = {0};
353
354 fd = mkostemp_safe(fn, O_RDWR);
355 assert_se(fd >= 0);
356
357 assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0);
358 assert_se(write_string_file(fn, "boohoo", 0) == 0);
359
360 assert_se(read(fd, buf, sizeof(buf)) == strlen("boohoo\n"));
361 assert_se(streq(buf, "boohoo\n"));
362
363 unlink(fn);
364 }
365
366 static void test_write_string_file_verify(void) {
367 _cleanup_free_ char *buf = NULL, *buf2 = NULL;
368 int r;
369
370 assert_se(read_one_line_file("/proc/cmdline", &buf) >= 0);
371 assert_se((buf2 = strjoin(buf, "\n", NULL)));
372
373 r = write_string_file("/proc/cmdline", buf, 0);
374 assert_se(r == -EACCES || r == -EIO);
375 r = write_string_file("/proc/cmdline", buf2, 0);
376 assert_se(r == -EACCES || r == -EIO);
377
378 assert_se(write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
379 assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
380
381 r = write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE);
382 assert_se(r == -EACCES || r == -EIO);
383 assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
384 }
385
386 static void test_load_env_file_pairs(void) {
387 char fn[] = "/tmp/test-load_env_file_pairs-XXXXXX";
388 int fd;
389 int r;
390 _cleanup_fclose_ FILE *f = NULL;
391 _cleanup_strv_free_ char **l = NULL;
392 char **k, **v;
393
394 fd = mkostemp_safe(fn, O_RDWR);
395 assert_se(fd >= 0);
396
397 r = write_string_file(fn,
398 "NAME=\"Arch Linux\"\n"
399 "ID=arch\n"
400 "PRETTY_NAME=\"Arch Linux\"\n"
401 "ANSI_COLOR=\"0;36\"\n"
402 "HOME_URL=\"https://www.archlinux.org/\"\n"
403 "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n"
404 "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n",
405 WRITE_STRING_FILE_CREATE);
406 assert_se(r == 0);
407
408 f = fdopen(fd, "r");
409 assert_se(f);
410
411 r = load_env_file_pairs(f, fn, NULL, &l);
412 assert_se(r >= 0);
413
414 assert_se(strv_length(l) == 14);
415 STRV_FOREACH_PAIR(k, v, l) {
416 assert_se(STR_IN_SET(*k, "NAME", "ID", "PRETTY_NAME", "ANSI_COLOR", "HOME_URL", "SUPPORT_URL", "BUG_REPORT_URL"));
417 printf("%s=%s\n", *k, *v);
418 if (streq(*k, "NAME")) assert_se(streq(*v, "Arch Linux"));
419 if (streq(*k, "ID")) assert_se(streq(*v, "arch"));
420 if (streq(*k, "PRETTY_NAME")) assert_se(streq(*v, "Arch Linux"));
421 if (streq(*k, "ANSI_COLOR")) assert_se(streq(*v, "0;36"));
422 if (streq(*k, "HOME_URL")) assert_se(streq(*v, "https://www.archlinux.org/"));
423 if (streq(*k, "SUPPORT_URL")) assert_se(streq(*v, "https://bbs.archlinux.org/"));
424 if (streq(*k, "BUG_REPORT_URL")) assert_se(streq(*v, "https://bugs.archlinux.org/"));
425 }
426
427 unlink(fn);
428 }
429
430 int main(int argc, char *argv[]) {
431 log_parse_environment();
432 log_open();
433
434 test_parse_env_file();
435 test_parse_multiline_env_file();
436 test_executable_is_script();
437 test_status_field();
438 test_capeff();
439 test_write_string_stream();
440 test_write_string_file();
441 test_write_string_file_no_create();
442 test_write_string_file_verify();
443 test_load_env_file_pairs();
444
445 return 0;
446 }