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