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