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