]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/fileio.c
48dd44239fa6dc9930072b808eb2e140d3a7ffb9
[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 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 }