chiark / gitweb /
337b9e41476d2c418c14453d2f3a9f578ef072d6
[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_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                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
216                                         r = -ENOMEM;
217                                         goto fail;
218                                 }
219
220                                 key[n_key++] = c;
221                         }
222                         break;
223
224                 case KEY:
225                         if (strchr(newline, c)) {
226                                 state = PRE_KEY;
227                                 n_key = 0;
228                         } else if (c == '=')
229                                 state = PRE_VALUE;
230                         else {
231                                 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
232                                         r = -ENOMEM;
233                                         goto fail;
234                                 }
235
236                                 key[n_key++] = c;
237                         }
238
239                         break;
240
241                 case PRE_VALUE:
242                         if (strchr(newline, c)) {
243                                 state = PRE_KEY;
244                                 key[n_key] = 0;
245
246                                 if (value)
247                                         value[n_value] = 0;
248
249                                 /* strip trailing whitespace from key */
250                                 while(n_key && strchr(WHITESPACE, key[--n_key]))
251                                         key[n_key]=0;
252
253                                 r = push(key, value, userdata);
254                                 if (r < 0)
255                                         goto fail;
256
257                                 n_key = 0;
258                                 value = NULL;
259                                 value_alloc = n_value = 0;
260                         } else if (c == '\'')
261                                 state = SINGLE_QUOTE_VALUE;
262                         else if (c == '\"')
263                                 state = DOUBLE_QUOTE_VALUE;
264                         else if (c == '\\')
265                                 state = VALUE_ESCAPE;
266                         else if (!strchr(WHITESPACE, c)) {
267                                 state = VALUE;
268
269                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
270                                         r = -ENOMEM;
271                                         goto fail;
272                                 }
273
274                                 value[n_value++] = c;
275                         }
276
277                         break;
278
279                 case VALUE:
280                         if (strchr(newline, c)) {
281                                 state = PRE_KEY;
282
283                                 key[n_key] = 0;
284
285                                 if (value)
286                                         value[n_value] = 0;
287
288                                 /* Chomp off trailing whitespace */
289                                 if (last_whitespace != (size_t) -1)
290                                         value[last_whitespace] = 0;
291
292                                 /* strip trailing whitespace from key */
293                                 while(n_key && strchr(WHITESPACE, key[--n_key]))
294                                         key[n_key]=0;
295
296                                 r = push(key, value, userdata);
297                                 if (r < 0)
298                                         goto fail;
299
300                                 n_key = 0;
301                                 value = NULL;
302                                 value_alloc = n_value = 0;
303                         } else if (c == '\\') {
304                                 state = VALUE_ESCAPE;
305                                 last_whitespace = (size_t) -1;
306                         } else {
307                                 if (!strchr(WHITESPACE, c))
308                                         last_whitespace = (size_t) -1;
309                                 else if (last_whitespace == (size_t) -1)
310                                         last_whitespace = n_value;
311
312                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
313                                         r = -ENOMEM;
314                                         goto fail;
315                                 }
316
317                                 value[n_value++] = c;
318                         }
319
320                         break;
321
322                 case VALUE_ESCAPE:
323                         state = VALUE;
324
325                         if (!strchr(newline, c)) {
326                                 /* Escaped newlines we eat up entirely */
327                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
328                                         r = -ENOMEM;
329                                         goto fail;
330                                 }
331
332                                 value[n_value++] = c;
333                         }
334                         break;
335
336                 case SINGLE_QUOTE_VALUE:
337                         if (c == '\'')
338                                 state = PRE_VALUE;
339                         else if (c == '\\')
340                                 state = SINGLE_QUOTE_VALUE_ESCAPE;
341                         else {
342                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
343                                         r = -ENOMEM;
344                                         goto fail;
345                                 }
346
347                                 value[n_value++] = c;
348                         }
349
350                         break;
351
352                 case SINGLE_QUOTE_VALUE_ESCAPE:
353                         state = SINGLE_QUOTE_VALUE;
354
355                         if (!strchr(newline, c)) {
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                         break;
364
365                 case DOUBLE_QUOTE_VALUE:
366                         if (c == '\"')
367                                 state = PRE_VALUE;
368                         else if (c == '\\')
369                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
370                         else {
371                                 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
372                                         r = -ENOMEM;
373                                         goto fail;
374                                 }
375
376                                 value[n_value++] = c;
377                         }
378
379                         break;
380
381                 case DOUBLE_QUOTE_VALUE_ESCAPE:
382                         state = DOUBLE_QUOTE_VALUE;
383
384                         if (!strchr(newline, c)) {
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                         break;
393
394                 case COMMENT:
395                         if (c == '\\')
396                                 state = COMMENT_ESCAPE;
397                         else if (strchr(newline, c))
398                                 state = PRE_KEY;
399                         break;
400
401                 case COMMENT_ESCAPE:
402                         state = COMMENT;
403                         break;
404                 }
405         }
406
407         if (state == PRE_VALUE ||
408             state == VALUE ||
409             state == VALUE_ESCAPE ||
410             state == SINGLE_QUOTE_VALUE ||
411             state == SINGLE_QUOTE_VALUE_ESCAPE ||
412             state == DOUBLE_QUOTE_VALUE ||
413             state == DOUBLE_QUOTE_VALUE_ESCAPE) {
414
415                 key[n_key] = 0;
416
417                 if (value)
418                         value[n_value] = 0;
419
420                 /* strip trailing whitespace from key */
421                 while(n_key && strchr(WHITESPACE, key[--n_key]))
422                         key[n_key]=0;
423
424                 r = push(key, value, userdata);
425                 if (r < 0)
426                         goto fail;
427         }
428
429         return 0;
430
431 fail:
432         free(value);
433         return r;
434 }
435
436 static int parse_env_file_push(const char *key, char *value, void *userdata) {
437         const char *k;
438         va_list* ap = (va_list*) userdata;
439         va_list aq;
440
441         va_copy(aq, *ap);
442
443         while ((k = va_arg(aq, const char *))) {
444                 char **v;
445
446                 v = va_arg(aq, char **);
447
448                 if (streq(key, k)) {
449                         va_end(aq);
450                         free(*v);
451                         *v = value;
452                         return 1;
453                 }
454         }
455
456         va_end(aq);
457
458         free(value);
459         return 0;
460 }
461
462 int parse_env_file(
463                 const char *fname,
464                 const char *newline, ...) {
465
466         va_list ap;
467         int r;
468
469         if (!newline)
470                 newline = NEWLINE;
471
472         va_start(ap, newline);
473         r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
474         va_end(ap);
475
476         return r;
477 }
478
479 static int load_env_file_push(const char *key, char *value, void *userdata) {
480         char ***m = userdata;
481         char *p;
482         int r;
483
484         p = strjoin(key, "=", strempty(value), NULL);
485         if (!p)
486                 return -ENOMEM;
487
488         r = strv_push(m, p);
489         if (r < 0) {
490                 free(p);
491                 return r;
492         }
493
494         free(value);
495         return 0;
496 }
497
498 int load_env_file(const char *fname, const char *newline, char ***rl) {
499         char **m = NULL;
500         int r;
501
502         if (!newline)
503                 newline = NEWLINE;
504
505         r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
506         if (r < 0) {
507                 strv_free(m);
508                 return r;
509         }
510
511         *rl = m;
512         return 0;
513 }
514
515 static void write_env_var(FILE *f, const char *v) {
516         const char *p;
517
518         p = strchr(v, '=');
519         if (!p) {
520                 /* Fallback */
521                 fputs(v, f);
522                 fputc('\n', f);
523                 return;
524         }
525
526         p++;
527         fwrite(v, 1, p-v, f);
528
529         if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
530                 fputc('\"', f);
531
532                 for (; *p; p++) {
533                         if (strchr("\'\"\\`$", *p))
534                                 fputc('\\', f);
535
536                         fputc(*p, f);
537                 }
538
539                 fputc('\"', f);
540         } else
541                 fputs(p, f);
542
543         fputc('\n', f);
544 }
545
546 int write_env_file(const char *fname, char **l) {
547         char **i;
548         _cleanup_free_ char *p = NULL;
549         _cleanup_fclose_ FILE *f = NULL;
550         int r;
551
552         r = fopen_temporary(fname, &f, &p);
553         if (r < 0)
554                 return r;
555
556         fchmod_umask(fileno(f), 0644);
557
558         errno = 0;
559         STRV_FOREACH(i, l)
560                 write_env_var(f, *i);
561
562         fflush(f);
563
564         if (ferror(f))
565                 r = errno ? -errno : -EIO;
566         else {
567                 if (rename(p, fname) < 0)
568                         r = -errno;
569                 else
570                         r = 0;
571         }
572
573         if (r < 0)
574                 unlink(p);
575
576         return r;
577 }