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
) {
37 if (!endswith(line
, "\n"))
43 return errno
? -errno
: -EIO
;
48 int write_string_file(const char *fn
, const char *line
) {
49 _cleanup_fclose_
FILE *f
= NULL
;
58 return write_string_stream(f
, line
);
61 int write_string_file_no_create(const char *fn
, const char *line
) {
62 _cleanup_fclose_
FILE *f
= NULL
;
68 /* We manually build our own version of fopen(..., "we") that
69 * works without O_CREAT */
70 fd
= open(fn
, O_WRONLY
|O_CLOEXEC
|O_NOCTTY
);
80 return write_string_stream(f
, line
);
83 int write_string_file_atomic(const char *fn
, const char *line
) {
84 _cleanup_fclose_
FILE *f
= NULL
;
85 _cleanup_free_
char *p
= NULL
;
91 r
= fopen_temporary(fn
, &f
, &p
);
95 fchmod_umask(fileno(f
), 0644);
97 r
= write_string_stream(f
, line
);
99 if (rename(p
, fn
) < 0)
109 int read_one_line_file(const char *fn
, char **line
) {
110 _cleanup_fclose_
FILE *f
= NULL
;
111 char t
[LINE_MAX
], *c
;
120 if (!fgets(t
, sizeof(t
), f
)) {
123 return errno
? -errno
: -EIO
;
137 int read_full_stream(FILE *f
, char **contents
, size_t *size
) {
139 _cleanup_free_
char *buf
= NULL
;
145 if (fstat(fileno(f
), &st
) < 0)
150 if (S_ISREG(st
.st_mode
)) {
153 if (st
.st_size
> 4*1024*1024)
156 /* Start with the right file size, but be prepared for
157 * files from /proc which generally report a file size
168 t
= realloc(buf
, n
+1);
173 k
= fread(buf
+ l
, 1, n
- l
, f
);
192 buf
= NULL
; /* do not free */
200 int read_full_file(const char *fn
, char **contents
, size_t *size
) {
201 _cleanup_fclose_
FILE *f
= NULL
;
210 return read_full_stream(f
, contents
, size
);
213 static int parse_env_file_internal(
217 int (*push
) (const char *filename
, unsigned line
,
218 const char *key
, char *value
, void *userdata
, int *n_pushed
),
222 _cleanup_free_
char *contents
= NULL
, *key
= NULL
;
223 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;
224 char *p
, *value
= NULL
;
235 SINGLE_QUOTE_VALUE_ESCAPE
,
237 DOUBLE_QUOTE_VALUE_ESCAPE
,
245 r
= read_full_stream(f
, &contents
, NULL
);
247 r
= read_full_file(fname
, &contents
, NULL
);
251 for (p
= contents
; *p
; p
++) {
257 if (strchr(COMMENTS
, c
))
259 else if (!strchr(WHITESPACE
, c
)) {
261 last_key_whitespace
= (size_t) -1;
263 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
273 if (strchr(newline
, c
)) {
277 } else if (c
== '=') {
279 last_value_whitespace
= (size_t) -1;
281 if (!strchr(WHITESPACE
, c
))
282 last_key_whitespace
= (size_t) -1;
283 else if (last_key_whitespace
== (size_t) -1)
284 last_key_whitespace
= n_key
;
286 if (!GREEDY_REALLOC(key
, key_alloc
, n_key
+2)) {
297 if (strchr(newline
, c
)) {
305 /* strip trailing whitespace from key */
306 if (last_key_whitespace
!= (size_t) -1)
307 key
[last_key_whitespace
] = 0;
309 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
315 value_alloc
= n_value
= 0;
317 } else if (c
== '\'')
318 state
= SINGLE_QUOTE_VALUE
;
320 state
= DOUBLE_QUOTE_VALUE
;
322 state
= VALUE_ESCAPE
;
323 else if (!strchr(WHITESPACE
, c
)) {
326 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
331 value
[n_value
++] = c
;
337 if (strchr(newline
, c
)) {
346 /* Chomp off trailing whitespace from value */
347 if (last_value_whitespace
!= (size_t) -1)
348 value
[last_value_whitespace
] = 0;
350 /* strip trailing whitespace from key */
351 if (last_key_whitespace
!= (size_t) -1)
352 key
[last_key_whitespace
] = 0;
354 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
360 value_alloc
= n_value
= 0;
362 } else if (c
== '\\') {
363 state
= VALUE_ESCAPE
;
364 last_value_whitespace
= (size_t) -1;
366 if (!strchr(WHITESPACE
, c
))
367 last_value_whitespace
= (size_t) -1;
368 else if (last_value_whitespace
== (size_t) -1)
369 last_value_whitespace
= n_value
;
371 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
376 value
[n_value
++] = c
;
384 if (!strchr(newline
, c
)) {
385 /* Escaped newlines we eat up entirely */
386 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
391 value
[n_value
++] = c
;
395 case SINGLE_QUOTE_VALUE
:
399 state
= SINGLE_QUOTE_VALUE_ESCAPE
;
401 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
406 value
[n_value
++] = c
;
411 case SINGLE_QUOTE_VALUE_ESCAPE
:
412 state
= SINGLE_QUOTE_VALUE
;
414 if (!strchr(newline
, c
)) {
415 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
420 value
[n_value
++] = c
;
424 case DOUBLE_QUOTE_VALUE
:
428 state
= DOUBLE_QUOTE_VALUE_ESCAPE
;
430 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
435 value
[n_value
++] = c
;
440 case DOUBLE_QUOTE_VALUE_ESCAPE
:
441 state
= DOUBLE_QUOTE_VALUE
;
443 if (!strchr(newline
, c
)) {
444 if (!GREEDY_REALLOC(value
, value_alloc
, n_value
+2)) {
449 value
[n_value
++] = c
;
455 state
= COMMENT_ESCAPE
;
456 else if (strchr(newline
, c
)) {
468 if (state
== PRE_VALUE
||
470 state
== VALUE_ESCAPE
||
471 state
== SINGLE_QUOTE_VALUE
||
472 state
== SINGLE_QUOTE_VALUE_ESCAPE
||
473 state
== DOUBLE_QUOTE_VALUE
||
474 state
== DOUBLE_QUOTE_VALUE_ESCAPE
) {
482 if (last_value_whitespace
!= (size_t) -1)
483 value
[last_value_whitespace
] = 0;
485 /* strip trailing whitespace from key */
486 if (last_key_whitespace
!= (size_t) -1)
487 key
[last_key_whitespace
] = 0;
489 r
= push(fname
, line
, key
, value
, userdata
, n_pushed
);
501 static int parse_env_file_push(
502 const char *filename
, unsigned line
,
503 const char *key
, char *value
,
508 va_list aq
, *ap
= userdata
;
510 if (!utf8_is_valid(key
)) {
511 _cleanup_free_
char *p
;
513 p
= utf8_escape_invalid(key
);
514 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename
), line
, p
);
518 if (value
&& !utf8_is_valid(value
)) {
519 _cleanup_free_
char *p
;
521 p
= utf8_escape_invalid(value
);
522 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, p
);
528 while ((k
= va_arg(aq
, const char *))) {
531 v
= va_arg(aq
, char **);
553 const char *newline
, ...) {
561 va_start(ap
, newline
);
562 r
= parse_env_file_internal(NULL
, fname
, newline
, parse_env_file_push
, &ap
, &n_pushed
);
565 return r
< 0 ? r
: n_pushed
;
568 static int load_env_file_push(
569 const char *filename
, unsigned line
,
570 const char *key
, char *value
,
573 char ***m
= userdata
;
577 if (!utf8_is_valid(key
)) {
578 _cleanup_free_
char *t
= utf8_escape_invalid(key
);
580 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename
), line
, t
);
584 if (value
&& !utf8_is_valid(value
)) {
585 _cleanup_free_
char *t
= utf8_escape_invalid(value
);
587 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, t
);
591 p
= strjoin(key
, "=", strempty(value
), NULL
);
595 r
= strv_consume(m
, p
);
606 int load_env_file(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
613 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push
, &m
, NULL
);
623 static int load_env_file_push_pairs(
624 const char *filename
, unsigned line
,
625 const char *key
, char *value
,
628 char ***m
= userdata
;
631 if (!utf8_is_valid(key
)) {
632 _cleanup_free_
char *t
= utf8_escape_invalid(key
);
634 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename
), line
, t
);
638 if (value
&& !utf8_is_valid(value
)) {
639 _cleanup_free_
char *t
= utf8_escape_invalid(value
);
641 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename
), line
, key
, t
);
645 r
= strv_extend(m
, key
);
650 r
= strv_extend(m
, "");
654 r
= strv_push(m
, value
);
665 int load_env_file_pairs(FILE *f
, const char *fname
, const char *newline
, char ***rl
) {
672 r
= parse_env_file_internal(f
, fname
, newline
, load_env_file_push_pairs
, &m
, NULL
);
682 static void write_env_var(FILE *f
, const char *v
) {
694 fwrite(v
, 1, p
-v
, f
);
696 if (string_has_cc(p
, NULL
) || chars_intersect(p
, WHITESPACE SHELL_NEED_QUOTES
)) {
700 if (strchr(SHELL_NEED_ESCAPE
, *p
))
713 int write_env_file(const char *fname
, char **l
) {
714 _cleanup_fclose_
FILE *f
= NULL
;
715 _cleanup_free_
char *p
= NULL
;
721 r
= fopen_temporary(fname
, &f
, &p
);
725 fchmod_umask(fileno(f
), 0644);
728 write_env_var(f
, *i
);
730 r
= fflush_and_check(f
);
732 if (rename(p
, fname
) >= 0)
742 int executable_is_script(const char *path
, char **interpreter
) {
744 _cleanup_free_
char *line
= NULL
;
750 r
= read_one_line_file(path
, &line
);
754 if (!startswith(line
, "#!"))
757 ans
= strstrip(line
+ 2);
758 len
= strcspn(ans
, " \t");
763 ans
= strndup(ans
, len
);
772 * Retrieve one field from a file like /proc/self/status. pattern
773 * should start with '\n' and end with a ':'. Whitespace and zeros
774 * after the ':' will be skipped. field must be freed afterwards.
776 int get_status_field(const char *filename
, const char *pattern
, char **field
) {
777 _cleanup_free_
char *status
= NULL
;
786 r
= read_full_file(filename
, &status
, NULL
);
790 t
= strstr(status
, pattern
);
794 t
+= strlen(pattern
);
796 t
+= strspn(t
, " \t");
798 /* Also skip zeros, because when this is used for
799 * capabilities, we don't want the zeros. This way the
800 * same capability set always maps to the same string,
801 * irrespective of the total capability set size. For
802 * other numbers it shouldn't matter. */
804 /* Back off one char if there's nothing but whitespace
806 if (!*t
|| isspace(*t
))
810 len
= strcspn(t
, WHITESPACE
);
812 *field
= strndup(t
, len
);