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, int *n_pushed),
303 _cleanup_free_ char *contents = NULL, *key = NULL;
304 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;
305 char *p, *value = NULL;
316 SINGLE_QUOTE_VALUE_ESCAPE,
318 DOUBLE_QUOTE_VALUE_ESCAPE,
326 r = read_full_stream(f, &contents, NULL);
328 r = read_full_file(fname, &contents, NULL);
332 for (p = contents; *p; p++) {
338 if (strchr(COMMENTS, c))
340 else if (!strchr(WHITESPACE, c)) {
342 last_key_whitespace = (size_t) -1;
344 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
354 if (strchr(newline, c)) {
358 } else if (c == '=') {
360 last_value_whitespace = (size_t) -1;
362 if (!strchr(WHITESPACE, c))
363 last_key_whitespace = (size_t) -1;
364 else if (last_key_whitespace == (size_t) -1)
365 last_key_whitespace = n_key;
367 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
378 if (strchr(newline, c)) {
386 /* strip trailing whitespace from key */
387 if (last_key_whitespace != (size_t) -1)
388 key[last_key_whitespace] = 0;
390 r = push(fname, line, key, value, userdata, n_pushed);
396 value_alloc = n_value = 0;
398 } else if (c == '\'')
399 state = SINGLE_QUOTE_VALUE;
401 state = DOUBLE_QUOTE_VALUE;
403 state = VALUE_ESCAPE;
404 else if (!strchr(WHITESPACE, c)) {
407 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
412 value[n_value++] = c;
418 if (strchr(newline, c)) {
427 /* Chomp off trailing whitespace from value */
428 if (last_value_whitespace != (size_t) -1)
429 value[last_value_whitespace] = 0;
431 /* strip trailing whitespace from key */
432 if (last_key_whitespace != (size_t) -1)
433 key[last_key_whitespace] = 0;
435 r = push(fname, line, key, value, userdata, n_pushed);
441 value_alloc = n_value = 0;
443 } else if (c == '\\') {
444 state = VALUE_ESCAPE;
445 last_value_whitespace = (size_t) -1;
447 if (!strchr(WHITESPACE, c))
448 last_value_whitespace = (size_t) -1;
449 else if (last_value_whitespace == (size_t) -1)
450 last_value_whitespace = n_value;
452 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
457 value[n_value++] = c;
465 if (!strchr(newline, c)) {
466 /* Escaped newlines we eat up entirely */
467 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
472 value[n_value++] = c;
476 case SINGLE_QUOTE_VALUE:
480 state = SINGLE_QUOTE_VALUE_ESCAPE;
482 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
487 value[n_value++] = c;
492 case SINGLE_QUOTE_VALUE_ESCAPE:
493 state = SINGLE_QUOTE_VALUE;
495 if (!strchr(newline, c)) {
496 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
501 value[n_value++] = c;
505 case DOUBLE_QUOTE_VALUE:
509 state = DOUBLE_QUOTE_VALUE_ESCAPE;
511 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
516 value[n_value++] = c;
521 case DOUBLE_QUOTE_VALUE_ESCAPE:
522 state = DOUBLE_QUOTE_VALUE;
524 if (!strchr(newline, c)) {
525 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
530 value[n_value++] = c;
536 state = COMMENT_ESCAPE;
537 else if (strchr(newline, c)) {
549 if (state == PRE_VALUE ||
551 state == VALUE_ESCAPE ||
552 state == SINGLE_QUOTE_VALUE ||
553 state == SINGLE_QUOTE_VALUE_ESCAPE ||
554 state == DOUBLE_QUOTE_VALUE ||
555 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
563 if (last_value_whitespace != (size_t) -1)
564 value[last_value_whitespace] = 0;
566 /* strip trailing whitespace from key */
567 if (last_key_whitespace != (size_t) -1)
568 key[last_key_whitespace] = 0;
570 r = push(fname, line, key, value, userdata, n_pushed);
582 static int parse_env_file_push(
583 const char *filename, unsigned line,
584 const char *key, char *value,
589 va_list aq, *ap = userdata;
591 if (!utf8_is_valid(key)) {
592 _cleanup_free_ char *p;
594 p = utf8_escape_invalid(key);
595 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
599 if (value && !utf8_is_valid(value)) {
600 _cleanup_free_ char *p;
602 p = utf8_escape_invalid(value);
603 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
609 while ((k = va_arg(aq, const char *))) {
612 v = va_arg(aq, char **);
634 const char *newline, ...) {
642 va_start(ap, newline);
643 r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
646 return r < 0 ? r : n_pushed;
649 static int load_env_file_push(
650 const char *filename, unsigned line,
651 const char *key, char *value,
654 char ***m = userdata;
658 if (!utf8_is_valid(key)) {
659 _cleanup_free_ char *t = utf8_escape_invalid(key);
661 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
665 if (value && !utf8_is_valid(value)) {
666 _cleanup_free_ char *t = utf8_escape_invalid(value);
668 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
672 p = strjoin(key, "=", strempty(value), NULL);
676 r = strv_consume(m, p);
687 int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
694 r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
704 static int load_env_file_push_pairs(
705 const char *filename, unsigned line,
706 const char *key, char *value,
709 char ***m = userdata;
712 if (!utf8_is_valid(key)) {
713 _cleanup_free_ char *t = utf8_escape_invalid(key);
715 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
719 if (value && !utf8_is_valid(value)) {
720 _cleanup_free_ char *t = utf8_escape_invalid(value);
722 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
726 r = strv_extend(m, key);
731 r = strv_extend(m, "");
735 r = strv_push(m, value);
746 int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
753 r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
763 static void write_env_var(FILE *f, const char *v) {
775 fwrite(v, 1, p-v, f);
777 if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
781 if (strchr(SHELL_NEED_ESCAPE, *p))
794 int write_env_file(const char *fname, char **l) {
795 _cleanup_fclose_ FILE *f = NULL;
796 _cleanup_free_ char *p = NULL;
802 r = fopen_temporary(fname, &f, &p);
806 fchmod_umask(fileno(f), 0644);
809 write_env_var(f, *i);
811 r = fflush_and_check(f);
813 if (rename(p, fname) >= 0)
823 int executable_is_script(const char *path, char **interpreter) {
825 _cleanup_free_ char *line = NULL;
831 r = read_one_line_file(path, &line);
835 if (!startswith(line, "#!"))
838 ans = strstrip(line + 2);
839 len = strcspn(ans, " \t");
844 ans = strndup(ans, len);
853 * Retrieve one field from a file like /proc/self/status. pattern
854 * should start with '\n' and end with a ':'. Whitespace and zeros
855 * after the ':' will be skipped. field must be freed afterwards.
857 int get_status_field(const char *filename, const char *pattern, char **field) {
858 _cleanup_free_ char *status = NULL;
867 r = read_full_file(filename, &status, NULL);
871 t = strstr(status, pattern);
875 t += strlen(pattern);
877 t += strspn(t, " \t");
879 /* Also skip zeros, because when this is used for
880 * capabilities, we don't want the zeros. This way the
881 * same capability set always maps to the same string,
882 * irrespective of the total capability set size. For
883 * other numbers it shouldn't matter. */
885 /* Back off one char if there's nothing but whitespace
887 if (!*t || isspace(*t))
891 len = strcspn(t, WHITESPACE);
893 *field = strndup(t, len);