]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/fileio.c
util-lib: split out allocation calls into alloc-util.[ch]
[thirdparty/systemd.git] / src / basic / 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>
cda134ab 23
b5efdb8a 24#include "alloc-util.h"
4f5dd394
LP
25#include "ctype.h"
26#include "escape.h"
3ffd4af2
LP
27#include "fd-util.h"
28#include "fileio.h"
f4f15635 29#include "fs-util.h"
0d39fa9c
LP
30#include "hexdecoct.h"
31#include "path-util.h"
32#include "random-util.h"
07630cea 33#include "string-util.h"
a5c32cff 34#include "strv.h"
affb60b1 35#include "umask-util.h"
335c46b5 36#include "utf8.h"
4f5dd394 37#include "util.h"
a5c32cff 38
40beecdb 39int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
dacd6cee 40
717603e3
LP
41 assert(f);
42 assert(line);
43
fec6fc6b 44 fputs(line, f);
40beecdb 45 if (enforce_newline && !endswith(line, "\n"))
a5c32cff
HH
46 fputc('\n', f);
47
dacd6cee 48 return fflush_and_check(f);
a5c32cff
HH
49}
50
4c1fc3e4 51static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
a5c32cff
HH
52 _cleanup_fclose_ FILE *f = NULL;
53 _cleanup_free_ char *p = NULL;
54 int r;
55
56 assert(fn);
57 assert(line);
58
59 r = fopen_temporary(fn, &f, &p);
60 if (r < 0)
61 return r;
62
0d39fa9c 63 (void) fchmod_umask(fileno(f), 0644);
a5c32cff 64
4c1fc3e4 65 r = write_string_stream(f, line, enforce_newline);
f2997962 66 if (r >= 0) {
a5c32cff
HH
67 if (rename(p, fn) < 0)
68 r = -errno;
a5c32cff
HH
69 }
70
a5c32cff 71 if (r < 0)
0d39fa9c 72 (void) unlink(p);
a5c32cff
HH
73
74 return r;
75}
76
4c1fc3e4
DM
77int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
78 _cleanup_fclose_ FILE *f = NULL;
79
80 assert(fn);
81 assert(line);
82
83 if (flags & WRITE_STRING_FILE_ATOMIC) {
84 assert(flags & WRITE_STRING_FILE_CREATE);
85
86 return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
87 }
88
89 if (flags & WRITE_STRING_FILE_CREATE) {
90 f = fopen(fn, "we");
91 if (!f)
92 return -errno;
93 } else {
94 int fd;
95
96 /* We manually build our own version of fopen(..., "we") that
97 * works without O_CREAT */
98 fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
99 if (fd < 0)
100 return -errno;
101
102 f = fdopen(fd, "we");
103 if (!f) {
104 safe_close(fd);
105 return -errno;
106 }
107 }
108
109 return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
110}
111
a5c32cff
HH
112int read_one_line_file(const char *fn, char **line) {
113 _cleanup_fclose_ FILE *f = NULL;
114 char t[LINE_MAX], *c;
115
116 assert(fn);
117 assert(line);
118
119 f = fopen(fn, "re");
120 if (!f)
121 return -errno;
122
123 if (!fgets(t, sizeof(t), f)) {
124
125 if (ferror(f))
126 return errno ? -errno : -EIO;
127
128 t[0] = 0;
129 }
130
131 c = strdup(t);
132 if (!c)
133 return -ENOMEM;
134 truncate_nl(c);
135
136 *line = c;
137 return 0;
138}
139
15dee3f0
LP
140int verify_one_line_file(const char *fn, const char *line) {
141 _cleanup_free_ char *value = NULL;
142 int r;
143
144 r = read_one_line_file(fn, &value);
145 if (r < 0)
146 return r;
147
148 return streq(value, line);
149}
150
717603e3 151int read_full_stream(FILE *f, char **contents, size_t *size) {
a5c32cff
HH
152 size_t n, l;
153 _cleanup_free_ char *buf = NULL;
154 struct stat st;
155
717603e3 156 assert(f);
a5c32cff
HH
157 assert(contents);
158
a5c32cff
HH
159 if (fstat(fileno(f), &st) < 0)
160 return -errno;
161
717603e3 162 n = LINE_MAX;
a5c32cff 163
717603e3
LP
164 if (S_ISREG(st.st_mode)) {
165
166 /* Safety check */
167 if (st.st_size > 4*1024*1024)
168 return -E2BIG;
169
170 /* Start with the right file size, but be prepared for
171 * files from /proc which generally report a file size
172 * of 0 */
173 if (st.st_size > 0)
174 n = st.st_size;
175 }
a5c32cff 176
717603e3 177 l = 0;
a5c32cff
HH
178 for (;;) {
179 char *t;
180 size_t k;
181
182 t = realloc(buf, n+1);
183 if (!t)
184 return -ENOMEM;
185
186 buf = t;
187 k = fread(buf + l, 1, n - l, f);
188
189 if (k <= 0) {
190 if (ferror(f))
191 return -errno;
192
193 break;
194 }
195
196 l += k;
197 n *= 2;
198
199 /* Safety check */
200 if (n > 4*1024*1024)
201 return -E2BIG;
202 }
203
204 buf[l] = 0;
205 *contents = buf;
d78a28e3 206 buf = NULL; /* do not free */
a5c32cff
HH
207
208 if (size)
209 *size = l;
210
211 return 0;
212}
213
717603e3
LP
214int read_full_file(const char *fn, char **contents, size_t *size) {
215 _cleanup_fclose_ FILE *f = NULL;
216
217 assert(fn);
218 assert(contents);
219
220 f = fopen(fn, "re");
221 if (!f)
222 return -errno;
223
224 return read_full_stream(f, contents, size);
225}
226
f73141d7 227static int parse_env_file_internal(
717603e3 228 FILE *f,
a5c32cff 229 const char *fname,
f73141d7 230 const char *newline,
335c46b5 231 int (*push) (const char *filename, unsigned line,
a5f6c30d
MS
232 const char *key, char *value, void *userdata, int *n_pushed),
233 void *userdata,
234 int *n_pushed) {
f73141d7
LP
235
236 _cleanup_free_ char *contents = NULL, *key = NULL;
2b77f67e 237 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
238 char *p, *value = NULL;
239 int r;
335c46b5 240 unsigned line = 1;
a5c32cff 241
f73141d7
LP
242 enum {
243 PRE_KEY,
244 KEY,
f73141d7
LP
245 PRE_VALUE,
246 VALUE,
247 VALUE_ESCAPE,
248 SINGLE_QUOTE_VALUE,
249 SINGLE_QUOTE_VALUE_ESCAPE,
250 DOUBLE_QUOTE_VALUE,
251 DOUBLE_QUOTE_VALUE_ESCAPE,
252 COMMENT,
253 COMMENT_ESCAPE
254 } state = PRE_KEY;
a5c32cff 255
f73141d7 256 assert(newline);
a5c32cff 257
717603e3
LP
258 if (f)
259 r = read_full_stream(f, &contents, NULL);
260 else
261 r = read_full_file(fname, &contents, NULL);
1c17cbed 262 if (r < 0)
a5c32cff
HH
263 return r;
264
f73141d7
LP
265 for (p = contents; *p; p++) {
266 char c = *p;
267
268 switch (state) {
269
270 case PRE_KEY:
ebc05a09 271 if (strchr(COMMENTS, c))
f73141d7
LP
272 state = COMMENT;
273 else if (!strchr(WHITESPACE, c)) {
274 state = KEY;
2b77f67e
LP
275 last_key_whitespace = (size_t) -1;
276
ca2d3784 277 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
f73141d7
LP
278 r = -ENOMEM;
279 goto fail;
280 }
281
282 key[n_key++] = c;
283 }
284 break;
285
286 case KEY:
287 if (strchr(newline, c)) {
288 state = PRE_KEY;
335c46b5 289 line ++;
f73141d7 290 n_key = 0;
2b77f67e 291 } else if (c == '=') {
f73141d7 292 state = PRE_VALUE;
2b77f67e
LP
293 last_value_whitespace = (size_t) -1;
294 } else {
295 if (!strchr(WHITESPACE, c))
296 last_key_whitespace = (size_t) -1;
297 else if (last_key_whitespace == (size_t) -1)
298 last_key_whitespace = n_key;
299
ca2d3784 300 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
f73141d7
LP
301 r = -ENOMEM;
302 goto fail;
303 }
304
305 key[n_key++] = c;
306 }
a5c32cff 307
f73141d7
LP
308 break;
309
f73141d7 310 case PRE_VALUE:
98f59e59 311 if (strchr(newline, c)) {
f73141d7 312 state = PRE_KEY;
335c46b5 313 line ++;
f73141d7 314 key[n_key] = 0;
a5c32cff 315
f73141d7
LP
316 if (value)
317 value[n_value] = 0;
a5c32cff 318
ebc05a09 319 /* strip trailing whitespace from key */
2b77f67e
LP
320 if (last_key_whitespace != (size_t) -1)
321 key[last_key_whitespace] = 0;
ebc05a09 322
a5f6c30d 323 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
324 if (r < 0)
325 goto fail;
326
327 n_key = 0;
328 value = NULL;
329 value_alloc = n_value = 0;
2b77f67e 330
f73141d7
LP
331 } else if (c == '\'')
332 state = SINGLE_QUOTE_VALUE;
333 else if (c == '\"')
334 state = DOUBLE_QUOTE_VALUE;
335 else if (c == '\\')
336 state = VALUE_ESCAPE;
337 else if (!strchr(WHITESPACE, c)) {
338 state = VALUE;
339
ca2d3784 340 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
341 r = -ENOMEM;
342 goto fail;
343 }
344
345 value[n_value++] = c;
346 }
347
348 break;
349
350 case VALUE:
351 if (strchr(newline, c)) {
352 state = PRE_KEY;
335c46b5 353 line ++;
98f59e59 354
f73141d7
LP
355 key[n_key] = 0;
356
357 if (value)
358 value[n_value] = 0;
359
2b77f67e
LP
360 /* Chomp off trailing whitespace from value */
361 if (last_value_whitespace != (size_t) -1)
362 value[last_value_whitespace] = 0;
f73141d7 363
ebc05a09 364 /* strip trailing whitespace from key */
2b77f67e
LP
365 if (last_key_whitespace != (size_t) -1)
366 key[last_key_whitespace] = 0;
ebc05a09 367
a5f6c30d 368 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
369 if (r < 0)
370 goto fail;
371
372 n_key = 0;
373 value = NULL;
374 value_alloc = n_value = 0;
2b77f67e 375
f73141d7
LP
376 } else if (c == '\\') {
377 state = VALUE_ESCAPE;
2b77f67e 378 last_value_whitespace = (size_t) -1;
f73141d7
LP
379 } else {
380 if (!strchr(WHITESPACE, c))
2b77f67e
LP
381 last_value_whitespace = (size_t) -1;
382 else if (last_value_whitespace == (size_t) -1)
383 last_value_whitespace = n_value;
f73141d7 384
ca2d3784 385 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
386 r = -ENOMEM;
387 goto fail;
388 }
389
390 value[n_value++] = c;
391 }
392
393 break;
394
395 case VALUE_ESCAPE:
396 state = VALUE;
397
398 if (!strchr(newline, c)) {
399 /* Escaped newlines we eat up entirely */
ca2d3784 400 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
401 r = -ENOMEM;
402 goto fail;
403 }
404
405 value[n_value++] = c;
406 }
407 break;
408
409 case SINGLE_QUOTE_VALUE:
410 if (c == '\'')
411 state = PRE_VALUE;
412 else if (c == '\\')
413 state = SINGLE_QUOTE_VALUE_ESCAPE;
414 else {
ca2d3784 415 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
416 r = -ENOMEM;
417 goto fail;
418 }
a5c32cff 419
f73141d7
LP
420 value[n_value++] = c;
421 }
a5c32cff 422
f73141d7 423 break;
a5c32cff 424
f73141d7
LP
425 case SINGLE_QUOTE_VALUE_ESCAPE:
426 state = SINGLE_QUOTE_VALUE;
a5c32cff 427
f73141d7 428 if (!strchr(newline, c)) {
ca2d3784 429 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
a5c32cff 430 r = -ENOMEM;
a5c32cff
HH
431 goto fail;
432 }
433
f73141d7
LP
434 value[n_value++] = c;
435 }
436 break;
437
438 case DOUBLE_QUOTE_VALUE:
439 if (c == '\"')
440 state = PRE_VALUE;
441 else if (c == '\\')
442 state = DOUBLE_QUOTE_VALUE_ESCAPE;
443 else {
ca2d3784 444 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
445 r = -ENOMEM;
446 goto fail;
a5c32cff
HH
447 }
448
f73141d7
LP
449 value[n_value++] = c;
450 }
451
452 break;
453
454 case DOUBLE_QUOTE_VALUE_ESCAPE:
455 state = DOUBLE_QUOTE_VALUE;
a5c32cff 456
f73141d7 457 if (!strchr(newline, c)) {
ca2d3784 458 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
459 r = -ENOMEM;
460 goto fail;
461 }
a5c32cff 462
f73141d7 463 value[n_value++] = c;
a5c32cff 464 }
f73141d7
LP
465 break;
466
467 case COMMENT:
468 if (c == '\\')
469 state = COMMENT_ESCAPE;
335c46b5 470 else if (strchr(newline, c)) {
f73141d7 471 state = PRE_KEY;
335c46b5
ZJS
472 line ++;
473 }
f73141d7
LP
474 break;
475
476 case COMMENT_ESCAPE:
477 state = COMMENT;
478 break;
a5c32cff 479 }
f73141d7
LP
480 }
481
482 if (state == PRE_VALUE ||
483 state == VALUE ||
484 state == VALUE_ESCAPE ||
485 state == SINGLE_QUOTE_VALUE ||
486 state == SINGLE_QUOTE_VALUE_ESCAPE ||
487 state == DOUBLE_QUOTE_VALUE ||
488 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
a5c32cff 489
f73141d7
LP
490 key[n_key] = 0;
491
492 if (value)
493 value[n_value] = 0;
494
2b77f67e
LP
495 if (state == VALUE)
496 if (last_value_whitespace != (size_t) -1)
497 value[last_value_whitespace] = 0;
498
ebc05a09 499 /* strip trailing whitespace from key */
2b77f67e
LP
500 if (last_key_whitespace != (size_t) -1)
501 key[last_key_whitespace] = 0;
ebc05a09 502
a5f6c30d 503 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
504 if (r < 0)
505 goto fail;
a5c32cff
HH
506 }
507
f73141d7
LP
508 return 0;
509
a5c32cff 510fail:
f73141d7 511 free(value);
a5c32cff
HH
512 return r;
513}
514
717603e3
LP
515static int parse_env_file_push(
516 const char *filename, unsigned line,
517 const char *key, char *value,
a5f6c30d
MS
518 void *userdata,
519 int *n_pushed) {
335c46b5 520
f27f0e21
GB
521 const char *k;
522 va_list aq, *ap = userdata;
523
524 if (!utf8_is_valid(key)) {
717603e3 525 _cleanup_free_ char *p;
550a40ec 526
717603e3
LP
527 p = utf8_escape_invalid(key);
528 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
f27f0e21
GB
529 return -EINVAL;
530 }
531
532 if (value && !utf8_is_valid(value)) {
717603e3 533 _cleanup_free_ char *p;
550a40ec 534
717603e3
LP
535 p = utf8_escape_invalid(value);
536 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
f27f0e21
GB
537 return -EINVAL;
538 }
a5c32cff 539
f27f0e21 540 va_copy(aq, *ap);
a5c32cff 541
f27f0e21
GB
542 while ((k = va_arg(aq, const char *))) {
543 char **v;
a5c32cff 544
f27f0e21 545 v = va_arg(aq, char **);
a5c32cff 546
f27f0e21
GB
547 if (streq(key, k)) {
548 va_end(aq);
549 free(*v);
550 *v = value;
a5f6c30d
MS
551
552 if (n_pushed)
553 (*n_pushed)++;
554
f27f0e21 555 return 1;
f73141d7 556 }
335c46b5 557 }
a5c32cff 558
f27f0e21 559 va_end(aq);
f73141d7 560 free(value);
717603e3 561
f73141d7
LP
562 return 0;
563}
a5c32cff 564
f73141d7
LP
565int parse_env_file(
566 const char *fname,
567 const char *newline, ...) {
a5c32cff 568
f73141d7 569 va_list ap;
a5f6c30d 570 int r, n_pushed = 0;
a5c32cff 571
f73141d7
LP
572 if (!newline)
573 newline = NEWLINE;
a5c32cff 574
f73141d7 575 va_start(ap, newline);
a5f6c30d 576 r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
f73141d7 577 va_end(ap);
a5c32cff 578
a5f6c30d 579 return r < 0 ? r : n_pushed;
f73141d7 580}
a5c32cff 581
717603e3
LP
582static int load_env_file_push(
583 const char *filename, unsigned line,
584 const char *key, char *value,
a5f6c30d
MS
585 void *userdata,
586 int *n_pushed) {
f27f0e21
GB
587 char ***m = userdata;
588 char *p;
589 int r;
a5c32cff 590
f27f0e21 591 if (!utf8_is_valid(key)) {
b5d74213
ZJS
592 _cleanup_free_ char *t = utf8_escape_invalid(key);
593
717603e3 594 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
f27f0e21
GB
595 return -EINVAL;
596 }
597
598 if (value && !utf8_is_valid(value)) {
b5d74213
ZJS
599 _cleanup_free_ char *t = utf8_escape_invalid(value);
600
717603e3 601 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
f27f0e21
GB
602 return -EINVAL;
603 }
a5c32cff 604
f27f0e21
GB
605 p = strjoin(key, "=", strempty(value), NULL);
606 if (!p)
607 return -ENOMEM;
335c46b5 608
6e18964d
ZJS
609 r = strv_consume(m, p);
610 if (r < 0)
f27f0e21 611 return r;
a5c32cff 612
a5f6c30d
MS
613 if (n_pushed)
614 (*n_pushed)++;
615
f73141d7
LP
616 free(value);
617 return 0;
618}
619
717603e3
LP
620int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
621 char **m = NULL;
622 int r;
623
624 if (!newline)
625 newline = NEWLINE;
626
a5f6c30d 627 r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
717603e3
LP
628 if (r < 0) {
629 strv_free(m);
630 return r;
631 }
632
633 *rl = m;
634 return 0;
635}
636
637static int load_env_file_push_pairs(
638 const char *filename, unsigned line,
639 const char *key, char *value,
a5f6c30d
MS
640 void *userdata,
641 int *n_pushed) {
717603e3
LP
642 char ***m = userdata;
643 int r;
644
645 if (!utf8_is_valid(key)) {
646 _cleanup_free_ char *t = utf8_escape_invalid(key);
647
648 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
649 return -EINVAL;
650 }
651
652 if (value && !utf8_is_valid(value)) {
653 _cleanup_free_ char *t = utf8_escape_invalid(value);
654
655 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
656 return -EINVAL;
657 }
658
659 r = strv_extend(m, key);
660 if (r < 0)
661 return -ENOMEM;
662
663 if (!value) {
664 r = strv_extend(m, "");
665 if (r < 0)
666 return -ENOMEM;
667 } else {
668 r = strv_push(m, value);
669 if (r < 0)
670 return r;
671 }
672
a5f6c30d
MS
673 if (n_pushed)
674 (*n_pushed)++;
675
717603e3
LP
676 return 0;
677}
678
679int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
f73141d7
LP
680 char **m = NULL;
681 int r;
a5c32cff 682
f73141d7
LP
683 if (!newline)
684 newline = NEWLINE;
685
a5f6c30d 686 r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
f73141d7
LP
687 if (r < 0) {
688 strv_free(m);
689 return r;
a5c32cff
HH
690 }
691
692 *rl = m;
a5c32cff
HH
693 return 0;
694}
695
768100ef
LP
696static void write_env_var(FILE *f, const char *v) {
697 const char *p;
698
699 p = strchr(v, '=');
700 if (!p) {
701 /* Fallback */
702 fputs(v, f);
703 fputc('\n', f);
704 return;
705 }
706
707 p++;
708 fwrite(v, 1, p-v, f);
709
0ce5a806 710 if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
768100ef
LP
711 fputc('\"', f);
712
713 for (; *p; p++) {
0ce5a806 714 if (strchr(SHELL_NEED_ESCAPE, *p))
768100ef
LP
715 fputc('\\', f);
716
717 fputc(*p, f);
718 }
719
720 fputc('\"', f);
721 } else
722 fputs(p, f);
723
724 fputc('\n', f);
725}
726
a5c32cff 727int write_env_file(const char *fname, char **l) {
7fd1b19b 728 _cleanup_fclose_ FILE *f = NULL;
736937e5
LP
729 _cleanup_free_ char *p = NULL;
730 char **i;
a5c32cff
HH
731 int r;
732
736937e5
LP
733 assert(fname);
734
a5c32cff
HH
735 r = fopen_temporary(fname, &f, &p);
736 if (r < 0)
737 return r;
738
739 fchmod_umask(fileno(f), 0644);
740
768100ef
LP
741 STRV_FOREACH(i, l)
742 write_env_var(f, *i);
a5c32cff 743
736937e5
LP
744 r = fflush_and_check(f);
745 if (r >= 0) {
746 if (rename(p, fname) >= 0)
747 return 0;
a5c32cff 748
736937e5 749 r = -errno;
a5c32cff
HH
750 }
751
736937e5 752 unlink(p);
a5c32cff
HH
753 return r;
754}
68fee104
ZJS
755
756int executable_is_script(const char *path, char **interpreter) {
757 int r;
c8b32e11 758 _cleanup_free_ char *line = NULL;
68fee104
ZJS
759 int len;
760 char *ans;
761
762 assert(path);
763
764 r = read_one_line_file(path, &line);
765 if (r < 0)
766 return r;
767
768 if (!startswith(line, "#!"))
769 return 0;
770
771 ans = strstrip(line + 2);
772 len = strcspn(ans, " \t");
773
774 if (len == 0)
775 return 0;
776
777 ans = strndup(ans, len);
778 if (!ans)
779 return -ENOMEM;
780
781 *interpreter = ans;
782 return 1;
783}
69ab8088
ZJS
784
785/**
0a7b53bd 786 * Retrieve one field from a file like /proc/self/status. pattern
c4cd1d4d
AK
787 * should not include whitespace or the delimiter (':'). pattern matches only
788 * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
789 * zeros after the ':' will be skipped. field must be freed afterwards.
790 * terminator specifies the terminating characters of the field value (not
791 * included in the value).
69ab8088 792 */
c4cd1d4d 793int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
69ab8088 794 _cleanup_free_ char *status = NULL;
90110825 795 char *t, *f;
69ab8088
ZJS
796 size_t len;
797 int r;
798
c4cd1d4d 799 assert(terminator);
69ab8088 800 assert(filename);
7ff7394d 801 assert(pattern);
69ab8088
ZJS
802 assert(field);
803
804 r = read_full_file(filename, &status, NULL);
805 if (r < 0)
806 return r;
807
c4cd1d4d
AK
808 t = status;
809
810 do {
811 bool pattern_ok;
812
813 do {
814 t = strstr(t, pattern);
815 if (!t)
816 return -ENOENT;
817
818 /* Check that pattern occurs in beginning of line. */
819 pattern_ok = (t == status || t[-1] == '\n');
820
821 t += strlen(pattern);
822
823 } while (!pattern_ok);
824
825 t += strspn(t, " \t");
826 if (!*t)
827 return -ENOENT;
828
829 } while (*t != ':');
830
831 t++;
69ab8088 832
4ec29144 833 if (*t) {
1e5413f7
ZJS
834 t += strspn(t, " \t");
835
836 /* Also skip zeros, because when this is used for
837 * capabilities, we don't want the zeros. This way the
838 * same capability set always maps to the same string,
839 * irrespective of the total capability set size. For
840 * other numbers it shouldn't matter. */
841 t += strspn(t, "0");
4ec29144
ZJS
842 /* Back off one char if there's nothing but whitespace
843 and zeros */
1e5413f7 844 if (!*t || isspace(*t))
4ec29144
ZJS
845 t --;
846 }
69ab8088 847
c4cd1d4d 848 len = strcspn(t, terminator);
69ab8088 849
90110825
LP
850 f = strndup(t, len);
851 if (!f)
69ab8088
ZJS
852 return -ENOMEM;
853
90110825 854 *field = f;
69ab8088
ZJS
855 return 0;
856}
0d39fa9c
LP
857
858DIR *xopendirat(int fd, const char *name, int flags) {
859 int nfd;
860 DIR *d;
861
862 assert(!(flags & O_CREAT));
863
864 nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
865 if (nfd < 0)
866 return NULL;
867
868 d = fdopendir(nfd);
869 if (!d) {
870 safe_close(nfd);
871 return NULL;
872 }
873
874 return d;
875}
876
877static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
878 char **i;
879
880 assert(path);
881 assert(mode);
882 assert(_f);
883
884 if (!path_strv_resolve_uniq(search, root))
885 return -ENOMEM;
886
887 STRV_FOREACH(i, search) {
888 _cleanup_free_ char *p = NULL;
889 FILE *f;
890
891 if (root)
892 p = strjoin(root, *i, "/", path, NULL);
893 else
894 p = strjoin(*i, "/", path, NULL);
895 if (!p)
896 return -ENOMEM;
897
898 f = fopen(p, mode);
899 if (f) {
900 *_f = f;
901 return 0;
902 }
903
904 if (errno != ENOENT)
905 return -errno;
906 }
907
908 return -ENOENT;
909}
910
911int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
912 _cleanup_strv_free_ char **copy = NULL;
913
914 assert(path);
915 assert(mode);
916 assert(_f);
917
918 if (path_is_absolute(path)) {
919 FILE *f;
920
921 f = fopen(path, mode);
922 if (f) {
923 *_f = f;
924 return 0;
925 }
926
927 return -errno;
928 }
929
930 copy = strv_copy((char**) search);
931 if (!copy)
932 return -ENOMEM;
933
934 return search_and_fopen_internal(path, mode, root, copy, _f);
935}
936
937int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
938 _cleanup_strv_free_ char **s = NULL;
939
940 if (path_is_absolute(path)) {
941 FILE *f;
942
943 f = fopen(path, mode);
944 if (f) {
945 *_f = f;
946 return 0;
947 }
948
949 return -errno;
950 }
951
952 s = strv_split_nulstr(search);
953 if (!s)
954 return -ENOMEM;
955
956 return search_and_fopen_internal(path, mode, root, s, _f);
957}
958
959int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
960 FILE *f;
961 char *t;
962 int r, fd;
963
964 assert(path);
965 assert(_f);
966 assert(_temp_path);
967
968 r = tempfn_xxxxxx(path, NULL, &t);
969 if (r < 0)
970 return r;
971
972 fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
973 if (fd < 0) {
974 free(t);
975 return -errno;
976 }
977
978 f = fdopen(fd, "we");
979 if (!f) {
980 unlink_noerrno(t);
981 free(t);
982 safe_close(fd);
983 return -errno;
984 }
985
986 *_f = f;
987 *_temp_path = t;
988
989 return 0;
990}
991
992int fflush_and_check(FILE *f) {
993 assert(f);
994
995 errno = 0;
996 fflush(f);
997
998 if (ferror(f))
999 return errno ? -errno : -EIO;
1000
1001 return 0;
1002}
1003
1004/* This is much like like mkostemp() but is subject to umask(). */
1005int mkostemp_safe(char *pattern, int flags) {
1006 _cleanup_umask_ mode_t u;
1007 int fd;
1008
1009 assert(pattern);
1010
1011 u = umask(077);
1012
1013 fd = mkostemp(pattern, flags);
1014 if (fd < 0)
1015 return -errno;
1016
1017 return fd;
1018}
1019
1020int open_tmpfile(const char *path, int flags) {
1021 char *p;
1022 int fd;
1023
1024 assert(path);
1025
1026#ifdef O_TMPFILE
1027 /* Try O_TMPFILE first, if it is supported */
1028 fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
1029 if (fd >= 0)
1030 return fd;
1031#endif
1032
1033 /* Fall back to unguessable name + unlinking */
1034 p = strjoina(path, "/systemd-tmp-XXXXXX");
1035
1036 fd = mkostemp_safe(p, flags);
1037 if (fd < 0)
1038 return fd;
1039
1040 unlink(p);
1041 return fd;
1042}
1043
1044int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
1045 const char *fn;
1046 char *t;
1047
1048 assert(p);
1049 assert(ret);
1050
1051 /*
1052 * Turns this:
1053 * /foo/bar/waldo
1054 *
1055 * Into this:
1056 * /foo/bar/.#<extra>waldoXXXXXX
1057 */
1058
1059 fn = basename(p);
1060 if (!filename_is_valid(fn))
1061 return -EINVAL;
1062
1063 if (extra == NULL)
1064 extra = "";
1065
1066 t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
1067 if (!t)
1068 return -ENOMEM;
1069
1070 strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
1071
1072 *ret = path_kill_slashes(t);
1073 return 0;
1074}
1075
1076int tempfn_random(const char *p, const char *extra, char **ret) {
1077 const char *fn;
1078 char *t, *x;
1079 uint64_t u;
1080 unsigned i;
1081
1082 assert(p);
1083 assert(ret);
1084
1085 /*
1086 * Turns this:
1087 * /foo/bar/waldo
1088 *
1089 * Into this:
1090 * /foo/bar/.#<extra>waldobaa2a261115984a9
1091 */
1092
1093 fn = basename(p);
1094 if (!filename_is_valid(fn))
1095 return -EINVAL;
1096
1097 if (!extra)
1098 extra = "";
1099
1100 t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
1101 if (!t)
1102 return -ENOMEM;
1103
1104 x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
1105
1106 u = random_u64();
1107 for (i = 0; i < 16; i++) {
1108 *(x++) = hexchar(u & 0xF);
1109 u >>= 4;
1110 }
1111
1112 *x = 0;
1113
1114 *ret = path_kill_slashes(t);
1115 return 0;
1116}
1117
1118int tempfn_random_child(const char *p, const char *extra, char **ret) {
1119 char *t, *x;
1120 uint64_t u;
1121 unsigned i;
1122
1123 assert(p);
1124 assert(ret);
1125
1126 /* Turns this:
1127 * /foo/bar/waldo
1128 * Into this:
1129 * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
1130 */
1131
1132 if (!extra)
1133 extra = "";
1134
1135 t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
1136 if (!t)
1137 return -ENOMEM;
1138
1139 x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
1140
1141 u = random_u64();
1142 for (i = 0; i < 16; i++) {
1143 *(x++) = hexchar(u & 0xF);
1144 u >>= 4;
1145 }
1146
1147 *x = 0;
1148
1149 *ret = path_kill_slashes(t);
1150 return 0;
1151}