]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/fileio.c
Merge pull request #678 from eworm-de/oracle-kvm
[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
a5c32cff
HH
24#include "util.h"
25#include "strv.h"
335c46b5 26#include "utf8.h"
1e5413f7 27#include "ctype.h"
cda134ab 28#include "fileio.h"
a5c32cff 29
40beecdb 30int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
717603e3
LP
31 assert(f);
32 assert(line);
33
a5c32cff 34 errno = 0;
717603e3 35
fec6fc6b 36 fputs(line, f);
40beecdb 37 if (enforce_newline && !endswith(line, "\n"))
a5c32cff
HH
38 fputc('\n', f);
39
40 fflush(f);
41
42 if (ferror(f))
43 return errno ? -errno : -EIO;
44
45 return 0;
46}
47
4c1fc3e4 48static int write_string_file_atomic(const char *fn, const char *line, bool enforce_newline) {
a5c32cff
HH
49 _cleanup_fclose_ FILE *f = NULL;
50 _cleanup_free_ char *p = NULL;
51 int r;
52
53 assert(fn);
54 assert(line);
55
56 r = fopen_temporary(fn, &f, &p);
57 if (r < 0)
58 return r;
59
60 fchmod_umask(fileno(f), 0644);
61
4c1fc3e4 62 r = write_string_stream(f, line, enforce_newline);
f2997962 63 if (r >= 0) {
a5c32cff
HH
64 if (rename(p, fn) < 0)
65 r = -errno;
a5c32cff
HH
66 }
67
a5c32cff
HH
68 if (r < 0)
69 unlink(p);
70
71 return r;
72}
73
4c1fc3e4
DM
74int write_string_file(const char *fn, const char *line, WriteStringFileFlags flags) {
75 _cleanup_fclose_ FILE *f = NULL;
76
77 assert(fn);
78 assert(line);
79
80 if (flags & WRITE_STRING_FILE_ATOMIC) {
81 assert(flags & WRITE_STRING_FILE_CREATE);
82
83 return write_string_file_atomic(fn, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
84 }
85
86 if (flags & WRITE_STRING_FILE_CREATE) {
87 f = fopen(fn, "we");
88 if (!f)
89 return -errno;
90 } else {
91 int fd;
92
93 /* We manually build our own version of fopen(..., "we") that
94 * works without O_CREAT */
95 fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY);
96 if (fd < 0)
97 return -errno;
98
99 f = fdopen(fd, "we");
100 if (!f) {
101 safe_close(fd);
102 return -errno;
103 }
104 }
105
106 return write_string_stream(f, line, !(flags & WRITE_STRING_FILE_AVOID_NEWLINE));
107}
108
a5c32cff
HH
109int read_one_line_file(const char *fn, char **line) {
110 _cleanup_fclose_ FILE *f = NULL;
111 char t[LINE_MAX], *c;
112
113 assert(fn);
114 assert(line);
115
116 f = fopen(fn, "re");
117 if (!f)
118 return -errno;
119
120 if (!fgets(t, sizeof(t), f)) {
121
122 if (ferror(f))
123 return errno ? -errno : -EIO;
124
125 t[0] = 0;
126 }
127
128 c = strdup(t);
129 if (!c)
130 return -ENOMEM;
131 truncate_nl(c);
132
133 *line = c;
134 return 0;
135}
136
15dee3f0
LP
137int verify_one_line_file(const char *fn, const char *line) {
138 _cleanup_free_ char *value = NULL;
139 int r;
140
141 r = read_one_line_file(fn, &value);
142 if (r < 0)
143 return r;
144
145 return streq(value, line);
146}
147
717603e3 148int read_full_stream(FILE *f, char **contents, size_t *size) {
a5c32cff
HH
149 size_t n, l;
150 _cleanup_free_ char *buf = NULL;
151 struct stat st;
152
717603e3 153 assert(f);
a5c32cff
HH
154 assert(contents);
155
a5c32cff
HH
156 if (fstat(fileno(f), &st) < 0)
157 return -errno;
158
717603e3 159 n = LINE_MAX;
a5c32cff 160
717603e3
LP
161 if (S_ISREG(st.st_mode)) {
162
163 /* Safety check */
164 if (st.st_size > 4*1024*1024)
165 return -E2BIG;
166
167 /* Start with the right file size, but be prepared for
168 * files from /proc which generally report a file size
169 * of 0 */
170 if (st.st_size > 0)
171 n = st.st_size;
172 }
a5c32cff 173
717603e3 174 l = 0;
a5c32cff
HH
175 for (;;) {
176 char *t;
177 size_t k;
178
179 t = realloc(buf, n+1);
180 if (!t)
181 return -ENOMEM;
182
183 buf = t;
184 k = fread(buf + l, 1, n - l, f);
185
186 if (k <= 0) {
187 if (ferror(f))
188 return -errno;
189
190 break;
191 }
192
193 l += k;
194 n *= 2;
195
196 /* Safety check */
197 if (n > 4*1024*1024)
198 return -E2BIG;
199 }
200
201 buf[l] = 0;
202 *contents = buf;
d78a28e3 203 buf = NULL; /* do not free */
a5c32cff
HH
204
205 if (size)
206 *size = l;
207
208 return 0;
209}
210
717603e3
LP
211int read_full_file(const char *fn, char **contents, size_t *size) {
212 _cleanup_fclose_ FILE *f = NULL;
213
214 assert(fn);
215 assert(contents);
216
217 f = fopen(fn, "re");
218 if (!f)
219 return -errno;
220
221 return read_full_stream(f, contents, size);
222}
223
f73141d7 224static int parse_env_file_internal(
717603e3 225 FILE *f,
a5c32cff 226 const char *fname,
f73141d7 227 const char *newline,
335c46b5 228 int (*push) (const char *filename, unsigned line,
a5f6c30d
MS
229 const char *key, char *value, void *userdata, int *n_pushed),
230 void *userdata,
231 int *n_pushed) {
f73141d7
LP
232
233 _cleanup_free_ char *contents = NULL, *key = NULL;
2b77f67e 234 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
235 char *p, *value = NULL;
236 int r;
335c46b5 237 unsigned line = 1;
a5c32cff 238
f73141d7
LP
239 enum {
240 PRE_KEY,
241 KEY,
f73141d7
LP
242 PRE_VALUE,
243 VALUE,
244 VALUE_ESCAPE,
245 SINGLE_QUOTE_VALUE,
246 SINGLE_QUOTE_VALUE_ESCAPE,
247 DOUBLE_QUOTE_VALUE,
248 DOUBLE_QUOTE_VALUE_ESCAPE,
249 COMMENT,
250 COMMENT_ESCAPE
251 } state = PRE_KEY;
a5c32cff 252
f73141d7 253 assert(newline);
a5c32cff 254
717603e3
LP
255 if (f)
256 r = read_full_stream(f, &contents, NULL);
257 else
258 r = read_full_file(fname, &contents, NULL);
1c17cbed 259 if (r < 0)
a5c32cff
HH
260 return r;
261
f73141d7
LP
262 for (p = contents; *p; p++) {
263 char c = *p;
264
265 switch (state) {
266
267 case PRE_KEY:
ebc05a09 268 if (strchr(COMMENTS, c))
f73141d7
LP
269 state = COMMENT;
270 else if (!strchr(WHITESPACE, c)) {
271 state = KEY;
2b77f67e
LP
272 last_key_whitespace = (size_t) -1;
273
ca2d3784 274 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
f73141d7
LP
275 r = -ENOMEM;
276 goto fail;
277 }
278
279 key[n_key++] = c;
280 }
281 break;
282
283 case KEY:
284 if (strchr(newline, c)) {
285 state = PRE_KEY;
335c46b5 286 line ++;
f73141d7 287 n_key = 0;
2b77f67e 288 } else if (c == '=') {
f73141d7 289 state = PRE_VALUE;
2b77f67e
LP
290 last_value_whitespace = (size_t) -1;
291 } else {
292 if (!strchr(WHITESPACE, c))
293 last_key_whitespace = (size_t) -1;
294 else if (last_key_whitespace == (size_t) -1)
295 last_key_whitespace = n_key;
296
ca2d3784 297 if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) {
f73141d7
LP
298 r = -ENOMEM;
299 goto fail;
300 }
301
302 key[n_key++] = c;
303 }
a5c32cff 304
f73141d7
LP
305 break;
306
f73141d7 307 case PRE_VALUE:
98f59e59 308 if (strchr(newline, c)) {
f73141d7 309 state = PRE_KEY;
335c46b5 310 line ++;
f73141d7 311 key[n_key] = 0;
a5c32cff 312
f73141d7
LP
313 if (value)
314 value[n_value] = 0;
a5c32cff 315
ebc05a09 316 /* strip trailing whitespace from key */
2b77f67e
LP
317 if (last_key_whitespace != (size_t) -1)
318 key[last_key_whitespace] = 0;
ebc05a09 319
a5f6c30d 320 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
321 if (r < 0)
322 goto fail;
323
324 n_key = 0;
325 value = NULL;
326 value_alloc = n_value = 0;
2b77f67e 327
f73141d7
LP
328 } else if (c == '\'')
329 state = SINGLE_QUOTE_VALUE;
330 else if (c == '\"')
331 state = DOUBLE_QUOTE_VALUE;
332 else if (c == '\\')
333 state = VALUE_ESCAPE;
334 else if (!strchr(WHITESPACE, c)) {
335 state = VALUE;
336
ca2d3784 337 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
338 r = -ENOMEM;
339 goto fail;
340 }
341
342 value[n_value++] = c;
343 }
344
345 break;
346
347 case VALUE:
348 if (strchr(newline, c)) {
349 state = PRE_KEY;
335c46b5 350 line ++;
98f59e59 351
f73141d7
LP
352 key[n_key] = 0;
353
354 if (value)
355 value[n_value] = 0;
356
2b77f67e
LP
357 /* Chomp off trailing whitespace from value */
358 if (last_value_whitespace != (size_t) -1)
359 value[last_value_whitespace] = 0;
f73141d7 360
ebc05a09 361 /* strip trailing whitespace from key */
2b77f67e
LP
362 if (last_key_whitespace != (size_t) -1)
363 key[last_key_whitespace] = 0;
ebc05a09 364
a5f6c30d 365 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
366 if (r < 0)
367 goto fail;
368
369 n_key = 0;
370 value = NULL;
371 value_alloc = n_value = 0;
2b77f67e 372
f73141d7
LP
373 } else if (c == '\\') {
374 state = VALUE_ESCAPE;
2b77f67e 375 last_value_whitespace = (size_t) -1;
f73141d7
LP
376 } else {
377 if (!strchr(WHITESPACE, c))
2b77f67e
LP
378 last_value_whitespace = (size_t) -1;
379 else if (last_value_whitespace == (size_t) -1)
380 last_value_whitespace = n_value;
f73141d7 381
ca2d3784 382 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
383 r = -ENOMEM;
384 goto fail;
385 }
386
387 value[n_value++] = c;
388 }
389
390 break;
391
392 case VALUE_ESCAPE:
393 state = VALUE;
394
395 if (!strchr(newline, c)) {
396 /* Escaped newlines we eat up entirely */
ca2d3784 397 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
398 r = -ENOMEM;
399 goto fail;
400 }
401
402 value[n_value++] = c;
403 }
404 break;
405
406 case SINGLE_QUOTE_VALUE:
407 if (c == '\'')
408 state = PRE_VALUE;
409 else if (c == '\\')
410 state = SINGLE_QUOTE_VALUE_ESCAPE;
411 else {
ca2d3784 412 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
413 r = -ENOMEM;
414 goto fail;
415 }
a5c32cff 416
f73141d7
LP
417 value[n_value++] = c;
418 }
a5c32cff 419
f73141d7 420 break;
a5c32cff 421
f73141d7
LP
422 case SINGLE_QUOTE_VALUE_ESCAPE:
423 state = SINGLE_QUOTE_VALUE;
a5c32cff 424
f73141d7 425 if (!strchr(newline, c)) {
ca2d3784 426 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
a5c32cff 427 r = -ENOMEM;
a5c32cff
HH
428 goto fail;
429 }
430
f73141d7
LP
431 value[n_value++] = c;
432 }
433 break;
434
435 case DOUBLE_QUOTE_VALUE:
436 if (c == '\"')
437 state = PRE_VALUE;
438 else if (c == '\\')
439 state = DOUBLE_QUOTE_VALUE_ESCAPE;
440 else {
ca2d3784 441 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
442 r = -ENOMEM;
443 goto fail;
a5c32cff
HH
444 }
445
f73141d7
LP
446 value[n_value++] = c;
447 }
448
449 break;
450
451 case DOUBLE_QUOTE_VALUE_ESCAPE:
452 state = DOUBLE_QUOTE_VALUE;
a5c32cff 453
f73141d7 454 if (!strchr(newline, c)) {
ca2d3784 455 if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) {
f73141d7
LP
456 r = -ENOMEM;
457 goto fail;
458 }
a5c32cff 459
f73141d7 460 value[n_value++] = c;
a5c32cff 461 }
f73141d7
LP
462 break;
463
464 case COMMENT:
465 if (c == '\\')
466 state = COMMENT_ESCAPE;
335c46b5 467 else if (strchr(newline, c)) {
f73141d7 468 state = PRE_KEY;
335c46b5
ZJS
469 line ++;
470 }
f73141d7
LP
471 break;
472
473 case COMMENT_ESCAPE:
474 state = COMMENT;
475 break;
a5c32cff 476 }
f73141d7
LP
477 }
478
479 if (state == PRE_VALUE ||
480 state == VALUE ||
481 state == VALUE_ESCAPE ||
482 state == SINGLE_QUOTE_VALUE ||
483 state == SINGLE_QUOTE_VALUE_ESCAPE ||
484 state == DOUBLE_QUOTE_VALUE ||
485 state == DOUBLE_QUOTE_VALUE_ESCAPE) {
a5c32cff 486
f73141d7
LP
487 key[n_key] = 0;
488
489 if (value)
490 value[n_value] = 0;
491
2b77f67e
LP
492 if (state == VALUE)
493 if (last_value_whitespace != (size_t) -1)
494 value[last_value_whitespace] = 0;
495
ebc05a09 496 /* strip trailing whitespace from key */
2b77f67e
LP
497 if (last_key_whitespace != (size_t) -1)
498 key[last_key_whitespace] = 0;
ebc05a09 499
a5f6c30d 500 r = push(fname, line, key, value, userdata, n_pushed);
f73141d7
LP
501 if (r < 0)
502 goto fail;
a5c32cff
HH
503 }
504
f73141d7
LP
505 return 0;
506
a5c32cff 507fail:
f73141d7 508 free(value);
a5c32cff
HH
509 return r;
510}
511
717603e3
LP
512static int parse_env_file_push(
513 const char *filename, unsigned line,
514 const char *key, char *value,
a5f6c30d
MS
515 void *userdata,
516 int *n_pushed) {
335c46b5 517
f27f0e21
GB
518 const char *k;
519 va_list aq, *ap = userdata;
520
521 if (!utf8_is_valid(key)) {
717603e3 522 _cleanup_free_ char *p;
550a40ec 523
717603e3
LP
524 p = utf8_escape_invalid(key);
525 log_error("%s:%u: invalid UTF-8 in key '%s', ignoring.", strna(filename), line, p);
f27f0e21
GB
526 return -EINVAL;
527 }
528
529 if (value && !utf8_is_valid(value)) {
717603e3 530 _cleanup_free_ char *p;
550a40ec 531
717603e3
LP
532 p = utf8_escape_invalid(value);
533 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, p);
f27f0e21
GB
534 return -EINVAL;
535 }
a5c32cff 536
f27f0e21 537 va_copy(aq, *ap);
a5c32cff 538
f27f0e21
GB
539 while ((k = va_arg(aq, const char *))) {
540 char **v;
a5c32cff 541
f27f0e21 542 v = va_arg(aq, char **);
a5c32cff 543
f27f0e21
GB
544 if (streq(key, k)) {
545 va_end(aq);
546 free(*v);
547 *v = value;
a5f6c30d
MS
548
549 if (n_pushed)
550 (*n_pushed)++;
551
f27f0e21 552 return 1;
f73141d7 553 }
335c46b5 554 }
a5c32cff 555
f27f0e21 556 va_end(aq);
f73141d7 557 free(value);
717603e3 558
f73141d7
LP
559 return 0;
560}
a5c32cff 561
f73141d7
LP
562int parse_env_file(
563 const char *fname,
564 const char *newline, ...) {
a5c32cff 565
f73141d7 566 va_list ap;
a5f6c30d 567 int r, n_pushed = 0;
a5c32cff 568
f73141d7
LP
569 if (!newline)
570 newline = NEWLINE;
a5c32cff 571
f73141d7 572 va_start(ap, newline);
a5f6c30d 573 r = parse_env_file_internal(NULL, fname, newline, parse_env_file_push, &ap, &n_pushed);
f73141d7 574 va_end(ap);
a5c32cff 575
a5f6c30d 576 return r < 0 ? r : n_pushed;
f73141d7 577}
a5c32cff 578
717603e3
LP
579static int load_env_file_push(
580 const char *filename, unsigned line,
581 const char *key, char *value,
a5f6c30d
MS
582 void *userdata,
583 int *n_pushed) {
f27f0e21
GB
584 char ***m = userdata;
585 char *p;
586 int r;
a5c32cff 587
f27f0e21 588 if (!utf8_is_valid(key)) {
b5d74213
ZJS
589 _cleanup_free_ char *t = utf8_escape_invalid(key);
590
717603e3 591 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
f27f0e21
GB
592 return -EINVAL;
593 }
594
595 if (value && !utf8_is_valid(value)) {
b5d74213
ZJS
596 _cleanup_free_ char *t = utf8_escape_invalid(value);
597
717603e3 598 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
f27f0e21
GB
599 return -EINVAL;
600 }
a5c32cff 601
f27f0e21
GB
602 p = strjoin(key, "=", strempty(value), NULL);
603 if (!p)
604 return -ENOMEM;
335c46b5 605
6e18964d
ZJS
606 r = strv_consume(m, p);
607 if (r < 0)
f27f0e21 608 return r;
a5c32cff 609
a5f6c30d
MS
610 if (n_pushed)
611 (*n_pushed)++;
612
f73141d7
LP
613 free(value);
614 return 0;
615}
616
717603e3
LP
617int load_env_file(FILE *f, const char *fname, const char *newline, char ***rl) {
618 char **m = NULL;
619 int r;
620
621 if (!newline)
622 newline = NEWLINE;
623
a5f6c30d 624 r = parse_env_file_internal(f, fname, newline, load_env_file_push, &m, NULL);
717603e3
LP
625 if (r < 0) {
626 strv_free(m);
627 return r;
628 }
629
630 *rl = m;
631 return 0;
632}
633
634static int load_env_file_push_pairs(
635 const char *filename, unsigned line,
636 const char *key, char *value,
a5f6c30d
MS
637 void *userdata,
638 int *n_pushed) {
717603e3
LP
639 char ***m = userdata;
640 int r;
641
642 if (!utf8_is_valid(key)) {
643 _cleanup_free_ char *t = utf8_escape_invalid(key);
644
645 log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
646 return -EINVAL;
647 }
648
649 if (value && !utf8_is_valid(value)) {
650 _cleanup_free_ char *t = utf8_escape_invalid(value);
651
652 log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
653 return -EINVAL;
654 }
655
656 r = strv_extend(m, key);
657 if (r < 0)
658 return -ENOMEM;
659
660 if (!value) {
661 r = strv_extend(m, "");
662 if (r < 0)
663 return -ENOMEM;
664 } else {
665 r = strv_push(m, value);
666 if (r < 0)
667 return r;
668 }
669
a5f6c30d
MS
670 if (n_pushed)
671 (*n_pushed)++;
672
717603e3
LP
673 return 0;
674}
675
676int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char ***rl) {
f73141d7
LP
677 char **m = NULL;
678 int r;
a5c32cff 679
f73141d7
LP
680 if (!newline)
681 newline = NEWLINE;
682
a5f6c30d 683 r = parse_env_file_internal(f, fname, newline, load_env_file_push_pairs, &m, NULL);
f73141d7
LP
684 if (r < 0) {
685 strv_free(m);
686 return r;
a5c32cff
HH
687 }
688
689 *rl = m;
a5c32cff
HH
690 return 0;
691}
692
768100ef
LP
693static void write_env_var(FILE *f, const char *v) {
694 const char *p;
695
696 p = strchr(v, '=');
697 if (!p) {
698 /* Fallback */
699 fputs(v, f);
700 fputc('\n', f);
701 return;
702 }
703
704 p++;
705 fwrite(v, 1, p-v, f);
706
0ce5a806 707 if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
768100ef
LP
708 fputc('\"', f);
709
710 for (; *p; p++) {
0ce5a806 711 if (strchr(SHELL_NEED_ESCAPE, *p))
768100ef
LP
712 fputc('\\', f);
713
714 fputc(*p, f);
715 }
716
717 fputc('\"', f);
718 } else
719 fputs(p, f);
720
721 fputc('\n', f);
722}
723
a5c32cff 724int write_env_file(const char *fname, char **l) {
7fd1b19b 725 _cleanup_fclose_ FILE *f = NULL;
736937e5
LP
726 _cleanup_free_ char *p = NULL;
727 char **i;
a5c32cff
HH
728 int r;
729
736937e5
LP
730 assert(fname);
731
a5c32cff
HH
732 r = fopen_temporary(fname, &f, &p);
733 if (r < 0)
734 return r;
735
736 fchmod_umask(fileno(f), 0644);
737
768100ef
LP
738 STRV_FOREACH(i, l)
739 write_env_var(f, *i);
a5c32cff 740
736937e5
LP
741 r = fflush_and_check(f);
742 if (r >= 0) {
743 if (rename(p, fname) >= 0)
744 return 0;
a5c32cff 745
736937e5 746 r = -errno;
a5c32cff
HH
747 }
748
736937e5 749 unlink(p);
a5c32cff
HH
750 return r;
751}
68fee104
ZJS
752
753int executable_is_script(const char *path, char **interpreter) {
754 int r;
c8b32e11 755 _cleanup_free_ char *line = NULL;
68fee104
ZJS
756 int len;
757 char *ans;
758
759 assert(path);
760
761 r = read_one_line_file(path, &line);
762 if (r < 0)
763 return r;
764
765 if (!startswith(line, "#!"))
766 return 0;
767
768 ans = strstrip(line + 2);
769 len = strcspn(ans, " \t");
770
771 if (len == 0)
772 return 0;
773
774 ans = strndup(ans, len);
775 if (!ans)
776 return -ENOMEM;
777
778 *interpreter = ans;
779 return 1;
780}
69ab8088
ZJS
781
782/**
0a7b53bd
ZJS
783 * Retrieve one field from a file like /proc/self/status. pattern
784 * should start with '\n' and end with a ':'. Whitespace and zeros
785 * after the ':' will be skipped. field must be freed afterwards.
69ab8088
ZJS
786 */
787int get_status_field(const char *filename, const char *pattern, char **field) {
788 _cleanup_free_ char *status = NULL;
789 char *t;
790 size_t len;
791 int r;
792
793 assert(filename);
7ff7394d 794 assert(pattern);
69ab8088
ZJS
795 assert(field);
796
797 r = read_full_file(filename, &status, NULL);
798 if (r < 0)
799 return r;
800
801 t = strstr(status, pattern);
802 if (!t)
803 return -ENOENT;
804
805 t += strlen(pattern);
4ec29144 806 if (*t) {
1e5413f7
ZJS
807 t += strspn(t, " \t");
808
809 /* Also skip zeros, because when this is used for
810 * capabilities, we don't want the zeros. This way the
811 * same capability set always maps to the same string,
812 * irrespective of the total capability set size. For
813 * other numbers it shouldn't matter. */
814 t += strspn(t, "0");
4ec29144
ZJS
815 /* Back off one char if there's nothing but whitespace
816 and zeros */
1e5413f7 817 if (!*t || isspace(*t))
4ec29144
ZJS
818 t --;
819 }
69ab8088
ZJS
820
821 len = strcspn(t, WHITESPACE);
822
823 *field = strndup(t, len);
824 if (!*field)
825 return -ENOMEM;
826
827 return 0;
828}