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/>.
30 int write_string_stream(FILE *f
, const char *line
, bool enforce_newline
) {
36 if (enforce_newline
&& !endswith(line
, "\n"))
39 return fflush_and_check(f
);
42 static int write_string_file_atomic(const char *fn
, const char *line
, bool enforce_newline
) {
43 _cleanup_fclose_
FILE *f
= NULL
;
44 _cleanup_free_
char *p
= NULL
;
50 r
= fopen_temporary(fn
, &f
, &p
);
54 fchmod_umask(fileno(f
), 0644);
56 r
= write_string_stream(f
, line
, enforce_newline
);
58 if (rename(p
, fn
) < 0)
68 int write_string_file(const char *fn
, const char *line
, WriteStringFileFlags flags
) {
69 _cleanup_fclose_
FILE *f
= NULL
;
74 if (flags
& WRITE_STRING_FILE_ATOMIC
) {
75 assert(flags
& WRITE_STRING_FILE_CREATE
);
77 return write_string_file_atomic(fn
, line
, !(flags
& WRITE_STRING_FILE_AVOID_NEWLINE
));
80 if (flags
& WRITE_STRING_FILE_CREATE
) {
87 /* We manually build our own version of fopen(..., "we") that
88 * works without O_CREAT */
89 fd
= open(fn
, O_WRONLY
|O_CLOEXEC
|O_NOCTTY
);
100 return write_string_stream(f
, line
, !(flags
& WRITE_STRING_FILE_AVOID_NEWLINE
));
103 int read_one_line_file(const char *fn
, char **line
) {
104 _cleanup_fclose_
FILE *f
= NULL
;
105 char t
[LINE_MAX
], *c
;
114 if (!fgets(t
, sizeof(t
), f
)) {
117 return errno
? -errno
: -EIO
;
131 int verify_one_line_file(const char *fn
, const char *line
) {
132 _cleanup_free_
char *value
= NULL
;
135 r
= read_one_line_file(fn
, &value
);
139 return streq(value
, line
);
142 int read_full_stream(FILE *f
, char **contents
, size_t *size
) {
144 _cleanup_free_
char *buf
= NULL
;
150 if (fstat(fileno(f
), &st
) < 0)
155 if (S_ISREG(st
.st_mode
)) {
158 if (st
.st_size
> 4*1024*1024)
161 /* Start with the right file size, but be prepared for
162 * files from /proc which generally report a file size
173 t
= realloc(buf
, n
+1);
178 k
= fread(buf
+ l
, 1, n
- l
, f
);
197 buf
= NULL
; /* do not free */
205 int read_full_file(const char *fn
, char **contents
, size_t *size
) {
206 _cleanup_fclose_
FILE *f
= NULL
;
215 return read_full_stream(f
, contents
, size
);
218 static int parse_env_file_internal(
222 int (*push
) (const char *filename
, unsigned line
,
223 const char *key
, char *value
, void *userdata
, int *n_pushed
),
227 _cleanup_free_
char *contents
= NULL
, *key
= NULL
;
228 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;
229 char *p
, *value
= NULL
;
240 SINGLE_QUOTE_VALUE_ESCAPE
,
242 DOUBLE_QUOTE_VALUE_ESCAPE
,
250 r
= read_full_stream(f
, &contents
, NULL
);
252 r
= read_full_file(fname
, &contents
, NULL
);
256 for (p
= contents
; *p
; p
++) {
262 if (strchr(COMMENTS
, c
))
264 else if (!strchr(WHITESPACE
, c
)) {
266 last_key_whitespace
= (size_t) -1;
268 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
278 if (strchr(newline
, c
)) {
282 } else if (c
== '=') {
284 last_value_whitespace
= (size_t) -1;
286 if (!strchr(WHITESPACE
, c
))
287 last_key_whitespace
= (size_t) -1;
288 else if (last_key_whitespace
== (size_t) -1)
289 last_key_whitespace
= n_key
;
291 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
302 if (strchr(newline
, c
)) {
310 /* strip trailing whitespace from key */
311 if (last_key_whitespace
!= (size_t) -1)
312 key
[last_key_whitespace
] = 0;
314 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
320 value_alloc
= n_value
= 0;
322 } else if (c
== '\'')
323 state
= SINGLE_QUOTE_VALUE
;
325 state
= DOUBLE_QUOTE_VALUE
;
327 state
= VALUE_ESCAPE
;
328 else if (!strchr(WHITESPACE
, c
)) {
331 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
336 value
[n_value
++] = c
;
342 if (strchr(newline
, c
)) {
351 /* Chomp off trailing whitespace from value */
352 if (last_value_whitespace
!= (size_t) -1)
353 value
[last_value_whitespace
] = 0;
355 /* strip trailing whitespace from key */
356 if (last_key_whitespace
!= (size_t) -1)
357 key
[last_key_whitespace
] = 0;
359 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
365 value_alloc
= n_value
= 0;
367 } else if (c
== '\\') {
368 state
= VALUE_ESCAPE
;
369 last_value_whitespace
= (size_t) -1;
371 if (!strchr(WHITESPACE
, c
))
372 last_value_whitespace
= (size_t) -1;
373 else if (last_value_whitespace
== (size_t) -1)
374 last_value_whitespace
= n_value
;
376 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
381 value
[n_value
++] = c
;
389 if (!strchr(newline
, c
)) {
390 /* Escaped newlines we eat up entirely */
391 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
396 value
[n_value
++] = c
;
400 case SINGLE_QUOTE_VALUE
:
404 state
= SINGLE_QUOTE_VALUE_ESCAPE
;
406 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
411 value
[n_value
++] = c
;
416 case SINGLE_QUOTE_VALUE_ESCAPE
:
417 state
= SINGLE_QUOTE_VALUE
;
419 if (!strchr(newline
, c
)) {
420 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
425 value
[n_value
++] = c
;
429 case DOUBLE_QUOTE_VALUE
:
433 state
= DOUBLE_QUOTE_VALUE_ESCAPE
;
435 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
440 value
[n_value
++] = c
;
445 case DOUBLE_QUOTE_VALUE_ESCAPE
:
446 state
= DOUBLE_QUOTE_VALUE
;
448 if (!strchr(newline
, c
)) {
449 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
454 value
[n_value
++] = c
;
460 state
= COMMENT_ESCAPE
;
461 else if (strchr(newline
, c
)) {
473 if (state
== PRE_VALUE
||
475 state
== VALUE_ESCAPE
||
476 state
== SINGLE_QUOTE_VALUE
||
477 state
== SINGLE_QUOTE_VALUE_ESCAPE
||
478 state
== DOUBLE_QUOTE_VALUE
||
479 state
== DOUBLE_QUOTE_VALUE_ESCAPE
) {
487 if (last_value_whitespace
!= (size_t) -1)
488 value
[last_value_whitespace
] = 0;
490 /* strip trailing whitespace from key */
491 if (last_key_whitespace
!= (size_t) -1)
492 key
[last_key_whitespace
] = 0;
494 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
506 static int parse_env_file_push(
507 const char *filename
, unsigned line
,
508 const char *key
, char *value
,
513 va_list aq
, *ap
= userdata
;
515 if (!utf8_is_valid(key
)) {
516 _cleanup_free_
char *p
;
518 p
= utf8_escape_invalid(key
);
519 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename
), line
, p
);
523 if (value
&& !utf8_is_valid(value
)) {
524 _cleanup_free_
char *p
;
526 p
= utf8_escape_invalid(value
);
527 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, p
);
533 while ((k
= va_arg(aq
, const char *))) {
536 v
= va_arg(aq
, char **);
558 const char *newline
, ...) {
566 va_start(ap
, newline
);
567 r
= parse_env_file_internal(NULL
, fname
, newline
, parse_env_file_push
, &ap
, &n_pushed
);
570 return r
< 0 ? r
: n_pushed
;
573 static int load_env_file_push(
574 const char *filename
, unsigned line
,
575 const char *key
, char *value
,
578 char ***m
= userdata
;
582 if (!utf8_is_valid(key
)) {
583 _cleanup_free_
char *t
= utf8_escape_invalid(key
);
585 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename
), line
, t
);
589 if (value
&& !utf8_is_valid(value
)) {
590 _cleanup_free_
char *t
= utf8_escape_invalid(value
);
592 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, t
);
596 p
= strjoin(key
, "=", strempty(value
), NULL
);
600 r
= strv_consume(m
, p
);
611 int load_env_file(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
618 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push
, &m
, NULL
);
628 static int load_env_file_push_pairs(
629 const char *filename
, unsigned line
,
630 const char *key
, char *value
,
633 char ***m
= userdata
;
636 if (!utf8_is_valid(key
)) {
637 _cleanup_free_
char *t
= utf8_escape_invalid(key
);
639 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename
), line
, t
);
643 if (value
&& !utf8_is_valid(value
)) {
644 _cleanup_free_
char *t
= utf8_escape_invalid(value
);
646 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, t
);
650 r
= strv_extend(m
, key
);
655 r
= strv_extend(m
, "");
659 r
= strv_push(m
, value
);
670 int load_env_file_pairs(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
677 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push_pairs
, &m
, NULL
);
687 static void write_env_var(FILE *f
, const char *v
) {
699 fwrite(v
, 1, p
-v
, f
);
701 if (string_has_cc(p
, NULL
) || chars_intersect(p
, WHITESPACE SHELL_NEED_QUOTES
)) {
705 if (strchr(SHELL_NEED_ESCAPE
, *p
))
718 int write_env_file(const char *fname
, char **l
) {
719 _cleanup_fclose_
FILE *f
= NULL
;
720 _cleanup_free_
char *p
= NULL
;
726 r
= fopen_temporary(fname
, &f
, &p
);
730 fchmod_umask(fileno(f
), 0644);
733 write_env_var(f
, *i
);
735 r
= fflush_and_check(f
);
737 if (rename(p
, fname
) >= 0)
747 int executable_is_script(const char *path
, char **interpreter
) {
749 _cleanup_free_
char *line
= NULL
;
755 r
= read_one_line_file(path
, &line
);
759 if (!startswith(line
, "#!"))
762 ans
= strstrip(line
+ 2);
763 len
= strcspn(ans
, " \t");
768 ans
= strndup(ans
, len
);
777 * Retrieve one field from a file like /proc/self/status. pattern
778 * should start with '\n' and end with a ':'. Whitespace and zeros
779 * after the ':' will be skipped. field must be freed afterwards.
781 int get_status_field(const char *filename
, const char *pattern
, char **field
) {
782 _cleanup_free_
char *status
= NULL
;
791 r
= read_full_file(filename
, &status
, NULL
);
795 t
= strstr(status
, pattern
);
799 t
+= strlen(pattern
);
801 t
+= strspn(t
, " \t");
803 /* Also skip zeros, because when this is used for
804 * capabilities, we don't want the zeros. This way the
805 * same capability set always maps to the same string,
806 * irrespective of the total capability set size. For
807 * other numbers it shouldn't matter. */
809 /* Back off one char if there's nothing but whitespace
811 if (!*t
|| isspace(*t
))
815 len
= strcspn(t
, WHITESPACE
);