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