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