]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/fileio.c
strv: multiple cleanups
[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, "r");
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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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((void**) &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 assert(utf8_is_valid(key));
538
539 if (value && !utf8_is_valid(value))
540 /* FIXME: filter UTF-8 */
541 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
542 filename, line, key, value);
543 else {
544 const char *k;
545 va_list* ap = (va_list*) userdata;
546 va_list aq;
547
548 va_copy(aq, *ap);
549
550 while ((k = va_arg(aq, const char *))) {
551 char **v;
552
553 v = va_arg(aq, char **);
554
555 if (streq(key, k)) {
556 va_end(aq);
557 free(*v);
558 *v = value;
559 return 1;
560 }
561 }
562
563 va_end(aq);
564 }
565
566 free(value);
567 return 0;
568 }
569
570 int parse_env_file(
571 const char *fname,
572 const char *newline, ...) {
573
574 va_list ap;
575 int r;
576
577 if (!newline)
578 newline = NEWLINE;
579
580 va_start(ap, newline);
581 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
582 va_end(ap);
583
584 return r;
585 }
586
587 static int load_env_file_push(const char *filename, unsigned line,
588 const char *key, char *value, void *userdata) {
589 assert(utf8_is_valid(key));
590
591 if (value && !utf8_is_valid(value))
592 /* FIXME: filter UTF-8 */
593 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
594 filename, line, key, value);
595 else {
596 char ***m = userdata;
597 char *p;
598 int r;
599
600 p = strjoin(key, "=", strempty(value), NULL);
601 if (!p)
602 return -ENOMEM;
603
604 r = strv_push(m, p);
605 if (r < 0) {
606 free(p);
607 return r;
608 }
609 }
610
611 free(value);
612 return 0;
613 }
614
615 int load_env_file(const char *fname, const char *newline, char ***rl) {
616 char **m = NULL;
617 int r;
618
619 if (!newline)
620 newline = NEWLINE;
621
622 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
623 if (r < 0) {
624 strv_free(m);
625 return r;
626 }
627
628 *rl = m;
629 return 0;
630 }
631
632 static void write_env_var(FILE *f, const char *v) {
633 const char *p;
634
635 p = strchr(v, '=');
636 if (!p) {
637 /* Fallback */
638 fputs(v, f);
639 fputc('\n', f);
640 return;
641 }
642
643 p++;
644 fwrite(v, 1, p-v, f);
645
646 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
647 fputc('\"', f);
648
649 for (; *p; p++) {
650 if (strchr("\'\"\\`$", *p))
651 fputc('\\', f);
652
653 fputc(*p, f);
654 }
655
656 fputc('\"', f);
657 } else
658 fputs(p, f);
659
660 fputc('\n', f);
661 }
662
663 int write_env_file(const char *fname, char **l) {
664 char **i;
665 _cleanup_free_ char *p = NULL;
666 _cleanup_fclose_ FILE *f = NULL;
667 int r;
668
669 r = fopen_temporary(fname, &f, &p);
670 if (r < 0)
671 return r;
672
673 fchmod_umask(fileno(f), 0644);
674
675 errno = 0;
676 STRV_FOREACH(i, l)
677 write_env_var(f, *i);
678
679 fflush(f);
680
681 if (ferror(f))
682 r = errno ? -errno : -EIO;
683 else {
684 if (rename(p, fname) < 0)
685 r = -errno;
686 else
687 r = 0;
688 }
689
690 if (r < 0)
691 unlink(p);
692
693 return r;
694 }
695
696 int executable_is_script(const char *path, char **interpreter) {
697 int r;
698 char _cleanup_free_ *line = NULL;
699 int len;
700 char *ans;
701
702 assert(path);
703
704 r = read_one_line_file(path, &line);
705 if (r < 0)
706 return r;
707
708 if (!startswith(line, "#!"))
709 return 0;
710
711 ans = strstrip(line + 2);
712 len = strcspn(ans, " \t");
713
714 if (len == 0)
715 return 0;
716
717 ans = strndup(ans, len);
718 if (!ans)
719 return -ENOMEM;
720
721 *interpreter = ans;
722 return 1;
723 }
724
725 /**
726 * Retrieve one field from a file like /proc/self/status. pattern
727 * should start with '\n' and end with a ':'. Whitespace and zeros
728 * after the ':' will be skipped. field must be freed afterwards.
729 */
730 int get_status_field(const char *filename, const char *pattern, char **field) {
731 _cleanup_free_ char *status = NULL;
732 char *t;
733 size_t len;
734 int r;
735
736 assert(filename);
737 assert(pattern);
738 assert(field);
739
740 r = read_full_file(filename, &status, NULL);
741 if (r < 0)
742 return r;
743
744 t = strstr(status, pattern);
745 if (!t)
746 return -ENOENT;
747
748 t += strlen(pattern);
749 if (*t) {
750 t += strspn(t, " \t");
751
752 /* Also skip zeros, because when this is used for
753 * capabilities, we don't want the zeros. This way the
754 * same capability set always maps to the same string,
755 * irrespective of the total capability set size. For
756 * other numbers it shouldn't matter. */
757 t += strspn(t, "0");
758 /* Back off one char if there's nothing but whitespace
759 and zeros */
760 if (!*t || isspace(*t))
761 t --;
762 }
763
764 len = strcspn(t, WHITESPACE);
765
766 *field = strndup(t, len);
767 if (!*field)
768 return -ENOMEM;
769
770 return 0;
771 }