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