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