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