Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <fcntl.h>
4 : #include <stdio.h>
5 : #include <unistd.h>
6 :
7 : #include "alloc-util.h"
8 : #include "ctype.h"
9 : #include "env-file.h"
10 : #include "env-util.h"
11 : #include "fd-util.h"
12 : #include "fileio.h"
13 : #include "fs-util.h"
14 : #include "io-util.h"
15 : #include "parse-util.h"
16 : #include "process-util.h"
17 : #include "string-util.h"
18 : #include "strv.h"
19 : #include "tests.h"
20 : #include "tmpfile-util.h"
21 : #include "util.h"
22 :
23 1 : static void test_parse_env_file(void) {
24 : _cleanup_(unlink_tempfilep) char
25 1 : t[] = "/tmp/test-fileio-in-XXXXXX",
26 1 : p[] = "/tmp/test-fileio-out-XXXXXX";
27 : FILE *f;
28 1 : _cleanup_free_ char *one = NULL, *two = NULL, *three = NULL, *four = NULL, *five = NULL,
29 1 : *six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL,
30 1 : *eleven = NULL, *twelve = NULL, *thirteen = NULL;
31 1 : _cleanup_strv_free_ char **a = NULL, **b = NULL;
32 : char **i;
33 : unsigned k;
34 : int r;
35 :
36 1 : assert_se(fmkostemp_safe(t, "w", &f) == 0);
37 1 : fputs("one=BAR \n"
38 : "# comment\n"
39 : " # comment \n"
40 : " ; comment \n"
41 : " two = bar \n"
42 : "invalid line\n"
43 : "invalid line #comment\n"
44 : "three = \"333\n"
45 : "xxxx\"\n"
46 : "four = \'44\\\"44\'\n"
47 : "five = \"55\\\"55\" \"FIVE\" cinco \n"
48 : "six = seis sechs\\\n"
49 : " sis\n"
50 : "seven=\"sevenval\" #nocomment\n"
51 : "eight=eightval #nocomment\n"
52 : "export nine=nineval\n"
53 : "ten=ignored\n"
54 : "ten=ignored\n"
55 : "ten=\n"
56 : "eleven=\\value\n"
57 : "twelve=\"\\value\"\n"
58 : "thirteen='\\value'", f);
59 :
60 1 : fflush(f);
61 1 : fclose(f);
62 :
63 1 : r = load_env_file(NULL, t, &a);
64 1 : assert_se(r >= 0);
65 :
66 14 : STRV_FOREACH(i, a)
67 13 : log_info("Got: <%s>", *i);
68 :
69 1 : assert_se(streq_ptr(a[0], "one=BAR"));
70 1 : assert_se(streq_ptr(a[1], "two=bar"));
71 1 : assert_se(streq_ptr(a[2], "three=333\nxxxx"));
72 1 : assert_se(streq_ptr(a[3], "four=44\\\"44"));
73 1 : assert_se(streq_ptr(a[4], "five=55\"55FIVEcinco"));
74 1 : assert_se(streq_ptr(a[5], "six=seis sechs sis"));
75 1 : assert_se(streq_ptr(a[6], "seven=sevenval#nocomment"));
76 1 : assert_se(streq_ptr(a[7], "eight=eightval #nocomment"));
77 1 : assert_se(streq_ptr(a[8], "export nine=nineval"));
78 1 : assert_se(streq_ptr(a[9], "ten="));
79 1 : assert_se(streq_ptr(a[10], "eleven=value"));
80 1 : assert_se(streq_ptr(a[11], "twelve=\\value"));
81 1 : assert_se(streq_ptr(a[12], "thirteen=\\value"));
82 1 : assert_se(a[13] == NULL);
83 :
84 1 : strv_env_clean(a);
85 :
86 1 : k = 0;
87 1 : STRV_FOREACH(i, b) {
88 0 : log_info("Got2: <%s>", *i);
89 0 : assert_se(streq(*i, a[k++]));
90 : }
91 :
92 1 : r = parse_env_file(
93 : NULL, t,
94 : "one", &one,
95 : "two", &two,
96 : "three", &three,
97 : "four", &four,
98 : "five", &five,
99 : "six", &six,
100 : "seven", &seven,
101 : "eight", &eight,
102 : "export nine", &nine,
103 : "ten", &ten,
104 : "eleven", &eleven,
105 : "twelve", &twelve,
106 : "thirteen", &thirteen);
107 :
108 1 : assert_se(r >= 0);
109 :
110 1 : log_info("one=[%s]", strna(one));
111 1 : log_info("two=[%s]", strna(two));
112 1 : log_info("three=[%s]", strna(three));
113 1 : log_info("four=[%s]", strna(four));
114 1 : log_info("five=[%s]", strna(five));
115 1 : log_info("six=[%s]", strna(six));
116 1 : log_info("seven=[%s]", strna(seven));
117 1 : log_info("eight=[%s]", strna(eight));
118 1 : log_info("export nine=[%s]", strna(nine));
119 1 : log_info("ten=[%s]", strna(nine));
120 1 : log_info("eleven=[%s]", strna(eleven));
121 1 : log_info("twelve=[%s]", strna(twelve));
122 1 : log_info("thirteen=[%s]", strna(thirteen));
123 :
124 1 : assert_se(streq(one, "BAR"));
125 1 : assert_se(streq(two, "bar"));
126 1 : assert_se(streq(three, "333\nxxxx"));
127 1 : assert_se(streq(four, "44\\\"44"));
128 1 : assert_se(streq(five, "55\"55FIVEcinco"));
129 1 : assert_se(streq(six, "seis sechs sis"));
130 1 : assert_se(streq(seven, "sevenval#nocomment"));
131 1 : assert_se(streq(eight, "eightval #nocomment"));
132 1 : assert_se(streq(nine, "nineval"));
133 1 : assert_se(ten == NULL);
134 1 : assert_se(streq(eleven, "value"));
135 1 : assert_se(streq(twelve, "\\value"));
136 1 : assert_se(streq(thirteen, "\\value"));
137 :
138 : {
139 : /* prepare a temporary file to write the environment to */
140 2 : _cleanup_close_ int fd = mkostemp_safe(p);
141 1 : assert_se(fd >= 0);
142 : }
143 :
144 1 : r = write_env_file(p, a);
145 1 : assert_se(r >= 0);
146 :
147 1 : r = load_env_file(NULL, p, &b);
148 1 : assert_se(r >= 0);
149 1 : }
150 :
151 1 : static void test_parse_multiline_env_file(void) {
152 : _cleanup_(unlink_tempfilep) char
153 1 : t[] = "/tmp/test-fileio-in-XXXXXX",
154 1 : p[] = "/tmp/test-fileio-out-XXXXXX";
155 : FILE *f;
156 1 : _cleanup_strv_free_ char **a = NULL, **b = NULL;
157 : char **i;
158 : int r;
159 :
160 1 : assert_se(fmkostemp_safe(t, "w", &f) == 0);
161 1 : fputs("one=BAR\\\n"
162 : " VAR\\\n"
163 : "\tGAR\n"
164 : "#comment\n"
165 : "two=\"bar\\\n"
166 : " var\\\n"
167 : "\tgar\"\n"
168 : "#comment\n"
169 : "tri=\"bar \\\n"
170 : " var \\\n"
171 : "\tgar \"\n", f);
172 :
173 1 : fflush(f);
174 1 : fclose(f);
175 :
176 1 : r = load_env_file(NULL, t, &a);
177 1 : assert_se(r >= 0);
178 :
179 4 : STRV_FOREACH(i, a)
180 3 : log_info("Got: <%s>", *i);
181 :
182 1 : assert_se(streq_ptr(a[0], "one=BAR VAR\tGAR"));
183 1 : assert_se(streq_ptr(a[1], "two=bar var\tgar"));
184 1 : assert_se(streq_ptr(a[2], "tri=bar var \tgar "));
185 1 : assert_se(a[3] == NULL);
186 :
187 : {
188 2 : _cleanup_close_ int fd = mkostemp_safe(p);
189 1 : assert_se(fd >= 0);
190 : }
191 :
192 1 : r = write_env_file(p, a);
193 1 : assert_se(r >= 0);
194 :
195 1 : r = load_env_file(NULL, p, &b);
196 1 : assert_se(r >= 0);
197 1 : }
198 :
199 1 : static void test_merge_env_file(void) {
200 1 : _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
201 1 : _cleanup_fclose_ FILE *f = NULL;
202 1 : _cleanup_strv_free_ char **a = NULL;
203 : char **i;
204 : int r;
205 :
206 1 : assert_se(fmkostemp_safe(t, "w", &f) == 0);
207 1 : log_info("/* %s (%s) */", __func__, t);
208 :
209 1 : r = write_string_stream(f,
210 : "one=1 \n"
211 : "twelve=${one}2\n"
212 : "twentyone=2${one}\n"
213 : "one=2\n"
214 : "twentytwo=2${one}\n"
215 : "xxx_minus_three=$xxx - 3\n"
216 : "xxx=0x$one$one$one\n"
217 : "yyy=${one:-fallback}\n"
218 : "zzz=${one:+replacement}\n"
219 : "zzzz=${foobar:-${nothing}}\n"
220 : "zzzzz=${nothing:+${nothing}}\n"
221 : , WRITE_STRING_FILE_AVOID_NEWLINE);
222 1 : assert(r >= 0);
223 :
224 1 : r = merge_env_file(&a, NULL, t);
225 1 : assert_se(r >= 0);
226 1 : strv_sort(a);
227 :
228 11 : STRV_FOREACH(i, a)
229 10 : log_info("Got: <%s>", *i);
230 :
231 1 : assert_se(streq(a[0], "one=2"));
232 1 : assert_se(streq(a[1], "twelve=12"));
233 1 : assert_se(streq(a[2], "twentyone=21"));
234 1 : assert_se(streq(a[3], "twentytwo=22"));
235 1 : assert_se(streq(a[4], "xxx=0x222"));
236 1 : assert_se(streq(a[5], "xxx_minus_three= - 3"));
237 1 : assert_se(streq(a[6], "yyy=2"));
238 1 : assert_se(streq(a[7], "zzz=replacement"));
239 1 : assert_se(streq(a[8], "zzzz="));
240 1 : assert_se(streq(a[9], "zzzzz="));
241 1 : assert_se(a[10] == NULL);
242 :
243 1 : r = merge_env_file(&a, NULL, t);
244 1 : assert_se(r >= 0);
245 1 : strv_sort(a);
246 :
247 11 : STRV_FOREACH(i, a)
248 10 : log_info("Got2: <%s>", *i);
249 :
250 1 : assert_se(streq(a[0], "one=2"));
251 1 : assert_se(streq(a[1], "twelve=12"));
252 1 : assert_se(streq(a[2], "twentyone=21"));
253 1 : assert_se(streq(a[3], "twentytwo=22"));
254 1 : assert_se(streq(a[4], "xxx=0x222"));
255 1 : assert_se(streq(a[5], "xxx_minus_three=0x222 - 3"));
256 1 : assert_se(streq(a[6], "yyy=2"));
257 1 : assert_se(streq(a[7], "zzz=replacement"));
258 1 : assert_se(streq(a[8], "zzzz="));
259 1 : assert_se(streq(a[9], "zzzzz="));
260 1 : assert_se(a[10] == NULL);
261 1 : }
262 :
263 1 : static void test_merge_env_file_invalid(void) {
264 1 : _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
265 1 : _cleanup_fclose_ FILE *f = NULL;
266 1 : _cleanup_strv_free_ char **a = NULL;
267 : char **i;
268 : int r;
269 :
270 1 : assert_se(fmkostemp_safe(t, "w", &f) == 0);
271 1 : log_info("/* %s (%s) */", __func__, t);
272 :
273 1 : r = write_string_stream(f,
274 : "unset one \n"
275 : "unset one= \n"
276 : "unset one=1 \n"
277 : "one \n"
278 : "one = \n"
279 : "one two =\n"
280 : "\x20two=\n"
281 : "#comment=comment\n"
282 : ";comment2=comment2\n"
283 : "#\n"
284 : "\n\n" /* empty line */
285 : , WRITE_STRING_FILE_AVOID_NEWLINE);
286 1 : assert(r >= 0);
287 :
288 1 : r = merge_env_file(&a, NULL, t);
289 1 : assert_se(r >= 0);
290 :
291 1 : STRV_FOREACH(i, a)
292 0 : log_info("Got: <%s>", *i);
293 :
294 1 : assert_se(strv_isempty(a));
295 1 : }
296 :
297 1 : static void test_executable_is_script(void) {
298 1 : _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX";
299 1 : _cleanup_fclose_ FILE *f = NULL;
300 : char *command;
301 : int r;
302 :
303 1 : assert_se(fmkostemp_safe(t, "w", &f) == 0);
304 1 : fputs("#! /bin/script -a -b \ngoo goo", f);
305 1 : fflush(f);
306 :
307 1 : r = executable_is_script(t, &command);
308 1 : assert_se(r > 0);
309 1 : assert_se(streq(command, "/bin/script"));
310 1 : free(command);
311 :
312 1 : r = executable_is_script("/bin/sh", &command);
313 1 : assert_se(r == 0);
314 :
315 1 : r = executable_is_script("/usr/bin/yum", &command);
316 1 : assert_se(r > 0 || r == -ENOENT);
317 1 : if (r > 0) {
318 1 : assert_se(startswith(command, "/"));
319 1 : free(command);
320 : }
321 1 : }
322 :
323 1 : static void test_status_field(void) {
324 1 : _cleanup_free_ char *t = NULL, *p = NULL, *s = NULL, *z = NULL;
325 1 : unsigned long long total = 0, buffers = 0;
326 : int r;
327 :
328 1 : assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t) == 0);
329 1 : puts(t);
330 1 : assert_se(streq(t, "1"));
331 :
332 1 : r = get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE, &p);
333 1 : if (r != -ENOENT) {
334 1 : assert_se(r == 0);
335 1 : puts(p);
336 1 : assert_se(safe_atollu(p, &total) == 0);
337 : }
338 :
339 1 : r = get_proc_field("/proc/meminfo", "Buffers", WHITESPACE, &s);
340 1 : if (r != -ENOENT) {
341 1 : assert_se(r == 0);
342 1 : puts(s);
343 1 : assert_se(safe_atollu(s, &buffers) == 0);
344 : }
345 :
346 1 : if (p)
347 1 : assert_se(buffers < total);
348 :
349 : /* Seccomp should be a good test for field full of zeros. */
350 1 : r = get_proc_field("/proc/meminfo", "Seccomp", WHITESPACE, &z);
351 1 : if (r != -ENOENT) {
352 0 : assert_se(r == 0);
353 0 : puts(z);
354 0 : assert_se(safe_atollu(z, &buffers) == 0);
355 : }
356 1 : }
357 :
358 1 : static void test_capeff(void) {
359 : int pid, p;
360 :
361 3 : for (pid = 0; pid < 2; pid++) {
362 2 : _cleanup_free_ char *capeff = NULL;
363 : int r;
364 :
365 2 : r = get_process_capeff(0, &capeff);
366 2 : log_info("capeff: '%s' (r=%d)", capeff, r);
367 :
368 2 : if (IN_SET(r, -ENOENT, -EPERM))
369 0 : return;
370 :
371 2 : assert_se(r == 0);
372 2 : assert_se(*capeff);
373 2 : p = capeff[strspn(capeff, HEXDIGITS)];
374 2 : assert_se(!p || isspace(p));
375 : }
376 : }
377 :
378 1 : static void test_write_string_stream(void) {
379 1 : _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-write_string_stream-XXXXXX";
380 1 : _cleanup_fclose_ FILE *f = NULL;
381 : int fd;
382 : char buf[64];
383 :
384 1 : fd = mkostemp_safe(fn);
385 1 : assert_se(fd >= 0);
386 :
387 1 : f = fdopen(fd, "r");
388 1 : assert_se(f);
389 1 : assert_se(write_string_stream(f, "boohoo", 0) < 0);
390 1 : f = safe_fclose(f);
391 :
392 1 : f = fopen(fn, "r+");
393 1 : assert_se(f);
394 :
395 1 : assert_se(write_string_stream(f, "boohoo", 0) == 0);
396 1 : rewind(f);
397 :
398 1 : assert_se(fgets(buf, sizeof(buf), f));
399 1 : assert_se(streq(buf, "boohoo\n"));
400 1 : f = safe_fclose(f);
401 :
402 1 : f = fopen(fn, "w+");
403 1 : assert_se(f);
404 :
405 1 : assert_se(write_string_stream(f, "boohoo", WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
406 1 : rewind(f);
407 :
408 1 : assert_se(fgets(buf, sizeof(buf), f));
409 1 : printf(">%s<", buf);
410 1 : assert_se(streq(buf, "boohoo"));
411 1 : }
412 :
413 1 : static void test_write_string_file(void) {
414 1 : _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-write_string_file-XXXXXX";
415 1 : char buf[64] = {};
416 1 : _cleanup_close_ int fd;
417 :
418 1 : fd = mkostemp_safe(fn);
419 1 : assert_se(fd >= 0);
420 :
421 1 : assert_se(write_string_file(fn, "boohoo", WRITE_STRING_FILE_CREATE) == 0);
422 :
423 1 : assert_se(read(fd, buf, sizeof(buf)) == 7);
424 1 : assert_se(streq(buf, "boohoo\n"));
425 1 : }
426 :
427 1 : static void test_write_string_file_no_create(void) {
428 1 : _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-write_string_file_no_create-XXXXXX";
429 1 : _cleanup_close_ int fd;
430 1 : char buf[64] = {};
431 :
432 1 : fd = mkostemp_safe(fn);
433 1 : assert_se(fd >= 0);
434 :
435 1 : assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0);
436 1 : assert_se(write_string_file(fn, "boohoo", 0) == 0);
437 :
438 1 : assert_se(read(fd, buf, sizeof buf) == (ssize_t) strlen("boohoo\n"));
439 1 : assert_se(streq(buf, "boohoo\n"));
440 1 : }
441 :
442 1 : static void test_write_string_file_verify(void) {
443 1 : _cleanup_free_ char *buf = NULL, *buf2 = NULL;
444 : int r;
445 :
446 1 : assert_se(read_one_line_file("/proc/cmdline", &buf) >= 0);
447 1 : assert_se(buf2 = strjoin(buf, "\n"));
448 :
449 1 : r = write_string_file("/proc/cmdline", buf, 0);
450 1 : assert_se(IN_SET(r, -EACCES, -EIO));
451 1 : r = write_string_file("/proc/cmdline", buf2, 0);
452 1 : assert_se(IN_SET(r, -EACCES, -EIO));
453 :
454 1 : assert_se(write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
455 1 : assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE) == 0);
456 :
457 1 : r = write_string_file("/proc/cmdline", buf, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE);
458 1 : assert_se(IN_SET(r, -EACCES, -EIO));
459 1 : assert_se(write_string_file("/proc/cmdline", buf2, WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_AVOID_NEWLINE) == 0);
460 1 : }
461 :
462 1 : static void test_load_env_file_pairs(void) {
463 1 : _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-load_env_file_pairs-XXXXXX";
464 : int fd, r;
465 1 : _cleanup_fclose_ FILE *f = NULL;
466 1 : _cleanup_strv_free_ char **l = NULL;
467 : char **k, **v;
468 :
469 1 : fd = mkostemp_safe(fn);
470 1 : assert_se(fd >= 0);
471 :
472 1 : r = write_string_file(fn,
473 : "NAME=\"Arch Linux\"\n"
474 : "ID=arch\n"
475 : "PRETTY_NAME=\"Arch Linux\"\n"
476 : "ANSI_COLOR=\"0;36\"\n"
477 : "HOME_URL=\"https://www.archlinux.org/\"\n"
478 : "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n"
479 : "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n",
480 : WRITE_STRING_FILE_CREATE);
481 1 : assert_se(r == 0);
482 :
483 1 : f = fdopen(fd, "r");
484 1 : assert_se(f);
485 :
486 1 : r = load_env_file_pairs(f, fn, &l);
487 1 : assert_se(r >= 0);
488 :
489 1 : assert_se(strv_length(l) == 14);
490 8 : STRV_FOREACH_PAIR(k, v, l) {
491 7 : assert_se(STR_IN_SET(*k, "NAME", "ID", "PRETTY_NAME", "ANSI_COLOR", "HOME_URL", "SUPPORT_URL", "BUG_REPORT_URL"));
492 7 : printf("%s=%s\n", *k, *v);
493 7 : if (streq(*k, "NAME")) assert_se(streq(*v, "Arch Linux"));
494 7 : if (streq(*k, "ID")) assert_se(streq(*v, "arch"));
495 7 : if (streq(*k, "PRETTY_NAME")) assert_se(streq(*v, "Arch Linux"));
496 7 : if (streq(*k, "ANSI_COLOR")) assert_se(streq(*v, "0;36"));
497 7 : if (streq(*k, "HOME_URL")) assert_se(streq(*v, "https://www.archlinux.org/"));
498 7 : if (streq(*k, "SUPPORT_URL")) assert_se(streq(*v, "https://bbs.archlinux.org/"));
499 7 : if (streq(*k, "BUG_REPORT_URL")) assert_se(streq(*v, "https://bugs.archlinux.org/"));
500 : }
501 1 : }
502 :
503 1 : static void test_search_and_fopen(void) {
504 1 : const char *dirs[] = {"/tmp/foo/bar", "/tmp", NULL};
505 :
506 1 : char name[] = "/tmp/test-search_and_fopen.XXXXXX";
507 : int fd, r;
508 : FILE *f;
509 :
510 1 : fd = mkostemp_safe(name);
511 1 : assert_se(fd >= 0);
512 1 : close(fd);
513 :
514 1 : r = search_and_fopen(basename(name), "r", NULL, dirs, &f);
515 1 : assert_se(r >= 0);
516 1 : fclose(f);
517 :
518 1 : r = search_and_fopen(name, "r", NULL, dirs, &f);
519 1 : assert_se(r >= 0);
520 1 : fclose(f);
521 :
522 1 : r = search_and_fopen(basename(name), "r", "/", dirs, &f);
523 1 : assert_se(r >= 0);
524 1 : fclose(f);
525 :
526 1 : r = search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f);
527 1 : assert_se(r < 0);
528 1 : r = search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f);
529 1 : assert_se(r < 0);
530 :
531 1 : r = unlink(name);
532 1 : assert_se(r == 0);
533 :
534 1 : r = search_and_fopen(basename(name), "r", NULL, dirs, &f);
535 1 : assert_se(r < 0);
536 1 : }
537 :
538 1 : static void test_search_and_fopen_nulstr(void) {
539 1 : const char dirs[] = "/tmp/foo/bar\0/tmp\0";
540 :
541 1 : _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-search_and_fopen.XXXXXX";
542 : int fd, r;
543 : FILE *f;
544 :
545 1 : fd = mkostemp_safe(name);
546 1 : assert_se(fd >= 0);
547 1 : close(fd);
548 :
549 1 : r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f);
550 1 : assert_se(r >= 0);
551 1 : fclose(f);
552 :
553 1 : r = search_and_fopen_nulstr(name, "r", NULL, dirs, &f);
554 1 : assert_se(r >= 0);
555 1 : fclose(f);
556 :
557 1 : r = search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL, dirs, &f);
558 1 : assert_se(r < 0);
559 1 : r = search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL, dirs, &f);
560 1 : assert_se(r < 0);
561 :
562 1 : r = unlink(name);
563 1 : assert_se(r == 0);
564 :
565 1 : r = search_and_fopen_nulstr(basename(name), "r", NULL, dirs, &f);
566 1 : assert_se(r < 0);
567 1 : }
568 :
569 1 : static void test_writing_tmpfile(void) {
570 1 : _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-systemd_writing_tmpfile.XXXXXX";
571 1 : _cleanup_free_ char *contents = NULL;
572 : size_t size;
573 1 : _cleanup_close_ int fd = -1;
574 : struct iovec iov[3];
575 : int r;
576 :
577 1 : iov[0] = IOVEC_MAKE_STRING("abc\n");
578 1 : iov[1] = IOVEC_MAKE_STRING(ALPHANUMERICAL "\n");
579 1 : iov[2] = IOVEC_MAKE_STRING("");
580 :
581 1 : fd = mkostemp_safe(name);
582 1 : printf("tmpfile: %s", name);
583 :
584 1 : r = writev(fd, iov, 3);
585 1 : assert_se(r >= 0);
586 :
587 1 : r = read_full_file(name, &contents, &size);
588 1 : assert_se(r == 0);
589 1 : printf("contents: %s", contents);
590 1 : assert_se(streq(contents, "abc\n" ALPHANUMERICAL "\n"));
591 1 : }
592 :
593 1 : static void test_tempfn(void) {
594 1 : char *ret = NULL, *p;
595 :
596 1 : assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL, &ret) >= 0);
597 1 : assert_se(streq_ptr(ret, "/foo/bar/.#waldoXXXXXX"));
598 1 : free(ret);
599 :
600 1 : assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret) >= 0);
601 1 : assert_se(streq_ptr(ret, "/foo/bar/.#[miau]waldoXXXXXX"));
602 1 : free(ret);
603 :
604 1 : assert_se(tempfn_random("/foo/bar/waldo", NULL, &ret) >= 0);
605 1 : assert_se(p = startswith(ret, "/foo/bar/.#waldo"));
606 1 : assert_se(strlen(p) == 16);
607 1 : assert_se(in_charset(p, "0123456789abcdef"));
608 1 : free(ret);
609 :
610 1 : assert_se(tempfn_random("/foo/bar/waldo", "[wuff]", &ret) >= 0);
611 1 : assert_se(p = startswith(ret, "/foo/bar/.#[wuff]waldo"));
612 1 : assert_se(strlen(p) == 16);
613 1 : assert_se(in_charset(p, "0123456789abcdef"));
614 1 : free(ret);
615 :
616 1 : assert_se(tempfn_random_child("/foo/bar/waldo", NULL, &ret) >= 0);
617 1 : assert_se(p = startswith(ret, "/foo/bar/waldo/.#"));
618 1 : assert_se(strlen(p) == 16);
619 1 : assert_se(in_charset(p, "0123456789abcdef"));
620 1 : free(ret);
621 :
622 1 : assert_se(tempfn_random_child("/foo/bar/waldo", "[kikiriki]", &ret) >= 0);
623 1 : assert_se(p = startswith(ret, "/foo/bar/waldo/.#[kikiriki]"));
624 1 : assert_se(strlen(p) == 16);
625 1 : assert_se(in_charset(p, "0123456789abcdef"));
626 1 : free(ret);
627 1 : }
628 :
629 : static const char chars[] =
630 : "Aąę„”\n루\377";
631 :
632 : #pragma GCC diagnostic push
633 : #pragma GCC diagnostic ignored "-Wtype-limits"
634 :
635 1 : static void test_fgetc(void) {
636 1 : _cleanup_fclose_ FILE *f = NULL;
637 : char c;
638 :
639 1 : f = fmemopen_unlocked((void*) chars, sizeof(chars), "re");
640 1 : assert_se(f);
641 :
642 18 : for (unsigned i = 0; i < sizeof(chars); i++) {
643 17 : assert_se(safe_fgetc(f, &c) == 1);
644 17 : assert_se(c == chars[i]);
645 :
646 : /* EOF is -1, and hence we can't push value 255 in this way if char is signed */
647 17 : assert_se(ungetc(c, f) != EOF || c == EOF);
648 17 : assert_se(c == EOF || safe_fgetc(f, &c) == 1);
649 17 : assert_se(c == chars[i]);
650 :
651 : /* But it works when we push it properly cast */
652 17 : assert_se(ungetc((unsigned char) c, f) != EOF);
653 17 : assert_se(safe_fgetc(f, &c) == 1);
654 17 : assert_se(c == chars[i]);
655 : }
656 :
657 1 : assert_se(safe_fgetc(f, &c) == 0);
658 1 : }
659 :
660 : #pragma GCC diagnostic pop
661 :
662 : static const char buffer[] =
663 : "Some test data\n"
664 : "루Non-ascii chars: ąę„”\n"
665 : "terminators\r\n"
666 : "and even more\n\r"
667 : "now the same with a NUL\n\0"
668 : "and more\r\0"
669 : "and even more\r\n\0"
670 : "and yet even more\n\r\0"
671 : "With newlines, and a NUL byte\0"
672 : "\n"
673 : "an empty line\n"
674 : "an ignored line\n"
675 : "and a very long line that is supposed to be truncated, because it is so long\n";
676 :
677 2 : static void test_read_line_one_file(FILE *f) {
678 2 : _cleanup_free_ char *line = NULL;
679 :
680 2 : assert_se(read_line(f, (size_t) -1, &line) == 15 && streq(line, "Some test data"));
681 2 : line = mfree(line);
682 :
683 2 : assert_se(read_line(f, (size_t) -1, &line) > 0 && streq(line, "루Non-ascii chars: ąę„”"));
684 2 : line = mfree(line);
685 :
686 2 : assert_se(read_line(f, (size_t) -1, &line) == 13 && streq(line, "terminators"));
687 2 : line = mfree(line);
688 :
689 2 : assert_se(read_line(f, (size_t) -1, &line) == 15 && streq(line, "and even more"));
690 2 : line = mfree(line);
691 :
692 2 : assert_se(read_line(f, (size_t) -1, &line) == 25 && streq(line, "now the same with a NUL"));
693 2 : line = mfree(line);
694 :
695 2 : assert_se(read_line(f, (size_t) -1, &line) == 10 && streq(line, "and more"));
696 2 : line = mfree(line);
697 :
698 2 : assert_se(read_line(f, (size_t) -1, &line) == 16 && streq(line, "and even more"));
699 2 : line = mfree(line);
700 :
701 2 : assert_se(read_line(f, (size_t) -1, &line) == 20 && streq(line, "and yet even more"));
702 2 : line = mfree(line);
703 :
704 2 : assert_se(read_line(f, 1024, &line) == 30 && streq(line, "With newlines, and a NUL byte"));
705 2 : line = mfree(line);
706 :
707 2 : assert_se(read_line(f, 1024, &line) == 1 && streq(line, ""));
708 2 : line = mfree(line);
709 :
710 2 : assert_se(read_line(f, 1024, &line) == 14 && streq(line, "an empty line"));
711 2 : line = mfree(line);
712 :
713 2 : assert_se(read_line(f, (size_t) -1, NULL) == 16);
714 :
715 2 : assert_se(read_line(f, 16, &line) == -ENOBUFS);
716 2 : line = mfree(line);
717 :
718 : /* read_line() stopped when it hit the limit, that means when we continue reading we'll read at the first
719 : * character after the previous limit. Let's make use of that to continue our test. */
720 2 : assert_se(read_line(f, 1024, &line) == 62 && streq(line, "line that is supposed to be truncated, because it is so long"));
721 2 : line = mfree(line);
722 :
723 2 : assert_se(read_line(f, 1024, &line) == 0 && streq(line, ""));
724 2 : }
725 :
726 1 : static void test_read_line(void) {
727 1 : _cleanup_fclose_ FILE *f = NULL;
728 :
729 1 : f = fmemopen_unlocked((void*) buffer, sizeof(buffer), "re");
730 1 : assert_se(f);
731 :
732 1 : test_read_line_one_file(f);
733 1 : }
734 :
735 1 : static void test_read_line2(void) {
736 1 : _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-fileio.XXXXXX";
737 : int fd;
738 1 : _cleanup_fclose_ FILE *f = NULL;
739 :
740 1 : fd = mkostemp_safe(name);
741 1 : assert_se(fd >= 0);
742 1 : assert_se((size_t) write(fd, buffer, sizeof(buffer)) == sizeof(buffer));
743 :
744 1 : assert_se(lseek(fd, 0, SEEK_SET) == 0);
745 1 : assert_se(f = fdopen(fd, "r"));
746 :
747 1 : test_read_line_one_file(f);
748 1 : }
749 :
750 1 : static void test_read_line3(void) {
751 1 : _cleanup_fclose_ FILE *f = NULL;
752 1 : _cleanup_free_ char *line = NULL;
753 : int r;
754 :
755 1 : f = fopen("/proc/cmdline", "re");
756 1 : if (!f && IN_SET(errno, ENOENT, EPERM))
757 0 : return;
758 1 : assert_se(f);
759 :
760 1 : r = read_line(f, LINE_MAX, &line);
761 1 : assert_se(r >= 0);
762 1 : if (r == 0)
763 0 : assert_se(line && isempty(line));
764 : else
765 1 : assert_se((size_t) r == strlen(line) + 1);
766 1 : assert_se(read_line(f, LINE_MAX, NULL) == 0);
767 : }
768 :
769 1 : static void test_read_line4(void) {
770 : static const struct {
771 : size_t length;
772 : const char *string;
773 : } eof_endings[] = {
774 : /* Each of these will be followed by EOF and should generate the one same single string */
775 : { 3, "foo" },
776 : { 4, "foo\n" },
777 : { 4, "foo\r" },
778 : { 4, "foo\0" },
779 : { 5, "foo\n\0" },
780 : { 5, "foo\r\0" },
781 : { 5, "foo\r\n" },
782 : { 5, "foo\n\r" },
783 : { 6, "foo\r\n\0" },
784 : { 6, "foo\n\r\0" },
785 : };
786 :
787 : size_t i;
788 : int r;
789 :
790 11 : for (i = 0; i < ELEMENTSOF(eof_endings); i++) {
791 10 : _cleanup_fclose_ FILE *f = NULL;
792 10 : _cleanup_free_ char *s = NULL;
793 :
794 10 : assert_se(f = fmemopen_unlocked((void*) eof_endings[i].string, eof_endings[i].length, "r"));
795 :
796 10 : r = read_line(f, (size_t) -1, &s);
797 10 : assert_se((size_t) r == eof_endings[i].length);
798 10 : assert_se(streq_ptr(s, "foo"));
799 :
800 10 : assert_se(read_line(f, (size_t) -1, NULL) == 0); /* Ensure we hit EOF */
801 : }
802 1 : }
803 :
804 1 : static void test_read_nul_string(void) {
805 : static const char test[] = "string nr. 1\0"
806 : "string nr. 2\n\0"
807 : "\377empty string follows\0"
808 : "\0"
809 : "final string\n is empty\0"
810 : "\0";
811 :
812 1 : _cleanup_fclose_ FILE *f = NULL;
813 1 : _cleanup_free_ char *s = NULL;
814 :
815 1 : assert_se(f = fmemopen_unlocked((void*) test, sizeof(test)-1, "r"));
816 :
817 1 : assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 13 && streq_ptr(s, "string nr. 1"));
818 1 : s = mfree(s);
819 :
820 1 : assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 14 && streq_ptr(s, "string nr. 2\n"));
821 1 : s = mfree(s);
822 :
823 1 : assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 22 && streq_ptr(s, "\377empty string follows"));
824 1 : s = mfree(s);
825 :
826 1 : assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 1 && streq_ptr(s, ""));
827 1 : s = mfree(s);
828 :
829 1 : assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 23 && streq_ptr(s, "final string\n is empty"));
830 1 : s = mfree(s);
831 :
832 1 : assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 1 && streq_ptr(s, ""));
833 1 : s = mfree(s);
834 :
835 1 : assert_se(read_nul_string(f, LONG_LINE_MAX, &s) == 0 && streq_ptr(s, ""));
836 1 : }
837 :
838 1 : int main(int argc, char *argv[]) {
839 1 : test_setup_logging(LOG_DEBUG);
840 :
841 1 : test_parse_env_file();
842 1 : test_parse_multiline_env_file();
843 1 : test_merge_env_file();
844 1 : test_merge_env_file_invalid();
845 1 : test_executable_is_script();
846 1 : test_status_field();
847 1 : test_capeff();
848 1 : test_write_string_stream();
849 1 : test_write_string_file();
850 1 : test_write_string_file_no_create();
851 1 : test_write_string_file_verify();
852 1 : test_load_env_file_pairs();
853 1 : test_search_and_fopen();
854 1 : test_search_and_fopen_nulstr();
855 1 : test_writing_tmpfile();
856 1 : test_tempfn();
857 1 : test_fgetc();
858 1 : test_read_line();
859 1 : test_read_line2();
860 1 : test_read_line3();
861 1 : test_read_line4();
862 1 : test_read_nul_string();
863 :
864 1 : return 0;
865 : }
|