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