]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/fileio.c
Remove six unused variables and add annotation
[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"
335c46b5 26#include "utf8.h"
a5c32cff 27
b4bc041b 28int write_string_to_file(FILE *f, const char *line) {
a5c32cff 29 errno = 0;
fec6fc6b 30 fputs(line, f);
a5c32cff
HH
31 if (!endswith(line, "\n"))
32 fputc('\n', f);
33
34 fflush(f);
35
36 if (ferror(f))
37 return errno ? -errno : -EIO;
38
39 return 0;
40}
41
b4bc041b
ZJS
42int write_string_file(const char *fn, const char *line) {
43 _cleanup_fclose_ FILE *f = NULL;
44
45 assert(fn);
46 assert(line);
47
48 f = fopen(fn, "we");
49 if (!f)
50 return -errno;
51
52 return write_string_to_file(f, line);
53}
54
574d5f2d 55int write_string_file_atomic(const char *fn, const char *line) {
a5c32cff
HH
56 _cleanup_fclose_ FILE *f = NULL;
57 _cleanup_free_ char *p = NULL;
58 int r;
59
60 assert(fn);
61 assert(line);
62
63 r = fopen_temporary(fn, &f, &p);
64 if (r < 0)
65 return r;
66
67 fchmod_umask(fileno(f), 0644);
68
69 errno = 0;
fec6fc6b 70 fputs(line, f);
a5c32cff
HH
71 if (!endswith(line, "\n"))
72 fputc('\n', f);
73
74 fflush(f);
75
76 if (ferror(f))
77 r = errno ? -errno : -EIO;
78 else {
79 if (rename(p, fn) < 0)
80 r = -errno;
81 else
82 r = 0;
83 }
84
a5c32cff
HH
85 if (r < 0)
86 unlink(p);
87
88 return r;
89}
90
91int read_one_line_file(const char *fn, char **line) {
92 _cleanup_fclose_ FILE *f = NULL;
93 char t[LINE_MAX], *c;
94
95 assert(fn);
96 assert(line);
97
98 f = fopen(fn, "re");
99 if (!f)
100 return -errno;
101
102 if (!fgets(t, sizeof(t), f)) {
103
104 if (ferror(f))
105 return errno ? -errno : -EIO;
106
107 t[0] = 0;
108 }
109
110 c = strdup(t);
111 if (!c)
112 return -ENOMEM;
113 truncate_nl(c);
114
115 *line = c;
116 return 0;
117}
118
119int read_full_file(const char *fn, char **contents, size_t *size) {
120 _cleanup_fclose_ FILE *f = NULL;
121 size_t n, l;
122 _cleanup_free_ char *buf = NULL;
123 struct stat st;
124
125 assert(fn);
126 assert(contents);
127
128 f = fopen(fn, "re");
129 if (!f)
130 return -errno;
131
132 if (fstat(fileno(f), &st) < 0)
133 return -errno;
134
135 /* Safety check */
136 if (st.st_size > 4*1024*1024)
137 return -E2BIG;
138
139 n = st.st_size > 0 ? st.st_size : LINE_MAX;
140 l = 0;
141
142 for (;;) {
143 char *t;
144 size_t k;
145
146 t = realloc(buf, n+1);
147 if (!t)
148 return -ENOMEM;
149
150 buf = t;
151 k = fread(buf + l, 1, n - l, f);
152
153 if (k <= 0) {
154 if (ferror(f))
155 return -errno;
156
157 break;
158 }
159
160 l += k;
161 n *= 2;
162
163 /* Safety check */
164 if (n > 4*1024*1024)
165 return -E2BIG;
166 }
167
168 buf[l] = 0;
169 *contents = buf;
170 buf = NULL;
171
172 if (size)
173 *size = l;
174
175 return 0;
176}
177
f73141d7 178static int parse_env_file_internal(
a5c32cff 179 const char *fname,
f73141d7 180 const char *newline,
335c46b5
ZJS
181 int (*push) (const char *filename, unsigned line,
182 const char *key, char *value, void *userdata),
f73141d7
LP
183 void *userdata) {
184
185 _cleanup_free_ char *contents = NULL, *key = NULL;
2b77f67e 186 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
187 char *p, *value = NULL;
188 int r;
335c46b5 189 unsigned line = 1;
a5c32cff 190
f73141d7
LP
191 enum {
192 PRE_KEY,
193 KEY,
f73141d7
LP
194 PRE_VALUE,
195 VALUE,
196 VALUE_ESCAPE,
197 SINGLE_QUOTE_VALUE,
198 SINGLE_QUOTE_VALUE_ESCAPE,
199 DOUBLE_QUOTE_VALUE,
200 DOUBLE_QUOTE_VALUE_ESCAPE,
201 COMMENT,
202 COMMENT_ESCAPE
203 } state = PRE_KEY;
a5c32cff
HH
204
205 assert(fname);
f73141d7 206 assert(newline);
a5c32cff 207
1c17cbed
ZJS
208 r = read_full_file(fname, &contents, NULL);
209 if (r < 0)
a5c32cff
HH
210 return r;
211
f73141d7
LP
212 for (p = contents; *p; p++) {
213 char c = *p;
214
215 switch (state) {
216
217 case PRE_KEY:
ebc05a09 218 if (strchr(COMMENTS, c))
f73141d7
LP
219 state = COMMENT;
220 else if (!strchr(WHITESPACE, c)) {
221 state = KEY;
2b77f67e
LP
222 last_key_whitespace = (size_t) -1;
223
f73141d7
LP
224 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
225 r = -ENOMEM;
226 goto fail;
227 }
228
229 key[n_key++] = c;
230 }
231 break;
232
233 case KEY:
234 if (strchr(newline, c)) {
235 state = PRE_KEY;
335c46b5 236 line ++;
f73141d7 237 n_key = 0;
2b77f67e 238 } else if (c == '=') {
f73141d7 239 state = PRE_VALUE;
2b77f67e
LP
240 last_value_whitespace = (size_t) -1;
241 } else {
242 if (!strchr(WHITESPACE, c))
243 last_key_whitespace = (size_t) -1;
244 else if (last_key_whitespace == (size_t) -1)
245 last_key_whitespace = n_key;
246
f73141d7
LP
247 if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) {
248 r = -ENOMEM;
249 goto fail;
250 }
251
252 key[n_key++] = c;
253 }
a5c32cff 254
f73141d7
LP
255 break;
256
f73141d7 257 case PRE_VALUE:
98f59e59 258 if (strchr(newline, c)) {
f73141d7 259 state = PRE_KEY;
335c46b5 260 line ++;
f73141d7 261 key[n_key] = 0;
a5c32cff 262
f73141d7
LP
263 if (value)
264 value[n_value] = 0;
a5c32cff 265
ebc05a09 266 /* strip trailing whitespace from key */
2b77f67e
LP
267 if (last_key_whitespace != (size_t) -1)
268 key[last_key_whitespace] = 0;
ebc05a09 269
335c46b5 270 r = push(fname, line, key, value, userdata);
f73141d7
LP
271 if (r < 0)
272 goto fail;
273
274 n_key = 0;
275 value = NULL;
276 value_alloc = n_value = 0;
2b77f67e 277
f73141d7
LP
278 } else if (c == '\'')
279 state = SINGLE_QUOTE_VALUE;
280 else if (c == '\"')
281 state = DOUBLE_QUOTE_VALUE;
282 else if (c == '\\')
283 state = VALUE_ESCAPE;
284 else if (!strchr(WHITESPACE, c)) {
285 state = VALUE;
286
287 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
288 r = -ENOMEM;
289 goto fail;
290 }
291
292 value[n_value++] = c;
293 }
294
295 break;
296
297 case VALUE:
298 if (strchr(newline, c)) {
299 state = PRE_KEY;
335c46b5 300 line ++;
98f59e59 301
f73141d7
LP
302 key[n_key] = 0;
303
304 if (value)
305 value[n_value] = 0;
306
2b77f67e
LP
307 /* Chomp off trailing whitespace from value */
308 if (last_value_whitespace != (size_t) -1)
309 value[last_value_whitespace] = 0;
f73141d7 310
ebc05a09 311 /* strip trailing whitespace from key */
2b77f67e
LP
312 if (last_key_whitespace != (size_t) -1)
313 key[last_key_whitespace] = 0;
ebc05a09 314
335c46b5 315 r = push(fname, line, key, value, userdata);
f73141d7
LP
316 if (r < 0)
317 goto fail;
318
319 n_key = 0;
320 value = NULL;
321 value_alloc = n_value = 0;
2b77f67e 322
f73141d7
LP
323 } else if (c == '\\') {
324 state = VALUE_ESCAPE;
2b77f67e 325 last_value_whitespace = (size_t) -1;
f73141d7
LP
326 } else {
327 if (!strchr(WHITESPACE, c))
2b77f67e
LP
328 last_value_whitespace = (size_t) -1;
329 else if (last_value_whitespace == (size_t) -1)
330 last_value_whitespace = n_value;
f73141d7
LP
331
332 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
333 r = -ENOMEM;
334 goto fail;
335 }
336
337 value[n_value++] = c;
338 }
339
340 break;
341
342 case VALUE_ESCAPE:
343 state = VALUE;
344
345 if (!strchr(newline, c)) {
346 /* Escaped newlines we eat up entirely */
347 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
348 r = -ENOMEM;
349 goto fail;
350 }
351
352 value[n_value++] = c;
353 }
354 break;
355
356 case SINGLE_QUOTE_VALUE:
357 if (c == '\'')
358 state = PRE_VALUE;
359 else if (c == '\\')
360 state = SINGLE_QUOTE_VALUE_ESCAPE;
361 else {
362 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
363 r = -ENOMEM;
364 goto fail;
365 }
a5c32cff 366
f73141d7
LP
367 value[n_value++] = c;
368 }
a5c32cff 369
f73141d7 370 break;
a5c32cff 371
f73141d7
LP
372 case SINGLE_QUOTE_VALUE_ESCAPE:
373 state = SINGLE_QUOTE_VALUE;
a5c32cff 374
f73141d7
LP
375 if (!strchr(newline, c)) {
376 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
a5c32cff 377 r = -ENOMEM;
a5c32cff
HH
378 goto fail;
379 }
380
f73141d7
LP
381 value[n_value++] = c;
382 }
383 break;
384
385 case DOUBLE_QUOTE_VALUE:
386 if (c == '\"')
387 state = PRE_VALUE;
388 else if (c == '\\')
389 state = DOUBLE_QUOTE_VALUE_ESCAPE;
390 else {
391 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
392 r = -ENOMEM;
393 goto fail;
a5c32cff
HH
394 }
395
f73141d7
LP
396 value[n_value++] = c;
397 }
398
399 break;
400
401 case DOUBLE_QUOTE_VALUE_ESCAPE:
402 state = DOUBLE_QUOTE_VALUE;
a5c32cff 403
f73141d7
LP
404 if (!strchr(newline, c)) {
405 if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) {
406 r = -ENOMEM;
407 goto fail;
408 }
a5c32cff 409
f73141d7 410 value[n_value++] = c;
a5c32cff 411 }
f73141d7
LP
412 break;
413
414 case COMMENT:
415 if (c == '\\')
416 state = COMMENT_ESCAPE;
335c46b5 417 else if (strchr(newline, c)) {
f73141d7 418 state = PRE_KEY;
335c46b5
ZJS
419 line ++;
420 }
f73141d7
LP
421 break;
422
423 case COMMENT_ESCAPE:
424 state = COMMENT;
425 break;
a5c32cff 426 }
f73141d7
LP
427 }
428
429 if (state == PRE_VALUE ||
430 state == VALUE ||
431 state == VALUE_ESCAPE ||
432 state == SINGLE_QUOTE_VALUE ||
433 state == SINGLE_QUOTE_VALUE_ESCAPE ||
434 state == DOUBLE_QUOTE_VALUE ||
435 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
a5c32cff 436
f73141d7
LP
437 key[n_key] = 0;
438
439 if (value)
440 value[n_value] = 0;
441
2b77f67e
LP
442 if (state == VALUE)
443 if (last_value_whitespace != (size_t) -1)
444 value[last_value_whitespace] = 0;
445
ebc05a09 446 /* strip trailing whitespace from key */
2b77f67e
LP
447 if (last_key_whitespace != (size_t) -1)
448 key[last_key_whitespace] = 0;
ebc05a09 449
335c46b5 450 r = push(fname, line, key, value, userdata);
f73141d7
LP
451 if (r < 0)
452 goto fail;
a5c32cff
HH
453 }
454
f73141d7
LP
455 return 0;
456
a5c32cff 457fail:
f73141d7 458 free(value);
a5c32cff
HH
459 return r;
460}
461
335c46b5
ZJS
462static int parse_env_file_push(const char *filename, unsigned line,
463 const char *key, char *value, void *userdata) {
464 assert(utf8_is_valid(key));
465
466 if (value && !utf8_is_valid(value))
467 /* FIXME: filter UTF-8 */
468 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
469 filename, line, key, value);
470 else {
471 const char *k;
472 va_list* ap = (va_list*) userdata;
473 va_list aq;
a5c32cff 474
335c46b5 475 va_copy(aq, *ap);
a5c32cff 476
335c46b5
ZJS
477 while ((k = va_arg(aq, const char *))) {
478 char **v;
a5c32cff 479
335c46b5 480 v = va_arg(aq, char **);
a5c32cff 481
335c46b5
ZJS
482 if (streq(key, k)) {
483 va_end(aq);
484 free(*v);
485 *v = value;
486 return 1;
487 }
f73141d7 488 }
a5c32cff 489
335c46b5
ZJS
490 va_end(aq);
491 }
a5c32cff 492
f73141d7
LP
493 free(value);
494 return 0;
495}
a5c32cff 496
f73141d7
LP
497int parse_env_file(
498 const char *fname,
499 const char *newline, ...) {
a5c32cff 500
f73141d7
LP
501 va_list ap;
502 int r;
a5c32cff 503
f73141d7
LP
504 if (!newline)
505 newline = NEWLINE;
a5c32cff 506
f73141d7
LP
507 va_start(ap, newline);
508 r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap);
509 va_end(ap);
a5c32cff 510
f73141d7
LP
511 return r;
512}
a5c32cff 513
335c46b5
ZJS
514static int load_env_file_push(const char *filename, unsigned line,
515 const char *key, char *value, void *userdata) {
516 assert(utf8_is_valid(key));
a5c32cff 517
335c46b5
ZJS
518 if (value && !utf8_is_valid(value))
519 /* FIXME: filter UTF-8 */
520 log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.",
521 filename, line, key, value);
522 else {
523 char ***m = userdata;
524 char *p;
525 int r;
a5c32cff 526
335c46b5
ZJS
527 p = strjoin(key, "=", strempty(value), NULL);
528 if (!p)
529 return -ENOMEM;
530
531 r = strv_push(m, p);
532 if (r < 0) {
533 free(p);
534 return r;
535 }
f73141d7 536 }
a5c32cff 537
f73141d7
LP
538 free(value);
539 return 0;
540}
541
542int load_env_file(const char *fname, const char *newline, char ***rl) {
543 char **m = NULL;
544 int r;
a5c32cff 545
f73141d7
LP
546 if (!newline)
547 newline = NEWLINE;
548
549 r = parse_env_file_internal(fname, newline, load_env_file_push, &m);
550 if (r < 0) {
551 strv_free(m);
552 return r;
a5c32cff
HH
553 }
554
555 *rl = m;
a5c32cff
HH
556 return 0;
557}
558
768100ef
LP
559static void write_env_var(FILE *f, const char *v) {
560 const char *p;
561
562 p = strchr(v, '=');
563 if (!p) {
564 /* Fallback */
565 fputs(v, f);
566 fputc('\n', f);
567 return;
568 }
569
570 p++;
571 fwrite(v, 1, p-v, f);
572
ced2d10a 573 if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) {
768100ef
LP
574 fputc('\"', f);
575
576 for (; *p; p++) {
ced2d10a 577 if (strchr("\'\"\\`$", *p))
768100ef
LP
578 fputc('\\', f);
579
580 fputc(*p, f);
581 }
582
583 fputc('\"', f);
584 } else
585 fputs(p, f);
586
587 fputc('\n', f);
588}
589
a5c32cff 590int write_env_file(const char *fname, char **l) {
1c17cbed 591 char **i;
7fd1b19b
HH
592 _cleanup_free_ char *p = NULL;
593 _cleanup_fclose_ FILE *f = NULL;
a5c32cff
HH
594 int r;
595
596 r = fopen_temporary(fname, &f, &p);
597 if (r < 0)
598 return r;
599
600 fchmod_umask(fileno(f), 0644);
601
602 errno = 0;
768100ef
LP
603 STRV_FOREACH(i, l)
604 write_env_var(f, *i);
a5c32cff
HH
605
606 fflush(f);
607
fec6fc6b
LP
608 if (ferror(f))
609 r = errno ? -errno : -EIO;
610 else {
a5c32cff
HH
611 if (rename(p, fname) < 0)
612 r = -errno;
613 else
614 r = 0;
615 }
616
617 if (r < 0)
618 unlink(p);
619
a5c32cff
HH
620 return r;
621}
68fee104
ZJS
622
623int executable_is_script(const char *path, char **interpreter) {
624 int r;
625 char _cleanup_free_ *line = NULL;
626 int len;
627 char *ans;
628
629 assert(path);
630
631 r = read_one_line_file(path, &line);
632 if (r < 0)
633 return r;
634
635 if (!startswith(line, "#!"))
636 return 0;
637
638 ans = strstrip(line + 2);
639 len = strcspn(ans, " \t");
640
641 if (len == 0)
642 return 0;
643
644 ans = strndup(ans, len);
645 if (!ans)
646 return -ENOMEM;
647
648 *interpreter = ans;
649 return 1;
650}
69ab8088
ZJS
651
652/**
653 * Retrieve one field from a file like /proc/self/status.
654 * pattern should start with '\n' and end with ':'. Whitespace
655 * after ':' will be skipped. field must be freed afterwards.
656 */
657int get_status_field(const char *filename, const char *pattern, char **field) {
658 _cleanup_free_ char *status = NULL;
659 char *t;
660 size_t len;
661 int r;
662
663 assert(filename);
664 assert(field);
665
666 r = read_full_file(filename, &status, NULL);
667 if (r < 0)
668 return r;
669
670 t = strstr(status, pattern);
671 if (!t)
672 return -ENOENT;
673
674 t += strlen(pattern);
675 t += strspn(t, WHITESPACE);
676
677 len = strcspn(t, WHITESPACE);
678
679 *field = strndup(t, len);
680 if (!*field)
681 return -ENOMEM;
682
683 return 0;
684}