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