]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/fileio.c
core: general cgroup rework
[thirdparty/systemd.git] / src / shared / fileio.c
CommitLineData
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 27int 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
41int 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 54int 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
90int 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
118int 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 177static 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 449fail:
f73141d7 450 free(value);
a5c32cff
HH
451 return r;
452}
453
f73141d7
LP
454static 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
480int 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
497static 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
516int 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
533static 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 564int 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}