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