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