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";
409 _cleanup_fclose_
FILE *f
= NULL
;
413 fd
= mkostemp_safe(fn
);
418 assert_se(write_string_stream(f
, "boohoo", 0) < 0);
420 f
= freopen(fn
, "r+", f
);
423 assert_se(write_string_stream(f
, "boohoo", 0) == 0);
426 assert_se(fgets(buf
, sizeof(buf
), f
));
427 assert_se(streq(buf
, "boohoo\n"));
429 f
= freopen(fn
, "w+", f
);
432 assert_se(write_string_stream(f
, "boohoo", WRITE_STRING_FILE_AVOID_NEWLINE
) == 0);
435 assert_se(fgets(buf
, sizeof(buf
), f
));
437 assert_se(streq(buf
, "boohoo"));
442 static void test_write_string_file(void) {
443 char fn
[] = "/tmp/test-write_string_file-XXXXXX";
445 _cleanup_close_
int fd
;
447 fd
= mkostemp_safe(fn
);
450 assert_se(write_string_file(fn
, "boohoo", WRITE_STRING_FILE_CREATE
) == 0);
452 assert_se(read(fd
, buf
, sizeof(buf
)) == 7);
453 assert_se(streq(buf
, "boohoo\n"));
458 static void test_write_string_file_no_create(void) {
459 char fn
[] = "/tmp/test-write_string_file_no_create-XXXXXX";
460 _cleanup_close_
int fd
;
463 fd
= mkostemp_safe(fn
);
466 assert_se(write_string_file("/a/file/which/does/not/exists/i/guess", "boohoo", 0) < 0);
467 assert_se(write_string_file(fn
, "boohoo", 0) == 0);
469 assert_se(read(fd
, buf
, sizeof(buf
)) == STRLEN("boohoo\n"));
470 assert_se(streq(buf
, "boohoo\n"));
475 static void test_write_string_file_verify(void) {
476 _cleanup_free_
char *buf
= NULL
, *buf2
= NULL
;
479 assert_se(read_one_line_file("/proc/cmdline", &buf
) >= 0);
480 assert_se((buf2
= strjoin(buf
, "\n")));
482 r
= write_string_file("/proc/cmdline", buf
, 0);
483 assert_se(IN_SET(r
, -EACCES
, -EIO
));
484 r
= write_string_file("/proc/cmdline", buf2
, 0);
485 assert_se(IN_SET(r
, -EACCES
, -EIO
));
487 assert_se(write_string_file("/proc/cmdline", buf
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
) == 0);
488 assert_se(write_string_file("/proc/cmdline", buf2
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
) == 0);
490 r
= write_string_file("/proc/cmdline", buf
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
|WRITE_STRING_FILE_AVOID_NEWLINE
);
491 assert_se(IN_SET(r
, -EACCES
, -EIO
));
492 assert_se(write_string_file("/proc/cmdline", buf2
, WRITE_STRING_FILE_VERIFY_ON_FAILURE
|WRITE_STRING_FILE_AVOID_NEWLINE
) == 0);
495 static void test_load_env_file_pairs(void) {
496 char fn
[] = "/tmp/test-load_env_file_pairs-XXXXXX";
499 _cleanup_fclose_
FILE *f
= NULL
;
500 _cleanup_strv_free_
char **l
= NULL
;
503 fd
= mkostemp_safe(fn
);
506 r
= write_string_file(fn
,
507 "NAME=\"Arch Linux\"\n"
509 "PRETTY_NAME=\"Arch Linux\"\n"
510 "ANSI_COLOR=\"0;36\"\n"
511 "HOME_URL=\"https://www.archlinux.org/\"\n"
512 "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n"
513 "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n",
514 WRITE_STRING_FILE_CREATE
);
520 r
= load_env_file_pairs(f
, fn
, NULL
, &l
);
523 assert_se(strv_length(l
) == 14);
524 STRV_FOREACH_PAIR(k
, v
, l
) {
525 assert_se(STR_IN_SET(*k
, "NAME", "ID", "PRETTY_NAME", "ANSI_COLOR", "HOME_URL", "SUPPORT_URL", "BUG_REPORT_URL"));
526 printf("%s=%s\n", *k
, *v
);
527 if (streq(*k
, "NAME")) assert_se(streq(*v
, "Arch Linux"));
528 if (streq(*k
, "ID")) assert_se(streq(*v
, "arch"));
529 if (streq(*k
, "PRETTY_NAME")) assert_se(streq(*v
, "Arch Linux"));
530 if (streq(*k
, "ANSI_COLOR")) assert_se(streq(*v
, "0;36"));
531 if (streq(*k
, "HOME_URL")) assert_se(streq(*v
, "https://www.archlinux.org/"));
532 if (streq(*k
, "SUPPORT_URL")) assert_se(streq(*v
, "https://bbs.archlinux.org/"));
533 if (streq(*k
, "BUG_REPORT_URL")) assert_se(streq(*v
, "https://bugs.archlinux.org/"));
539 static void test_search_and_fopen(void) {
540 const char *dirs
[] = {"/tmp/foo/bar", "/tmp", NULL
};
541 char name
[] = "/tmp/test-search_and_fopen.XXXXXX";
546 fd
= mkostemp_safe(name
);
550 r
= search_and_fopen(basename(name
), "r", NULL
, dirs
, &f
);
554 r
= search_and_fopen(name
, "r", NULL
, dirs
, &f
);
558 r
= search_and_fopen(basename(name
), "r", "/", dirs
, &f
);
562 r
= search_and_fopen("/a/file/which/does/not/exist/i/guess", "r", NULL
, dirs
, &f
);
564 r
= search_and_fopen("afilewhichdoesnotexistiguess", "r", NULL
, dirs
, &f
);
570 r
= search_and_fopen(basename(name
), "r", NULL
, dirs
, &f
);
575 static void test_search_and_fopen_nulstr(void) {
576 const char dirs
[] = "/tmp/foo/bar\0/tmp\0";
577 char name
[] = "/tmp/test-search_and_fopen.XXXXXX";
582 fd
= mkostemp_safe(name
);
586 r
= search_and_fopen_nulstr(basename(name
), "r", NULL
, dirs
, &f
);
590 r
= search_and_fopen_nulstr(name
, "r", NULL
, dirs
, &f
);
594 r
= search_and_fopen_nulstr("/a/file/which/does/not/exist/i/guess", "r", NULL
, dirs
, &f
);
596 r
= search_and_fopen_nulstr("afilewhichdoesnotexistiguess", "r", NULL
, dirs
, &f
);
602 r
= search_and_fopen_nulstr(basename(name
), "r", NULL
, dirs
, &f
);
606 static void test_writing_tmpfile(void) {
607 char name
[] = "/tmp/test-systemd_writing_tmpfile.XXXXXX";
608 _cleanup_free_
char *contents
= NULL
;
613 iov
[0] = IOVEC_MAKE_STRING("abc\n");
614 iov
[1] = IOVEC_MAKE_STRING(ALPHANUMERICAL
"\n");
615 iov
[2] = IOVEC_MAKE_STRING("");
617 fd
= mkostemp_safe(name
);
618 printf("tmpfile: %s", name
);
620 r
= writev(fd
, iov
, 3);
623 r
= read_full_file(name
, &contents
, &size
);
625 printf("contents: %s", contents
);
626 assert_se(streq(contents
, "abc\n" ALPHANUMERICAL
"\n"));
631 static void test_tempfn(void) {
632 char *ret
= NULL
, *p
;
634 assert_se(tempfn_xxxxxx("/foo/bar/waldo", NULL
, &ret
) >= 0);
635 assert_se(streq_ptr(ret
, "/foo/bar/.#waldoXXXXXX"));
638 assert_se(tempfn_xxxxxx("/foo/bar/waldo", "[miau]", &ret
) >= 0);
639 assert_se(streq_ptr(ret
, "/foo/bar/.#[miau]waldoXXXXXX"));
642 assert_se(tempfn_random("/foo/bar/waldo", NULL
, &ret
) >= 0);
643 assert_se(p
= startswith(ret
, "/foo/bar/.#waldo"));
644 assert_se(strlen(p
) == 16);
645 assert_se(in_charset(p
, "0123456789abcdef"));
648 assert_se(tempfn_random("/foo/bar/waldo", "[wuff]", &ret
) >= 0);
649 assert_se(p
= startswith(ret
, "/foo/bar/.#[wuff]waldo"));
650 assert_se(strlen(p
) == 16);
651 assert_se(in_charset(p
, "0123456789abcdef"));
654 assert_se(tempfn_random_child("/foo/bar/waldo", NULL
, &ret
) >= 0);
655 assert_se(p
= startswith(ret
, "/foo/bar/waldo/.#"));
656 assert_se(strlen(p
) == 16);
657 assert_se(in_charset(p
, "0123456789abcdef"));
660 assert_se(tempfn_random_child("/foo/bar/waldo", "[kikiriki]", &ret
) >= 0);
661 assert_se(p
= startswith(ret
, "/foo/bar/waldo/.#[kikiriki]"));
662 assert_se(strlen(p
) == 16);
663 assert_se(in_charset(p
, "0123456789abcdef"));
667 static const char buffer
[] =
669 "With newlines, and a NUL byte\0"
673 "and a very long line that is supposed to be truncated, because it is so long\n";
675 static void test_read_line_one_file(FILE *f
) {
676 _cleanup_free_
char *line
= NULL
;
678 assert_se(read_line(f
, (size_t) -1, &line
) == 15 && streq(line
, "Some test data"));
681 assert_se(read_line(f
, 1024, &line
) == 30 && streq(line
, "With newlines, and a NUL byte"));
684 assert_se(read_line(f
, 1024, &line
) == 1 && streq(line
, ""));
687 assert_se(read_line(f
, 1024, &line
) == 14 && streq(line
, "an empty line"));
690 assert_se(read_line(f
, (size_t) -1, NULL
) == 16);
692 assert_se(read_line(f
, 16, &line
) == -ENOBUFS
);
695 /* read_line() stopped when it hit the limit, that means when we continue reading we'll read at the first
696 * character after the previous limit. Let's make use of tha to continue our test. */
697 assert_se(read_line(f
, 1024, &line
) == 61 && streq(line
, "line that is supposed to be truncated, because it is so long"));
700 assert_se(read_line(f
, 1024, &line
) == 1 && streq(line
, ""));
703 assert_se(read_line(f
, 1024, &line
) == 0 && streq(line
, ""));
706 static void test_read_line(void) {
707 _cleanup_fclose_
FILE *f
= NULL
;
709 f
= fmemopen((void*) buffer
, sizeof(buffer
), "re");
712 test_read_line_one_file(f
);
715 static void test_read_line2(void) {
716 char name
[] = "/tmp/test-fileio.XXXXXX";
718 _cleanup_fclose_
FILE *f
= NULL
;
720 fd
= mkostemp_safe(name
);
722 assert_se((size_t) write(fd
, buffer
, sizeof(buffer
)) == sizeof(buffer
));
724 assert_se(lseek(fd
, 0, SEEK_SET
) == 0);
725 assert_se(f
= fdopen(fd
, "r"));
727 test_read_line_one_file(f
);
730 static void test_read_line3(void) {
731 _cleanup_fclose_
FILE *f
= NULL
;
732 _cleanup_free_
char *line
= NULL
;
735 f
= fopen("/proc/cmdline", "re");
736 if (!f
&& IN_SET(errno
, ENOENT
, EPERM
))
740 r
= read_line(f
, LINE_MAX
, &line
);
741 assert_se((size_t) r
== strlen(line
) + 1);
742 assert_se(read_line(f
, LINE_MAX
, NULL
) == 0);
745 int main(int argc
, char *argv
[]) {
746 log_set_max_level(LOG_DEBUG
);
747 log_parse_environment();
750 test_parse_env_file();
751 test_parse_multiline_env_file();
752 test_merge_env_file();
753 test_merge_env_file_invalid();
754 test_executable_is_script();
757 test_write_string_stream();
758 test_write_string_file();
759 test_write_string_file_no_create();
760 test_write_string_file_verify();
761 test_load_env_file_pairs();
762 test_search_and_fopen();
763 test_search_and_fopen_nulstr();
764 test_writing_tmpfile();