]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/fileio.c
shared: rework env file reader
[thirdparty/systemd.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 int write_env_file(const char *fname, char **l) {
526 char **i;
527 char _cleanup_free_ *p = NULL;
528 FILE _cleanup_fclose_ *f = NULL;
529 int r;
530
531 r = fopen_temporary(fname, &f, &p);
532 if (r < 0)
533 return r;
534
535 fchmod_umask(fileno(f), 0644);
536
537 errno = 0;
538 STRV_FOREACH(i, l) {
539 fputs(*i, f);
540 fputc('\n', f);
541 }
542
543 fflush(f);
544
545 if (ferror(f)) {
546 if (errno > 0)
547 r = -errno;
548 else
549 r = -EIO;
550 } else {
551 if (rename(p, fname) < 0)
552 r = -errno;
553 else
554 r = 0;
555 }
556
557 if (r < 0)
558 unlink(p);
559
560 return r;
561 }