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