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