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) {
37 if (enforce_newline && !endswith(line, "\n"))
43 return errno ? -errno : -EIO;
48 static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
49 _cleanup_fclose_ FILE *f = NULL;
50 _cleanup_free_ char *p = NULL;
56 r = fopen_temporary(fn, &f, &p);
60 fchmod_umask(fileno(f), 0644);
62 r = write_string_stream(f, line, enforce_newline);
64 if (rename(p, fn) < 0)
74 int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
75 _cleanup_fclose_ FILE *f = NULL;
80 if (flags & WRITE_STRING_FILE_ATOMIC) {
81 assert(flags & WRITE_STRING_FILE_CREATE);
83 return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
86 if (flags & WRITE_STRING_FILE_CREATE) {
93 /* We manually build our own version of fopen(..., "we") that
94 * works without O_CREAT */
95 fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
106 return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
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 verify_one_line_file(const char *fn, const char *line) {
138 _cleanup_free_ char *value = NULL;
141 r = read_one_line_file(fn, &value);
145 return streq(value, line);
148 int read_full_stream(FILE *f, char **contents, size_t *size) {
150 _cleanup_free_ char *buf = NULL;
156 if (fstat(fileno(f), &st) < 0)
161 if (S_ISREG(st.st_mode)) {
164 if (st.st_size > 4*1024*1024)
167 /* Start with the right file size, but be prepared for
168 * files from /proc which generally report a file size
179 t = realloc(buf, n+1);
184 k = fread(buf + l, 1, n - l, f);
203 buf = NULL; /* do not free */
211 int read_full_file(const char *fn, char **contents, size_t *size) {
212 _cleanup_fclose_ FILE *f = NULL;
221 return read_full_stream(f, contents, size);
224 static int parse_env_file_internal(
228 int (*push) (const char *filename, unsigned line,
229 const char *key, char *value, void *userdata, int *n_pushed),
233 _cleanup_free_ char *contents = NULL, *key = NULL;
234 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;
235 char *p, *value = NULL;
246 SINGLE_QUOTE_VALUE_ESCAPE,
248 DOUBLE_QUOTE_VALUE_ESCAPE,
256 r = read_full_stream(f, &contents, NULL);
258 r = read_full_file(fname, &contents, NULL);
262 for (p = contents; *p; p++) {
268 if (strchr(COMMENTS, c))
270 else if (!strchr(WHITESPACE, c)) {
272 last_key_whitespace = (size_t) -1;
274 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
284 if (strchr(newline, c)) {
288 } else if (c == '=') {
290 last_value_whitespace = (size_t) -1;
292 if (!strchr(WHITESPACE, c))
293 last_key_whitespace = (size_t) -1;
294 else if (last_key_whitespace == (size_t) -1)
295 last_key_whitespace = n_key;
297 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
308 if (strchr(newline, c)) {
316 /* strip trailing whitespace from key */
317 if (last_key_whitespace != (size_t) -1)
318 key[last_key_whitespace] = 0;
320 r = push(fname, line, key, value, userdata, n_pushed);
326 value_alloc = n_value = 0;
328 } else if (c == '\'')
329 state = SINGLE_QUOTE_VALUE;
331 state = DOUBLE_QUOTE_VALUE;
333 state = VALUE_ESCAPE;
334 else if (!strchr(WHITESPACE, c)) {
337 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
342 value[n_value++] = c;
348 if (strchr(newline, c)) {
357 /* Chomp off trailing whitespace from value */
358 if (last_value_whitespace != (size_t) -1)
359 value[last_value_whitespace] = 0;
361 /* strip trailing whitespace from key */
362 if (last_key_whitespace != (size_t) -1)
363 key[last_key_whitespace] = 0;
365 r = push(fname, line, key, value, userdata, n_pushed);
371 value_alloc = n_value = 0;
373 } else if (c == '\\') {
374 state = VALUE_ESCAPE;
375 last_value_whitespace = (size_t) -1;
377 if (!strchr(WHITESPACE, c))
378 last_value_whitespace = (size_t) -1;
379 else if (last_value_whitespace == (size_t) -1)
380 last_value_whitespace = n_value;
382 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
387 value[n_value++] = c;
395 if (!strchr(newline, c)) {
396 /* Escaped newlines we eat up entirely */
397 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
402 value[n_value++] = c;
406 case SINGLE_QUOTE_VALUE:
410 state = SINGLE_QUOTE_VALUE_ESCAPE;
412 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
417 value[n_value++] = c;
422 case SINGLE_QUOTE_VALUE_ESCAPE:
423 state = SINGLE_QUOTE_VALUE;
425 if (!strchr(newline, c)) {
426 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
431 value[n_value++] = c;
435 case DOUBLE_QUOTE_VALUE:
439 state = DOUBLE_QUOTE_VALUE_ESCAPE;
441 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
446 value[n_value++] = c;
451 case DOUBLE_QUOTE_VALUE_ESCAPE:
452 state = DOUBLE_QUOTE_VALUE;
454 if (!strchr(newline, c)) {
455 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
460 value[n_value++] = c;
466 state = COMMENT_ESCAPE;
467 else if (strchr(newline, c)) {
479 if (state == PRE_VALUE ||
481 state == VALUE_ESCAPE ||
482 state == SINGLE_QUOTE_VALUE ||
483 state == SINGLE_QUOTE_VALUE_ESCAPE ||
484 state == DOUBLE_QUOTE_VALUE ||
485 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
493 if (last_value_whitespace != (size_t) -1)
494 value[last_value_whitespace] = 0;
496 /* strip trailing whitespace from key */
497 if (last_key_whitespace != (size_t) -1)
498 key[last_key_whitespace] = 0;
500 r = push(fname, line, key, value, userdata, n_pushed);
512 static int parse_env_file_push(
513 const char *filename, unsigned line,
514 const char *key, char *value,
519 va_list aq, *ap = userdata;
521 if (!utf8_is_valid(key)) {
522 _cleanup_free_ char *p;
524 p = utf8_escape_invalid(key);
525 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
529 if (value && !utf8_is_valid(value)) {
530 _cleanup_free_ char *p;
532 p = utf8_escape_invalid(value);
533 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
539 while ((k = va_arg(aq, const char *))) {
542 v = va_arg(aq, char **);
564 const char *newline, ...) {
572 va_start(ap, newline);
573 r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
576 return r < 0 ? r : n_pushed;
579 static int load_env_file_push(
580 const char *filename, unsigned line,
581 const char *key, char *value,
584 char ***m = userdata;
588 if (!utf8_is_valid(key)) {
589 _cleanup_free_ char *t = utf8_escape_invalid(key);
591 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
595 if (value && !utf8_is_valid(value)) {
596 _cleanup_free_ char *t = utf8_escape_invalid(value);
598 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
602 p = strjoin(key, "=", strempty(value), NULL);
606 r = strv_consume(m, p);
617 int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
624 r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
634 static int load_env_file_push_pairs(
635 const char *filename, unsigned line,
636 const char *key, char *value,
639 char ***m = userdata;
642 if (!utf8_is_valid(key)) {
643 _cleanup_free_ char *t = utf8_escape_invalid(key);
645 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
649 if (value && !utf8_is_valid(value)) {
650 _cleanup_free_ char *t = utf8_escape_invalid(value);
652 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
656 r = strv_extend(m, key);
661 r = strv_extend(m, "");
665 r = strv_push(m, value);
676 int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
683 r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
693 static void write_env_var(FILE *f, const char *v) {
705 fwrite(v, 1, p-v, f);
707 if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
711 if (strchr(SHELL_NEED_ESCAPE, *p))
724 int write_env_file(const char *fname, char **l) {
725 _cleanup_fclose_ FILE *f = NULL;
726 _cleanup_free_ char *p = NULL;
732 r = fopen_temporary(fname, &f, &p);
736 fchmod_umask(fileno(f), 0644);
739 write_env_var(f, *i);
741 r = fflush_and_check(f);
743 if (rename(p, fname) >= 0)
753 int executable_is_script(const char *path, char **interpreter) {
755 _cleanup_free_ char *line = NULL;
761 r = read_one_line_file(path, &line);
765 if (!startswith(line, "#!"))
768 ans = strstrip(line + 2);
769 len = strcspn(ans, " \t");
774 ans = strndup(ans, len);
783 * Retrieve one field from a file like /proc/self/status. pattern
784 * should start with '\n' and end with a ':'. Whitespace and zeros
785 * after the ':' will be skipped. field must be freed afterwards.
787 int get_status_field(const char *filename, const char *pattern, char **field) {
788 _cleanup_free_ char *status = NULL;
797 r = read_full_file(filename, &status, NULL);
801 t = strstr(status, pattern);
805 t += strlen(pattern);
807 t += strspn(t, " \t");
809 /* Also skip zeros, because when this is used for
810 * capabilities, we don't want the zeros. This way the
811 * same capability set always maps to the same string,
812 * irrespective of the total capability set size. For
813 * other numbers it shouldn't matter. */
815 /* Back off one char if there's nothing but whitespace
817 if (!*t || isspace(*t))
821 len = strcspn(t, WHITESPACE);
823 *field = strndup(t, len);