1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
24 #include "alloc-util.h"
30 #include "hexdecoct.h"
31 #include "path-util.h"
32 #include "random-util.h"
33 #include "string-util.h"
35 #include "umask-util.h"
39 int write_string_stream(FILE *f
, const char *line
, bool enforce_newline
) {
45 if (enforce_newline
&& !endswith(line
, "\n"))
48 return fflush_and_check(f
);
51 static int write_string_file_atomic(const char *fn
, const char *line
, bool enforce_newline
) {
52 _cleanup_fclose_
FILE *f
= NULL
;
53 _cleanup_free_
char *p
= NULL
;
59 r
= fopen_temporary(fn
, &f
, &p
);
63 (void) fchmod_umask(fileno(f
), 0644);
65 r
= write_string_stream(f
, line
, enforce_newline
);
67 if (rename(p
, fn
) < 0)
77 int write_string_file(const char *fn
, const char *line
, WriteStringFileFlags flags
) {
78 _cleanup_fclose_
FILE *f
= NULL
;
83 if (flags
& WRITE_STRING_FILE_ATOMIC
) {
84 assert(flags
& WRITE_STRING_FILE_CREATE
);
86 return write_string_file_atomic(fn
, line
, !(flags
& WRITE_STRING_FILE_AVOID_NEWLINE
));
89 if (flags
& WRITE_STRING_FILE_CREATE
) {
96 /* We manually build our own version of fopen(..., "we") that
97 * works without O_CREAT */
98 fd
= open(fn
, O_WRONLY
|O_CLOEXEC
|O_NOCTTY
);
102 f
= fdopen(fd
, "we");
109 return write_string_stream(f
, line
, !(flags
& WRITE_STRING_FILE_AVOID_NEWLINE
));
112 int read_one_line_file(const char *fn
, char **line
) {
113 _cleanup_fclose_
FILE *f
= NULL
;
114 char t
[LINE_MAX
], *c
;
123 if (!fgets(t
, sizeof(t
), f
)) {
126 return errno
? -errno
: -EIO
;
140 int verify_one_line_file(const char *fn
, const char *line
) {
141 _cleanup_free_
char *value
= NULL
;
144 r
= read_one_line_file(fn
, &value
);
148 return streq(value
, line
);
151 int read_full_stream(FILE *f
, char **contents
, size_t *size
) {
153 _cleanup_free_
char *buf
= NULL
;
159 if (fstat(fileno(f
), &st
) < 0)
164 if (S_ISREG(st
.st_mode
)) {
167 if (st
.st_size
> 4*1024*1024)
170 /* Start with the right file size, but be prepared for
171 * files from /proc which generally report a file size
182 t
= realloc(buf
, n
+1);
187 k
= fread(buf
+ l
, 1, n
- l
, f
);
206 buf
= NULL
; /* do not free */
214 int read_full_file(const char *fn
, char **contents
, size_t *size
) {
215 _cleanup_fclose_
FILE *f
= NULL
;
224 return read_full_stream(f
, contents
, size
);
227 static int parse_env_file_internal(
231 int (*push
) (const char *filename
, unsigned line
,
232 const char *key
, char *value
, void *userdata
, int *n_pushed
),
236 _cleanup_free_
char *contents
= NULL
, *key
= NULL
;
237 size_t key_alloc
= 0, n_key
= 0, value_alloc
= 0, n_value
= 0, last_value_whitespace
= (size_t) -1, last_key_whitespace
= (size_t) -1;
238 char *p
, *value
= NULL
;
249 SINGLE_QUOTE_VALUE_ESCAPE
,
251 DOUBLE_QUOTE_VALUE_ESCAPE
,
259 r
= read_full_stream(f
, &contents
, NULL
);
261 r
= read_full_file(fname
, &contents
, NULL
);
265 for (p
= contents
; *p
; p
++) {
271 if (strchr(COMMENTS
, c
))
273 else if (!strchr(WHITESPACE
, c
)) {
275 last_key_whitespace
= (size_t) -1;
277 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
287 if (strchr(newline
, c
)) {
291 } else if (c
== '=') {
293 last_value_whitespace
= (size_t) -1;
295 if (!strchr(WHITESPACE
, c
))
296 last_key_whitespace
= (size_t) -1;
297 else if (last_key_whitespace
== (size_t) -1)
298 last_key_whitespace
= n_key
;
300 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
311 if (strchr(newline
, c
)) {
319 /* strip trailing whitespace from key */
320 if (last_key_whitespace
!= (size_t) -1)
321 key
[last_key_whitespace
] = 0;
323 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
329 value_alloc
= n_value
= 0;
331 } else if (c
== '\'')
332 state
= SINGLE_QUOTE_VALUE
;
334 state
= DOUBLE_QUOTE_VALUE
;
336 state
= VALUE_ESCAPE
;
337 else if (!strchr(WHITESPACE
, c
)) {
340 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
345 value
[n_value
++] = c
;
351 if (strchr(newline
, c
)) {
360 /* Chomp off trailing whitespace from value */
361 if (last_value_whitespace
!= (size_t) -1)
362 value
[last_value_whitespace
] = 0;
364 /* strip trailing whitespace from key */
365 if (last_key_whitespace
!= (size_t) -1)
366 key
[last_key_whitespace
] = 0;
368 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
374 value_alloc
= n_value
= 0;
376 } else if (c
== '\\') {
377 state
= VALUE_ESCAPE
;
378 last_value_whitespace
= (size_t) -1;
380 if (!strchr(WHITESPACE
, c
))
381 last_value_whitespace
= (size_t) -1;
382 else if (last_value_whitespace
== (size_t) -1)
383 last_value_whitespace
= n_value
;
385 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
390 value
[n_value
++] = c
;
398 if (!strchr(newline
, c
)) {
399 /* Escaped newlines we eat up entirely */
400 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
405 value
[n_value
++] = c
;
409 case SINGLE_QUOTE_VALUE
:
413 state
= SINGLE_QUOTE_VALUE_ESCAPE
;
415 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
420 value
[n_value
++] = c
;
425 case SINGLE_QUOTE_VALUE_ESCAPE
:
426 state
= SINGLE_QUOTE_VALUE
;
428 if (!strchr(newline
, c
)) {
429 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
434 value
[n_value
++] = c
;
438 case DOUBLE_QUOTE_VALUE
:
442 state
= DOUBLE_QUOTE_VALUE_ESCAPE
;
444 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
449 value
[n_value
++] = c
;
454 case DOUBLE_QUOTE_VALUE_ESCAPE
:
455 state
= DOUBLE_QUOTE_VALUE
;
457 if (!strchr(newline
, c
)) {
458 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
463 value
[n_value
++] = c
;
469 state
= COMMENT_ESCAPE
;
470 else if (strchr(newline
, c
)) {
482 if (state
== PRE_VALUE
||
484 state
== VALUE_ESCAPE
||
485 state
== SINGLE_QUOTE_VALUE
||
486 state
== SINGLE_QUOTE_VALUE_ESCAPE
||
487 state
== DOUBLE_QUOTE_VALUE
||
488 state
== DOUBLE_QUOTE_VALUE_ESCAPE
) {
496 if (last_value_whitespace
!= (size_t) -1)
497 value
[last_value_whitespace
] = 0;
499 /* strip trailing whitespace from key */
500 if (last_key_whitespace
!= (size_t) -1)
501 key
[last_key_whitespace
] = 0;
503 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
515 static int parse_env_file_push(
516 const char *filename
, unsigned line
,
517 const char *key
, char *value
,
522 va_list aq
, *ap
= userdata
;
524 if (!utf8_is_valid(key
)) {
525 _cleanup_free_
char *p
;
527 p
= utf8_escape_invalid(key
);
528 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename
), line
, p
);
532 if (value
&& !utf8_is_valid(value
)) {
533 _cleanup_free_
char *p
;
535 p
= utf8_escape_invalid(value
);
536 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, p
);
542 while ((k
= va_arg(aq
, const char *))) {
545 v
= va_arg(aq
, char **);
567 const char *newline
, ...) {
575 va_start(ap
, newline
);
576 r
= parse_env_file_internal(NULL
, fname
, newline
, parse_env_file_push
, &ap
, &n_pushed
);
579 return r
< 0 ? r
: n_pushed
;
582 static int load_env_file_push(
583 const char *filename
, unsigned line
,
584 const char *key
, char *value
,
587 char ***m
= userdata
;
591 if (!utf8_is_valid(key
)) {
592 _cleanup_free_
char *t
= utf8_escape_invalid(key
);
594 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename
), line
, t
);
598 if (value
&& !utf8_is_valid(value
)) {
599 _cleanup_free_
char *t
= utf8_escape_invalid(value
);
601 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, t
);
605 p
= strjoin(key
, "=", strempty(value
), NULL
);
609 r
= strv_consume(m
, p
);
620 int load_env_file(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
627 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push
, &m
, NULL
);
637 static int load_env_file_push_pairs(
638 const char *filename
, unsigned line
,
639 const char *key
, char *value
,
642 char ***m
= userdata
;
645 if (!utf8_is_valid(key
)) {
646 _cleanup_free_
char *t
= utf8_escape_invalid(key
);
648 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename
), line
, t
);
652 if (value
&& !utf8_is_valid(value
)) {
653 _cleanup_free_
char *t
= utf8_escape_invalid(value
);
655 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, t
);
659 r
= strv_extend(m
, key
);
664 r
= strv_extend(m
, "");
668 r
= strv_push(m
, value
);
679 int load_env_file_pairs(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
686 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push_pairs
, &m
, NULL
);
696 static void write_env_var(FILE *f
, const char *v
) {
708 fwrite(v
, 1, p
-v
, f
);
710 if (string_has_cc(p
, NULL
) || chars_intersect(p
, WHITESPACE SHELL_NEED_QUOTES
)) {
714 if (strchr(SHELL_NEED_ESCAPE
, *p
))
727 int write_env_file(const char *fname
, char **l
) {
728 _cleanup_fclose_
FILE *f
= NULL
;
729 _cleanup_free_
char *p
= NULL
;
735 r
= fopen_temporary(fname
, &f
, &p
);
739 fchmod_umask(fileno(f
), 0644);
742 write_env_var(f
, *i
);
744 r
= fflush_and_check(f
);
746 if (rename(p
, fname
) >= 0)
756 int executable_is_script(const char *path
, char **interpreter
) {
758 _cleanup_free_
char *line
= NULL
;
764 r
= read_one_line_file(path
, &line
);
768 if (!startswith(line
, "#!"))
771 ans
= strstrip(line
+ 2);
772 len
= strcspn(ans
, " \t");
777 ans
= strndup(ans
, len
);
786 * Retrieve one field from a file like /proc/self/status. pattern
787 * should not include whitespace or the delimiter (':'). pattern matches only
788 * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
789 * zeros after the ':' will be skipped. field must be freed afterwards.
790 * terminator specifies the terminating characters of the field value (not
791 * included in the value).
793 int get_proc_field(const char *filename
, const char *pattern
, const char *terminator
, char **field
) {
794 _cleanup_free_
char *status
= NULL
;
804 r
= read_full_file(filename
, &status
, NULL
);
814 t
= strstr(t
, pattern
);
818 /* Check that pattern occurs in beginning of line. */
819 pattern_ok
= (t
== status
|| t
[-1] == '\n');
821 t
+= strlen(pattern
);
823 } while (!pattern_ok
);
825 t
+= strspn(t
, " \t");
834 t
+= strspn(t
, " \t");
836 /* Also skip zeros, because when this is used for
837 * capabilities, we don't want the zeros. This way the
838 * same capability set always maps to the same string,
839 * irrespective of the total capability set size. For
840 * other numbers it shouldn't matter. */
842 /* Back off one char if there's nothing but whitespace
844 if (!*t
|| isspace(*t
))
848 len
= strcspn(t
, terminator
);
858 DIR *xopendirat(int fd
, const char *name
, int flags
) {
862 assert(!(flags
& O_CREAT
));
864 nfd
= openat(fd
, name
, O_RDONLY
|O_NONBLOCK
|O_DIRECTORY
|O_CLOEXEC
|flags
, 0);
877 static int search_and_fopen_internal(const char *path
, const char *mode
, const char *root
, char **search
, FILE **_f
) {
884 if (!path_strv_resolve_uniq(search
, root
))
887 STRV_FOREACH(i
, search
) {
888 _cleanup_free_
char *p
= NULL
;
892 p
= strjoin(root
, *i
, "/", path
, NULL
);
894 p
= strjoin(*i
, "/", path
, NULL
);
911 int search_and_fopen(const char *path
, const char *mode
, const char *root
, const char **search
, FILE **_f
) {
912 _cleanup_strv_free_
char **copy
= NULL
;
918 if (path_is_absolute(path
)) {
921 f
= fopen(path
, mode
);
930 copy
= strv_copy((char**) search
);
934 return search_and_fopen_internal(path
, mode
, root
, copy
, _f
);
937 int search_and_fopen_nulstr(const char *path
, const char *mode
, const char *root
, const char *search
, FILE **_f
) {
938 _cleanup_strv_free_
char **s
= NULL
;
940 if (path_is_absolute(path
)) {
943 f
= fopen(path
, mode
);
952 s
= strv_split_nulstr(search
);
956 return search_and_fopen_internal(path
, mode
, root
, s
, _f
);
959 int fopen_temporary(const char *path
, FILE **_f
, char **_temp_path
) {
968 r
= tempfn_xxxxxx(path
, NULL
, &t
);
972 fd
= mkostemp_safe(t
, O_WRONLY
|O_CLOEXEC
);
978 f
= fdopen(fd
, "we");
992 int fflush_and_check(FILE *f
) {
999 return errno
? -errno
: -EIO
;
1004 /* This is much like like mkostemp() but is subject to umask(). */
1005 int mkostemp_safe(char *pattern
, int flags
) {
1006 _cleanup_umask_ mode_t u
;
1013 fd
= mkostemp(pattern
, flags
);
1020 int open_tmpfile(const char *path
, int flags
) {
1027 /* Try O_TMPFILE first, if it is supported */
1028 fd
= open(path
, flags
|O_TMPFILE
|O_EXCL
, S_IRUSR
|S_IWUSR
);
1033 /* Fall back to unguessable name + unlinking */
1034 p
= strjoina(path
, "/systemd-tmp-XXXXXX");
1036 fd
= mkostemp_safe(p
, flags
);
1044 int tempfn_xxxxxx(const char *p
, const char *extra
, char **ret
) {
1056 * /foo/bar/.#<extra>waldoXXXXXX
1060 if (!filename_is_valid(fn
))
1066 t
= new(char, strlen(p
) + 2 + strlen(extra
) + 6 + 1);
1070 strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t
, p
, fn
- p
), ".#"), extra
), fn
), "XXXXXX");
1072 *ret
= path_kill_slashes(t
);
1076 int tempfn_random(const char *p
, const char *extra
, char **ret
) {
1090 * /foo/bar/.#<extra>waldobaa2a261115984a9
1094 if (!filename_is_valid(fn
))
1100 t
= new(char, strlen(p
) + 2 + strlen(extra
) + 16 + 1);
1104 x
= stpcpy(stpcpy(stpcpy(mempcpy(t
, p
, fn
- p
), ".#"), extra
), fn
);
1107 for (i
= 0; i
< 16; i
++) {
1108 *(x
++) = hexchar(u
& 0xF);
1114 *ret
= path_kill_slashes(t
);
1118 int tempfn_random_child(const char *p
, const char *extra
, char **ret
) {
1129 * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
1135 t
= new(char, strlen(p
) + 3 + strlen(extra
) + 16 + 1);
1139 x
= stpcpy(stpcpy(stpcpy(t
, p
), "/.#"), extra
);
1142 for (i
= 0; i
< 16; i
++) {
1143 *(x
++) = hexchar(u
& 0xF);
1149 *ret
= path_kill_slashes(t
);