1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
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.
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.
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/>.
25 #include "alloc-util.h"
32 #include "parse-util.h"
33 #include "process-util.h"
34 #include "string-util.h"
38 static void test_parse_env_file(void) {
39 char t
[] = "/tmp/test-fileio-in-XXXXXX",
40 p
[] = "/tmp/test-fileio-out-XXXXXX";
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
;
49 fd
= mkostemp_safe(p
);
53 fd
= mkostemp_safe(t
);
65 "invalid line #comment\n"
68 "four = \'44\\\"44\'\n"
69 "five = \'55\\\'55\' \"FIVE\" cinco \n"
70 "six = seis sechs\\\n"
72 "seven=\"sevenval\" #nocomment\n"
73 "eight=eightval #nocomment\n"
74 "export nine=nineval\n"
82 r
= load_env_file(NULL
, t
, NULL
, &a
);
86 log_info("Got: <%s>", *i
);
88 assert_se(streq_ptr(a
[0], "one=BAR"));
89 assert_se(streq_ptr(a
[1], "two=bar"));
90 assert_se(streq_ptr(a
[2], "three=333\nxxxx"));
91 assert_se(streq_ptr(a
[3], "four=44\"44"));
92 assert_se(streq_ptr(a
[4], "five=55\'55FIVEcinco"));
93 assert_se(streq_ptr(a
[5], "six=seis sechs sis"));
94 assert_se(streq_ptr(a
[6], "seven=sevenval#nocomment"));
95 assert_se(streq_ptr(a
[7], "eight=eightval #nocomment"));
96 assert_se(streq_ptr(a
[8], "export nine=nineval"));
97 assert_se(streq_ptr(a
[9], "ten="));
98 assert_se(a
[10] == NULL
);
104 log_info("Got2: <%s>", *i
);
105 assert_se(streq(*i
, a
[k
++]));
118 "export nine", &nine
,
124 log_info("one=[%s]", strna(one
));
125 log_info("two=[%s]", strna(two
));
126 log_info("three=[%s]", strna(three
));
127 log_info("four=[%s]", strna(four
));
128 log_info("five=[%s]", strna(five
));
129 log_info("six=[%s]", strna(six
));
130 log_info("seven=[%s]", strna(seven
));
131 log_info("eight=[%s]", strna(eight
));
132 log_info("export nine=[%s]", strna(nine
));
133 log_info("ten=[%s]", strna(nine
));
135 assert_se(streq(one
, "BAR"));
136 assert_se(streq(two
, "bar"));
137 assert_se(streq(three
, "333\nxxxx"));
138 assert_se(streq(four
, "44\"44"));
139 assert_se(streq(five
, "55\'55FIVEcinco"));
140 assert_se(streq(six
, "seis sechs sis"));
141 assert_se(streq(seven
, "sevenval#nocomment"));
142 assert_se(streq(eight
, "eightval #nocomment"));
143 assert_se(streq(nine
, "nineval"));
144 assert_se(ten
== NULL
);
146 r
= write_env_file(p
, a
);
149 r
= load_env_file(NULL
, p
, NULL
, &b
);
156 static void test_parse_multiline_env_file(void) {
157 char t
[] = "/tmp/test-fileio-in-XXXXXX",
158 p
[] = "/tmp/test-fileio-out-XXXXXX";
161 _cleanup_strv_free_
char **a
= NULL
, **b
= NULL
;
164 fd
= mkostemp_safe(p
);
168 fd
= mkostemp_safe(t
);
189 r
= load_env_file(NULL
, t
, NULL
, &a
);
193 log_info("Got: <%s>", *i
);
195 assert_se(streq_ptr(a
[0], "one=BAR VAR\tGAR"));
196 assert_se(streq_ptr(a
[1], "two=bar var\tgar"));
197 assert_se(streq_ptr(a
[2], "tri=bar var \tgar "));
198 assert_se(a
[3] == NULL
);
200 r
= write_env_file(p
, a
);
203 r
= load_env_file(NULL
, p
, NULL
, &b
);
210 static void test_merge_env_file(void) {
211 char t
[] = "/tmp/test-fileio-XXXXXX";
213 _cleanup_fclose_
FILE *f
= NULL
;
214 _cleanup_strv_free_
char **a
= NULL
;
217 fd
= mkostemp_safe(t
);
220 log_info("/* %s (%s) */", __func__
, t
);
225 r
= write_string_stream(f
,
228 "twentyone=2${one}\n"
230 "twentytwo=2${one}\n"
231 "xxx_minus_three=$xxx - 3\n"
232 "xxx=0x$one$one$one\n"
233 "yyy=${one:-fallback}\n"
234 "zzz=${one:+replacement}\n"
235 "zzzz=${foobar:-${nothing}}\n"
236 "zzzzz=${nothing:+${nothing}}\n"
237 , WRITE_STRING_FILE_AVOID_NEWLINE
);
240 r
= merge_env_file(&a
, NULL
, t
);
245 log_info("Got: <%s>", *i
);
247 assert_se(streq(a
[0], "one=2"));
248 assert_se(streq(a
[1], "twelve=12"));
249 assert_se(streq(a
[2], "twentyone=21"));
250 assert_se(streq(a
[3], "twentytwo=22"));
251 assert_se(streq(a
[4], "xxx=0x222"));
252 assert_se(streq(a
[5], "xxx_minus_three= - 3"));
253 assert_se(streq(a
[6], "yyy=2"));
254 assert_se(streq(a
[7], "zzz=replacement"));
255 assert_se(streq(a
[8], "zzzz="));
256 assert_se(streq(a
[9], "zzzzz="));
257 assert_se(a
[10] == NULL
);
259 r
= merge_env_file(&a
, NULL
, t
);
264 log_info("Got2: <%s>", *i
);
266 assert_se(streq(a
[0], "one=2"));
267 assert_se(streq(a
[1], "twelve=12"));
268 assert_se(streq(a
[2], "twentyone=21"));
269 assert_se(streq(a
[3], "twentytwo=22"));
270 assert_se(streq(a
[4], "xxx=0x222"));
271 assert_se(streq(a
[5], "xxx_minus_three=0x222 - 3"));
272 assert_se(streq(a
[6], "yyy=2"));
273 assert_se(streq(a
[7], "zzz=replacement"));
274 assert_se(streq(a
[8], "zzzz="));
275 assert_se(streq(a
[9], "zzzzz="));
276 assert_se(a
[10] == NULL
);
279 static void test_merge_env_file_invalid(void) {
280 char t
[] = "/tmp/test-fileio-XXXXXX";
282 _cleanup_fclose_
FILE *f
= NULL
;
283 _cleanup_strv_free_
char **a
= NULL
;
286 fd
= mkostemp_safe(t
);
289 log_info("/* %s (%s) */", __func__
, t
);
294 r
= write_string_stream(f
,
303 ";comment2=comment2\n"
305 "\n\n" /* empty line */
306 , WRITE_STRING_FILE_AVOID_NEWLINE
);
309 r
= merge_env_file(&a
, NULL
, t
);
313 log_info("Got: <%s>", *i
);
315 assert_se(strv_isempty(a
));
318 static void test_executable_is_script(void) {
319 char t
[] = "/tmp/test-executable-XXXXXX";
324 fd
= mkostemp_safe(t
);
330 fputs("#! /bin/script -a -b \ngoo goo", f
);
333 r
= executable_is_script(t
, &command
);
335 assert_se(streq(command
, "/bin/script"));
338 r
= executable_is_script("/bin/sh", &command
);
341 r
= executable_is_script("/usr/bin/yum", &command
);
342 assert_se(r
> 0 || r
== -ENOENT
);
344 assert_se(startswith(command
, "/"));
352 static void test_status_field(void) {
353 _cleanup_free_
char *t
= NULL
, *p
= NULL
, *s
= NULL
, *z
= NULL
;
354 unsigned long long total
= 0, buffers
= 0;
357 assert_se(get_proc_field("/proc/self/status", "Threads", WHITESPACE
, &t
) == 0);
359 assert_se(streq(t
, "1"));
361 r
= get_proc_field("/proc/meminfo", "MemTotal", WHITESPACE
, &p
);
365 assert_se(safe_atollu(p
, &total
) == 0);
368 r
= get_proc_field("/proc/meminfo", "Buffers", WHITESPACE
, &s
);
372 assert_se(safe_atollu(s
, &buffers
) == 0);
376 assert_se(buffers
< total
);
378 /* Seccomp should be a good test for field full of zeros. */
379 r
= get_proc_field("/proc/meminfo", "Seccomp", WHITESPACE
, &z
);
383 assert_se(safe_atollu(z
, &buffers
) == 0);
387 static void test_capeff(void) {
390 for (pid
= 0; pid
< 2; pid
++) {
391 _cleanup_free_
char *capeff
= NULL
;
394 r
= get_process_capeff(0, &capeff
);
395 log_info("capeff: '%s' (r=%d)", capeff
, r
);
397 if (IN_SET(r
, -ENOENT
, -EPERM
))
402 p
= capeff
[strspn(capeff
, HEXDIGITS
)];
403 assert_se(!p
|| isspace(p
));
407 static void test_write_string_stream(void) {
408 char fn
[] = "/tmp/test-write_string_stream-XXXXXX";
413 fd
= mkostemp_safe(fn
);
418 assert_se(write_string_stream(f
, "boohoo", 0) < 0);
424 assert_se(write_string_stream(f
, "boohoo", 0) == 0);
427 assert_se(fgets(buf
, sizeof(buf
), f
));
428 assert_se(streq(buf
, "boohoo\n"));
434 assert_se(write_string_stream(f
, "boohoo", WRITE_STRING_FILE_AVOID_NEWLINE
) == 0);
437 assert_se(fgets(buf
, sizeof(buf
), f
));
439 assert_se(streq(buf
, "boohoo"));
445 static void test_write_string_file(void) {
446 char fn
[] = "/tmp/test-write_string_file-XXXXXX";
448 _cleanup_close_
int fd
;
450 fd
= mkostemp_safe(fn
);
453 assert_se(write_string_file(fn
, "boohoo", WRITE_STRING_FILE_CREATE
) == 0);
455 assert_se(read(fd
, buf
, sizeof(buf
)) == 7);
456 assert_se(streq(buf
, "boohoo\n"));
461 static void test_write_string_file_no_create(void) {
462 char fn
[] = "/tmp/test-write_string_file_no_create-XXXXXX";
463 _cleanup_close_
int fd
;
466 fd
= mkostemp_safe(fn
);
469 assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0);
470 assert_se(write_string_file(fn
, "boohoo", 0) == 0);
472 assert_se(read(fd
, buf
, sizeof(buf
)) == STRLEN("boohoo\n"));
473 assert_se(streq(buf
, "boohoo\n"));
478 static void test_write_string_file_verify(void) {
479 _cleanup_free_
char *buf
= NULL
, *buf2
= NULL
;
482 assert_se(read_one_line_file("/proc/cmdline", &buf
) >= 0);
483 assert_se((buf2
= strjoin(buf
, "\n")));
485 r
= write_string_file("/proc/cmdline", buf
, 0);
486 assert_se(IN_SET(r
, -EACCES
, -EIO
));
487 r
= write_string_file("/proc/cmdline", buf2
, 0);
488 assert_se(IN_SET(r
, -EACCES
, -EIO
));
490 assert_se(write_string_file("/proc/cmdline", buf
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
) == 0);
491 assert_se(write_string_file("/proc/cmdline", buf2
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
) == 0);
493 r
= write_string_file("/proc/cmdline", buf
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
|WRITE_STRING_FILE_AVOID_NEWLINE
);
494 assert_se(IN_SET(r
, -EACCES
, -EIO
));
495 assert_se(write_string_file("/proc/cmdline", buf2
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
|WRITE_STRING_FILE_AVOID_NEWLINE
) == 0);
498 static void test_load_env_file_pairs(void) {
499 char fn
[] = "/tmp/test-load_env_file_pairs-XXXXXX";
502 _cleanup_fclose_
FILE *f
= NULL
;
503 _cleanup_strv_free_
char **l
= NULL
;
506 fd
= mkostemp_safe(fn
);
509 r
= write_string_file(fn
,
510 "NAME=\"Arch Linux\"\n"
512 "PRETTY_NAME=\"Arch Linux\"\n"
513 "ANSI_COLOR=\"0;36\"\n"
514 "HOME_URL=\"https://www.archlinux.org/\"\n"
515 "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n"
516 "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n",
517 WRITE_STRING_FILE_CREATE
);
523 r
= load_env_file_pairs(f
, fn
, NULL
, &l
);
526 assert_se(strv_length(l
) == 14);
527 STRV_FOREACH_PAIR(k
, v
, l
) {
528 assert_se(STR_IN_SET(*k
, "NAME", "ID", "PRETTY_NAME", "ANSI_COLOR", "HOME_URL", "SUPPORT_URL", "BUG_REPORT_URL"));
529 printf("%s=%s\n", *k
, *v
);
530 if (streq(*k
, "NAME")) assert_se(streq(*v
, "Arch Linux"));
531 if (streq(*k
, "ID")) assert_se(streq(*v
, "arch"));
532 if (streq(*k
, "PRETTY_NAME")) assert_se(streq(*v
, "Arch Linux"));
533 if (streq(*k
, "ANSI_COLOR")) assert_se(streq(*v
, "0;36"));
534 if (streq(*k
, "HOME_URL")) assert_se(streq(*v
, "https://www.archlinux.org/"));
535 if (streq(*k
, "SUPPORT_URL")) assert_se(streq(*v
, "https://bbs.archlinux.org/"));
536 if (streq(*k
, "BUG_REPORT_URL")) assert_se(streq(*v
, "https://bugs.archlinux.org/"));
542 static void test_search_and_fopen(void) {
543 const char *dirs
[] = {"/tmp/foo/bar", "/tmp", NULL
};
544 char name
[] = "/tmp/test-search_and_fopen.XXXXXX";
549 fd
= mkostemp_safe(name
);
553 r
= search_and_fopen(basename(name
), "r", NULL
, dirs
, &f
);
557 r
= search_and_fopen(name
, "r", NULL
, dirs
, &f
);
561 r
= search_and_fopen(basename(name
), "r", "/", dirs
, &f
);
565 r
= search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL
, dirs
, &f
);
567 r
= search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL
, dirs
, &f
);
573 r
= search_and_fopen(basename(name
), "r", NULL
, dirs
, &f
);
578 static void test_search_and_fopen_nulstr(void) {
579 const char dirs
[] = "/tmp/foo/bar\0/tmp\0";
580 char name
[] = "/tmp/test-search_and_fopen.XXXXXX";
585 fd
= mkostemp_safe(name
);
589 r
= search_and_fopen_nulstr(basename(name
), "r", NULL
, dirs
, &f
);
593 r
= search_and_fopen_nulstr(name
, "r", NULL
, dirs
, &f
);
597 r
= search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL
, dirs
, &f
);
599 r
= search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL
, dirs
, &f
);
605 r
= search_and_fopen_nulstr(basename(name
), "r", NULL
, dirs
, &f
);
609 static void test_writing_tmpfile(void) {
610 char name
[] = "/tmp/test-systemd_writing_tmpfile.XXXXXX";
611 _cleanup_free_
char *contents
= NULL
;
614 _cleanup_close_
int fd
= -1;
617 iov
[0] = IOVEC_MAKE_STRING("abc\n");
618 iov
[1] = IOVEC_MAKE_STRING(ALPHANUMERICAL
"\n");
619 iov
[2] = IOVEC_MAKE_STRING("");
621 fd
= mkostemp_safe(name
);
622 printf("tmpfile: %s", name
);
624 r
= writev(fd
, iov
, 3);
627 r
= read_full_file(name
, &contents
, &size
);
629 printf("contents: %s", contents
);
630 assert_se(streq(contents
, "abc\n" ALPHANUMERICAL
"\n"));
635 static void test_tempfn(void) {
636 char *ret
= NULL
, *p
;
638 assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL
, &ret
) >= 0);
639 assert_se(streq_ptr(ret
, "/foo/bar/.#waldoXXXXXX"));
642 assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret
) >= 0);
643 assert_se(streq_ptr(ret
, "/foo/bar/.#[miau]waldoXXXXXX"));
646 assert_se(tempfn_random("/foo/bar/waldo", NULL
, &ret
) >= 0);
647 assert_se(p
= startswith(ret
, "/foo/bar/.#waldo"));
648 assert_se(strlen(p
) == 16);
649 assert_se(in_charset(p
, "0123456789abcdef"));
652 assert_se(tempfn_random("/foo/bar/waldo", "[wuff]", &ret
) >= 0);
653 assert_se(p
= startswith(ret
, "/foo/bar/.#[wuff]waldo"));
654 assert_se(strlen(p
) == 16);
655 assert_se(in_charset(p
, "0123456789abcdef"));
658 assert_se(tempfn_random_child("/foo/bar/waldo", NULL
, &ret
) >= 0);
659 assert_se(p
= startswith(ret
, "/foo/bar/waldo/.#"));
660 assert_se(strlen(p
) == 16);
661 assert_se(in_charset(p
, "0123456789abcdef"));
664 assert_se(tempfn_random_child("/foo/bar/waldo", "[kikiriki]", &ret
) >= 0);
665 assert_se(p
= startswith(ret
, "/foo/bar/waldo/.#[kikiriki]"));
666 assert_se(strlen(p
) == 16);
667 assert_se(in_charset(p
, "0123456789abcdef"));
671 static const char buffer
[] =
673 "With newlines, and a NUL byte\0"
677 "and a very long line that is supposed to be truncated, because it is so long\n";
679 static void test_read_line_one_file(FILE *f
) {
680 _cleanup_free_
char *line
= NULL
;
682 assert_se(read_line(f
, (size_t) -1, &line
) == 15 && streq(line
, "Some test data"));
685 assert_se(read_line(f
, 1024, &line
) == 30 && streq(line
, "With newlines, and a NUL byte"));
688 assert_se(read_line(f
, 1024, &line
) == 1 && streq(line
, ""));
691 assert_se(read_line(f
, 1024, &line
) == 14 && streq(line
, "an empty line"));
694 assert_se(read_line(f
, (size_t) -1, NULL
) == 16);
696 assert_se(read_line(f
, 16, &line
) == -ENOBUFS
);
699 /* read_line() stopped when it hit the limit, that means when we continue reading we'll read at the first
700 * character after the previous limit. Let's make use of tha to continue our test. */
701 assert_se(read_line(f
, 1024, &line
) == 61 && streq(line
, "line that is supposed to be truncated, because it is so long"));
704 assert_se(read_line(f
, 1024, &line
) == 1 && streq(line
, ""));
707 assert_se(read_line(f
, 1024, &line
) == 0 && streq(line
, ""));
710 static void test_read_line(void) {
711 _cleanup_fclose_
FILE *f
= NULL
;
713 f
= fmemopen((void*) buffer
, sizeof(buffer
), "re");
716 test_read_line_one_file(f
);
719 static void test_read_line2(void) {
720 char name
[] = "/tmp/test-fileio.XXXXXX";
722 _cleanup_fclose_
FILE *f
= NULL
;
724 fd
= mkostemp_safe(name
);
726 assert_se((size_t) write(fd
, buffer
, sizeof(buffer
)) == sizeof(buffer
));
728 assert_se(lseek(fd
, 0, SEEK_SET
) == 0);
729 assert_se(f
= fdopen(fd
, "r"));
731 test_read_line_one_file(f
);
734 static void test_read_line3(void) {
735 _cleanup_fclose_
FILE *f
= NULL
;
736 _cleanup_free_
char *line
= NULL
;
739 f
= fopen("/proc/cmdline", "re");
740 if (!f
&& IN_SET(errno
, ENOENT
, EPERM
))
744 r
= read_line(f
, LINE_MAX
, &line
);
745 assert_se((size_t) r
== strlen(line
) + 1);
746 assert_se(read_line(f
, LINE_MAX
, NULL
) == 0);
749 int main(int argc
, char *argv
[]) {
750 log_set_max_level(LOG_DEBUG
);
751 log_parse_environment();
754 test_parse_env_file();
755 test_parse_multiline_env_file();
756 test_merge_env_file();
757 test_merge_env_file_invalid();
758 test_executable_is_script();
761 test_write_string_stream();
762 test_write_string_file();
763 test_write_string_file_no_create();
764 test_write_string_file_verify();
765 test_load_env_file_pairs();
766 test_search_and_fopen();
767 test_search_and_fopen_nulstr();
768 test_writing_tmpfile();