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