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