]>
Commit | Line | Data |
---|---|---|
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> | |
23 | #include "fileio.h" | |
24 | #include "util.h" | |
25 | #include "strv.h" | |
335c46b5 | 26 | #include "utf8.h" |
a5c32cff | 27 | |
b4bc041b | 28 | int write_string_to_file(FILE *f, const char *line) { |
a5c32cff | 29 | errno = 0; |
fec6fc6b | 30 | fputs(line, f); |
a5c32cff HH |
31 | if (!endswith(line, "\n")) |
32 | fputc('\n', f); | |
33 | ||
34 | fflush(f); | |
35 | ||
36 | if (ferror(f)) | |
37 | return errno ? -errno : -EIO; | |
38 | ||
39 | return 0; | |
40 | } | |
41 | ||
b4bc041b ZJS |
42 | int write_string_file(const char *fn, const char *line) { |
43 | _cleanup_fclose_ FILE *f = NULL; | |
44 | ||
45 | assert(fn); | |
46 | assert(line); | |
47 | ||
48 | f = fopen(fn, "we"); | |
49 | if (!f) | |
50 | return -errno; | |
51 | ||
52 | return write_string_to_file(f, line); | |
53 | } | |
54 | ||
574d5f2d | 55 | int write_string_file_atomic(const char *fn, const char *line) { |
a5c32cff HH |
56 | _cleanup_fclose_ FILE *f = NULL; |
57 | _cleanup_free_ char *p = NULL; | |
58 | int r; | |
59 | ||
60 | assert(fn); | |
61 | assert(line); | |
62 | ||
63 | r = fopen_temporary(fn, &f, &p); | |
64 | if (r < 0) | |
65 | return r; | |
66 | ||
67 | fchmod_umask(fileno(f), 0644); | |
68 | ||
69 | errno = 0; | |
fec6fc6b | 70 | fputs(line, f); |
a5c32cff HH |
71 | if (!endswith(line, "\n")) |
72 | fputc('\n', f); | |
73 | ||
74 | fflush(f); | |
75 | ||
76 | if (ferror(f)) | |
77 | r = errno ? -errno : -EIO; | |
78 | else { | |
79 | if (rename(p, fn) < 0) | |
80 | r = -errno; | |
81 | else | |
82 | r = 0; | |
83 | } | |
84 | ||
a5c32cff HH |
85 | if (r < 0) |
86 | unlink(p); | |
87 | ||
88 | return r; | |
89 | } | |
90 | ||
91 | int read_one_line_file(const char *fn, char **line) { | |
92 | _cleanup_fclose_ FILE *f = NULL; | |
93 | char t[LINE_MAX], *c; | |
94 | ||
95 | assert(fn); | |
96 | assert(line); | |
97 | ||
98 | f = fopen(fn, "re"); | |
99 | if (!f) | |
100 | return -errno; | |
101 | ||
102 | if (!fgets(t, sizeof(t), f)) { | |
103 | ||
104 | if (ferror(f)) | |
105 | return errno ? -errno : -EIO; | |
106 | ||
107 | t[0] = 0; | |
108 | } | |
109 | ||
110 | c = strdup(t); | |
111 | if (!c) | |
112 | return -ENOMEM; | |
113 | truncate_nl(c); | |
114 | ||
115 | *line = c; | |
116 | return 0; | |
117 | } | |
118 | ||
119 | int read_full_file(const char *fn, char **contents, size_t *size) { | |
120 | _cleanup_fclose_ FILE *f = NULL; | |
121 | size_t n, l; | |
122 | _cleanup_free_ char *buf = NULL; | |
123 | struct stat st; | |
124 | ||
125 | assert(fn); | |
126 | assert(contents); | |
127 | ||
128 | f = fopen(fn, "re"); | |
129 | if (!f) | |
130 | return -errno; | |
131 | ||
132 | if (fstat(fileno(f), &st) < 0) | |
133 | return -errno; | |
134 | ||
135 | /* Safety check */ | |
136 | if (st.st_size > 4*1024*1024) | |
137 | return -E2BIG; | |
138 | ||
139 | n = st.st_size > 0 ? st.st_size : LINE_MAX; | |
140 | l = 0; | |
141 | ||
142 | for (;;) { | |
143 | char *t; | |
144 | size_t k; | |
145 | ||
146 | t = realloc(buf, n+1); | |
147 | if (!t) | |
148 | return -ENOMEM; | |
149 | ||
150 | buf = t; | |
151 | k = fread(buf + l, 1, n - l, f); | |
152 | ||
153 | if (k <= 0) { | |
154 | if (ferror(f)) | |
155 | return -errno; | |
156 | ||
157 | break; | |
158 | } | |
159 | ||
160 | l += k; | |
161 | n *= 2; | |
162 | ||
163 | /* Safety check */ | |
164 | if (n > 4*1024*1024) | |
165 | return -E2BIG; | |
166 | } | |
167 | ||
168 | buf[l] = 0; | |
169 | *contents = buf; | |
170 | buf = NULL; | |
171 | ||
172 | if (size) | |
173 | *size = l; | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
f73141d7 | 178 | static int parse_env_file_internal( |
a5c32cff | 179 | const char *fname, |
f73141d7 | 180 | const char *newline, |
335c46b5 ZJS |
181 | int (*push) (const char *filename, unsigned line, |
182 | const char *key, char *value, void *userdata), | |
f73141d7 LP |
183 | void *userdata) { |
184 | ||
185 | _cleanup_free_ char *contents = NULL, *key = NULL; | |
2b77f67e | 186 | 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 |
187 | char *p, *value = NULL; |
188 | int r; | |
335c46b5 | 189 | unsigned line = 1; |
a5c32cff | 190 | |
f73141d7 LP |
191 | enum { |
192 | PRE_KEY, | |
193 | KEY, | |
f73141d7 LP |
194 | PRE_VALUE, |
195 | VALUE, | |
196 | VALUE_ESCAPE, | |
197 | SINGLE_QUOTE_VALUE, | |
198 | SINGLE_QUOTE_VALUE_ESCAPE, | |
199 | DOUBLE_QUOTE_VALUE, | |
200 | DOUBLE_QUOTE_VALUE_ESCAPE, | |
201 | COMMENT, | |
202 | COMMENT_ESCAPE | |
203 | } state = PRE_KEY; | |
a5c32cff HH |
204 | |
205 | assert(fname); | |
f73141d7 | 206 | assert(newline); |
a5c32cff | 207 | |
1c17cbed ZJS |
208 | r = read_full_file(fname, &contents, NULL); |
209 | if (r < 0) | |
a5c32cff HH |
210 | return r; |
211 | ||
f73141d7 LP |
212 | for (p = contents; *p; p++) { |
213 | char c = *p; | |
214 | ||
215 | switch (state) { | |
216 | ||
217 | case PRE_KEY: | |
ebc05a09 | 218 | if (strchr(COMMENTS, c)) |
f73141d7 LP |
219 | state = COMMENT; |
220 | else if (!strchr(WHITESPACE, c)) { | |
221 | state = KEY; | |
2b77f67e LP |
222 | last_key_whitespace = (size_t) -1; |
223 | ||
f73141d7 LP |
224 | if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) { |
225 | r = -ENOMEM; | |
226 | goto fail; | |
227 | } | |
228 | ||
229 | key[n_key++] = c; | |
230 | } | |
231 | break; | |
232 | ||
233 | case KEY: | |
234 | if (strchr(newline, c)) { | |
235 | state = PRE_KEY; | |
335c46b5 | 236 | line ++; |
f73141d7 | 237 | n_key = 0; |
2b77f67e | 238 | } else if (c == '=') { |
f73141d7 | 239 | state = PRE_VALUE; |
2b77f67e LP |
240 | last_value_whitespace = (size_t) -1; |
241 | } else { | |
242 | if (!strchr(WHITESPACE, c)) | |
243 | last_key_whitespace = (size_t) -1; | |
244 | else if (last_key_whitespace == (size_t) -1) | |
245 | last_key_whitespace = n_key; | |
246 | ||
f73141d7 LP |
247 | if (!greedy_realloc((void**) &key, &key_alloc, n_key+2)) { |
248 | r = -ENOMEM; | |
249 | goto fail; | |
250 | } | |
251 | ||
252 | key[n_key++] = c; | |
253 | } | |
a5c32cff | 254 | |
f73141d7 LP |
255 | break; |
256 | ||
f73141d7 | 257 | case PRE_VALUE: |
98f59e59 | 258 | if (strchr(newline, c)) { |
f73141d7 | 259 | state = PRE_KEY; |
335c46b5 | 260 | line ++; |
f73141d7 | 261 | key[n_key] = 0; |
a5c32cff | 262 | |
f73141d7 LP |
263 | if (value) |
264 | value[n_value] = 0; | |
a5c32cff | 265 | |
ebc05a09 | 266 | /* strip trailing whitespace from key */ |
2b77f67e LP |
267 | if (last_key_whitespace != (size_t) -1) |
268 | key[last_key_whitespace] = 0; | |
ebc05a09 | 269 | |
335c46b5 | 270 | r = push(fname, line, key, value, userdata); |
f73141d7 LP |
271 | if (r < 0) |
272 | goto fail; | |
273 | ||
274 | n_key = 0; | |
275 | value = NULL; | |
276 | value_alloc = n_value = 0; | |
2b77f67e | 277 | |
f73141d7 LP |
278 | } else if (c == '\'') |
279 | state = SINGLE_QUOTE_VALUE; | |
280 | else if (c == '\"') | |
281 | state = DOUBLE_QUOTE_VALUE; | |
282 | else if (c == '\\') | |
283 | state = VALUE_ESCAPE; | |
284 | else if (!strchr(WHITESPACE, c)) { | |
285 | state = VALUE; | |
286 | ||
287 | if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { | |
288 | r = -ENOMEM; | |
289 | goto fail; | |
290 | } | |
291 | ||
292 | value[n_value++] = c; | |
293 | } | |
294 | ||
295 | break; | |
296 | ||
297 | case VALUE: | |
298 | if (strchr(newline, c)) { | |
299 | state = PRE_KEY; | |
335c46b5 | 300 | line ++; |
98f59e59 | 301 | |
f73141d7 LP |
302 | key[n_key] = 0; |
303 | ||
304 | if (value) | |
305 | value[n_value] = 0; | |
306 | ||
2b77f67e LP |
307 | /* Chomp off trailing whitespace from value */ |
308 | if (last_value_whitespace != (size_t) -1) | |
309 | value[last_value_whitespace] = 0; | |
f73141d7 | 310 | |
ebc05a09 | 311 | /* strip trailing whitespace from key */ |
2b77f67e LP |
312 | if (last_key_whitespace != (size_t) -1) |
313 | key[last_key_whitespace] = 0; | |
ebc05a09 | 314 | |
335c46b5 | 315 | r = push(fname, line, key, value, userdata); |
f73141d7 LP |
316 | if (r < 0) |
317 | goto fail; | |
318 | ||
319 | n_key = 0; | |
320 | value = NULL; | |
321 | value_alloc = n_value = 0; | |
2b77f67e | 322 | |
f73141d7 LP |
323 | } else if (c == '\\') { |
324 | state = VALUE_ESCAPE; | |
2b77f67e | 325 | last_value_whitespace = (size_t) -1; |
f73141d7 LP |
326 | } else { |
327 | if (!strchr(WHITESPACE, c)) | |
2b77f67e LP |
328 | last_value_whitespace = (size_t) -1; |
329 | else if (last_value_whitespace == (size_t) -1) | |
330 | last_value_whitespace = n_value; | |
f73141d7 LP |
331 | |
332 | if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { | |
333 | r = -ENOMEM; | |
334 | goto fail; | |
335 | } | |
336 | ||
337 | value[n_value++] = c; | |
338 | } | |
339 | ||
340 | break; | |
341 | ||
342 | case VALUE_ESCAPE: | |
343 | state = VALUE; | |
344 | ||
345 | if (!strchr(newline, c)) { | |
346 | /* Escaped newlines we eat up entirely */ | |
347 | if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { | |
348 | r = -ENOMEM; | |
349 | goto fail; | |
350 | } | |
351 | ||
352 | value[n_value++] = c; | |
353 | } | |
354 | break; | |
355 | ||
356 | case SINGLE_QUOTE_VALUE: | |
357 | if (c == '\'') | |
358 | state = PRE_VALUE; | |
359 | else if (c == '\\') | |
360 | state = SINGLE_QUOTE_VALUE_ESCAPE; | |
361 | else { | |
362 | if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { | |
363 | r = -ENOMEM; | |
364 | goto fail; | |
365 | } | |
a5c32cff | 366 | |
f73141d7 LP |
367 | value[n_value++] = c; |
368 | } | |
a5c32cff | 369 | |
f73141d7 | 370 | break; |
a5c32cff | 371 | |
f73141d7 LP |
372 | case SINGLE_QUOTE_VALUE_ESCAPE: |
373 | state = SINGLE_QUOTE_VALUE; | |
a5c32cff | 374 | |
f73141d7 LP |
375 | if (!strchr(newline, c)) { |
376 | if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { | |
a5c32cff | 377 | r = -ENOMEM; |
a5c32cff HH |
378 | goto fail; |
379 | } | |
380 | ||
f73141d7 LP |
381 | value[n_value++] = c; |
382 | } | |
383 | break; | |
384 | ||
385 | case DOUBLE_QUOTE_VALUE: | |
386 | if (c == '\"') | |
387 | state = PRE_VALUE; | |
388 | else if (c == '\\') | |
389 | state = DOUBLE_QUOTE_VALUE_ESCAPE; | |
390 | else { | |
391 | if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { | |
392 | r = -ENOMEM; | |
393 | goto fail; | |
a5c32cff HH |
394 | } |
395 | ||
f73141d7 LP |
396 | value[n_value++] = c; |
397 | } | |
398 | ||
399 | break; | |
400 | ||
401 | case DOUBLE_QUOTE_VALUE_ESCAPE: | |
402 | state = DOUBLE_QUOTE_VALUE; | |
a5c32cff | 403 | |
f73141d7 LP |
404 | if (!strchr(newline, c)) { |
405 | if (!greedy_realloc((void**) &value, &value_alloc, n_value+2)) { | |
406 | r = -ENOMEM; | |
407 | goto fail; | |
408 | } | |
a5c32cff | 409 | |
f73141d7 | 410 | value[n_value++] = c; |
a5c32cff | 411 | } |
f73141d7 LP |
412 | break; |
413 | ||
414 | case COMMENT: | |
415 | if (c == '\\') | |
416 | state = COMMENT_ESCAPE; | |
335c46b5 | 417 | else if (strchr(newline, c)) { |
f73141d7 | 418 | state = PRE_KEY; |
335c46b5 ZJS |
419 | line ++; |
420 | } | |
f73141d7 LP |
421 | break; |
422 | ||
423 | case COMMENT_ESCAPE: | |
424 | state = COMMENT; | |
425 | break; | |
a5c32cff | 426 | } |
f73141d7 LP |
427 | } |
428 | ||
429 | if (state == PRE_VALUE || | |
430 | state == VALUE || | |
431 | state == VALUE_ESCAPE || | |
432 | state == SINGLE_QUOTE_VALUE || | |
433 | state == SINGLE_QUOTE_VALUE_ESCAPE || | |
434 | state == DOUBLE_QUOTE_VALUE || | |
435 | state == DOUBLE_QUOTE_VALUE_ESCAPE) { | |
a5c32cff | 436 | |
f73141d7 LP |
437 | key[n_key] = 0; |
438 | ||
439 | if (value) | |
440 | value[n_value] = 0; | |
441 | ||
2b77f67e LP |
442 | if (state == VALUE) |
443 | if (last_value_whitespace != (size_t) -1) | |
444 | value[last_value_whitespace] = 0; | |
445 | ||
ebc05a09 | 446 | /* strip trailing whitespace from key */ |
2b77f67e LP |
447 | if (last_key_whitespace != (size_t) -1) |
448 | key[last_key_whitespace] = 0; | |
ebc05a09 | 449 | |
335c46b5 | 450 | r = push(fname, line, key, value, userdata); |
f73141d7 LP |
451 | if (r < 0) |
452 | goto fail; | |
a5c32cff HH |
453 | } |
454 | ||
f73141d7 LP |
455 | return 0; |
456 | ||
a5c32cff | 457 | fail: |
f73141d7 | 458 | free(value); |
a5c32cff HH |
459 | return r; |
460 | } | |
461 | ||
335c46b5 ZJS |
462 | static int parse_env_file_push(const char *filename, unsigned line, |
463 | const char *key, char *value, void *userdata) { | |
464 | assert(utf8_is_valid(key)); | |
465 | ||
466 | if (value && !utf8_is_valid(value)) | |
467 | /* FIXME: filter UTF-8 */ | |
468 | log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.", | |
469 | filename, line, key, value); | |
470 | else { | |
471 | const char *k; | |
472 | va_list* ap = (va_list*) userdata; | |
473 | va_list aq; | |
a5c32cff | 474 | |
335c46b5 | 475 | va_copy(aq, *ap); |
a5c32cff | 476 | |
335c46b5 ZJS |
477 | while ((k = va_arg(aq, const char *))) { |
478 | char **v; | |
a5c32cff | 479 | |
335c46b5 | 480 | v = va_arg(aq, char **); |
a5c32cff | 481 | |
335c46b5 ZJS |
482 | if (streq(key, k)) { |
483 | va_end(aq); | |
484 | free(*v); | |
485 | *v = value; | |
486 | return 1; | |
487 | } | |
f73141d7 | 488 | } |
a5c32cff | 489 | |
335c46b5 ZJS |
490 | va_end(aq); |
491 | } | |
a5c32cff | 492 | |
f73141d7 LP |
493 | free(value); |
494 | return 0; | |
495 | } | |
a5c32cff | 496 | |
f73141d7 LP |
497 | int parse_env_file( |
498 | const char *fname, | |
499 | const char *newline, ...) { | |
a5c32cff | 500 | |
f73141d7 LP |
501 | va_list ap; |
502 | int r; | |
a5c32cff | 503 | |
f73141d7 LP |
504 | if (!newline) |
505 | newline = NEWLINE; | |
a5c32cff | 506 | |
f73141d7 LP |
507 | va_start(ap, newline); |
508 | r = parse_env_file_internal(fname, newline, parse_env_file_push, &ap); | |
509 | va_end(ap); | |
a5c32cff | 510 | |
f73141d7 LP |
511 | return r; |
512 | } | |
a5c32cff | 513 | |
335c46b5 ZJS |
514 | static int load_env_file_push(const char *filename, unsigned line, |
515 | const char *key, char *value, void *userdata) { | |
516 | assert(utf8_is_valid(key)); | |
a5c32cff | 517 | |
335c46b5 ZJS |
518 | if (value && !utf8_is_valid(value)) |
519 | /* FIXME: filter UTF-8 */ | |
520 | log_error("%s:%u: invalid UTF-8 for key %s: '%s', ignoring.", | |
521 | filename, line, key, value); | |
522 | else { | |
523 | char ***m = userdata; | |
524 | char *p; | |
525 | int r; | |
a5c32cff | 526 | |
335c46b5 ZJS |
527 | p = strjoin(key, "=", strempty(value), NULL); |
528 | if (!p) | |
529 | return -ENOMEM; | |
530 | ||
531 | r = strv_push(m, p); | |
532 | if (r < 0) { | |
533 | free(p); | |
534 | return r; | |
535 | } | |
f73141d7 | 536 | } |
a5c32cff | 537 | |
f73141d7 LP |
538 | free(value); |
539 | return 0; | |
540 | } | |
541 | ||
542 | int load_env_file(const char *fname, const char *newline, char ***rl) { | |
543 | char **m = NULL; | |
544 | int r; | |
a5c32cff | 545 | |
f73141d7 LP |
546 | if (!newline) |
547 | newline = NEWLINE; | |
548 | ||
549 | r = parse_env_file_internal(fname, newline, load_env_file_push, &m); | |
550 | if (r < 0) { | |
551 | strv_free(m); | |
552 | return r; | |
a5c32cff HH |
553 | } |
554 | ||
555 | *rl = m; | |
a5c32cff HH |
556 | return 0; |
557 | } | |
558 | ||
768100ef LP |
559 | static void write_env_var(FILE *f, const char *v) { |
560 | const char *p; | |
561 | ||
562 | p = strchr(v, '='); | |
563 | if (!p) { | |
564 | /* Fallback */ | |
565 | fputs(v, f); | |
566 | fputc('\n', f); | |
567 | return; | |
568 | } | |
569 | ||
570 | p++; | |
571 | fwrite(v, 1, p-v, f); | |
572 | ||
ced2d10a | 573 | if (string_has_cc(p) || chars_intersect(p, WHITESPACE "\'\"\\`$")) { |
768100ef LP |
574 | fputc('\"', f); |
575 | ||
576 | for (; *p; p++) { | |
ced2d10a | 577 | if (strchr("\'\"\\`$", *p)) |
768100ef LP |
578 | fputc('\\', f); |
579 | ||
580 | fputc(*p, f); | |
581 | } | |
582 | ||
583 | fputc('\"', f); | |
584 | } else | |
585 | fputs(p, f); | |
586 | ||
587 | fputc('\n', f); | |
588 | } | |
589 | ||
a5c32cff | 590 | int write_env_file(const char *fname, char **l) { |
1c17cbed | 591 | char **i; |
7fd1b19b HH |
592 | _cleanup_free_ char *p = NULL; |
593 | _cleanup_fclose_ FILE *f = NULL; | |
a5c32cff HH |
594 | int r; |
595 | ||
596 | r = fopen_temporary(fname, &f, &p); | |
597 | if (r < 0) | |
598 | return r; | |
599 | ||
600 | fchmod_umask(fileno(f), 0644); | |
601 | ||
602 | errno = 0; | |
768100ef LP |
603 | STRV_FOREACH(i, l) |
604 | write_env_var(f, *i); | |
a5c32cff HH |
605 | |
606 | fflush(f); | |
607 | ||
fec6fc6b LP |
608 | if (ferror(f)) |
609 | r = errno ? -errno : -EIO; | |
610 | else { | |
a5c32cff HH |
611 | if (rename(p, fname) < 0) |
612 | r = -errno; | |
613 | else | |
614 | r = 0; | |
615 | } | |
616 | ||
617 | if (r < 0) | |
618 | unlink(p); | |
619 | ||
a5c32cff HH |
620 | return r; |
621 | } | |
68fee104 ZJS |
622 | |
623 | int executable_is_script(const char *path, char **interpreter) { | |
624 | int r; | |
625 | char _cleanup_free_ *line = NULL; | |
626 | int len; | |
627 | char *ans; | |
628 | ||
629 | assert(path); | |
630 | ||
631 | r = read_one_line_file(path, &line); | |
632 | if (r < 0) | |
633 | return r; | |
634 | ||
635 | if (!startswith(line, "#!")) | |
636 | return 0; | |
637 | ||
638 | ans = strstrip(line + 2); | |
639 | len = strcspn(ans, " \t"); | |
640 | ||
641 | if (len == 0) | |
642 | return 0; | |
643 | ||
644 | ans = strndup(ans, len); | |
645 | if (!ans) | |
646 | return -ENOMEM; | |
647 | ||
648 | *interpreter = ans; | |
649 | return 1; | |
650 | } | |
69ab8088 ZJS |
651 | |
652 | /** | |
653 | * Retrieve one field from a file like /proc/self/status. | |
654 | * pattern should start with '\n' and end with ':'. Whitespace | |
655 | * after ':' will be skipped. field must be freed afterwards. | |
656 | */ | |
657 | int get_status_field(const char *filename, const char *pattern, char **field) { | |
658 | _cleanup_free_ char *status = NULL; | |
659 | char *t; | |
660 | size_t len; | |
661 | int r; | |
662 | ||
663 | assert(filename); | |
664 | assert(field); | |
665 | ||
666 | r = read_full_file(filename, &status, NULL); | |
667 | if (r < 0) | |
668 | return r; | |
669 | ||
670 | t = strstr(status, pattern); | |
671 | if (!t) | |
672 | return -ENOENT; | |
673 | ||
674 | t += strlen(pattern); | |
675 | t += strspn(t, WHITESPACE); | |
676 | ||
677 | len = strcspn(t, WHITESPACE); | |
678 | ||
679 | *field = strndup(t, len); | |
680 | if (!*field) | |
681 | return -ENOMEM; | |
682 | ||
683 | return 0; | |
684 | } |