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