LCOV - code coverage report
Current view: top level - basic - fileio.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 336 443 75.8 %
Date: 2015-07-29 18:47:03 Functions: 16 18 88.9 %

          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             : }

Generated by: LCOV version 1.11