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