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