chiark / gitweb /
copy: teach copy_bytes() sendfile() support, and then replace sendfile_full() by it
[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
24 #include "util.h"
25 #include "strv.h"
26 #include "utf8.h"
27 #include "ctype.h"
28 #include "fileio.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 int read_full_stream(FILE *f, char **contents, size_t *size) {
148         size_t n, l;
149         _cleanup_free_ char *buf = NULL;
150         struct stat st;
151
152         assert(f);
153         assert(contents);
154
155         if (fstat(fileno(f), &st) < 0)
156                 return -errno;
157
158         n = LINE_MAX;
159
160         if (S_ISREG(st.st_mode)) {
161
162                 /* Safety check */
163                 if (st.st_size > 4*1024*1024)
164                         return -E2BIG;
165
166                 /* Start with the right file size, but be prepared for
167                  * files from /proc which generally report a file size
168                  * of 0 */
169                 if (st.st_size > 0)
170                         n = st.st_size;
171         }
172
173         l = 0;
174         for (;;) {
175                 char *t;
176                 size_t k;
177
178                 t = realloc(buf, n+1);
179                 if (!t)
180                         return -ENOMEM;
181
182                 buf = t;
183                 k = fread(buf + l, 1, n - l, f);
184
185                 if (k <= 0) {
186                         if (ferror(f))
187                                 return -errno;
188
189                         break;
190                 }
191
192                 l += k;
193                 n *= 2;
194
195                 /* Safety check */
196                 if (n > 4*1024*1024)
197                         return -E2BIG;
198         }
199
200         buf[l] = 0;
201         *contents = buf;
202         buf = NULL; /* do not free */
203
204         if (size)
205                 *size = l;
206
207         return 0;
208 }
209
210 int read_full_file(const char *fn, char **contents, size_t *size) {
211         _cleanup_fclose_ FILE *f = NULL;
212
213         assert(fn);
214         assert(contents);
215
216         f = fopen(fn, "re");
217         if (!f)
218                 return -errno;
219
220         return read_full_stream(f, contents, size);
221 }
222
223 static int parse_env_file_internal(
224                 FILE *f,
225                 const char *fname,
226                 const char *newline,
227                 int (*push) (const char *filename, unsigned line,
228                              const char *key, char *value, void *userdata, int *n_pushed),
229                 void *userdata,
230                 int *n_pushed) {
231
232         _cleanup_free_ char *contents = NULL, *key = NULL;
233         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;
234         char *p, *value = NULL;
235         int r;
236         unsigned line = 1;
237
238         enum {
239                 PRE_KEY,
240                 KEY,
241                 PRE_VALUE,
242                 VALUE,
243                 VALUE_ESCAPE,
244                 SINGLE_QUOTE_VALUE,
245                 SINGLE_QUOTE_VALUE_ESCAPE,
246                 DOUBLE_QUOTE_VALUE,
247                 DOUBLE_QUOTE_VALUE_ESCAPE,
248                 COMMENT,
249                 COMMENT_ESCAPE
250         } state = PRE_KEY;
251
252         assert(newline);
253
254         if (f)
255                 r = read_full_stream(f, &contents, NULL);
256         else
257                 r = read_full_file(fname, &contents, NULL);
258         if (r < 0)
259                 return r;
260
261         for (p = contents; *p; p++) {
262                 char c = *p;
263
264                 switch (state) {
265
266                 case PRE_KEY:
267                         if (strchr(COMMENTS, c))
268                                 state = COMMENT;
269                         else if (!strchr(WHITESPACE, c)) {
270                                 state = KEY;
271                                 last_key_whitespace = (size_t) -1;
272
273                                 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
274                                         r = -ENOMEM;
275                                         goto fail;
276                                 }
277
278                                 key[n_key++] = c;
279                         }
280                         break;
281
282                 case KEY:
283                         if (strchr(newline, c)) {
284                                 state = PRE_KEY;
285                                 line ++;
286                                 n_key = 0;
287                         } else if (c == '=') {
288                                 state = PRE_VALUE;
289                                 last_value_whitespace = (size_t) -1;
290                         } else {
291                                 if (!strchr(WHITESPACE, c))
292                                         last_key_whitespace = (size_t) -1;
293                                 else if (last_key_whitespace == (size_t) -1)
294                                          last_key_whitespace = n_key;
295
296                                 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
297                                         r = -ENOMEM;
298                                         goto fail;
299                                 }
300
301                                 key[n_key++] = c;
302                         }
303
304                         break;
305
306                 case PRE_VALUE:
307                         if (strchr(newline, c)) {
308                                 state = PRE_KEY;
309                                 line ++;
310                                 key[n_key] = 0;
311
312                                 if (value)
313                                         value[n_value] = 0;
314
315                                 /* strip trailing whitespace from key */
316                                 if (last_key_whitespace != (size_t) -1)
317                                         key[last_key_whitespace] = 0;
318
319                                 r = push(fname, line, key, value, userdata, n_pushed);
320                                 if (r < 0)
321                                         goto fail;
322
323                                 n_key = 0;
324                                 value = NULL;
325                                 value_alloc = n_value = 0;
326
327                         } else if (c == '\'')
328                                 state = SINGLE_QUOTE_VALUE;
329                         else if (c == '\"')
330                                 state = DOUBLE_QUOTE_VALUE;
331                         else if (c == '\\')
332                                 state = VALUE_ESCAPE;
333                         else if (!strchr(WHITESPACE, c)) {
334                                 state = VALUE;
335
336                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
337                                         r = -ENOMEM;
338                                         goto fail;
339                                 }
340
341                                 value[n_value++] = c;
342                         }
343
344                         break;
345
346                 case VALUE:
347                         if (strchr(newline, c)) {
348                                 state = PRE_KEY;
349                                 line ++;
350
351                                 key[n_key] = 0;
352
353                                 if (value)
354                                         value[n_value] = 0;
355
356                                 /* Chomp off trailing whitespace from value */
357                                 if (last_value_whitespace != (size_t) -1)
358                                         value[last_value_whitespace] = 0;
359
360                                 /* strip trailing whitespace from key */
361                                 if (last_key_whitespace != (size_t) -1)
362                                         key[last_key_whitespace] = 0;
363
364                                 r = push(fname, line, key, value, userdata, n_pushed);
365                                 if (r < 0)
366                                         goto fail;
367
368                                 n_key = 0;
369                                 value = NULL;
370                                 value_alloc = n_value = 0;
371
372                         } else if (c == '\\') {
373                                 state = VALUE_ESCAPE;
374                                 last_value_whitespace = (size_t) -1;
375                         } else {
376                                 if (!strchr(WHITESPACE, c))
377                                         last_value_whitespace = (size_t) -1;
378                                 else if (last_value_whitespace == (size_t) -1)
379                                         last_value_whitespace = n_value;
380
381                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
382                                         r = -ENOMEM;
383                                         goto fail;
384                                 }
385
386                                 value[n_value++] = c;
387                         }
388
389                         break;
390
391                 case VALUE_ESCAPE:
392                         state = VALUE;
393
394                         if (!strchr(newline, c)) {
395                                 /* Escaped newlines we eat up entirely */
396                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
397                                         r = -ENOMEM;
398                                         goto fail;
399                                 }
400
401                                 value[n_value++] = c;
402                         }
403                         break;
404
405                 case SINGLE_QUOTE_VALUE:
406                         if (c == '\'')
407                                 state = PRE_VALUE;
408                         else if (c == '\\')
409                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
410                         else {
411                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
412                                         r = -ENOMEM;
413                                         goto fail;
414                                 }
415
416                                 value[n_value++] = c;
417                         }
418
419                         break;
420
421                 case SINGLE_QUOTE_VALUE_ESCAPE:
422                         state = SINGLE_QUOTE_VALUE;
423
424                         if (!strchr(newline, c)) {
425                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
426                                         r = -ENOMEM;
427                                         goto fail;
428                                 }
429
430                                 value[n_value++] = c;
431                         }
432                         break;
433
434                 case DOUBLE_QUOTE_VALUE:
435                         if (c == '\"')
436                                 state = PRE_VALUE;
437                         else if (c == '\\')
438                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
439                         else {
440                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
441                                         r = -ENOMEM;
442                                         goto fail;
443                                 }
444
445                                 value[n_value++] = c;
446                         }
447
448                         break;
449
450                 case DOUBLE_QUOTE_VALUE_ESCAPE:
451                         state = DOUBLE_QUOTE_VALUE;
452
453                         if (!strchr(newline, c)) {
454                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
455                                         r = -ENOMEM;
456                                         goto fail;
457                                 }
458
459                                 value[n_value++] = c;
460                         }
461                         break;
462
463                 case COMMENT:
464                         if (c == '\\')
465                                 state = COMMENT_ESCAPE;
466                         else if (strchr(newline, c)) {
467                                 state = PRE_KEY;
468                                 line ++;
469                         }
470                         break;
471
472                 case COMMENT_ESCAPE:
473                         state = COMMENT;
474                         break;
475                 }
476         }
477
478         if (state == PRE_VALUE ||
479             state == VALUE ||
480             state == VALUE_ESCAPE ||
481             state == SINGLE_QUOTE_VALUE ||
482             state == SINGLE_QUOTE_VALUE_ESCAPE ||
483             state == DOUBLE_QUOTE_VALUE ||
484             state == DOUBLE_QUOTE_VALUE_ESCAPE) {
485
486                 key[n_key] = 0;
487
488                 if (value)
489                         value[n_value] = 0;
490
491                 if (state == VALUE)
492                         if (last_value_whitespace != (size_t) -1)
493                                 value[last_value_whitespace] = 0;
494
495                 /* strip trailing whitespace from key */
496                 if (last_key_whitespace != (size_t) -1)
497                         key[last_key_whitespace] = 0;
498
499                 r = push(fname, line, key, value, userdata, n_pushed);
500                 if (r < 0)
501                         goto fail;
502         }
503
504         return 0;
505
506 fail:
507         free(value);
508         return r;
509 }
510
511 static int parse_env_file_push(
512                 const char *filename, unsigned line,
513                 const char *key, char *value,
514                 void *userdata,
515                 int *n_pushed) {
516
517         const char *k;
518         va_list aq, *ap = userdata;
519
520         if (!utf8_is_valid(key)) {
521                 _cleanup_free_ char *p;
522
523                 p = utf8_escape_invalid(key);
524                 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
525                 return -EINVAL;
526         }
527
528         if (value && !utf8_is_valid(value)) {
529                 _cleanup_free_ char *p;
530
531                 p = utf8_escape_invalid(value);
532                 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
533                 return -EINVAL;
534         }
535
536         va_copy(aq, *ap);
537
538         while ((k = va_arg(aq, const char *))) {
539                 char **v;
540
541                 v = va_arg(aq, char **);
542
543                 if (streq(key, k)) {
544                         va_end(aq);
545                         free(*v);
546                         *v = value;
547
548                         if (n_pushed)
549                                 (*n_pushed)++;
550
551                         return 1;
552                 }
553         }
554
555         va_end(aq);
556         free(value);
557
558         return 0;
559 }
560
561 int parse_env_file(
562                 const char *fname,
563                 const char *newline, ...) {
564
565         va_list ap;
566         int r, n_pushed = 0;
567
568         if (!newline)
569                 newline = NEWLINE;
570
571         va_start(ap, newline);
572         r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
573         va_end(ap);
574
575         return r < 0 ? r : n_pushed;
576 }
577
578 static int load_env_file_push(
579                 const char *filename, unsigned line,
580                 const char *key, char *value,
581                 void *userdata,
582                 int *n_pushed) {
583         char ***m = userdata;
584         char *p;
585         int r;
586
587         if (!utf8_is_valid(key)) {
588                 _cleanup_free_ char *t = utf8_escape_invalid(key);
589
590                 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
591                 return -EINVAL;
592         }
593
594         if (value && !utf8_is_valid(value)) {
595                 _cleanup_free_ char *t = utf8_escape_invalid(value);
596
597                 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
598                 return -EINVAL;
599         }
600
601         p = strjoin(key, "=", strempty(value), NULL);
602         if (!p)
603                 return -ENOMEM;
604
605         r = strv_consume(m, p);
606         if (r < 0)
607                 return r;
608
609         if (n_pushed)
610                 (*n_pushed)++;
611
612         free(value);
613         return 0;
614 }
615
616 int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
617         char **m = NULL;
618         int r;
619
620         if (!newline)
621                 newline = NEWLINE;
622
623         r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
624         if (r < 0) {
625                 strv_free(m);
626                 return r;
627         }
628
629         *rl = m;
630         return 0;
631 }
632
633 static int load_env_file_push_pairs(
634                 const char *filename, unsigned line,
635                 const char *key, char *value,
636                 void *userdata,
637                 int *n_pushed) {
638         char ***m = userdata;
639         int r;
640
641         if (!utf8_is_valid(key)) {
642                 _cleanup_free_ char *t = utf8_escape_invalid(key);
643
644                 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
645                 return -EINVAL;
646         }
647
648         if (value && !utf8_is_valid(value)) {
649                 _cleanup_free_ char *t = utf8_escape_invalid(value);
650
651                 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
652                 return -EINVAL;
653         }
654
655         r = strv_extend(m, key);
656         if (r < 0)
657                 return -ENOMEM;
658
659         if (!value) {
660                 r = strv_extend(m, "");
661                 if (r < 0)
662                         return -ENOMEM;
663         } else {
664                 r = strv_push(m, value);
665                 if (r < 0)
666                         return r;
667         }
668
669         if (n_pushed)
670                 (*n_pushed)++;
671
672         return 0;
673 }
674
675 int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
676         char **m = NULL;
677         int r;
678
679         if (!newline)
680                 newline = NEWLINE;
681
682         r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
683         if (r < 0) {
684                 strv_free(m);
685                 return r;
686         }
687
688         *rl = m;
689         return 0;
690 }
691
692 static void write_env_var(FILE *f, const char *v) {
693         const char *p;
694
695         p = strchr(v, '=');
696         if (!p) {
697                 /* Fallback */
698                 fputs(v, f);
699                 fputc('\n', f);
700                 return;
701         }
702
703         p++;
704         fwrite(v, 1, p-v, f);
705
706         if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
707                 fputc('\"', f);
708
709                 for (; *p; p++) {
710                         if (strchr(SHELL_NEED_ESCAPE, *p))
711                                 fputc('\\', f);
712
713                         fputc(*p, f);
714                 }
715
716                 fputc('\"', f);
717         } else
718                 fputs(p, f);
719
720         fputc('\n', f);
721 }
722
723 int write_env_file(const char *fname, char **l) {
724         _cleanup_fclose_ FILE *f = NULL;
725         _cleanup_free_ char *p = NULL;
726         char **i;
727         int r;
728
729         assert(fname);
730
731         r = fopen_temporary(fname, &f, &p);
732         if (r < 0)
733                 return r;
734
735         fchmod_umask(fileno(f), 0644);
736
737         STRV_FOREACH(i, l)
738                 write_env_var(f, *i);
739
740         r = fflush_and_check(f);
741         if (r >= 0) {
742                 if (rename(p, fname) >= 0)
743                         return 0;
744
745                 r = -errno;
746         }
747
748         unlink(p);
749         return r;
750 }
751
752 int executable_is_script(const char *path, char **interpreter) {
753         int r;
754         _cleanup_free_ char *line = NULL;
755         int len;
756         char *ans;
757
758         assert(path);
759
760         r = read_one_line_file(path, &line);
761         if (r < 0)
762                 return r;
763
764         if (!startswith(line, "#!"))
765                 return 0;
766
767         ans = strstrip(line + 2);
768         len = strcspn(ans, " \t");
769
770         if (len == 0)
771                 return 0;
772
773         ans = strndup(ans, len);
774         if (!ans)
775                 return -ENOMEM;
776
777         *interpreter = ans;
778         return 1;
779 }
780
781 /**
782  * Retrieve one field from a file like /proc/self/status.  pattern
783  * should start with '\n' and end with a ':'. Whitespace and zeros
784  * after the ':' will be skipped. field must be freed afterwards.
785  */
786 int get_status_field(const char *filename, const char *pattern, char **field) {
787         _cleanup_free_ char *status = NULL;
788         char *t;
789         size_t len;
790         int r;
791
792         assert(filename);
793         assert(pattern);
794         assert(field);
795
796         r = read_full_file(filename, &status, NULL);
797         if (r < 0)
798                 return r;
799
800         t = strstr(status, pattern);
801         if (!t)
802                 return -ENOENT;
803
804         t += strlen(pattern);
805         if (*t) {
806                 t += strspn(t, " \t");
807
808                 /* Also skip zeros, because when this is used for
809                  * capabilities, we don't want the zeros. This way the
810                  * same capability set always maps to the same string,
811                  * irrespective of the total capability set size. For
812                  * other numbers it shouldn't matter. */
813                 t += strspn(t, "0");
814                 /* Back off one char if there's nothing but whitespace
815                    and zeros */
816                 if (!*t || isspace(*t))
817                         t --;
818         }
819
820         len = strcspn(t, WHITESPACE);
821
822         *field = strndup(t, len);
823         if (!*field)
824                 return -ENOMEM;
825
826         return 0;
827 }