chiark / gitweb /
2ccb9e7edc70453b6e027e758475881431a05357
[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 "fileio.h"
24 #include "util.h"
25 #include "strv.h"
26
27 int write_string_file(const char *fn, const char *line) {
28         _cleanup_fclose_ FILE *f = NULL;
29
30         assert(fn);
31         assert(line);
32
33         f = fopen(fn, "we");
34         if (!f)
35                 return -errno;
36
37         errno = 0;
38         if (fputs(line, f) < 0)
39                 return errno ? -errno : -EIO;
40
41         if (!endswith(line, "\n"))
42                 fputc('\n', f);
43
44         fflush(f);
45
46         if (ferror(f))
47                 return errno ? -errno : -EIO;
48
49         return 0;
50 }
51
52 int write_string_file_atomic(const char *fn, const char *line) {
53         _cleanup_fclose_ FILE *f = NULL;
54         _cleanup_free_ char *p = NULL;
55         int r;
56
57         assert(fn);
58         assert(line);
59
60         r = fopen_temporary(fn, &f, &p);
61         if (r < 0)
62                 return r;
63
64         fchmod_umask(fileno(f), 0644);
65
66         errno = 0;
67         if (fputs(line, f) < 0) {
68                 r = -errno;
69                 goto finish;
70         }
71
72         if (!endswith(line, "\n"))
73                 fputc('\n', f);
74
75         fflush(f);
76
77         if (ferror(f))
78                 r = errno ? -errno : -EIO;
79         else {
80                 if (rename(p, fn) < 0)
81                         r = -errno;
82                 else
83                         r = 0;
84         }
85
86 finish:
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 int read_full_file(const char *fn, char **contents, size_t *size) {
122         _cleanup_fclose_ FILE *f = NULL;
123         size_t n, l;
124         _cleanup_free_ char *buf = NULL;
125         struct stat st;
126
127         assert(fn);
128         assert(contents);
129
130         f = fopen(fn, "re");
131         if (!f)
132                 return -errno;
133
134         if (fstat(fileno(f), &st) < 0)
135                 return -errno;
136
137         /* Safety check */
138         if (st.st_size > 4*1024*1024)
139                 return -E2BIG;
140
141         n = st.st_size > 0 ? st.st_size : LINE_MAX;
142         l = 0;
143
144         for (;;) {
145                 char *t;
146                 size_t k;
147
148                 t = realloc(buf, n+1);
149                 if (!t)
150                         return -ENOMEM;
151
152                 buf = t;
153                 k = fread(buf + l, 1, n - l, f);
154
155                 if (k <= 0) {
156                         if (ferror(f))
157                                 return -errno;
158
159                         break;
160                 }
161
162                 l += k;
163                 n *= 2;
164
165                 /* Safety check */
166                 if (n > 4*1024*1024)
167                         return -E2BIG;
168         }
169
170         buf[l] = 0;
171         *contents = buf;
172         buf = NULL;
173
174         if (size)
175                 *size = l;
176
177         return 0;
178 }
179
180 static int parse_env_file_internal(
181                 const char *fname,
182                 const char *newline,
183                 int (*push) (const char *key, char *value, void *userdata),
184                 void *userdata) {
185
186         _cleanup_free_ char *contents = NULL, *key = NULL;
187         size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_whitespace = (size_t) -1;
188         char *p, *value = NULL;
189         int r;
190
191         enum {
192                 PRE_KEY,
193                 KEY,
194                 PRE_EQUAL,
195                 PRE_VALUE,
196                 VALUE,
197                 VALUE_ESCAPE,
198                 SINGLE_QUOTE_VALUE,
199                 SINGLE_QUOTE_VALUE_ESCAPE,
200                 DOUBLE_QUOTE_VALUE,
201                 DOUBLE_QUOTE_VALUE_ESCAPE,
202                 COMMENT,
203                 COMMENT_ESCAPE
204         } state = PRE_KEY;
205
206         assert(fname);
207         assert(newline);
208
209         r = read_full_file(fname, &contents, NULL);
210         if (r < 0)
211                 return r;
212
213         for (p = contents; *p; p++) {
214                 char c = *p;
215
216                 switch (state) {
217
218                 case PRE_KEY:
219                         if (strchr(COMMENTS, c))
220                                 state = COMMENT;
221                         else if (!strchr(WHITESPACE, c)) {
222                                 state = KEY;
223                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
224                                         r = -ENOMEM;
225                                         goto fail;
226                                 }
227
228                                 key[n_key++] = c;
229                         }
230                         break;
231
232                 case KEY:
233                         if (strchr(newline, c)) {
234                                 state = PRE_KEY;
235                                 n_key = 0;
236                         } else if (strchr(WHITESPACE, c))
237                                 state = PRE_EQUAL;
238                         else if (c == '=')
239                                 state = PRE_VALUE;
240                         else {
241                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
242                                         r = -ENOMEM;
243                                         goto fail;
244                                 }
245
246                                 key[n_key++] = c;
247                         }
248
249                         break;
250
251                 case PRE_EQUAL:
252                         if (strchr(newline, c)) {
253                                 state = PRE_KEY;
254                                 n_key = 0;
255                         } else if (c == '=')
256                                 state = PRE_VALUE;
257                         else if (!strchr(WHITESPACE, c)) {
258                                 n_key = 0;
259                                 state = COMMENT;
260                         }
261
262                         break;
263
264                 case PRE_VALUE:
265                         if (strchr(newline, c)) {
266                                 state = PRE_KEY;
267                                 key[n_key] = 0;
268
269                                 if (value)
270                                         value[n_value] = 0;
271
272                                 r = push(key, value, userdata);
273                                 if (r < 0)
274                                         goto fail;
275
276                                 n_key = 0;
277                                 value = NULL;
278                                 value_alloc = n_value = 0;
279                         } else if (c == '\'')
280                                 state = SINGLE_QUOTE_VALUE;
281                         else if (c == '\"')
282                                 state = DOUBLE_QUOTE_VALUE;
283                         else if (c == '\\')
284                                 state = VALUE_ESCAPE;
285                         else if (!strchr(WHITESPACE, c)) {
286                                 state = VALUE;
287
288                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
289                                         r = -ENOMEM;
290                                         goto fail;
291                                 }
292
293                                 value[n_value++] = c;
294                         }
295
296                         break;
297
298                 case VALUE:
299                         if (strchr(newline, c)) {
300                                 state = PRE_KEY;
301                                 key[n_key] = 0;
302
303                                 if (value)
304                                         value[n_value] = 0;
305
306                                 /* Chomp off trailing whitespace */
307                                 if (last_whitespace != (size_t) -1)
308                                         value[last_whitespace] = 0;
309
310                                 r = push(key, value, userdata);
311                                 if (r < 0)
312                                         goto fail;
313
314                                 n_key = 0;
315                                 value = NULL;
316                                 value_alloc = n_value = 0;
317                         } else if (c == '\\') {
318                                 state = VALUE_ESCAPE;
319                                 last_whitespace = (size_t) -1;
320                         } else {
321                                 if (!strchr(WHITESPACE, c))
322                                         last_whitespace = (size_t) -1;
323                                 else if (last_whitespace == (size_t) -1)
324                                         last_whitespace = n_value;
325
326                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
327                                         r = -ENOMEM;
328                                         goto fail;
329                                 }
330
331                                 value[n_value++] = c;
332                         }
333
334                         break;
335
336                 case VALUE_ESCAPE:
337                         state = VALUE;
338
339                         if (!strchr(newline, c)) {
340                                 /* Escaped newlines we eat up entirely */
341                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
342                                         r = -ENOMEM;
343                                         goto fail;
344                                 }
345
346                                 value[n_value++] = c;
347                         }
348                         break;
349
350                 case SINGLE_QUOTE_VALUE:
351                         if (c == '\'')
352                                 state = PRE_VALUE;
353                         else if (c == '\\')
354                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
355                         else {
356                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
357                                         r = -ENOMEM;
358                                         goto fail;
359                                 }
360
361                                 value[n_value++] = c;
362                         }
363
364                         break;
365
366                 case SINGLE_QUOTE_VALUE_ESCAPE:
367                         state = SINGLE_QUOTE_VALUE;
368
369                         if (!strchr(newline, c)) {
370                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
371                                         r = -ENOMEM;
372                                         goto fail;
373                                 }
374
375                                 value[n_value++] = c;
376                         }
377                         break;
378
379                 case DOUBLE_QUOTE_VALUE:
380                         if (c == '\"')
381                                 state = PRE_VALUE;
382                         else if (c == '\\')
383                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
384                         else {
385                                 if (!greedy_realloc((void**) &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 DOUBLE_QUOTE_VALUE_ESCAPE:
396                         state = DOUBLE_QUOTE_VALUE;
397
398                         if (!strchr(newline, c)) {
399                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
400                                         r = -ENOMEM;
401                                         goto fail;
402                                 }
403
404                                 value[n_value++] = c;
405                         }
406                         break;
407
408                 case COMMENT:
409                         if (c == '\\')
410                                 state = COMMENT_ESCAPE;
411                         else if (strchr(newline, c))
412                                 state = PRE_KEY;
413                         break;
414
415                 case COMMENT_ESCAPE:
416                         state = COMMENT;
417                         break;
418                 }
419         }
420
421         if (state == PRE_VALUE ||
422             state == VALUE ||
423             state == VALUE_ESCAPE ||
424             state == SINGLE_QUOTE_VALUE ||
425             state == SINGLE_QUOTE_VALUE_ESCAPE ||
426             state == DOUBLE_QUOTE_VALUE ||
427             state == DOUBLE_QUOTE_VALUE_ESCAPE) {
428
429                 key[n_key] = 0;
430
431                 if (value)
432                         value[n_value] = 0;
433
434                 r = push(key, value, userdata);
435                 if (r < 0)
436                         goto fail;
437         }
438
439         return 0;
440
441 fail:
442         free(value);
443         return r;
444 }
445
446 static int parse_env_file_push(const char *key, char *value, void *userdata) {
447         const char *k;
448         va_list* ap = (va_list*) userdata;
449         va_list aq;
450
451         va_copy(aq, *ap);
452
453         while ((k = va_arg(aq, const char *))) {
454                 char **v;
455
456                 v = va_arg(aq, char **);
457
458                 if (streq(key, k)) {
459                         va_end(aq);
460                         free(*v);
461                         *v = value;
462                         return 1;
463                 }
464         }
465
466         va_end(aq);
467
468         free(value);
469         return 0;
470 }
471
472 int parse_env_file(
473                 const char *fname,
474                 const char *newline, ...) {
475
476         va_list ap;
477         int r;
478
479         if (!newline)
480                 newline = NEWLINE;
481
482         va_start(ap, newline);
483         r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
484         va_end(ap);
485
486         return r;
487 }
488
489 static int load_env_file_push(const char *key, char *value, void *userdata) {
490         char ***m = userdata;
491         char *p;
492         int r;
493
494         p = strjoin(key, "=", strempty(value), NULL);
495         if (!p)
496                 return -ENOMEM;
497
498         r = strv_push(m, p);
499         if (r < 0) {
500                 free(p);
501                 return r;
502         }
503
504         free(value);
505         return 0;
506 }
507
508 int load_env_file(const char *fname, const char *newline, char ***rl) {
509         char **m = NULL;
510         int r;
511
512         if (!newline)
513                 newline = NEWLINE;
514
515         r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
516         if (r < 0) {
517                 strv_free(m);
518                 return r;
519         }
520
521         *rl = m;
522         return 0;
523 }
524
525 static void write_env_var(FILE *f, const char *v) {
526         const char *p;
527
528         p = strchr(v, '=');
529         if (!p) {
530                 /* Fallback */
531                 fputs(v, f);
532                 fputc('\n', f);
533                 return;
534         }
535
536         p++;
537         fwrite(v, 1, p-v, f);
538
539         if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\")) {
540                 fputc('\"', f);
541
542                 for (; *p; p++) {
543                         if (strchr("\'\"\\", *p))
544                                 fputc('\\', f);
545
546                         fputc(*p, f);
547                 }
548
549                 fputc('\"', f);
550         } else
551                 fputs(p, f);
552
553         fputc('\n', f);
554 }
555
556 int write_env_file(const char *fname, char **l) {
557         char **i;
558         char _cleanup_free_ *p = NULL;
559         FILE _cleanup_fclose_ *f = NULL;
560         int r;
561
562         r = fopen_temporary(fname, &f, &p);
563         if (r < 0)
564                 return r;
565
566         fchmod_umask(fileno(f), 0644);
567
568         errno = 0;
569         STRV_FOREACH(i, l)
570                 write_env_var(f, *i);
571
572         fflush(f);
573
574         if (ferror(f)) {
575                 if (errno > 0)
576                         r = -errno;
577                 else
578                         r = -EIO;
579         } else {
580                 if (rename(p, fname) < 0)
581                         r = -errno;
582                 else
583                         r = 0;
584         }
585
586         if (r < 0)
587                 unlink(p);
588
589         return r;
590 }