]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/fileio.c
move _cleanup_ attribute in front of the type
[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_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) || strchr(COMMENTS, 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(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 key[n_key] = 0;
283
284 if (value)
285 value[n_value] = 0;
286
287 /* Chomp off trailing whitespace */
288 if (last_whitespace != (size_t) -1)
289 value[last_whitespace] = 0;
290
291 /* strip trailing whitespace from key */
292 while(strchr(WHITESPACE, key[--n_key]))
293 key[n_key]=0;
294
295 r = push(key, value, userdata);
296 if (r < 0)
297 goto fail;
298
299 n_key = 0;
300 value = NULL;
301 value_alloc = n_value = 0;
302 } else if (c == '\\') {
303 state = VALUE_ESCAPE;
304 last_whitespace = (size_t) -1;
305 } else {
306 if (!strchr(WHITESPACE, c))
307 last_whitespace = (size_t) -1;
308 else if (last_whitespace == (size_t) -1)
309 last_whitespace = n_value;
310
311 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
312 r = -ENOMEM;
313 goto fail;
314 }
315
316 value[n_value++] = c;
317 }
318
319 break;
320
321 case VALUE_ESCAPE:
322 state = VALUE;
323
324 if (!strchr(newline, c)) {
325 /* Escaped newlines we eat up entirely */
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 break;
334
335 case SINGLE_QUOTE_VALUE:
336 if (c == '\'')
337 state = PRE_VALUE;
338 else if (c == '\\')
339 state = SINGLE_QUOTE_VALUE_ESCAPE;
340 else {
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
349 break;
350
351 case SINGLE_QUOTE_VALUE_ESCAPE:
352 state = SINGLE_QUOTE_VALUE;
353
354 if (!strchr(newline, c)) {
355 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
356 r = -ENOMEM;
357 goto fail;
358 }
359
360 value[n_value++] = c;
361 }
362 break;
363
364 case DOUBLE_QUOTE_VALUE:
365 if (c == '\"')
366 state = PRE_VALUE;
367 else if (c == '\\')
368 state = DOUBLE_QUOTE_VALUE_ESCAPE;
369 else {
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
378 break;
379
380 case DOUBLE_QUOTE_VALUE_ESCAPE:
381 state = DOUBLE_QUOTE_VALUE;
382
383 if (!strchr(newline, c)) {
384 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
385 r = -ENOMEM;
386 goto fail;
387 }
388
389 value[n_value++] = c;
390 }
391 break;
392
393 case COMMENT:
394 if (c == '\\')
395 state = COMMENT_ESCAPE;
396 else if (strchr(newline, c))
397 state = PRE_KEY;
398 break;
399
400 case COMMENT_ESCAPE:
401 state = COMMENT;
402 break;
403 }
404 }
405
406 if (state == PRE_VALUE ||
407 state == VALUE ||
408 state == VALUE_ESCAPE ||
409 state == SINGLE_QUOTE_VALUE ||
410 state == SINGLE_QUOTE_VALUE_ESCAPE ||
411 state == DOUBLE_QUOTE_VALUE ||
412 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
413
414 key[n_key] = 0;
415
416 if (value)
417 value[n_value] = 0;
418
419 /* strip trailing whitespace from key */
420 while(strchr(WHITESPACE, key[--n_key]))
421 key[n_key]=0;
422
423 r = push(key, value, userdata);
424 if (r < 0)
425 goto fail;
426 }
427
428 return 0;
429
430 fail:
431 free(value);
432 return r;
433 }
434
435 static int parse_env_file_push(const char *key, char *value, void *userdata) {
436 const char *k;
437 va_list* ap = (va_list*) userdata;
438 va_list aq;
439
440 va_copy(aq, *ap);
441
442 while ((k = va_arg(aq, const char *))) {
443 char **v;
444
445 v = va_arg(aq, char **);
446
447 if (streq(key, k)) {
448 va_end(aq);
449 free(*v);
450 *v = value;
451 return 1;
452 }
453 }
454
455 va_end(aq);
456
457 free(value);
458 return 0;
459 }
460
461 int parse_env_file(
462 const char *fname,
463 const char *newline, ...) {
464
465 va_list ap;
466 int r;
467
468 if (!newline)
469 newline = NEWLINE;
470
471 va_start(ap, newline);
472 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
473 va_end(ap);
474
475 return r;
476 }
477
478 static int load_env_file_push(const char *key, char *value, void *userdata) {
479 char ***m = userdata;
480 char *p;
481 int r;
482
483 p = strjoin(key, "=", strempty(value), NULL);
484 if (!p)
485 return -ENOMEM;
486
487 r = strv_push(m, p);
488 if (r < 0) {
489 free(p);
490 return r;
491 }
492
493 free(value);
494 return 0;
495 }
496
497 int load_env_file(const char *fname, const char *newline, char ***rl) {
498 char **m = NULL;
499 int r;
500
501 if (!newline)
502 newline = NEWLINE;
503
504 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
505 if (r < 0) {
506 strv_free(m);
507 return r;
508 }
509
510 *rl = m;
511 return 0;
512 }
513
514 static void write_env_var(FILE *f, const char *v) {
515 const char *p;
516
517 p = strchr(v, '=');
518 if (!p) {
519 /* Fallback */
520 fputs(v, f);
521 fputc('\n', f);
522 return;
523 }
524
525 p++;
526 fwrite(v, 1, p-v, f);
527
528 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
529 fputc('\"', f);
530
531 for (; *p; p++) {
532 if (strchr("\'\"\\`$", *p))
533 fputc('\\', f);
534
535 fputc(*p, f);
536 }
537
538 fputc('\"', f);
539 } else
540 fputs(p, f);
541
542 fputc('\n', f);
543 }
544
545 int write_env_file(const char *fname, char **l) {
546 char **i;
547 _cleanup_free_ char *p = NULL;
548 _cleanup_fclose_ FILE *f = NULL;
549 int r;
550
551 r = fopen_temporary(fname, &f, &p);
552 if (r < 0)
553 return r;
554
555 fchmod_umask(fileno(f), 0644);
556
557 errno = 0;
558 STRV_FOREACH(i, l)
559 write_env_var(f, *i);
560
561 fflush(f);
562
563 if (ferror(f))
564 r = errno ? -errno : -EIO;
565 else {
566 if (rename(p, fname) < 0)
567 r = -errno;
568 else
569 r = 0;
570 }
571
572 if (r < 0)
573 unlink(p);
574
575 return r;
576 }