chiark / gitweb /
Unify GREEDY_REALLOC and GREEDY_REALLOC_T
[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, "re");
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(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(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(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(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(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(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(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(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(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
538         const char *k;
539         va_list aq, *ap = userdata;
540
541         if (!utf8_is_valid(key)) {
542                 _cleanup_free_ char *p = utf8_escape_invalid(key);
543
544                 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.",
545                           filename, line, p);
546                 return -EINVAL;
547         }
548
549         if (value && !utf8_is_valid(value)) {
550                 _cleanup_free_ char *p = utf8_escape_invalid(value);
551
552                 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
553                           filename, line, key, p);
554                 return -EINVAL;
555         }
556
557         va_copy(aq, *ap);
558
559         while ((k = va_arg(aq, const char *))) {
560                 char **v;
561
562                 v = va_arg(aq, char **);
563
564                 if (streq(key, k)) {
565                         va_end(aq);
566                         free(*v);
567                         *v = value;
568                         return 1;
569                 }
570         }
571
572         va_end(aq);
573         free(value);
574         return 0;
575 }
576
577 int parse_env_file(
578                 const char *fname,
579                 const char *newline, ...) {
580
581         va_list ap;
582         int r;
583
584         if (!newline)
585                 newline = NEWLINE;
586
587         va_start(ap, newline);
588         r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
589         va_end(ap);
590
591         return r;
592 }
593
594 static int load_env_file_push(const char *filename, unsigned line,
595                               const char *key, char *value, void *userdata) {
596         char ***m = userdata;
597         char *p;
598         int r;
599
600         if (!utf8_is_valid(key)) {
601                 _cleanup_free_ char *t = utf8_escape_invalid(key);
602
603                 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.",
604                           filename, line, t);
605                 return -EINVAL;
606         }
607
608         if (value && !utf8_is_valid(value)) {
609                 _cleanup_free_ char *t = utf8_escape_invalid(value);
610
611                 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
612                           filename, line, key, t);
613                 return -EINVAL;
614         }
615
616         p = strjoin(key, "=", strempty(value), NULL);
617         if (!p)
618                 return -ENOMEM;
619
620         r = strv_consume(m, p);
621         if (r < 0)
622                 return r;
623
624         free(value);
625         return 0;
626 }
627
628 int load_env_file(const char *fname, const char *newline, char ***rl) {
629         char **m = NULL;
630         int r;
631
632         if (!newline)
633                 newline = NEWLINE;
634
635         r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
636         if (r < 0) {
637                 strv_free(m);
638                 return r;
639         }
640
641         *rl = m;
642         return 0;
643 }
644
645 static void write_env_var(FILE *f, const char *v) {
646         const char *p;
647
648         p = strchr(v, '=');
649         if (!p) {
650                 /* Fallback */
651                 fputs(v, f);
652                 fputc('\n', f);
653                 return;
654         }
655
656         p++;
657         fwrite(v, 1, p-v, f);
658
659         if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
660                 fputc('\"', f);
661
662                 for (; *p; p++) {
663                         if (strchr("\'\"\\`$", *p))
664                                 fputc('\\', f);
665
666                         fputc(*p, f);
667                 }
668
669                 fputc('\"', f);
670         } else
671                 fputs(p, f);
672
673         fputc('\n', f);
674 }
675
676 int write_env_file(const char *fname, char **l) {
677         char **i;
678         _cleanup_free_ char *p = NULL;
679         _cleanup_fclose_ FILE *f = NULL;
680         int r;
681
682         r = fopen_temporary(fname, &f, &p);
683         if (r < 0)
684                 return r;
685
686         fchmod_umask(fileno(f), 0644);
687
688         errno = 0;
689         STRV_FOREACH(i, l)
690                 write_env_var(f, *i);
691
692         fflush(f);
693
694         if (ferror(f))
695                 r = errno ? -errno : -EIO;
696         else {
697                 if (rename(p, fname) < 0)
698                         r = -errno;
699                 else
700                         r = 0;
701         }
702
703         if (r < 0)
704                 unlink(p);
705
706         return r;
707 }
708
709 int executable_is_script(const char *path, char **interpreter) {
710         int r;
711         char _cleanup_free_ *line = NULL;
712         int len;
713         char *ans;
714
715         assert(path);
716
717         r = read_one_line_file(path, &line);
718         if (r < 0)
719                 return r;
720
721         if (!startswith(line, "#!"))
722                 return 0;
723
724         ans = strstrip(line + 2);
725         len = strcspn(ans, " \t");
726
727         if (len == 0)
728                 return 0;
729
730         ans = strndup(ans, len);
731         if (!ans)
732                 return -ENOMEM;
733
734         *interpreter = ans;
735         return 1;
736 }
737
738 /**
739  * Retrieve one field from a file like /proc/self/status.  pattern
740  * should start with '\n' and end with a ':'. Whitespace and zeros
741  * after the ':' will be skipped. field must be freed afterwards.
742  */
743 int get_status_field(const char *filename, const char *pattern, char **field) {
744         _cleanup_free_ char *status = NULL;
745         char *t;
746         size_t len;
747         int r;
748
749         assert(filename);
750         assert(pattern);
751         assert(field);
752
753         r = read_full_file(filename, &status, NULL);
754         if (r < 0)
755                 return r;
756
757         t = strstr(status, pattern);
758         if (!t)
759                 return -ENOENT;
760
761         t += strlen(pattern);
762         if (*t) {
763                 t += strspn(t, " \t");
764
765                 /* Also skip zeros, because when this is used for
766                  * capabilities, we don't want the zeros. This way the
767                  * same capability set always maps to the same string,
768                  * irrespective of the total capability set size. For
769                  * other numbers it shouldn't matter. */
770                 t += strspn(t, "0");
771                 /* Back off one char if there's nothing but whitespace
772                    and zeros */
773                 if (!*t || isspace(*t))
774                         t --;
775         }
776
777         len = strcspn(t, WHITESPACE);
778
779         *field = strndup(t, len);
780         if (!*field)
781                 return -ENOMEM;
782
783         return 0;
784 }