]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/fileio.c
fileio: support writing atomic files with timestamp
[thirdparty/systemd.git] / src / basic / fileio.c
CommitLineData
a5c32cff
HH
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18***/
19
11c3a366
TA
20#include <errno.h>
21#include <fcntl.h>
22#include <limits.h>
23#include <stdarg.h>
24#include <stdint.h>
25#include <stdlib.h>
26#include <string.h>
27#include <sys/stat.h>
28#include <sys/types.h>
a5c32cff 29#include <unistd.h>
cda134ab 30
b5efdb8a 31#include "alloc-util.h"
4f5dd394 32#include "ctype.h"
99003e01 33#include "env-util.h"
4f5dd394 34#include "escape.h"
3ffd4af2
LP
35#include "fd-util.h"
36#include "fileio.h"
f4f15635 37#include "fs-util.h"
0d39fa9c 38#include "hexdecoct.h"
93cc7779
TA
39#include "log.h"
40#include "macro.h"
1d9ed171 41#include "missing.h"
33d52ab9 42#include "parse-util.h"
0d39fa9c 43#include "path-util.h"
df0ff127 44#include "process-util.h"
0d39fa9c 45#include "random-util.h"
33d52ab9 46#include "stdio-util.h"
07630cea 47#include "string-util.h"
a5c32cff 48#include "strv.h"
93cc7779 49#include "time-util.h"
affb60b1 50#include "umask-util.h"
335c46b5 51#include "utf8.h"
a5c32cff 52
c2d11a63
VC
53#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
54
39c38d77 55int write_string_stream_ts(FILE *f, const char *line, bool enforce_newline, struct timespec *ts) {
dacd6cee 56
717603e3
LP
57 assert(f);
58 assert(line);
59
fec6fc6b 60 fputs(line, f);
40beecdb 61 if (enforce_newline && !endswith(line, "\n"))
a5c32cff
HH
62 fputc('\n', f);
63
39c38d77
ZJS
64 if (ts) {
65 struct timespec twice[2] = {*ts, *ts};
66
67 if (futimens(fileno(f), twice) < 0)
68 return -errno;
69 }
70
dacd6cee 71 return fflush_and_check(f);
a5c32cff
HH
72}
73
2eabcc77
LP
74static int write_string_file_atomic(
75 const char *fn,
76 const char *line,
77 bool enforce_newline,
78 bool do_fsync,
79 struct timespec *ts) {
80
a5c32cff
HH
81 _cleanup_fclose_ FILE *f = NULL;
82 _cleanup_free_ char *p = NULL;
83 int r;
84
85 assert(fn);
86 assert(line);
87
88 r = fopen_temporary(fn, &f, &p);
89 if (r < 0)
90 return r;
91
0d39fa9c 92 (void) fchmod_umask(fileno(f), 0644);
a5c32cff 93
2eabcc77 94 r = write_string_stream_ts(f, line, enforce_newline, ts);
265710c2 95 if (r >= 0 && do_fsync)
0675e94a 96 r = fflush_sync_and_check(f);
f2997962 97 if (r >= 0) {
a5c32cff
HH
98 if (rename(p, fn) < 0)
99 r = -errno;
a5c32cff
HH
100 }
101
a5c32cff 102 if (r < 0)
0d39fa9c 103 (void) unlink(p);
a5c32cff
HH
104
105 return r;
106}
107
39c38d77 108int write_string_file_ts(const char *fn, const char *line, WriteStringFileFlags flags, struct timespec *ts) {
4c1fc3e4 109 _cleanup_fclose_ FILE *f = NULL;
eb3da901 110 int q, r;
4c1fc3e4
DM
111
112 assert(fn);
113 assert(line);
114
265710c2
AJ
115 /* We don't know how to verify whether the file contents was already on-disk. */
116 assert(!((flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE) && (flags & WRITE_STRING_FILE_SYNC)));
0675e94a 117
4c1fc3e4
DM
118 if (flags & WRITE_STRING_FILE_ATOMIC) {
119 assert(flags & WRITE_STRING_FILE_CREATE);
120
2eabcc77
LP
121 r = write_string_file_atomic(fn,
122 line,
123 !(flags & WRITE_STRING_FILE_AVOID_NEWLINE),
124 flags & WRITE_STRING_FILE_SYNC, ts);
eb3da901
LP
125 if (r < 0)
126 goto fail;
127
128 return r;
39c38d77
ZJS
129 } else
130 assert(ts == NULL);
4c1fc3e4
DM
131
132 if (flags & WRITE_STRING_FILE_CREATE) {
133 f = fopen(fn, "we");
eb3da901
LP
134 if (!f) {
135 r = -errno;
136 goto fail;
137 }
4c1fc3e4
DM
138 } else {
139 int fd;
140
141 /* We manually build our own version of fopen(..., "we") that
142 * works without O_CREAT */
143 fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
eb3da901
LP
144 if (fd < 0) {
145 r = -errno;
146 goto fail;
147 }
4c1fc3e4
DM
148
149 f = fdopen(fd, "we");
150 if (!f) {
eb3da901 151 r = -errno;
4c1fc3e4 152 safe_close(fd);
eb3da901 153 goto fail;
4c1fc3e4
DM
154 }
155 }
156
39c38d77 157 r = write_string_stream_ts(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE), ts);
eb3da901
LP
158 if (r < 0)
159 goto fail;
160
0675e94a
AJ
161 if (flags & WRITE_STRING_FILE_SYNC) {
162 r = fflush_sync_and_check(f);
163 if (r < 0)
164 return r;
165 }
166
eb3da901
LP
167 return 0;
168
169fail:
170 if (!(flags & WRITE_STRING_FILE_VERIFY_ON_FAILURE))
171 return r;
172
173 f = safe_fclose(f);
174
175 /* OK, the operation failed, but let's see if the right
176 * contents in place already. If so, eat up the error. */
177
178 q = verify_file(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
179 if (q <= 0)
180 return r;
181
182 return 0;
4c1fc3e4
DM
183}
184
a5c32cff
HH
185int read_one_line_file(const char *fn, char **line) {
186 _cleanup_fclose_ FILE *f = NULL;
187 char t[LINE_MAX], *c;
188
189 assert(fn);
190 assert(line);
191
192 f = fopen(fn, "re");
193 if (!f)
194 return -errno;
195
196 if (!fgets(t, sizeof(t), f)) {
197
198 if (ferror(f))
f5e5c28f 199 return errno > 0 ? -errno : -EIO;
a5c32cff
HH
200
201 t[0] = 0;
202 }
203
204 c = strdup(t);
205 if (!c)
206 return -ENOMEM;
207 truncate_nl(c);
208
209 *line = c;
210 return 0;
211}
212
eb3da901
LP
213int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
214 _cleanup_fclose_ FILE *f = NULL;
215 _cleanup_free_ char *buf = NULL;
216 size_t l, k;
15dee3f0 217
eb3da901
LP
218 assert(fn);
219 assert(blob);
220
221 l = strlen(blob);
222
223 if (accept_extra_nl && endswith(blob, "\n"))
224 accept_extra_nl = false;
225
226 buf = malloc(l + accept_extra_nl + 1);
227 if (!buf)
228 return -ENOMEM;
229
230 f = fopen(fn, "re");
231 if (!f)
232 return -errno;
233
234 /* We try to read one byte more than we need, so that we know whether we hit eof */
235 errno = 0;
236 k = fread(buf, 1, l + accept_extra_nl + 1, f);
237 if (ferror(f))
238 return errno > 0 ? -errno : -EIO;
239
240 if (k != l && k != l + accept_extra_nl)
241 return 0;
242 if (memcmp(buf, blob, l) != 0)
243 return 0;
244 if (k > l && buf[l] != '\n')
245 return 0;
15dee3f0 246
eb3da901 247 return 1;
15dee3f0
LP
248}
249
717603e3 250int read_full_stream(FILE *f, char **contents, size_t *size) {
a5c32cff
HH
251 size_t n, l;
252 _cleanup_free_ char *buf = NULL;
253 struct stat st;
254
717603e3 255 assert(f);
a5c32cff
HH
256 assert(contents);
257
a5c32cff
HH
258 if (fstat(fileno(f), &st) < 0)
259 return -errno;
260
717603e3 261 n = LINE_MAX;
a5c32cff 262
717603e3
LP
263 if (S_ISREG(st.st_mode)) {
264
265 /* Safety check */
c2d11a63 266 if (st.st_size > READ_FULL_BYTES_MAX)
717603e3
LP
267 return -E2BIG;
268
269 /* Start with the right file size, but be prepared for
270 * files from /proc which generally report a file size
271 * of 0 */
272 if (st.st_size > 0)
273 n = st.st_size;
274 }
a5c32cff 275
717603e3 276 l = 0;
a5c32cff
HH
277 for (;;) {
278 char *t;
279 size_t k;
280
c2d11a63 281 t = realloc(buf, n + 1);
a5c32cff
HH
282 if (!t)
283 return -ENOMEM;
284
285 buf = t;
286 k = fread(buf + l, 1, n - l, f);
c2d11a63
VC
287 if (k > 0)
288 l += k;
a5c32cff 289
c2d11a63
VC
290 if (ferror(f))
291 return -errno;
a5c32cff 292
c2d11a63 293 if (feof(f))
a5c32cff 294 break;
a5c32cff 295
c2d11a63
VC
296 /* We aren't expecting fread() to return a short read outside
297 * of (error && eof), assert buffer is full and enlarge buffer.
298 */
299 assert(l == n);
a5c32cff
HH
300
301 /* Safety check */
c2d11a63 302 if (n >= READ_FULL_BYTES_MAX)
a5c32cff 303 return -E2BIG;
c2d11a63 304
82b103a7 305 n = MIN(n * 2, READ_FULL_BYTES_MAX);
a5c32cff
HH
306 }
307
308 buf[l] = 0;
309 *contents = buf;
d78a28e3 310 buf = NULL; /* do not free */
a5c32cff
HH
311
312 if (size)
313 *size = l;
314
315 return 0;
316}
317
717603e3
LP
318int read_full_file(const char *fn, char **contents, size_t *size) {
319 _cleanup_fclose_ FILE *f = NULL;
320
321 assert(fn);
322 assert(contents);
323
324 f = fopen(fn, "re");
325 if (!f)
326 return -errno;
327
328 return read_full_stream(f, contents, size);
329}
330
f73141d7 331static int parse_env_file_internal(
717603e3 332 FILE *f,
a5c32cff 333 const char *fname,
f73141d7 334 const char *newline,
335c46b5 335 int (*push) (const char *filename, unsigned line,
a5f6c30d
MS
336 const char *key, char *value, void *userdata, int *n_pushed),
337 void *userdata,
338 int *n_pushed) {
f73141d7
LP
339
340 _cleanup_free_ char *contents = NULL, *key = NULL;
2b77f67e 341 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
342 char *p, *value = NULL;
343 int r;
335c46b5 344 unsigned line = 1;
a5c32cff 345
f73141d7
LP
346 enum {
347 PRE_KEY,
348 KEY,
f73141d7
LP
349 PRE_VALUE,
350 VALUE,
351 VALUE_ESCAPE,
352 SINGLE_QUOTE_VALUE,
353 SINGLE_QUOTE_VALUE_ESCAPE,
354 DOUBLE_QUOTE_VALUE,
355 DOUBLE_QUOTE_VALUE_ESCAPE,
356 COMMENT,
357 COMMENT_ESCAPE
358 } state = PRE_KEY;
a5c32cff 359
f73141d7 360 assert(newline);
a5c32cff 361
717603e3
LP
362 if (f)
363 r = read_full_stream(f, &contents, NULL);
364 else
365 r = read_full_file(fname, &contents, NULL);
1c17cbed 366 if (r < 0)
a5c32cff
HH
367 return r;
368
f73141d7
LP
369 for (p = contents; *p; p++) {
370 char c = *p;
371
372 switch (state) {
373
374 case PRE_KEY:
ebc05a09 375 if (strchr(COMMENTS, c))
f73141d7
LP
376 state = COMMENT;
377 else if (!strchr(WHITESPACE, c)) {
378 state = KEY;
2b77f67e
LP
379 last_key_whitespace = (size_t) -1;
380
ca2d3784 381 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
f73141d7
LP
382 r = -ENOMEM;
383 goto fail;
384 }
385
386 key[n_key++] = c;
387 }
388 break;
389
390 case KEY:
391 if (strchr(newline, c)) {
392 state = PRE_KEY;
313cefa1 393 line++;
f73141d7 394 n_key = 0;
2b77f67e 395 } else if (c == '=') {
f73141d7 396 state = PRE_VALUE;
2b77f67e
LP
397 last_value_whitespace = (size_t) -1;
398 } else {
399 if (!strchr(WHITESPACE, c))
400 last_key_whitespace = (size_t) -1;
401 else if (last_key_whitespace == (size_t) -1)
402 last_key_whitespace = n_key;
403
ca2d3784 404 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
f73141d7
LP
405 r = -ENOMEM;
406 goto fail;
407 }
408
409 key[n_key++] = c;
410 }
a5c32cff 411
f73141d7
LP
412 break;
413
f73141d7 414 case PRE_VALUE:
98f59e59 415 if (strchr(newline, c)) {
f73141d7 416 state = PRE_KEY;
313cefa1 417 line++;
f73141d7 418 key[n_key] = 0;
a5c32cff 419
f73141d7
LP
420 if (value)
421 value[n_value] = 0;
a5c32cff 422
ebc05a09 423 /* strip trailing whitespace from key */
2b77f67e
LP
424 if (last_key_whitespace != (size_t) -1)
425 key[last_key_whitespace] = 0;
ebc05a09 426
a5f6c30d 427 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
428 if (r < 0)
429 goto fail;
430
431 n_key = 0;
432 value = NULL;
433 value_alloc = n_value = 0;
2b77f67e 434
f73141d7
LP
435 } else if (c == '\'')
436 state = SINGLE_QUOTE_VALUE;
437 else if (c == '\"')
438 state = DOUBLE_QUOTE_VALUE;
439 else if (c == '\\')
440 state = VALUE_ESCAPE;
441 else if (!strchr(WHITESPACE, c)) {
442 state = VALUE;
443
ca2d3784 444 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
445 r = -ENOMEM;
446 goto fail;
447 }
448
449 value[n_value++] = c;
450 }
451
452 break;
453
454 case VALUE:
455 if (strchr(newline, c)) {
456 state = PRE_KEY;
313cefa1 457 line++;
98f59e59 458
f73141d7
LP
459 key[n_key] = 0;
460
461 if (value)
462 value[n_value] = 0;
463
2b77f67e
LP
464 /* Chomp off trailing whitespace from value */
465 if (last_value_whitespace != (size_t) -1)
466 value[last_value_whitespace] = 0;
f73141d7 467
ebc05a09 468 /* strip trailing whitespace from key */
2b77f67e
LP
469 if (last_key_whitespace != (size_t) -1)
470 key[last_key_whitespace] = 0;
ebc05a09 471
a5f6c30d 472 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
473 if (r < 0)
474 goto fail;
475
476 n_key = 0;
477 value = NULL;
478 value_alloc = n_value = 0;
2b77f67e 479
f73141d7
LP
480 } else if (c == '\\') {
481 state = VALUE_ESCAPE;
2b77f67e 482 last_value_whitespace = (size_t) -1;
f73141d7
LP
483 } else {
484 if (!strchr(WHITESPACE, c))
2b77f67e
LP
485 last_value_whitespace = (size_t) -1;
486 else if (last_value_whitespace == (size_t) -1)
487 last_value_whitespace = n_value;
f73141d7 488
ca2d3784 489 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
490 r = -ENOMEM;
491 goto fail;
492 }
493
494 value[n_value++] = c;
495 }
496
497 break;
498
499 case VALUE_ESCAPE:
500 state = VALUE;
501
502 if (!strchr(newline, c)) {
503 /* Escaped newlines we eat up entirely */
ca2d3784 504 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
505 r = -ENOMEM;
506 goto fail;
507 }
508
509 value[n_value++] = c;
510 }
511 break;
512
513 case SINGLE_QUOTE_VALUE:
514 if (c == '\'')
515 state = PRE_VALUE;
516 else if (c == '\\')
517 state = SINGLE_QUOTE_VALUE_ESCAPE;
518 else {
ca2d3784 519 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
520 r = -ENOMEM;
521 goto fail;
522 }
a5c32cff 523
f73141d7
LP
524 value[n_value++] = c;
525 }
a5c32cff 526
f73141d7 527 break;
a5c32cff 528
f73141d7
LP
529 case SINGLE_QUOTE_VALUE_ESCAPE:
530 state = SINGLE_QUOTE_VALUE;
a5c32cff 531
f73141d7 532 if (!strchr(newline, c)) {
ca2d3784 533 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
a5c32cff 534 r = -ENOMEM;
a5c32cff
HH
535 goto fail;
536 }
537
f73141d7
LP
538 value[n_value++] = c;
539 }
540 break;
541
542 case DOUBLE_QUOTE_VALUE:
543 if (c == '\"')
544 state = PRE_VALUE;
545 else if (c == '\\')
546 state = DOUBLE_QUOTE_VALUE_ESCAPE;
547 else {
ca2d3784 548 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
549 r = -ENOMEM;
550 goto fail;
a5c32cff
HH
551 }
552
f73141d7
LP
553 value[n_value++] = c;
554 }
555
556 break;
557
558 case DOUBLE_QUOTE_VALUE_ESCAPE:
559 state = DOUBLE_QUOTE_VALUE;
a5c32cff 560
f73141d7 561 if (!strchr(newline, c)) {
ca2d3784 562 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
563 r = -ENOMEM;
564 goto fail;
565 }
a5c32cff 566
f73141d7 567 value[n_value++] = c;
a5c32cff 568 }
f73141d7
LP
569 break;
570
571 case COMMENT:
572 if (c == '\\')
573 state = COMMENT_ESCAPE;
335c46b5 574 else if (strchr(newline, c)) {
f73141d7 575 state = PRE_KEY;
313cefa1 576 line++;
335c46b5 577 }
f73141d7
LP
578 break;
579
580 case COMMENT_ESCAPE:
581 state = COMMENT;
582 break;
a5c32cff 583 }
f73141d7
LP
584 }
585
371328dc
ZJS
586 if (IN_SET(state,
587 PRE_VALUE,
588 VALUE,
589 VALUE_ESCAPE,
590 SINGLE_QUOTE_VALUE,
591 SINGLE_QUOTE_VALUE_ESCAPE,
592 DOUBLE_QUOTE_VALUE,
593 DOUBLE_QUOTE_VALUE_ESCAPE)) {
a5c32cff 594
f73141d7
LP
595 key[n_key] = 0;
596
597 if (value)
598 value[n_value] = 0;
599
2b77f67e
LP
600 if (state == VALUE)
601 if (last_value_whitespace != (size_t) -1)
602 value[last_value_whitespace] = 0;
603
ebc05a09 604 /* strip trailing whitespace from key */
2b77f67e
LP
605 if (last_key_whitespace != (size_t) -1)
606 key[last_key_whitespace] = 0;
ebc05a09 607
a5f6c30d 608 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
609 if (r < 0)
610 goto fail;
a5c32cff
HH
611 }
612
f73141d7
LP
613 return 0;
614
a5c32cff 615fail:
f73141d7 616 free(value);
a5c32cff
HH
617 return r;
618}
619
ac466818 620static int check_utf8ness_and_warn(
717603e3 621 const char *filename, unsigned line,
ac466818 622 const char *key, char *value) {
f27f0e21
GB
623
624 if (!utf8_is_valid(key)) {
3587161a 625 _cleanup_free_ char *p = NULL;
550a40ec 626
717603e3
LP
627 p = utf8_escape_invalid(key);
628 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
f27f0e21
GB
629 return -EINVAL;
630 }
631
632 if (value && !utf8_is_valid(value)) {
3587161a 633 _cleanup_free_ char *p = NULL;
550a40ec 634
717603e3
LP
635 p = utf8_escape_invalid(value);
636 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
f27f0e21
GB
637 return -EINVAL;
638 }
a5c32cff 639
ac466818
ZJS
640 return 0;
641}
642
643static int parse_env_file_push(
644 const char *filename, unsigned line,
645 const char *key, char *value,
646 void *userdata,
647 int *n_pushed) {
648
649 const char *k;
650 va_list aq, *ap = userdata;
651 int r;
652
653 r = check_utf8ness_and_warn(filename, line, key, value);
654 if (r < 0)
655 return r;
656
f27f0e21 657 va_copy(aq, *ap);
a5c32cff 658
f27f0e21
GB
659 while ((k = va_arg(aq, const char *))) {
660 char **v;
a5c32cff 661
f27f0e21 662 v = va_arg(aq, char **);
a5c32cff 663
f27f0e21
GB
664 if (streq(key, k)) {
665 va_end(aq);
666 free(*v);
667 *v = value;
a5f6c30d
MS
668
669 if (n_pushed)
670 (*n_pushed)++;
671
f27f0e21 672 return 1;
f73141d7 673 }
335c46b5 674 }
a5c32cff 675
f27f0e21 676 va_end(aq);
f73141d7 677 free(value);
717603e3 678
f73141d7
LP
679 return 0;
680}
a5c32cff 681
f73141d7
LP
682int parse_env_file(
683 const char *fname,
684 const char *newline, ...) {
a5c32cff 685
f73141d7 686 va_list ap;
a5f6c30d 687 int r, n_pushed = 0;
a5c32cff 688
f73141d7
LP
689 if (!newline)
690 newline = NEWLINE;
a5c32cff 691
f73141d7 692 va_start(ap, newline);
a5f6c30d 693 r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
f73141d7 694 va_end(ap);
a5c32cff 695
a5f6c30d 696 return r < 0 ? r : n_pushed;
f73141d7 697}
a5c32cff 698
717603e3
LP
699static int load_env_file_push(
700 const char *filename, unsigned line,
701 const char *key, char *value,
a5f6c30d
MS
702 void *userdata,
703 int *n_pushed) {
f27f0e21
GB
704 char ***m = userdata;
705 char *p;
706 int r;
a5c32cff 707
ac466818
ZJS
708 r = check_utf8ness_and_warn(filename, line, key, value);
709 if (r < 0)
710 return r;
a5c32cff 711
99003e01 712 p = strjoin(key, "=", value);
f27f0e21
GB
713 if (!p)
714 return -ENOMEM;
335c46b5 715
99003e01
ZJS
716 r = strv_env_replace(m, p);
717 if (r < 0) {
718 free(p);
f27f0e21 719 return r;
99003e01 720 }
a5c32cff 721
a5f6c30d
MS
722 if (n_pushed)
723 (*n_pushed)++;
724
f73141d7
LP
725 free(value);
726 return 0;
727}
728
717603e3
LP
729int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
730 char **m = NULL;
731 int r;
732
733 if (!newline)
734 newline = NEWLINE;
735
a5f6c30d 736 r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
717603e3
LP
737 if (r < 0) {
738 strv_free(m);
739 return r;
740 }
741
742 *rl = m;
743 return 0;
744}
745
746static int load_env_file_push_pairs(
747 const char *filename, unsigned line,
748 const char *key, char *value,
a5f6c30d
MS
749 void *userdata,
750 int *n_pushed) {
717603e3
LP
751 char ***m = userdata;
752 int r;
753
ac466818
ZJS
754 r = check_utf8ness_and_warn(filename, line, key, value);
755 if (r < 0)
756 return r;
717603e3
LP
757
758 r = strv_extend(m, key);
759 if (r < 0)
760 return -ENOMEM;
761
762 if (!value) {
763 r = strv_extend(m, "");
764 if (r < 0)
765 return -ENOMEM;
766 } else {
767 r = strv_push(m, value);
768 if (r < 0)
769 return r;
770 }
771
a5f6c30d
MS
772 if (n_pushed)
773 (*n_pushed)++;
774
717603e3
LP
775 return 0;
776}
777
778int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
f73141d7
LP
779 char **m = NULL;
780 int r;
a5c32cff 781
f73141d7
LP
782 if (!newline)
783 newline = NEWLINE;
784
a5f6c30d 785 r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
f73141d7
LP
786 if (r < 0) {
787 strv_free(m);
788 return r;
a5c32cff
HH
789 }
790
791 *rl = m;
a5c32cff
HH
792 return 0;
793}
794
37f3ffca
RS
795static int merge_env_file_push(
796 const char *filename, unsigned line,
797 const char *key, char *value,
798 void *userdata,
799 int *n_pushed) {
800
801 char ***env = userdata;
802 char *expanded_value;
803
804 assert(env);
805
184d1904
ZJS
806 if (!value) {
807 log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
808 return 0;
809 }
810
811 if (!env_name_is_valid(key)) {
812 log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
2ea8081a 813 free(value);
184d1904
ZJS
814 return 0;
815 }
816
ccad1fd0 817 expanded_value = replace_env(value, *env,
b82f58bf
RS
818 REPLACE_ENV_USE_ENVIRONMENT|
819 REPLACE_ENV_ALLOW_BRACELESS|
820 REPLACE_ENV_ALLOW_EXTENDED);
37f3ffca
RS
821 if (!expanded_value)
822 return -ENOMEM;
823
824 free_and_replace(value, expanded_value);
825
826 return load_env_file_push(filename, line, key, value, env, n_pushed);
827}
828
829int merge_env_file(
830 char ***env,
831 FILE *f,
832 const char *fname) {
833
ccad1fd0 834 /* NOTE: this function supports braceful and braceless variable expansions,
b82f58bf 835 * plus "extended" substitutions, unlike other exported parsing functions.
ccad1fd0
ZJS
836 */
837
37f3ffca
RS
838 return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
839}
840
768100ef
LP
841static void write_env_var(FILE *f, const char *v) {
842 const char *p;
843
844 p = strchr(v, '=');
845 if (!p) {
846 /* Fallback */
4b61c875
LP
847 fputs_unlocked(v, f);
848 fputc_unlocked('\n', f);
768100ef
LP
849 return;
850 }
851
852 p++;
4b61c875 853 fwrite_unlocked(v, 1, p-v, f);
768100ef 854
0ce5a806 855 if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
4b61c875 856 fputc_unlocked('\"', f);
768100ef
LP
857
858 for (; *p; p++) {
0ce5a806 859 if (strchr(SHELL_NEED_ESCAPE, *p))
4b61c875 860 fputc_unlocked('\\', f);
768100ef 861
4b61c875 862 fputc_unlocked(*p, f);
768100ef
LP
863 }
864
4b61c875 865 fputc_unlocked('\"', f);
768100ef 866 } else
4b61c875 867 fputs_unlocked(p, f);
768100ef 868
4b61c875 869 fputc_unlocked('\n', f);
768100ef
LP
870}
871
a5c32cff 872int write_env_file(const char *fname, char **l) {
7fd1b19b 873 _cleanup_fclose_ FILE *f = NULL;
736937e5
LP
874 _cleanup_free_ char *p = NULL;
875 char **i;
a5c32cff
HH
876 int r;
877
736937e5
LP
878 assert(fname);
879
a5c32cff
HH
880 r = fopen_temporary(fname, &f, &p);
881 if (r < 0)
882 return r;
883
884 fchmod_umask(fileno(f), 0644);
885
768100ef
LP
886 STRV_FOREACH(i, l)
887 write_env_var(f, *i);
a5c32cff 888
736937e5
LP
889 r = fflush_and_check(f);
890 if (r >= 0) {
891 if (rename(p, fname) >= 0)
892 return 0;
a5c32cff 893
736937e5 894 r = -errno;
a5c32cff
HH
895 }
896
736937e5 897 unlink(p);
a5c32cff
HH
898 return r;
899}
68fee104
ZJS
900
901int executable_is_script(const char *path, char **interpreter) {
902 int r;
c8b32e11 903 _cleanup_free_ char *line = NULL;
68fee104
ZJS
904 int len;
905 char *ans;
906
907 assert(path);
908
909 r = read_one_line_file(path, &line);
910 if (r < 0)
911 return r;
912
913 if (!startswith(line, "#!"))
914 return 0;
915
916 ans = strstrip(line + 2);
917 len = strcspn(ans, " \t");
918
919 if (len == 0)
920 return 0;
921
922 ans = strndup(ans, len);
923 if (!ans)
924 return -ENOMEM;
925
926 *interpreter = ans;
927 return 1;
928}
69ab8088
ZJS
929
930/**
0a7b53bd 931 * Retrieve one field from a file like /proc/self/status. pattern
c4cd1d4d
AK
932 * should not include whitespace or the delimiter (':'). pattern matches only
933 * the beginning of a line. Whitespace before ':' is skipped. Whitespace and
934 * zeros after the ':' will be skipped. field must be freed afterwards.
935 * terminator specifies the terminating characters of the field value (not
936 * included in the value).
69ab8088 937 */
c4cd1d4d 938int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
69ab8088 939 _cleanup_free_ char *status = NULL;
90110825 940 char *t, *f;
69ab8088
ZJS
941 size_t len;
942 int r;
943
c4cd1d4d 944 assert(terminator);
69ab8088 945 assert(filename);
7ff7394d 946 assert(pattern);
69ab8088
ZJS
947 assert(field);
948
949 r = read_full_file(filename, &status, NULL);
950 if (r < 0)
951 return r;
952
c4cd1d4d
AK
953 t = status;
954
955 do {
956 bool pattern_ok;
957
958 do {
959 t = strstr(t, pattern);
960 if (!t)
961 return -ENOENT;
962
963 /* Check that pattern occurs in beginning of line. */
964 pattern_ok = (t == status || t[-1] == '\n');
965
966 t += strlen(pattern);
967
968 } while (!pattern_ok);
969
970 t += strspn(t, " \t");
971 if (!*t)
972 return -ENOENT;
973
974 } while (*t != ':');
975
976 t++;
69ab8088 977
4ec29144 978 if (*t) {
1e5413f7
ZJS
979 t += strspn(t, " \t");
980
981 /* Also skip zeros, because when this is used for
982 * capabilities, we don't want the zeros. This way the
983 * same capability set always maps to the same string,
984 * irrespective of the total capability set size. For
985 * other numbers it shouldn't matter. */
986 t += strspn(t, "0");
4ec29144
ZJS
987 /* Back off one char if there's nothing but whitespace
988 and zeros */
1e5413f7 989 if (!*t || isspace(*t))
313cefa1 990 t--;
4ec29144 991 }
69ab8088 992
c4cd1d4d 993 len = strcspn(t, terminator);
69ab8088 994
90110825
LP
995 f = strndup(t, len);
996 if (!f)
69ab8088
ZJS
997 return -ENOMEM;
998
90110825 999 *field = f;
69ab8088
ZJS
1000 return 0;
1001}
0d39fa9c
LP
1002
1003DIR *xopendirat(int fd, const char *name, int flags) {
1004 int nfd;
1005 DIR *d;
1006
1007 assert(!(flags & O_CREAT));
1008
1009 nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags, 0);
1010 if (nfd < 0)
1011 return NULL;
1012
1013 d = fdopendir(nfd);
1014 if (!d) {
1015 safe_close(nfd);
1016 return NULL;
1017 }
1018
1019 return d;
1020}
1021
1022static int search_and_fopen_internal(const char *path, const char *mode, const char *root, char **search, FILE **_f) {
1023 char **i;
1024
1025 assert(path);
1026 assert(mode);
1027 assert(_f);
1028
1029 if (!path_strv_resolve_uniq(search, root))
1030 return -ENOMEM;
1031
1032 STRV_FOREACH(i, search) {
1033 _cleanup_free_ char *p = NULL;
1034 FILE *f;
1035
1036 if (root)
605405c6 1037 p = strjoin(root, *i, "/", path);
0d39fa9c 1038 else
605405c6 1039 p = strjoin(*i, "/", path);
0d39fa9c
LP
1040 if (!p)
1041 return -ENOMEM;
1042
1043 f = fopen(p, mode);
1044 if (f) {
1045 *_f = f;
1046 return 0;
1047 }
1048
1049 if (errno != ENOENT)
1050 return -errno;
1051 }
1052
1053 return -ENOENT;
1054}
1055
1056int search_and_fopen(const char *path, const char *mode, const char *root, const char **search, FILE **_f) {
1057 _cleanup_strv_free_ char **copy = NULL;
1058
1059 assert(path);
1060 assert(mode);
1061 assert(_f);
1062
1063 if (path_is_absolute(path)) {
1064 FILE *f;
1065
1066 f = fopen(path, mode);
1067 if (f) {
1068 *_f = f;
1069 return 0;
1070 }
1071
1072 return -errno;
1073 }
1074
1075 copy = strv_copy((char**) search);
1076 if (!copy)
1077 return -ENOMEM;
1078
1079 return search_and_fopen_internal(path, mode, root, copy, _f);
1080}
1081
1082int search_and_fopen_nulstr(const char *path, const char *mode, const char *root, const char *search, FILE **_f) {
1083 _cleanup_strv_free_ char **s = NULL;
1084
1085 if (path_is_absolute(path)) {
1086 FILE *f;
1087
1088 f = fopen(path, mode);
1089 if (f) {
1090 *_f = f;
1091 return 0;
1092 }
1093
1094 return -errno;
1095 }
1096
1097 s = strv_split_nulstr(search);
1098 if (!s)
1099 return -ENOMEM;
1100
1101 return search_and_fopen_internal(path, mode, root, s, _f);
1102}
1103
1104int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
1105 FILE *f;
1106 char *t;
1107 int r, fd;
1108
1109 assert(path);
1110 assert(_f);
1111 assert(_temp_path);
1112
1113 r = tempfn_xxxxxx(path, NULL, &t);
1114 if (r < 0)
1115 return r;
1116
646853bd 1117 fd = mkostemp_safe(t);
0d39fa9c
LP
1118 if (fd < 0) {
1119 free(t);
1120 return -errno;
1121 }
1122
1123 f = fdopen(fd, "we");
1124 if (!f) {
1125 unlink_noerrno(t);
1126 free(t);
1127 safe_close(fd);
1128 return -errno;
1129 }
1130
1131 *_f = f;
1132 *_temp_path = t;
1133
1134 return 0;
1135}
1136
1137int fflush_and_check(FILE *f) {
1138 assert(f);
1139
1140 errno = 0;
1141 fflush(f);
1142
1143 if (ferror(f))
f5e5c28f 1144 return errno > 0 ? -errno : -EIO;
0d39fa9c
LP
1145
1146 return 0;
1147}
1148
0675e94a
AJ
1149int fflush_sync_and_check(FILE *f) {
1150 int r;
1151
1152 assert(f);
1153
1154 r = fflush_and_check(f);
1155 if (r < 0)
1156 return r;
1157
1158 if (fsync(fileno(f)) < 0)
1159 return -errno;
1160
1161 return 0;
1162}
1163
61233823 1164/* This is much like mkostemp() but is subject to umask(). */
646853bd 1165int mkostemp_safe(char *pattern) {
3587161a 1166 _cleanup_umask_ mode_t u = 0;
0d39fa9c
LP
1167 int fd;
1168
1169 assert(pattern);
1170
1171 u = umask(077);
1172
646853bd 1173 fd = mkostemp(pattern, O_CLOEXEC);
0d39fa9c
LP
1174 if (fd < 0)
1175 return -errno;
1176
1177 return fd;
1178}
1179
0d39fa9c
LP
1180int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
1181 const char *fn;
1182 char *t;
1183
1184 assert(p);
1185 assert(ret);
1186
1187 /*
1188 * Turns this:
1189 * /foo/bar/waldo
1190 *
1191 * Into this:
1192 * /foo/bar/.#<extra>waldoXXXXXX
1193 */
1194
1195 fn = basename(p);
1196 if (!filename_is_valid(fn))
1197 return -EINVAL;
1198
1199 if (extra == NULL)
1200 extra = "";
1201
1202 t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
1203 if (!t)
1204 return -ENOMEM;
1205
1206 strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
1207
1208 *ret = path_kill_slashes(t);
1209 return 0;
1210}
1211
1212int tempfn_random(const char *p, const char *extra, char **ret) {
1213 const char *fn;
1214 char *t, *x;
1215 uint64_t u;
1216 unsigned i;
1217
1218 assert(p);
1219 assert(ret);
1220
1221 /*
1222 * Turns this:
1223 * /foo/bar/waldo
1224 *
1225 * Into this:
1226 * /foo/bar/.#<extra>waldobaa2a261115984a9
1227 */
1228
1229 fn = basename(p);
1230 if (!filename_is_valid(fn))
1231 return -EINVAL;
1232
1233 if (!extra)
1234 extra = "";
1235
1236 t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
1237 if (!t)
1238 return -ENOMEM;
1239
1240 x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
1241
1242 u = random_u64();
1243 for (i = 0; i < 16; i++) {
1244 *(x++) = hexchar(u & 0xF);
1245 u >>= 4;
1246 }
1247
1248 *x = 0;
1249
1250 *ret = path_kill_slashes(t);
1251 return 0;
1252}
1253
1254int tempfn_random_child(const char *p, const char *extra, char **ret) {
1255 char *t, *x;
1256 uint64_t u;
1257 unsigned i;
992e8f22 1258 int r;
0d39fa9c 1259
0d39fa9c
LP
1260 assert(ret);
1261
1262 /* Turns this:
1263 * /foo/bar/waldo
1264 * Into this:
1265 * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
1266 */
1267
992e8f22
LP
1268 if (!p) {
1269 r = tmp_dir(&p);
1270 if (r < 0)
1271 return r;
1272 }
1273
0d39fa9c
LP
1274 if (!extra)
1275 extra = "";
1276
1277 t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
1278 if (!t)
1279 return -ENOMEM;
1280
1281 x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
1282
1283 u = random_u64();
1284 for (i = 0; i < 16; i++) {
1285 *(x++) = hexchar(u & 0xF);
1286 u >>= 4;
1287 }
1288
1289 *x = 0;
1290
1291 *ret = path_kill_slashes(t);
1292 return 0;
1293}
33d52ab9
LP
1294
1295int write_timestamp_file_atomic(const char *fn, usec_t n) {
1296 char ln[DECIMAL_STR_MAX(n)+2];
1297
1298 /* Creates a "timestamp" file, that contains nothing but a
1299 * usec_t timestamp, formatted in ASCII. */
1300
1301 if (n <= 0 || n >= USEC_INFINITY)
1302 return -ERANGE;
1303
1304 xsprintf(ln, USEC_FMT "\n", n);
1305
1306 return write_string_file(fn, ln, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC);
1307}
1308
1309int read_timestamp_file(const char *fn, usec_t *ret) {
1310 _cleanup_free_ char *ln = NULL;
1311 uint64_t t;
1312 int r;
1313
1314 r = read_one_line_file(fn, &ln);
1315 if (r < 0)
1316 return r;
1317
1318 r = safe_atou64(ln, &t);
1319 if (r < 0)
1320 return r;
1321
1322 if (t <= 0 || t >= (uint64_t) USEC_INFINITY)
1323 return -ERANGE;
1324
1325 *ret = (usec_t) t;
1326 return 0;
1327}
d390f8ef
LP
1328
1329int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) {
1330 int r;
1331
1332 assert(s);
1333
1334 /* Outputs the specified string with fputs(), but optionally prefixes it with a separator. The *space parameter
1335 * when specified shall initially point to a boolean variable initialized to false. It is set to true after the
1336 * first invocation. This call is supposed to be use in loops, where a separator shall be inserted between each
1337 * element, but not before the first one. */
1338
1339 if (!f)
1340 f = stdout;
1341
1342 if (space) {
1343 if (!separator)
1344 separator = " ";
1345
1346 if (*space) {
1347 r = fputs(separator, f);
1348 if (r < 0)
1349 return r;
1350 }
1351
1352 *space = true;
1353 }
1354
1355 return fputs(s, f);
1356}
03532f0a
LP
1357
1358int open_tmpfile_unlinkable(const char *directory, int flags) {
1359 char *p;
992e8f22 1360 int fd, r;
03532f0a 1361
992e8f22
LP
1362 if (!directory) {
1363 r = tmp_dir(&directory);
1364 if (r < 0)
1365 return r;
1366 }
03532f0a
LP
1367
1368 /* Returns an unlinked temporary file that cannot be linked into the file system anymore */
1369
03532f0a
LP
1370 /* Try O_TMPFILE first, if it is supported */
1371 fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
1372 if (fd >= 0)
1373 return fd;
03532f0a
LP
1374
1375 /* Fall back to unguessable name + unlinking */
1376 p = strjoina(directory, "/systemd-tmp-XXXXXX");
1377
646853bd 1378 fd = mkostemp_safe(p);
03532f0a
LP
1379 if (fd < 0)
1380 return fd;
1381
1382 (void) unlink(p);
1383
1384 return fd;
1385}
1386
1387int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
1388 _cleanup_free_ char *tmp = NULL;
1389 int r, fd;
1390
1391 assert(target);
1392 assert(ret_path);
1393
1394 /* Don't allow O_EXCL, as that has a special meaning for O_TMPFILE */
1395 assert((flags & O_EXCL) == 0);
1396
1397 /* Creates a temporary file, that shall be renamed to "target" later. If possible, this uses O_TMPFILE – in
1398 * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
1399 * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
1400
03532f0a
LP
1401 {
1402 _cleanup_free_ char *dn = NULL;
1403
1404 dn = dirname_malloc(target);
1405 if (!dn)
1406 return -ENOMEM;
1407
1408 fd = open(dn, O_TMPFILE|flags, 0640);
1409 if (fd >= 0) {
1410 *ret_path = NULL;
1411 return fd;
1412 }
1413
1414 log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
1415 }
03532f0a
LP
1416
1417 r = tempfn_random(target, NULL, &tmp);
1418 if (r < 0)
1419 return r;
1420
1421 fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|flags, 0640);
1422 if (fd < 0)
1423 return -errno;
1424
1425 *ret_path = tmp;
1426 tmp = NULL;
1427
1428 return fd;
1429}
1430
504afd7c
ZJS
1431int open_serialization_fd(const char *ident) {
1432 int fd = -1;
1433
1434 fd = memfd_create(ident, MFD_CLOEXEC);
1435 if (fd < 0) {
1436 const char *path;
1437
df0ff127 1438 path = getpid_cached() == 1 ? "/run/systemd" : "/tmp";
504afd7c
ZJS
1439 fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
1440 if (fd < 0)
1441 return fd;
1442
1443 log_debug("Serializing %s to %s.", ident, path);
1444 } else
1445 log_debug("Serializing %s to memfd.", ident);
1446
1447 return fd;
1448}
1449
03532f0a
LP
1450int link_tmpfile(int fd, const char *path, const char *target) {
1451
1452 assert(fd >= 0);
1453 assert(target);
1454
1455 /* Moves a temporary file created with open_tmpfile() above into its final place. if "path" is NULL an fd
1456 * created with O_TMPFILE is assumed, and linkat() is used. Otherwise it is assumed O_TMPFILE is not supported
1457 * on the directory, and renameat2() is used instead.
1458 *
f8e2f4d6 1459 * Note that in both cases we will not replace existing files. This is because linkat() does not support this
03532f0a
LP
1460 * operation currently (renameat2() does), and there is no nice way to emulate this. */
1461
1462 if (path) {
1463 if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0)
1464 return -errno;
1465 } else {
1466 char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
1467
1468 xsprintf(proc_fd_path, "/proc/self/fd/%i", fd);
1469
1470 if (linkat(AT_FDCWD, proc_fd_path, AT_FDCWD, target, AT_SYMLINK_FOLLOW) < 0)
1471 return -errno;
1472 }
1473
1474 return 0;
1475}
03c2b288
LP
1476
1477int read_nul_string(FILE *f, char **ret) {
1478 _cleanup_free_ char *x = NULL;
1479 size_t allocated = 0, n = 0;
1480
1481 assert(f);
1482 assert(ret);
1483
1484 /* Reads a NUL-terminated string from the specified file. */
1485
1486 for (;;) {
1487 int c;
1488
1489 if (!GREEDY_REALLOC(x, allocated, n+2))
1490 return -ENOMEM;
1491
1492 c = fgetc(f);
1493 if (c == 0) /* Terminate at NUL byte */
1494 break;
1495 if (c == EOF) {
1496 if (ferror(f))
1497 return -errno;
1498 break; /* Terminate at EOF */
1499 }
1500
1501 x[n++] = (char) c;
1502 }
1503
1504 if (x)
1505 x[n] = 0;
1506 else {
1507 x = new0(char, 1);
1508 if (!x)
1509 return -ENOMEM;
1510 }
1511
1512 *ret = x;
1513 x = NULL;
1514
1515 return 0;
1516}
676bcb0f
LP
1517
1518int mkdtemp_malloc(const char *template, char **ret) {
1519 char *p;
1520
1521 assert(template);
1522 assert(ret);
1523
1524 p = strdup(template);
1525 if (!p)
1526 return -ENOMEM;
1527
1528 if (!mkdtemp(p)) {
1529 free(p);
1530 return -errno;
1531 }
1532
1533 *ret = p;
1534 return 0;
1535}