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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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) {
537 assert(utf8_is_valid(key));
539 if (value && !utf8_is_valid(value))
540 /* FIXME: filter UTF-8 */
541 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
542 filename, line, key, value);
545 va_list* ap = (va_list*) userdata;
550 while ((k = va_arg(aq, const char *))) {
553 v = va_arg(aq, char **);
572 const char *newline, ...) {
580 va_start(ap, newline);
581 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
587 static int load_env_file_push(const char *filename, unsigned line,
588 const char *key, char *value, void *userdata) {
589 assert(utf8_is_valid(key));
591 if (value && !utf8_is_valid(value))
592 /* FIXME: filter UTF-8 */
593 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
594 filename, line, key, value);
596 char ***m = userdata;
600 p = strjoin(key, "=", strempty(value), NULL);
615 int load_env_file(const char *fname, const char *newline, char ***rl) {
622 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
632 static void write_env_var(FILE *f, const char *v) {
644 fwrite(v, 1, p-v, f);
646 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
650 if (strchr("\'\"\\`$", *p))
663 int write_env_file(const char *fname, char **l) {
665 _cleanup_free_ char *p = NULL;
666 _cleanup_fclose_ FILE *f = NULL;
669 r = fopen_temporary(fname, &f, &p);
673 fchmod_umask(fileno(f), 0644);
677 write_env_var(f, *i);
682 r = errno ? -errno : -EIO;
684 if (rename(p, fname) < 0)
696 int executable_is_script(const char *path, char **interpreter) {
698 char _cleanup_free_ *line = NULL;
704 r = read_one_line_file(path, &line);
708 if (!startswith(line, "#!"))
711 ans = strstrip(line + 2);
712 len = strcspn(ans, " \t");
717 ans = strndup(ans, len);
726 * Retrieve one field from a file like /proc/self/status. pattern
727 * should start with '\n' and end with a ':'. Whitespace and zeros
728 * after the ':' will be skipped. field must be freed afterwards.
730 int get_status_field(const char *filename, const char *pattern, char **field) {
731 _cleanup_free_ char *status = NULL;
740 r = read_full_file(filename, &status, NULL);
744 t = strstr(status, pattern);
748 t += strlen(pattern);
750 t += strspn(t, " \t");
752 /* Also skip zeros, because when this is used for
753 * capabilities, we don't want the zeros. This way the
754 * same capability set always maps to the same string,
755 * irrespective of the total capability set size. For
756 * other numbers it shouldn't matter. */
758 /* Back off one char if there's nothing but whitespace
760 if (!*t || isspace(*t))
764 len = strcspn(t, WHITESPACE);
766 *field = strndup(t, len);