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