chiark / gitweb /
tmpfiles: rename --unsafe to --boot
[elogind.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 }