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