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/>.
23 #include <sys/sendfile.h>
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
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);
99 if (!endswith(line, "\n"))
105 r = errno ? -errno : -EIO;
107 if (rename(p, fn) < 0)
119 int read_one_line_file(const char *fn, char **line) {
120 _cleanup_fclose_ FILE *f = NULL;
121 char t[LINE_MAX], *c;
130 if (!fgets(t, sizeof(t), f)) {
133 return errno ? -errno : -EIO;
147 ssize_t sendfile_full(int out_fd, const char *fn) {
148 _cleanup_fclose_ FILE *f;
154 _cleanup_free_ char *buf = NULL;
163 r = fstat(fileno(f), &st);
167 s = sendfile(out_fd, fileno(f), NULL, st.st_size);
169 if (errno == EINVAL || errno == ENOSYS) {
176 /* sendfile() failed, fall back to read/write */
179 if (st.st_size > 4*1024*1024)
182 n = st.st_size > 0 ? st.st_size : LINE_MAX;
194 k = fread(buf + l, 1, n - l, f);
211 r = write(out_fd, buf, l);
218 int read_full_stream(FILE *f, char **contents, size_t *size) {
220 _cleanup_free_ char *buf = NULL;
226 if (fstat(fileno(f), &st) < 0)
231 if (S_ISREG(st.st_mode)) {
234 if (st.st_size > 4*1024*1024)
237 /* Start with the right file size, but be prepared for
238 * files from /proc which generally report a file size
249 t = realloc(buf, n+1);
254 k = fread(buf + l, 1, n - l, f);
273 buf = NULL; /* do not free */
281 int read_full_file(const char *fn, char **contents, size_t *size) {
282 _cleanup_fclose_ FILE *f = NULL;
291 return read_full_stream(f, contents, size);
294 static int parse_env_file_internal(
298 int (*push) (const char *filename, unsigned line,
299 const char *key, char *value, void *userdata),
302 _cleanup_free_ char *contents = NULL, *key = NULL;
303 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;
304 char *p, *value = NULL;
315 SINGLE_QUOTE_VALUE_ESCAPE,
317 DOUBLE_QUOTE_VALUE_ESCAPE,
325 r = read_full_stream(f, &contents, NULL);
327 r = read_full_file(fname, &contents, NULL);
331 for (p = contents; *p; p++) {
337 if (strchr(COMMENTS, c))
339 else if (!strchr(WHITESPACE, c)) {
341 last_key_whitespace = (size_t) -1;
343 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
353 if (strchr(newline, c)) {
357 } else if (c == '=') {
359 last_value_whitespace = (size_t) -1;
361 if (!strchr(WHITESPACE, c))
362 last_key_whitespace = (size_t) -1;
363 else if (last_key_whitespace == (size_t) -1)
364 last_key_whitespace = n_key;
366 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
377 if (strchr(newline, c)) {
385 /* strip trailing whitespace from key */
386 if (last_key_whitespace != (size_t) -1)
387 key[last_key_whitespace] = 0;
389 r = push(fname, line, key, value, userdata);
395 value_alloc = n_value = 0;
397 } else if (c == '\'')
398 state = SINGLE_QUOTE_VALUE;
400 state = DOUBLE_QUOTE_VALUE;
402 state = VALUE_ESCAPE;
403 else if (!strchr(WHITESPACE, c)) {
406 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
411 value[n_value++] = c;
417 if (strchr(newline, c)) {
426 /* Chomp off trailing whitespace from value */
427 if (last_value_whitespace != (size_t) -1)
428 value[last_value_whitespace] = 0;
430 /* strip trailing whitespace from key */
431 if (last_key_whitespace != (size_t) -1)
432 key[last_key_whitespace] = 0;
434 r = push(fname, line, key, value, userdata);
440 value_alloc = n_value = 0;
442 } else if (c == '\\') {
443 state = VALUE_ESCAPE;
444 last_value_whitespace = (size_t) -1;
446 if (!strchr(WHITESPACE, c))
447 last_value_whitespace = (size_t) -1;
448 else if (last_value_whitespace == (size_t) -1)
449 last_value_whitespace = n_value;
451 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
456 value[n_value++] = c;
464 if (!strchr(newline, c)) {
465 /* Escaped newlines we eat up entirely */
466 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
471 value[n_value++] = c;
475 case SINGLE_QUOTE_VALUE:
479 state = SINGLE_QUOTE_VALUE_ESCAPE;
481 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
486 value[n_value++] = c;
491 case SINGLE_QUOTE_VALUE_ESCAPE:
492 state = SINGLE_QUOTE_VALUE;
494 if (!strchr(newline, c)) {
495 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
500 value[n_value++] = c;
504 case DOUBLE_QUOTE_VALUE:
508 state = DOUBLE_QUOTE_VALUE_ESCAPE;
510 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
515 value[n_value++] = c;
520 case DOUBLE_QUOTE_VALUE_ESCAPE:
521 state = DOUBLE_QUOTE_VALUE;
523 if (!strchr(newline, c)) {
524 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
529 value[n_value++] = c;
535 state = COMMENT_ESCAPE;
536 else if (strchr(newline, c)) {
548 if (state == PRE_VALUE ||
550 state == VALUE_ESCAPE ||
551 state == SINGLE_QUOTE_VALUE ||
552 state == SINGLE_QUOTE_VALUE_ESCAPE ||
553 state == DOUBLE_QUOTE_VALUE ||
554 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
562 if (last_value_whitespace != (size_t) -1)
563 value[last_value_whitespace] = 0;
565 /* strip trailing whitespace from key */
566 if (last_key_whitespace != (size_t) -1)
567 key[last_key_whitespace] = 0;
569 r = push(fname, line, key, value, userdata);
581 static int parse_env_file_push(
582 const char *filename, unsigned line,
583 const char *key, char *value,
587 va_list aq, *ap = userdata;
589 if (!utf8_is_valid(key)) {
590 _cleanup_free_ char *p;
592 p = utf8_escape_invalid(key);
593 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
597 if (value && !utf8_is_valid(value)) {
598 _cleanup_free_ char *p;
600 p = utf8_escape_invalid(value);
601 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
607 while ((k = va_arg(aq, const char *))) {
610 v = va_arg(aq, char **);
628 const char *newline, ...) {
636 va_start(ap, newline);
637 r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap);
643 static int load_env_file_push(
644 const char *filename, unsigned line,
645 const char *key, char *value,
647 char ***m = userdata;
651 if (!utf8_is_valid(key)) {
652 _cleanup_free_ char *t = utf8_escape_invalid(key);
654 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
658 if (value && !utf8_is_valid(value)) {
659 _cleanup_free_ char *t = utf8_escape_invalid(value);
661 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
665 p = strjoin(key, "=", strempty(value), NULL);
669 r = strv_consume(m, p);
677 int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
684 r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m);
694 static int load_env_file_push_pairs(
695 const char *filename, unsigned line,
696 const char *key, char *value,
698 char ***m = userdata;
701 if (!utf8_is_valid(key)) {
702 _cleanup_free_ char *t = utf8_escape_invalid(key);
704 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
708 if (value && !utf8_is_valid(value)) {
709 _cleanup_free_ char *t = utf8_escape_invalid(value);
711 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
715 r = strv_extend(m, key);
720 r = strv_extend(m, "");
724 r = strv_push(m, value);
732 int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
739 r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m);
749 static void write_env_var(FILE *f, const char *v) {
761 fwrite(v, 1, p-v, f);
763 if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
767 if (strchr(SHELL_NEED_ESCAPE, *p))
780 int write_env_file(const char *fname, char **l) {
781 _cleanup_fclose_ FILE *f = NULL;
782 _cleanup_free_ char *p = NULL;
788 r = fopen_temporary(fname, &f, &p);
792 fchmod_umask(fileno(f), 0644);
795 write_env_var(f, *i);
797 r = fflush_and_check(f);
799 if (rename(p, fname) >= 0)
809 int executable_is_script(const char *path, char **interpreter) {
811 _cleanup_free_ char *line = NULL;
817 r = read_one_line_file(path, &line);
821 if (!startswith(line, "#!"))
824 ans = strstrip(line + 2);
825 len = strcspn(ans, " \t");
830 ans = strndup(ans, len);
839 * Retrieve one field from a file like /proc/self/status. pattern
840 * should start with '\n' and end with a ':'. Whitespace and zeros
841 * after the ':' will be skipped. field must be freed afterwards.
843 int get_status_field(const char *filename, const char *pattern, char **field) {
844 _cleanup_free_ char *status = NULL;
853 r = read_full_file(filename, &status, NULL);
857 t = strstr(status, pattern);
861 t += strlen(pattern);
863 t += strspn(t, " \t");
865 /* Also skip zeros, because when this is used for
866 * capabilities, we don't want the zeros. This way the
867 * same capability set always maps to the same string,
868 * irrespective of the total capability set size. For
869 * other numbers it shouldn't matter. */
871 /* Back off one char if there's nothing but whitespace
873 if (!*t || isspace(*t))
877 len = strcspn(t, WHITESPACE);
879 *field = strndup(t, len);