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