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_to_file(FILE *f, const char *line) {
33 if (!endswith(line, "\n"))
39 return errno ? -errno : -EIO;
44 int write_string_file(const char *fn, const char *line) {
45 _cleanup_fclose_ FILE *f = NULL;
54 return write_string_to_file(f, line);
57 int write_string_file_atomic(const char *fn, const char *line) {
58 _cleanup_fclose_ FILE *f = NULL;
59 _cleanup_free_ char *p = NULL;
65 r = fopen_temporary(fn, &f, &p);
69 fchmod_umask(fileno(f), 0644);
73 if (!endswith(line, "\n"))
79 r = errno ? -errno : -EIO;
81 if (rename(p, fn) < 0)
93 int read_one_line_file(const char *fn, char **line) {
94 _cleanup_fclose_ FILE *f = NULL;
104 if (!fgets(t, sizeof(t), f)) {
107 return errno ? -errno : -EIO;
121 ssize_t sendfile_full(int out_fd, const char *fn) {
122 _cleanup_fclose_ FILE *f;
128 _cleanup_free_ char *buf = NULL;
137 r = fstat(fileno(f), &st);
141 s = sendfile(out_fd, fileno(f), NULL, st.st_size);
143 if (errno == EINVAL || errno == ENOSYS) {
150 /* sendfile() failed, fall back to read/write */
153 if (st.st_size > 4*1024*1024)
156 n = st.st_size > 0 ? st.st_size : LINE_MAX;
168 k = fread(buf + l, 1, n - l, f);
185 r = write(out_fd, buf, l);
192 int read_full_file(const char *fn, char **contents, size_t *size) {
193 _cleanup_fclose_ FILE *f = NULL;
195 _cleanup_free_ char *buf = NULL;
205 if (fstat(fileno(f), &st) < 0)
209 if (st.st_size > 4*1024*1024)
212 n = st.st_size > 0 ? st.st_size : LINE_MAX;
219 t = realloc(buf, n+1);
224 k = fread(buf + l, 1, n - l, f);
243 buf = NULL; /* do not free */
251 static int parse_env_file_internal(
254 int (*push) (const char *filename, unsigned line,
255 const char *key, char *value, void *userdata),
258 _cleanup_free_ char *contents = NULL, *key = NULL;
259 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;
260 char *p, *value = NULL;
271 SINGLE_QUOTE_VALUE_ESCAPE,
273 DOUBLE_QUOTE_VALUE_ESCAPE,
281 r = read_full_file(fname, &contents, NULL);
285 for (p = contents; *p; p++) {
291 if (strchr(COMMENTS, c))
293 else if (!strchr(WHITESPACE, c)) {
295 last_key_whitespace = (size_t) -1;
297 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
307 if (strchr(newline, c)) {
311 } else if (c == '=') {
313 last_value_whitespace = (size_t) -1;
315 if (!strchr(WHITESPACE, c))
316 last_key_whitespace = (size_t) -1;
317 else if (last_key_whitespace == (size_t) -1)
318 last_key_whitespace = n_key;
320 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
331 if (strchr(newline, c)) {
339 /* strip trailing whitespace from key */
340 if (last_key_whitespace != (size_t) -1)
341 key[last_key_whitespace] = 0;
343 r = push(fname, line, key, value, userdata);
349 value_alloc = n_value = 0;
351 } else if (c == '\'')
352 state = SINGLE_QUOTE_VALUE;
354 state = DOUBLE_QUOTE_VALUE;
356 state = VALUE_ESCAPE;
357 else if (!strchr(WHITESPACE, c)) {
360 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
365 value[n_value++] = c;
371 if (strchr(newline, c)) {
380 /* Chomp off trailing whitespace from value */
381 if (last_value_whitespace != (size_t) -1)
382 value[last_value_whitespace] = 0;
384 /* strip trailing whitespace from key */
385 if (last_key_whitespace != (size_t) -1)
386 key[last_key_whitespace] = 0;
388 r = push(fname, line, key, value, userdata);
394 value_alloc = n_value = 0;
396 } else if (c == '\\') {
397 state = VALUE_ESCAPE;
398 last_value_whitespace = (size_t) -1;
400 if (!strchr(WHITESPACE, c))
401 last_value_whitespace = (size_t) -1;
402 else if (last_value_whitespace == (size_t) -1)
403 last_value_whitespace = n_value;
405 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
410 value[n_value++] = c;
418 if (!strchr(newline, c)) {
419 /* Escaped newlines we eat up entirely */
420 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
425 value[n_value++] = c;
429 case SINGLE_QUOTE_VALUE:
433 state = SINGLE_QUOTE_VALUE_ESCAPE;
435 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
440 value[n_value++] = c;
445 case SINGLE_QUOTE_VALUE_ESCAPE:
446 state = SINGLE_QUOTE_VALUE;
448 if (!strchr(newline, c)) {
449 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
454 value[n_value++] = c;
458 case DOUBLE_QUOTE_VALUE:
462 state = DOUBLE_QUOTE_VALUE_ESCAPE;
464 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
469 value[n_value++] = c;
474 case DOUBLE_QUOTE_VALUE_ESCAPE:
475 state = DOUBLE_QUOTE_VALUE;
477 if (!strchr(newline, c)) {
478 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
483 value[n_value++] = c;
489 state = COMMENT_ESCAPE;
490 else if (strchr(newline, c)) {
502 if (state == PRE_VALUE ||
504 state == VALUE_ESCAPE ||
505 state == SINGLE_QUOTE_VALUE ||
506 state == SINGLE_QUOTE_VALUE_ESCAPE ||
507 state == DOUBLE_QUOTE_VALUE ||
508 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
516 if (last_value_whitespace != (size_t) -1)
517 value[last_value_whitespace] = 0;
519 /* strip trailing whitespace from key */
520 if (last_key_whitespace != (size_t) -1)
521 key[last_key_whitespace] = 0;
523 r = push(fname, line, key, value, userdata);
535 static int parse_env_file_push(const char *filename, unsigned line,
536 const char *key, char *value, void *userdata) {
539 va_list aq, *ap = userdata;
541 if (!utf8_is_valid(key)) {
542 _cleanup_free_ char *p = utf8_escape_invalid(key);
544 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.",
549 if (value && !utf8_is_valid(value)) {
550 _cleanup_free_ char *p = utf8_escape_invalid(value);
552 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
553 filename, line, key, p);
559 while ((k = va_arg(aq, const char *))) {
562 v = va_arg(aq, char **);
579 const char *newline, ...) {
587 va_start(ap, newline);
588 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
594 static int load_env_file_push(const char *filename, unsigned line,
595 const char *key, char *value, void *userdata) {
596 char ***m = userdata;
600 if (!utf8_is_valid(key)) {
601 _cleanup_free_ char *t = utf8_escape_invalid(key);
603 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.",
608 if (value && !utf8_is_valid(value)) {
609 _cleanup_free_ char *t = utf8_escape_invalid(value);
611 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
612 filename, line, key, t);
616 p = strjoin(key, "=", strempty(value), NULL);
620 r = strv_consume(m, p);
628 int load_env_file(const char *fname, const char *newline, char ***rl) {
635 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
645 static void write_env_var(FILE *f, const char *v) {
657 fwrite(v, 1, p-v, f);
659 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
663 if (strchr("\'\"\\`$", *p))
676 int write_env_file(const char *fname, char **l) {
678 _cleanup_free_ char *p = NULL;
679 _cleanup_fclose_ FILE *f = NULL;
682 r = fopen_temporary(fname, &f, &p);
686 fchmod_umask(fileno(f), 0644);
690 write_env_var(f, *i);
695 r = errno ? -errno : -EIO;
697 if (rename(p, fname) < 0)
709 int executable_is_script(const char *path, char **interpreter) {
711 char _cleanup_free_ *line = NULL;
717 r = read_one_line_file(path, &line);
721 if (!startswith(line, "#!"))
724 ans = strstrip(line + 2);
725 len = strcspn(ans, " \t");
730 ans = strndup(ans, len);
739 * Retrieve one field from a file like /proc/self/status. pattern
740 * should start with '\n' and end with a ':'. Whitespace and zeros
741 * after the ':' will be skipped. field must be freed afterwards.
743 int get_status_field(const char *filename, const char *pattern, char **field) {
744 _cleanup_free_ char *status = NULL;
753 r = read_full_file(filename, &status, NULL);
757 t = strstr(status, pattern);
761 t += strlen(pattern);
763 t += strspn(t, " \t");
765 /* Also skip zeros, because when this is used for
766 * capabilities, we don't want the zeros. This way the
767 * same capability set always maps to the same string,
768 * irrespective of the total capability set size. For
769 * other numbers it shouldn't matter. */
771 /* Back off one char if there's nothing but whitespace
773 if (!*t || isspace(*t))
777 len = strcspn(t, WHITESPACE);
779 *field = strndup(t, len);