]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/fileio.c
Merge commit 'refs/pull/436/head' of https://github.com/systemd/systemd
[thirdparty/systemd.git] / src / basic / fileio.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <unistd.h>
23
24 #include "util.h"
25 #include "strv.h"
26 #include "utf8.h"
27 #include "ctype.h"
28 #include "fileio.h"
29
30 int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
31 assert(f);
32 assert(line);
33
34 errno = 0;
35
36 fputs(line, f);
37 if (enforce_newline && !endswith(line, "\n"))
38 fputc('\n', f);
39
40 fflush(f);
41
42 if (ferror(f))
43 return errno ? -errno : -EIO;
44
45 return 0;
46 }
47
48 static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
49 _cleanup_fclose_ FILE *f = NULL;
50 _cleanup_free_ char *p = NULL;
51 int r;
52
53 assert(fn);
54 assert(line);
55
56 r = fopen_temporary(fn, &f, &p);
57 if (r < 0)
58 return r;
59
60 fchmod_umask(fileno(f), 0644);
61
62 r = write_string_stream(f, line, enforce_newline);
63 if (r >= 0) {
64 if (rename(p, fn) < 0)
65 r = -errno;
66 }
67
68 if (r < 0)
69 unlink(p);
70
71 return r;
72 }
73
74 int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
75 _cleanup_fclose_ FILE *f = NULL;
76
77 assert(fn);
78 assert(line);
79
80 if (flags & WRITE_STRING_FILE_ATOMIC) {
81 assert(flags & WRITE_STRING_FILE_CREATE);
82
83 return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
84 }
85
86 if (flags & WRITE_STRING_FILE_CREATE) {
87 f = fopen(fn, "we");
88 if (!f)
89 return -errno;
90 } else {
91 int fd;
92
93 /* We manually build our own version of fopen(..., "we") that
94 * works without O_CREAT */
95 fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
96 if (fd < 0)
97 return -errno;
98
99 f = fdopen(fd, "we");
100 if (!f) {
101 safe_close(fd);
102 return -errno;
103 }
104 }
105
106 return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
107 }
108
109 int read_one_line_file(const char *fn, char **line) {
110 _cleanup_fclose_ FILE *f = NULL;
111 char t[LINE_MAX], *c;
112
113 assert(fn);
114 assert(line);
115
116 f = fopen(fn, "re");
117 if (!f)
118 return -errno;
119
120 if (!fgets(t, sizeof(t), f)) {
121
122 if (ferror(f))
123 return errno ? -errno : -EIO;
124
125 t[0] = 0;
126 }
127
128 c = strdup(t);
129 if (!c)
130 return -ENOMEM;
131 truncate_nl(c);
132
133 *line = c;
134 return 0;
135 }
136
137 int verify_one_line_file(const char *fn, const char *line) {
138 _cleanup_free_ char *value = NULL;
139 int r;
140
141 r = read_one_line_file(fn, &value);
142 if (r < 0)
143 return r;
144
145 return streq(value, line);
146 }
147
148 int read_full_stream(FILE *f, char **contents, size_t *size) {
149 size_t n, l;
150 _cleanup_free_ char *buf = NULL;
151 struct stat st;
152
153 assert(f);
154 assert(contents);
155
156 if (fstat(fileno(f), &st) < 0)
157 return -errno;
158
159 n = LINE_MAX;
160
161 if (S_ISREG(st.st_mode)) {
162
163 /* Safety check */
164 if (st.st_size > 4*1024*1024)
165 return -E2BIG;
166
167 /* Start with the right file size, but be prepared for
168 * files from /proc which generally report a file size
169 * of 0 */
170 if (st.st_size > 0)
171 n = st.st_size;
172 }
173
174 l = 0;
175 for (;;) {
176 char *t;
177 size_t k;
178
179 t = realloc(buf, n+1);
180 if (!t)
181 return -ENOMEM;
182
183 buf = t;
184 k = fread(buf + l, 1, n - l, f);
185
186 if (k <= 0) {
187 if (ferror(f))
188 return -errno;
189
190 break;
191 }
192
193 l += k;
194 n *= 2;
195
196 /* Safety check */
197 if (n > 4*1024*1024)
198 return -E2BIG;
199 }
200
201 buf[l] = 0;
202 *contents = buf;
203 buf = NULL; /* do not free */
204
205 if (size)
206 *size = l;
207
208 return 0;
209 }
210
211 int read_full_file(const char *fn, char **contents, size_t *size) {
212 _cleanup_fclose_ FILE *f = NULL;
213
214 assert(fn);
215 assert(contents);
216
217 f = fopen(fn, "re");
218 if (!f)
219 return -errno;
220
221 return read_full_stream(f, contents, size);
222 }
223
224 static int parse_env_file_internal(
225 FILE *f,
226 const char *fname,
227 const char *newline,
228 int (*push) (const char *filename, unsigned line,
229 const char *key, char *value, void *userdata, int *n_pushed),
230 void *userdata,
231 int *n_pushed) {
232
233 _cleanup_free_ char *contents = NULL, *key = NULL;
234 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;
235 char *p, *value = NULL;
236 int r;
237 unsigned line = 1;
238
239 enum {
240 PRE_KEY,
241 KEY,
242 PRE_VALUE,
243 VALUE,
244 VALUE_ESCAPE,
245 SINGLE_QUOTE_VALUE,
246 SINGLE_QUOTE_VALUE_ESCAPE,
247 DOUBLE_QUOTE_VALUE,
248 DOUBLE_QUOTE_VALUE_ESCAPE,
249 COMMENT,
250 COMMENT_ESCAPE
251 } state = PRE_KEY;
252
253 assert(newline);
254
255 if (f)
256 r = read_full_stream(f, &contents, NULL);
257 else
258 r = read_full_file(fname, &contents, NULL);
259 if (r < 0)
260 return r;
261
262 for (p = contents; *p; p++) {
263 char c = *p;
264
265 switch (state) {
266
267 case PRE_KEY:
268 if (strchr(COMMENTS, c))
269 state = COMMENT;
270 else if (!strchr(WHITESPACE, c)) {
271 state = KEY;
272 last_key_whitespace = (size_t) -1;
273
274 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
275 r = -ENOMEM;
276 goto fail;
277 }
278
279 key[n_key++] = c;
280 }
281 break;
282
283 case KEY:
284 if (strchr(newline, c)) {
285 state = PRE_KEY;
286 line ++;
287 n_key = 0;
288 } else if (c == '=') {
289 state = PRE_VALUE;
290 last_value_whitespace = (size_t) -1;
291 } else {
292 if (!strchr(WHITESPACE, c))
293 last_key_whitespace = (size_t) -1;
294 else if (last_key_whitespace == (size_t) -1)
295 last_key_whitespace = n_key;
296
297 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
298 r = -ENOMEM;
299 goto fail;
300 }
301
302 key[n_key++] = c;
303 }
304
305 break;
306
307 case PRE_VALUE:
308 if (strchr(newline, c)) {
309 state = PRE_KEY;
310 line ++;
311 key[n_key] = 0;
312
313 if (value)
314 value[n_value] = 0;
315
316 /* strip trailing whitespace from key */
317 if (last_key_whitespace != (size_t) -1)
318 key[last_key_whitespace] = 0;
319
320 r = push(fname, line, key, value, userdata, n_pushed);
321 if (r < 0)
322 goto fail;
323
324 n_key = 0;
325 value = NULL;
326 value_alloc = n_value = 0;
327
328 } else if (c == '\'')
329 state = SINGLE_QUOTE_VALUE;
330 else if (c == '\"')
331 state = DOUBLE_QUOTE_VALUE;
332 else if (c == '\\')
333 state = VALUE_ESCAPE;
334 else if (!strchr(WHITESPACE, c)) {
335 state = VALUE;
336
337 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
338 r = -ENOMEM;
339 goto fail;
340 }
341
342 value[n_value++] = c;
343 }
344
345 break;
346
347 case VALUE:
348 if (strchr(newline, c)) {
349 state = PRE_KEY;
350 line ++;
351
352 key[n_key] = 0;
353
354 if (value)
355 value[n_value] = 0;
356
357 /* Chomp off trailing whitespace from value */
358 if (last_value_whitespace != (size_t) -1)
359 value[last_value_whitespace] = 0;
360
361 /* strip trailing whitespace from key */
362 if (last_key_whitespace != (size_t) -1)
363 key[last_key_whitespace] = 0;
364
365 r = push(fname, line, key, value, userdata, n_pushed);
366 if (r < 0)
367 goto fail;
368
369 n_key = 0;
370 value = NULL;
371 value_alloc = n_value = 0;
372
373 } else if (c == '\\') {
374 state = VALUE_ESCAPE;
375 last_value_whitespace = (size_t) -1;
376 } else {
377 if (!strchr(WHITESPACE, c))
378 last_value_whitespace = (size_t) -1;
379 else if (last_value_whitespace == (size_t) -1)
380 last_value_whitespace = n_value;
381
382 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
383 r = -ENOMEM;
384 goto fail;
385 }
386
387 value[n_value++] = c;
388 }
389
390 break;
391
392 case VALUE_ESCAPE:
393 state = VALUE;
394
395 if (!strchr(newline, c)) {
396 /* Escaped newlines we eat up entirely */
397 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
398 r = -ENOMEM;
399 goto fail;
400 }
401
402 value[n_value++] = c;
403 }
404 break;
405
406 case SINGLE_QUOTE_VALUE:
407 if (c == '\'')
408 state = PRE_VALUE;
409 else if (c == '\\')
410 state = SINGLE_QUOTE_VALUE_ESCAPE;
411 else {
412 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
413 r = -ENOMEM;
414 goto fail;
415 }
416
417 value[n_value++] = c;
418 }
419
420 break;
421
422 case SINGLE_QUOTE_VALUE_ESCAPE:
423 state = SINGLE_QUOTE_VALUE;
424
425 if (!strchr(newline, c)) {
426 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
427 r = -ENOMEM;
428 goto fail;
429 }
430
431 value[n_value++] = c;
432 }
433 break;
434
435 case DOUBLE_QUOTE_VALUE:
436 if (c == '\"')
437 state = PRE_VALUE;
438 else if (c == '\\')
439 state = DOUBLE_QUOTE_VALUE_ESCAPE;
440 else {
441 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
442 r = -ENOMEM;
443 goto fail;
444 }
445
446 value[n_value++] = c;
447 }
448
449 break;
450
451 case DOUBLE_QUOTE_VALUE_ESCAPE:
452 state = DOUBLE_QUOTE_VALUE;
453
454 if (!strchr(newline, c)) {
455 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
456 r = -ENOMEM;
457 goto fail;
458 }
459
460 value[n_value++] = c;
461 }
462 break;
463
464 case COMMENT:
465 if (c == '\\')
466 state = COMMENT_ESCAPE;
467 else if (strchr(newline, c)) {
468 state = PRE_KEY;
469 line ++;
470 }
471 break;
472
473 case COMMENT_ESCAPE:
474 state = COMMENT;
475 break;
476 }
477 }
478
479 if (state == PRE_VALUE ||
480 state == VALUE ||
481 state == VALUE_ESCAPE ||
482 state == SINGLE_QUOTE_VALUE ||
483 state == SINGLE_QUOTE_VALUE_ESCAPE ||
484 state == DOUBLE_QUOTE_VALUE ||
485 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
486
487 key[n_key] = 0;
488
489 if (value)
490 value[n_value] = 0;
491
492 if (state == VALUE)
493 if (last_value_whitespace != (size_t) -1)
494 value[last_value_whitespace] = 0;
495
496 /* strip trailing whitespace from key */
497 if (last_key_whitespace != (size_t) -1)
498 key[last_key_whitespace] = 0;
499
500 r = push(fname, line, key, value, userdata, n_pushed);
501 if (r < 0)
502 goto fail;
503 }
504
505 return 0;
506
507 fail:
508 free(value);
509 return r;
510 }
511
512 static int parse_env_file_push(
513 const char *filename, unsigned line,
514 const char *key, char *value,
515 void *userdata,
516 int *n_pushed) {
517
518 const char *k;
519 va_list aq, *ap = userdata;
520
521 if (!utf8_is_valid(key)) {
522 _cleanup_free_ char *p;
523
524 p = utf8_escape_invalid(key);
525 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
526 return -EINVAL;
527 }
528
529 if (value && !utf8_is_valid(value)) {
530 _cleanup_free_ char *p;
531
532 p = utf8_escape_invalid(value);
533 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
534 return -EINVAL;
535 }
536
537 va_copy(aq, *ap);
538
539 while ((k = va_arg(aq, const char *))) {
540 char **v;
541
542 v = va_arg(aq, char **);
543
544 if (streq(key, k)) {
545 va_end(aq);
546 free(*v);
547 *v = value;
548
549 if (n_pushed)
550 (*n_pushed)++;
551
552 return 1;
553 }
554 }
555
556 va_end(aq);
557 free(value);
558
559 return 0;
560 }
561
562 int parse_env_file(
563 const char *fname,
564 const char *newline, ...) {
565
566 va_list ap;
567 int r, n_pushed = 0;
568
569 if (!newline)
570 newline = NEWLINE;
571
572 va_start(ap, newline);
573 r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
574 va_end(ap);
575
576 return r < 0 ? r : n_pushed;
577 }
578
579 static int load_env_file_push(
580 const char *filename, unsigned line,
581 const char *key, char *value,
582 void *userdata,
583 int *n_pushed) {
584 char ***m = userdata;
585 char *p;
586 int r;
587
588 if (!utf8_is_valid(key)) {
589 _cleanup_free_ char *t = utf8_escape_invalid(key);
590
591 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
592 return -EINVAL;
593 }
594
595 if (value && !utf8_is_valid(value)) {
596 _cleanup_free_ char *t = utf8_escape_invalid(value);
597
598 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
599 return -EINVAL;
600 }
601
602 p = strjoin(key, "=", strempty(value), NULL);
603 if (!p)
604 return -ENOMEM;
605
606 r = strv_consume(m, p);
607 if (r < 0)
608 return r;
609
610 if (n_pushed)
611 (*n_pushed)++;
612
613 free(value);
614 return 0;
615 }
616
617 int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
618 char **m = NULL;
619 int r;
620
621 if (!newline)
622 newline = NEWLINE;
623
624 r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
625 if (r < 0) {
626 strv_free(m);
627 return r;
628 }
629
630 *rl = m;
631 return 0;
632 }
633
634 static int load_env_file_push_pairs(
635 const char *filename, unsigned line,
636 const char *key, char *value,
637 void *userdata,
638 int *n_pushed) {
639 char ***m = userdata;
640 int r;
641
642 if (!utf8_is_valid(key)) {
643 _cleanup_free_ char *t = utf8_escape_invalid(key);
644
645 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
646 return -EINVAL;
647 }
648
649 if (value && !utf8_is_valid(value)) {
650 _cleanup_free_ char *t = utf8_escape_invalid(value);
651
652 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
653 return -EINVAL;
654 }
655
656 r = strv_extend(m, key);
657 if (r < 0)
658 return -ENOMEM;
659
660 if (!value) {
661 r = strv_extend(m, "");
662 if (r < 0)
663 return -ENOMEM;
664 } else {
665 r = strv_push(m, value);
666 if (r < 0)
667 return r;
668 }
669
670 if (n_pushed)
671 (*n_pushed)++;
672
673 return 0;
674 }
675
676 int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
677 char **m = NULL;
678 int r;
679
680 if (!newline)
681 newline = NEWLINE;
682
683 r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
684 if (r < 0) {
685 strv_free(m);
686 return r;
687 }
688
689 *rl = m;
690 return 0;
691 }
692
693 static void write_env_var(FILE *f, const char *v) {
694 const char *p;
695
696 p = strchr(v, '=');
697 if (!p) {
698 /* Fallback */
699 fputs(v, f);
700 fputc('\n', f);
701 return;
702 }
703
704 p++;
705 fwrite(v, 1, p-v, f);
706
707 if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
708 fputc('\"', f);
709
710 for (; *p; p++) {
711 if (strchr(SHELL_NEED_ESCAPE, *p))
712 fputc('\\', f);
713
714 fputc(*p, f);
715 }
716
717 fputc('\"', f);
718 } else
719 fputs(p, f);
720
721 fputc('\n', f);
722 }
723
724 int write_env_file(const char *fname, char **l) {
725 _cleanup_fclose_ FILE *f = NULL;
726 _cleanup_free_ char *p = NULL;
727 char **i;
728 int r;
729
730 assert(fname);
731
732 r = fopen_temporary(fname, &f, &p);
733 if (r < 0)
734 return r;
735
736 fchmod_umask(fileno(f), 0644);
737
738 STRV_FOREACH(i, l)
739 write_env_var(f, *i);
740
741 r = fflush_and_check(f);
742 if (r >= 0) {
743 if (rename(p, fname) >= 0)
744 return 0;
745
746 r = -errno;
747 }
748
749 unlink(p);
750 return r;
751 }
752
753 int executable_is_script(const char *path, char **interpreter) {
754 int r;
755 _cleanup_free_ char *line = NULL;
756 int len;
757 char *ans;
758
759 assert(path);
760
761 r = read_one_line_file(path, &line);
762 if (r < 0)
763 return r;
764
765 if (!startswith(line, "#!"))
766 return 0;
767
768 ans = strstrip(line + 2);
769 len = strcspn(ans, " \t");
770
771 if (len == 0)
772 return 0;
773
774 ans = strndup(ans, len);
775 if (!ans)
776 return -ENOMEM;
777
778 *interpreter = ans;
779 return 1;
780 }
781
782 /**
783 * Retrieve one field from a file like /proc/self/status. pattern
784 * should start with '\n' and end with a ':'. Whitespace and zeros
785 * after the ':' will be skipped. field must be freed afterwards.
786 */
787 int get_status_field(const char *filename, const char *pattern, char **field) {
788 _cleanup_free_ char *status = NULL;
789 char *t;
790 size_t len;
791 int r;
792
793 assert(filename);
794 assert(pattern);
795 assert(field);
796
797 r = read_full_file(filename, &status, NULL);
798 if (r < 0)
799 return r;
800
801 t = strstr(status, pattern);
802 if (!t)
803 return -ENOENT;
804
805 t += strlen(pattern);
806 if (*t) {
807 t += strspn(t, " \t");
808
809 /* Also skip zeros, because when this is used for
810 * capabilities, we don't want the zeros. This way the
811 * same capability set always maps to the same string,
812 * irrespective of the total capability set size. For
813 * other numbers it shouldn't matter. */
814 t += strspn(t, "0");
815 /* Back off one char if there's nothing but whitespace
816 and zeros */
817 if (!*t || isspace(*t))
818 t --;
819 }
820
821 len = strcspn(t, WHITESPACE);
822
823 *field = strndup(t, len);
824 if (!*field)
825 return -ENOMEM;
826
827 return 0;
828 }