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);
250 static int parse_env_file_internal(
253 int (*push) (const char *filename, unsigned line,
254 const char *key, char *value, void *userdata),
257 _cleanup_free_ char *contents = NULL, *key = NULL;
258 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;
259 char *p, *value = NULL;
270 SINGLE_QUOTE_VALUE_ESCAPE,
272 DOUBLE_QUOTE_VALUE_ESCAPE,
280 r = read_full_file(fname, &contents, NULL);
284 for (p = contents; *p; p++) {
290 if (strchr(COMMENTS, c))
292 else if (!strchr(WHITESPACE, c)) {
294 last_key_whitespace = (size_t) -1;
296 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
306 if (strchr(newline, c)) {
310 } else if (c == '=') {
312 last_value_whitespace = (size_t) -1;
314 if (!strchr(WHITESPACE, c))
315 last_key_whitespace = (size_t) -1;
316 else if (last_key_whitespace == (size_t) -1)
317 last_key_whitespace = n_key;
319 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
330 if (strchr(newline, c)) {
338 /* strip trailing whitespace from key */
339 if (last_key_whitespace != (size_t) -1)
340 key[last_key_whitespace] = 0;
342 r = push(fname, line, key, value, userdata);
348 value_alloc = n_value = 0;
350 } else if (c == '\'')
351 state = SINGLE_QUOTE_VALUE;
353 state = DOUBLE_QUOTE_VALUE;
355 state = VALUE_ESCAPE;
356 else if (!strchr(WHITESPACE, c)) {
359 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
364 value[n_value++] = c;
370 if (strchr(newline, c)) {
379 /* Chomp off trailing whitespace from value */
380 if (last_value_whitespace != (size_t) -1)
381 value[last_value_whitespace] = 0;
383 /* strip trailing whitespace from key */
384 if (last_key_whitespace != (size_t) -1)
385 key[last_key_whitespace] = 0;
387 r = push(fname, line, key, value, userdata);
393 value_alloc = n_value = 0;
395 } else if (c == '\\') {
396 state = VALUE_ESCAPE;
397 last_value_whitespace = (size_t) -1;
399 if (!strchr(WHITESPACE, c))
400 last_value_whitespace = (size_t) -1;
401 else if (last_value_whitespace == (size_t) -1)
402 last_value_whitespace = n_value;
404 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
409 value[n_value++] = c;
417 if (!strchr(newline, c)) {
418 /* Escaped newlines we eat up entirely */
419 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
424 value[n_value++] = c;
428 case SINGLE_QUOTE_VALUE:
432 state = SINGLE_QUOTE_VALUE_ESCAPE;
434 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
439 value[n_value++] = c;
444 case SINGLE_QUOTE_VALUE_ESCAPE:
445 state = SINGLE_QUOTE_VALUE;
447 if (!strchr(newline, c)) {
448 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
453 value[n_value++] = c;
457 case DOUBLE_QUOTE_VALUE:
461 state = DOUBLE_QUOTE_VALUE_ESCAPE;
463 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
468 value[n_value++] = c;
473 case DOUBLE_QUOTE_VALUE_ESCAPE:
474 state = DOUBLE_QUOTE_VALUE;
476 if (!strchr(newline, c)) {
477 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
482 value[n_value++] = c;
488 state = COMMENT_ESCAPE;
489 else if (strchr(newline, c)) {
501 if (state == PRE_VALUE ||
503 state == VALUE_ESCAPE ||
504 state == SINGLE_QUOTE_VALUE ||
505 state == SINGLE_QUOTE_VALUE_ESCAPE ||
506 state == DOUBLE_QUOTE_VALUE ||
507 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
515 if (last_value_whitespace != (size_t) -1)
516 value[last_value_whitespace] = 0;
518 /* strip trailing whitespace from key */
519 if (last_key_whitespace != (size_t) -1)
520 key[last_key_whitespace] = 0;
522 r = push(fname, line, key, value, userdata);
534 static int parse_env_file_push(const char *filename, unsigned line,
535 const char *key, char *value, void *userdata) {
536 assert(utf8_is_valid(key));
538 if (value && !utf8_is_valid(value))
539 /* FIXME: filter UTF-8 */
540 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
541 filename, line, key, value);
544 va_list* ap = (va_list*) userdata;
549 while ((k = va_arg(aq, const char *))) {
552 v = va_arg(aq, char **);
571 const char *newline, ...) {
579 va_start(ap, newline);
580 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
586 static int load_env_file_push(const char *filename, unsigned line,
587 const char *key, char *value, void *userdata) {
588 assert(utf8_is_valid(key));
590 if (value && !utf8_is_valid(value))
591 /* FIXME: filter UTF-8 */
592 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
593 filename, line, key, value);
595 char ***m = userdata;
599 p = strjoin(key, "=", strempty(value), NULL);
614 int load_env_file(const char *fname, const char *newline, char ***rl) {
621 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
631 static void write_env_var(FILE *f, const char *v) {
643 fwrite(v, 1, p-v, f);
645 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
649 if (strchr("\'\"\\`$", *p))
662 int write_env_file(const char *fname, char **l) {
664 _cleanup_free_ char *p = NULL;
665 _cleanup_fclose_ FILE *f = NULL;
668 r = fopen_temporary(fname, &f, &p);
672 fchmod_umask(fileno(f), 0644);
676 write_env_var(f, *i);
681 r = errno ? -errno : -EIO;
683 if (rename(p, fname) < 0)
695 int executable_is_script(const char *path, char **interpreter) {
697 char _cleanup_free_ *line = NULL;
703 r = read_one_line_file(path, &line);
707 if (!startswith(line, "#!"))
710 ans = strstrip(line + 2);
711 len = strcspn(ans, " \t");
716 ans = strndup(ans, len);
725 * Retrieve one field from a file like /proc/self/status. pattern
726 * should start with '\n' and end with a ':'. Whitespace and zeros
727 * after the ':' will be skipped. field must be freed afterwards.
729 int get_status_field(const char *filename, const char *pattern, char **field) {
730 _cleanup_free_ char *status = NULL;
739 r = read_full_file(filename, &status, NULL);
743 t = strstr(status, pattern);
747 t += strlen(pattern);
749 t += strspn(t, " \t");
751 /* Also skip zeros, because when this is used for
752 * capabilities, we don't want the zeros. This way the
753 * same capability set always maps to the same string,
754 * irrespective of the total capability set size. For
755 * other numbers it shouldn't matter. */
757 /* Back off one char if there's nothing but whitespace
759 if (!*t || isspace(*t))
763 len = strcspn(t, WHITESPACE);
765 *field = strndup(t, len);