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/>.
29 int write_string_to_file(FILE *f, const char *line) {
32 if (!endswith(line, "\n"))
38 return errno ? -errno : -EIO;
43 int write_string_file(const char *fn, const char *line) {
44 _cleanup_fclose_ FILE *f = NULL;
53 return write_string_to_file(f, line);
56 int write_string_file_atomic(const char *fn, const char *line) {
57 _cleanup_fclose_ FILE *f = NULL;
58 _cleanup_free_ char *p = NULL;
64 r = fopen_temporary(fn, &f, &p);
68 fchmod_umask(fileno(f), 0644);
72 if (!endswith(line, "\n"))
78 r = errno ? -errno : -EIO;
80 if (rename(p, fn) < 0)
92 int read_one_line_file(const char *fn, char **line) {
93 _cleanup_fclose_ FILE *f = NULL;
103 if (!fgets(t, sizeof(t), f)) {
106 return errno ? -errno : -EIO;
120 int read_full_file(const char *fn, char **contents, size_t *size) {
121 _cleanup_fclose_ FILE *f = NULL;
123 _cleanup_free_ char *buf = NULL;
133 if (fstat(fileno(f), &st) < 0)
137 if (st.st_size > 4*1024*1024)
140 n = st.st_size > 0 ? st.st_size : LINE_MAX;
147 t = realloc(buf, n+1);
152 k = fread(buf + l, 1, n - l, f);
179 static int parse_env_file_internal(
182 int (*push) (const char *filename, unsigned line,
183 const char *key, char *value, void *userdata),
186 _cleanup_free_ char *contents = NULL, *key = NULL;
187 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;
188 char *p, *value = NULL;
199 SINGLE_QUOTE_VALUE_ESCAPE,
201 DOUBLE_QUOTE_VALUE_ESCAPE,
209 r = read_full_file(fname, &contents, NULL);
213 for (p = contents; *p; p++) {
219 if (strchr(COMMENTS, c))
221 else if (!strchr(WHITESPACE, c)) {
223 last_key_whitespace = (size_t) -1;
225 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
235 if (strchr(newline, c)) {
239 } else if (c == '=') {
241 last_value_whitespace = (size_t) -1;
243 if (!strchr(WHITESPACE, c))
244 last_key_whitespace = (size_t) -1;
245 else if (last_key_whitespace == (size_t) -1)
246 last_key_whitespace = n_key;
248 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
259 if (strchr(newline, c)) {
267 /* strip trailing whitespace from key */
268 if (last_key_whitespace != (size_t) -1)
269 key[last_key_whitespace] = 0;
271 r = push(fname, line, key, value, userdata);
277 value_alloc = n_value = 0;
279 } else if (c == '\'')
280 state = SINGLE_QUOTE_VALUE;
282 state = DOUBLE_QUOTE_VALUE;
284 state = VALUE_ESCAPE;
285 else if (!strchr(WHITESPACE, c)) {
288 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
293 value[n_value++] = c;
299 if (strchr(newline, c)) {
308 /* Chomp off trailing whitespace from value */
309 if (last_value_whitespace != (size_t) -1)
310 value[last_value_whitespace] = 0;
312 /* strip trailing whitespace from key */
313 if (last_key_whitespace != (size_t) -1)
314 key[last_key_whitespace] = 0;
316 r = push(fname, line, key, value, userdata);
322 value_alloc = n_value = 0;
324 } else if (c == '\\') {
325 state = VALUE_ESCAPE;
326 last_value_whitespace = (size_t) -1;
328 if (!strchr(WHITESPACE, c))
329 last_value_whitespace = (size_t) -1;
330 else if (last_value_whitespace == (size_t) -1)
331 last_value_whitespace = n_value;
333 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
338 value[n_value++] = c;
346 if (!strchr(newline, c)) {
347 /* Escaped newlines we eat up entirely */
348 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
353 value[n_value++] = c;
357 case SINGLE_QUOTE_VALUE:
361 state = SINGLE_QUOTE_VALUE_ESCAPE;
363 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
368 value[n_value++] = c;
373 case SINGLE_QUOTE_VALUE_ESCAPE:
374 state = SINGLE_QUOTE_VALUE;
376 if (!strchr(newline, c)) {
377 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
382 value[n_value++] = c;
386 case DOUBLE_QUOTE_VALUE:
390 state = DOUBLE_QUOTE_VALUE_ESCAPE;
392 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
397 value[n_value++] = c;
402 case DOUBLE_QUOTE_VALUE_ESCAPE:
403 state = DOUBLE_QUOTE_VALUE;
405 if (!strchr(newline, c)) {
406 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
411 value[n_value++] = c;
417 state = COMMENT_ESCAPE;
418 else if (strchr(newline, c)) {
430 if (state == PRE_VALUE ||
432 state == VALUE_ESCAPE ||
433 state == SINGLE_QUOTE_VALUE ||
434 state == SINGLE_QUOTE_VALUE_ESCAPE ||
435 state == DOUBLE_QUOTE_VALUE ||
436 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
444 if (last_value_whitespace != (size_t) -1)
445 value[last_value_whitespace] = 0;
447 /* strip trailing whitespace from key */
448 if (last_key_whitespace != (size_t) -1)
449 key[last_key_whitespace] = 0;
451 r = push(fname, line, key, value, userdata);
463 static int parse_env_file_push(const char *filename, unsigned line,
464 const char *key, char *value, void *userdata) {
465 assert(utf8_is_valid(key));
467 if (value && !utf8_is_valid(value))
468 /* FIXME: filter UTF-8 */
469 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
470 filename, line, key, value);
473 va_list* ap = (va_list*) userdata;
478 while ((k = va_arg(aq, const char *))) {
481 v = va_arg(aq, char **);
500 const char *newline, ...) {
508 va_start(ap, newline);
509 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
515 static int load_env_file_push(const char *filename, unsigned line,
516 const char *key, char *value, void *userdata) {
517 assert(utf8_is_valid(key));
519 if (value && !utf8_is_valid(value))
520 /* FIXME: filter UTF-8 */
521 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
522 filename, line, key, value);
524 char ***m = userdata;
528 p = strjoin(key, "=", strempty(value), NULL);
543 int load_env_file(const char *fname, const char *newline, char ***rl) {
550 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
560 static void write_env_var(FILE *f, const char *v) {
572 fwrite(v, 1, p-v, f);
574 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
578 if (strchr("\'\"\\`$", *p))
591 int write_env_file(const char *fname, char **l) {
593 _cleanup_free_ char *p = NULL;
594 _cleanup_fclose_ FILE *f = NULL;
597 r = fopen_temporary(fname, &f, &p);
601 fchmod_umask(fileno(f), 0644);
605 write_env_var(f, *i);
610 r = errno ? -errno : -EIO;
612 if (rename(p, fname) < 0)
624 int executable_is_script(const char *path, char **interpreter) {
626 char _cleanup_free_ *line = NULL;
632 r = read_one_line_file(path, &line);
636 if (!startswith(line, "#!"))
639 ans = strstrip(line + 2);
640 len = strcspn(ans, " \t");
645 ans = strndup(ans, len);
654 * Retrieve one field from a file like /proc/self/status. pattern
655 * should start with '\n' and end with a ':'. Whitespace and zeros
656 * after the ':' will be skipped. field must be freed afterwards.
658 int get_status_field(const char *filename, const char *pattern, char **field) {
659 _cleanup_free_ char *status = NULL;
667 r = read_full_file(filename, &status, NULL);
671 t = strstr(status, pattern);
675 t += strlen(pattern);
677 t += strspn(t, " \t");
679 /* Also skip zeros, because when this is used for
680 * capabilities, we don't want the zeros. This way the
681 * same capability set always maps to the same string,
682 * irrespective of the total capability set size. For
683 * other numbers it shouldn't matter. */
685 /* Back off one char if there's nothing but whitespace
687 if (!*t || isspace(*t))
691 len = strcspn(t, WHITESPACE);
693 *field = strndup(t, len);