LCOV - code coverage report
Current view: top level - basic - env-file.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 251 296 84.8 %
Date: 2019-08-22 15:41:25 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include "alloc-util.h"
       4             : #include "env-file.h"
       5             : #include "env-util.h"
       6             : #include "escape.h"
       7             : #include "fd-util.h"
       8             : #include "fileio.h"
       9             : #include "fs-util.h"
      10             : #include "string-util.h"
      11             : #include "strv.h"
      12             : #include "tmpfile-util.h"
      13             : #include "utf8.h"
      14             : 
      15          68 : static int parse_env_file_internal(
      16             :                 FILE *f,
      17             :                 const char *fname,
      18             :                 int (*push) (const char *filename, unsigned line,
      19             :                              const char *key, char *value, void *userdata, int *n_pushed),
      20             :                 void *userdata,
      21             :                 int *n_pushed) {
      22             : 
      23          68 :         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;
      24          68 :         _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
      25          68 :         unsigned line = 1;
      26             :         char *p;
      27             :         int r;
      28             : 
      29             :         enum {
      30             :                 PRE_KEY,
      31             :                 KEY,
      32             :                 PRE_VALUE,
      33             :                 VALUE,
      34             :                 VALUE_ESCAPE,
      35             :                 SINGLE_QUOTE_VALUE,
      36             :                 DOUBLE_QUOTE_VALUE,
      37             :                 DOUBLE_QUOTE_VALUE_ESCAPE,
      38             :                 COMMENT,
      39             :                 COMMENT_ESCAPE
      40          68 :         } state = PRE_KEY;
      41             : 
      42          68 :         if (f)
      43           5 :                 r = read_full_stream(f, &contents, NULL);
      44             :         else
      45          63 :                 r = read_full_file(fname, &contents, NULL);
      46          68 :         if (r < 0)
      47           7 :                 return r;
      48             : 
      49       10850 :         for (p = contents; *p; p++) {
      50       10789 :                 char c = *p;
      51             : 
      52       10789 :                 switch (state) {
      53             : 
      54         572 :                 case PRE_KEY:
      55         572 :                         if (strchr(COMMENTS, c))
      56          80 :                                 state = COMMENT;
      57         492 :                         else if (!strchr(WHITESPACE, c)) {
      58         470 :                                 state = KEY;
      59         470 :                                 last_key_whitespace = (size_t) -1;
      60             : 
      61         470 :                                 if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
      62           0 :                                         return -ENOMEM;
      63             : 
      64         470 :                                 key[n_key++] = c;
      65             :                         }
      66         572 :                         break;
      67             : 
      68        3369 :                 case KEY:
      69        3369 :                         if (strchr(NEWLINE, c)) {
      70           6 :                                 state = PRE_KEY;
      71           6 :                                 line++;
      72           6 :                                 n_key = 0;
      73        3363 :                         } else if (c == '=') {
      74         464 :                                 state = PRE_VALUE;
      75         464 :                                 last_value_whitespace = (size_t) -1;
      76             :                         } else {
      77        2899 :                                 if (!strchr(WHITESPACE, c))
      78        2865 :                                         last_key_whitespace = (size_t) -1;
      79          34 :                                 else if (last_key_whitespace == (size_t) -1)
      80          26 :                                          last_key_whitespace = n_key;
      81             : 
      82        2899 :                                 if (!GREEDY_REALLOC(key, key_alloc, n_key+2))
      83           0 :                                         return -ENOMEM;
      84             : 
      85        2899 :                                 key[n_key++] = c;
      86             :                         }
      87             : 
      88        3369 :                         break;
      89             : 
      90         519 :                 case PRE_VALUE:
      91         519 :                         if (strchr(NEWLINE, c)) {
      92          33 :                                 state = PRE_KEY;
      93          33 :                                 line++;
      94          33 :                                 key[n_key] = 0;
      95             : 
      96          33 :                                 if (value)
      97          25 :                                         value[n_value] = 0;
      98             : 
      99             :                                 /* strip trailing whitespace from key */
     100          33 :                                 if (last_key_whitespace != (size_t) -1)
     101           6 :                                         key[last_key_whitespace] = 0;
     102             : 
     103          33 :                                 r = push(fname, line, key, value, userdata, n_pushed);
     104          33 :                                 if (r < 0)
     105           0 :                                         return r;
     106             : 
     107          33 :                                 n_key = 0;
     108          33 :                                 value = NULL;
     109          33 :                                 value_alloc = n_value = 0;
     110             : 
     111         486 :                         } else if (c == '\'')
     112           4 :                                 state = SINGLE_QUOTE_VALUE;
     113         482 :                         else if (c == '"')
     114          29 :                                 state = DOUBLE_QUOTE_VALUE;
     115         453 :                         else if (c == '\\')
     116           2 :                                 state = VALUE_ESCAPE;
     117         451 :                         else if (!strchr(WHITESPACE, c)) {
     118         426 :                                 state = VALUE;
     119             : 
     120         426 :                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
     121           0 :                                         return  -ENOMEM;
     122             : 
     123         426 :                                 value[n_value++] = c;
     124             :                         }
     125             : 
     126         519 :                         break;
     127             : 
     128        3015 :                 case VALUE:
     129        3015 :                         if (strchr(NEWLINE, c)) {
     130         425 :                                 state = PRE_KEY;
     131         425 :                                 line++;
     132             : 
     133         425 :                                 key[n_key] = 0;
     134             : 
     135         425 :                                 if (value)
     136         425 :                                         value[n_value] = 0;
     137             : 
     138             :                                 /* Chomp off trailing whitespace from value */
     139         425 :                                 if (last_value_whitespace != (size_t) -1)
     140           9 :                                         value[last_value_whitespace] = 0;
     141             : 
     142             :                                 /* strip trailing whitespace from key */
     143         425 :                                 if (last_key_whitespace != (size_t) -1)
     144           6 :                                         key[last_key_whitespace] = 0;
     145             : 
     146         425 :                                 r = push(fname, line, key, value, userdata, n_pushed);
     147         425 :                                 if (r < 0)
     148           0 :                                         return r;
     149             : 
     150         425 :                                 n_key = 0;
     151         425 :                                 value = NULL;
     152         425 :                                 value_alloc = n_value = 0;
     153             : 
     154        2590 :                         } else if (c == '\\') {
     155          10 :                                 state = VALUE_ESCAPE;
     156          10 :                                 last_value_whitespace = (size_t) -1;
     157             :                         } else {
     158        2580 :                                 if (!strchr(WHITESPACE, c))
     159        2524 :                                         last_value_whitespace = (size_t) -1;
     160          56 :                                 else if (last_value_whitespace == (size_t) -1)
     161          33 :                                         last_value_whitespace = n_value;
     162             : 
     163        2580 :                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
     164           0 :                                         return -ENOMEM;
     165             : 
     166        2580 :                                 value[n_value++] = c;
     167             :                         }
     168             : 
     169        3015 :                         break;
     170             : 
     171          11 :                 case VALUE_ESCAPE:
     172          11 :                         state = VALUE;
     173             : 
     174          11 :                         if (!strchr(NEWLINE, c)) {
     175             :                                 /* Escaped newlines we eat up entirely */
     176           3 :                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
     177           0 :                                         return -ENOMEM;
     178             : 
     179           3 :                                 value[n_value++] = c;
     180             :                         }
     181          11 :                         break;
     182             : 
     183          28 :                 case SINGLE_QUOTE_VALUE:
     184          28 :                         if (c == '\'')
     185           4 :                                 state = PRE_VALUE;
     186             :                         else {
     187          24 :                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
     188           0 :                                         return -ENOMEM;
     189             : 
     190          24 :                                 value[n_value++] = c;
     191             :                         }
     192             : 
     193          28 :                         break;
     194             : 
     195         362 :                 case DOUBLE_QUOTE_VALUE:
     196         362 :                         if (c == '"')
     197          29 :                                 state = PRE_VALUE;
     198         333 :                         else if (c == '\\')
     199          13 :                                 state = DOUBLE_QUOTE_VALUE_ESCAPE;
     200             :                         else {
     201         320 :                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
     202           0 :                                         return -ENOMEM;
     203             : 
     204         320 :                                 value[n_value++] = c;
     205             :                         }
     206             : 
     207         362 :                         break;
     208             : 
     209          13 :                 case DOUBLE_QUOTE_VALUE_ESCAPE:
     210          13 :                         state = DOUBLE_QUOTE_VALUE;
     211             : 
     212          13 :                         if (c == '"') {
     213           4 :                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+2))
     214           0 :                                         return -ENOMEM;
     215           4 :                                 value[n_value++] = '"';
     216           9 :                         } else if (!strchr(NEWLINE, c)) {
     217           5 :                                 if (!GREEDY_REALLOC(value, value_alloc, n_value+3))
     218           0 :                                         return -ENOMEM;
     219           5 :                                 value[n_value++] = '\\';
     220           5 :                                 value[n_value++] = c;
     221             :                         }
     222             : 
     223          13 :                         break;
     224             : 
     225        2898 :                 case COMMENT:
     226        2898 :                         if (c == '\\')
     227           2 :                                 state = COMMENT_ESCAPE;
     228        2896 :                         else if (strchr(NEWLINE, c)) {
     229          79 :                                 state = PRE_KEY;
     230          79 :                                 line++;
     231             :                         }
     232        2898 :                         break;
     233             : 
     234           2 :                 case COMMENT_ESCAPE:
     235           2 :                         state = COMMENT;
     236           2 :                         break;
     237             :                 }
     238       10789 :         }
     239             : 
     240          61 :         if (IN_SET(state,
     241             :                    PRE_VALUE,
     242             :                    VALUE,
     243             :                    VALUE_ESCAPE,
     244             :                    SINGLE_QUOTE_VALUE,
     245             :                    DOUBLE_QUOTE_VALUE,
     246             :                    DOUBLE_QUOTE_VALUE_ESCAPE)) {
     247             : 
     248           6 :                 key[n_key] = 0;
     249             : 
     250           6 :                 if (value)
     251           5 :                         value[n_value] = 0;
     252             : 
     253           6 :                 if (state == VALUE)
     254           2 :                         if (last_value_whitespace != (size_t) -1)
     255           0 :                                 value[last_value_whitespace] = 0;
     256             : 
     257             :                 /* strip trailing whitespace from key */
     258           6 :                 if (last_key_whitespace != (size_t) -1)
     259           0 :                         key[last_key_whitespace] = 0;
     260             : 
     261           6 :                 r = push(fname, line, key, value, userdata, n_pushed);
     262           6 :                 if (r < 0)
     263           0 :                         return r;
     264             : 
     265           6 :                 value = NULL;
     266             :         }
     267             : 
     268          61 :         return 0;
     269             : }
     270             : 
     271         459 : static int check_utf8ness_and_warn(
     272             :                 const char *filename, unsigned line,
     273             :                 const char *key, char *value) {
     274             : 
     275         459 :         if (!utf8_is_valid(key)) {
     276           0 :                 _cleanup_free_ char *p = NULL;
     277             : 
     278           0 :                 p = utf8_escape_invalid(key);
     279           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     280             :                                        "%s:%u: invalid UTF-8 in key '%s', ignoring.",
     281             :                                        strna(filename), line, p);
     282             :         }
     283             : 
     284         459 :         if (value && !utf8_is_valid(value)) {
     285           0 :                 _cleanup_free_ char *p = NULL;
     286             : 
     287           0 :                 p = utf8_escape_invalid(value);
     288           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     289             :                                        "%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.",
     290             :                                        strna(filename), line, key, p);
     291             :         }
     292             : 
     293         459 :         return 0;
     294             : }
     295             : 
     296         363 : static int parse_env_file_push(
     297             :                 const char *filename, unsigned line,
     298             :                 const char *key, char *value,
     299             :                 void *userdata,
     300             :                 int *n_pushed) {
     301             : 
     302             :         const char *k;
     303         363 :         va_list aq, *ap = userdata;
     304             :         int r;
     305             : 
     306         363 :         r = check_utf8ness_and_warn(filename, line, key, value);
     307         363 :         if (r < 0)
     308           0 :                 return r;
     309             : 
     310         363 :         va_copy(aq, *ap);
     311             : 
     312         807 :         while ((k = va_arg(aq, const char *))) {
     313             :                 char **v;
     314             : 
     315         487 :                 v = va_arg(aq, char **);
     316             : 
     317         487 :                 if (streq(key, k)) {
     318          43 :                         va_end(aq);
     319          43 :                         free(*v);
     320          43 :                         *v = value;
     321             : 
     322          43 :                         if (n_pushed)
     323          43 :                                 (*n_pushed)++;
     324             : 
     325          43 :                         return 1;
     326             :                 }
     327             :         }
     328             : 
     329         320 :         va_end(aq);
     330         320 :         free(value);
     331             : 
     332         320 :         return 0;
     333             : }
     334             : 
     335          42 : int parse_env_filev(
     336             :                 FILE *f,
     337             :                 const char *fname,
     338             :                 va_list ap) {
     339             : 
     340          42 :         int r, n_pushed = 0;
     341             :         va_list aq;
     342             : 
     343          42 :         va_copy(aq, ap);
     344          42 :         r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed);
     345          42 :         va_end(aq);
     346          42 :         if (r < 0)
     347           7 :                 return r;
     348             : 
     349          35 :         return n_pushed;
     350             : }
     351             : 
     352          42 : int parse_env_file_sentinel(
     353             :                 FILE *f,
     354             :                 const char *fname,
     355             :                 ...) {
     356             : 
     357             :         va_list ap;
     358             :         int r;
     359             : 
     360          42 :         va_start(ap, fname);
     361          42 :         r = parse_env_filev(f, fname, ap);
     362          42 :         va_end(ap);
     363             : 
     364          42 :         return r;
     365             : }
     366             : 
     367          67 : static int load_env_file_push(
     368             :                 const char *filename, unsigned line,
     369             :                 const char *key, char *value,
     370             :                 void *userdata,
     371             :                 int *n_pushed) {
     372          67 :         char ***m = userdata;
     373             :         char *p;
     374             :         int r;
     375             : 
     376          67 :         r = check_utf8ness_and_warn(filename, line, key, value);
     377          67 :         if (r < 0)
     378           0 :                 return r;
     379             : 
     380          67 :         p = strjoin(key, "=", value);
     381          67 :         if (!p)
     382           0 :                 return -ENOMEM;
     383             : 
     384          67 :         r = strv_env_replace(m, p);
     385          67 :         if (r < 0) {
     386           0 :                 free(p);
     387           0 :                 return r;
     388             :         }
     389             : 
     390          67 :         if (n_pushed)
     391           0 :                 (*n_pushed)++;
     392             : 
     393          67 :         free(value);
     394          67 :         return 0;
     395             : }
     396             : 
     397           9 : int load_env_file(FILE *f, const char *fname, char ***rl) {
     398           9 :         char **m = NULL;
     399             :         int r;
     400             : 
     401           9 :         r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL);
     402           9 :         if (r < 0) {
     403           0 :                 strv_free(m);
     404           0 :                 return r;
     405             :         }
     406             : 
     407           9 :         *rl = m;
     408           9 :         return 0;
     409             : }
     410             : 
     411          29 : static int load_env_file_push_pairs(
     412             :                 const char *filename, unsigned line,
     413             :                 const char *key, char *value,
     414             :                 void *userdata,
     415             :                 int *n_pushed) {
     416          29 :         char ***m = userdata;
     417             :         int r;
     418             : 
     419          29 :         r = check_utf8ness_and_warn(filename, line, key, value);
     420          29 :         if (r < 0)
     421           0 :                 return r;
     422             : 
     423          29 :         r = strv_extend(m, key);
     424          29 :         if (r < 0)
     425           0 :                 return -ENOMEM;
     426             : 
     427          29 :         if (!value) {
     428           0 :                 r = strv_extend(m, "");
     429           0 :                 if (r < 0)
     430           0 :                         return -ENOMEM;
     431             :         } else {
     432          29 :                 r = strv_push(m, value);
     433          29 :                 if (r < 0)
     434           0 :                         return r;
     435             :         }
     436             : 
     437          29 :         if (n_pushed)
     438           0 :                 (*n_pushed)++;
     439             : 
     440          29 :         return 0;
     441             : }
     442             : 
     443          14 : int load_env_file_pairs(FILE *f, const char *fname, char ***rl) {
     444          14 :         char **m = NULL;
     445             :         int r;
     446             : 
     447          14 :         r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL);
     448          14 :         if (r < 0) {
     449           0 :                 strv_free(m);
     450           0 :                 return r;
     451             :         }
     452             : 
     453          14 :         *rl = m;
     454          14 :         return 0;
     455             : }
     456             : 
     457          27 : static int merge_env_file_push(
     458             :                 const char *filename, unsigned line,
     459             :                 const char *key, char *value,
     460             :                 void *userdata,
     461             :                 int *n_pushed) {
     462             : 
     463          27 :         char ***env = userdata;
     464             :         char *expanded_value;
     465             : 
     466          27 :         assert(env);
     467             : 
     468          27 :         if (!value) {
     469           4 :                 log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
     470           4 :                 return 0;
     471             :         }
     472             : 
     473          23 :         if (!env_name_is_valid(key)) {
     474           1 :                 log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
     475           1 :                 free(value);
     476           1 :                 return 0;
     477             :         }
     478             : 
     479          22 :         expanded_value = replace_env(value, *env,
     480             :                                      REPLACE_ENV_USE_ENVIRONMENT|
     481             :                                      REPLACE_ENV_ALLOW_BRACELESS|
     482             :                                      REPLACE_ENV_ALLOW_EXTENDED);
     483          22 :         if (!expanded_value)
     484           0 :                 return -ENOMEM;
     485             : 
     486          22 :         free_and_replace(value, expanded_value);
     487             : 
     488          22 :         return load_env_file_push(filename, line, key, value, env, n_pushed);
     489             : }
     490             : 
     491           3 : int merge_env_file(
     492             :                 char ***env,
     493             :                 FILE *f,
     494             :                 const char *fname) {
     495             : 
     496             :         /* NOTE: this function supports braceful and braceless variable expansions,
     497             :          * plus "extended" substitutions, unlike other exported parsing functions.
     498             :          */
     499             : 
     500           3 :         return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL);
     501             : }
     502             : 
     503          15 : static void write_env_var(FILE *f, const char *v) {
     504             :         const char *p;
     505             : 
     506          15 :         p = strchr(v, '=');
     507          15 :         if (!p) {
     508             :                 /* Fallback */
     509           0 :                 fputs_unlocked(v, f);
     510           0 :                 fputc_unlocked('\n', f);
     511           0 :                 return;
     512             :         }
     513             : 
     514          15 :         p++;
     515          15 :         fwrite_unlocked(v, 1, p-v, f);
     516             : 
     517          15 :         if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) {
     518          10 :                 fputc_unlocked('"', f);
     519             : 
     520         128 :                 for (; *p; p++) {
     521         118 :                         if (strchr(SHELL_NEED_ESCAPE, *p))
     522           5 :                                 fputc_unlocked('\\', f);
     523             : 
     524         118 :                         fputc_unlocked(*p, f);
     525             :                 }
     526             : 
     527          10 :                 fputc_unlocked('"', f);
     528             :         } else
     529           5 :                 fputs_unlocked(p, f);
     530             : 
     531          15 :         fputc_unlocked('\n', f);
     532             : }
     533             : 
     534           2 : int write_env_file(const char *fname, char **l) {
     535           2 :         _cleanup_fclose_ FILE *f = NULL;
     536           2 :         _cleanup_free_ char *p = NULL;
     537             :         char **i;
     538             :         int r;
     539             : 
     540           2 :         assert(fname);
     541             : 
     542           2 :         r = fopen_temporary(fname, &f, &p);
     543           2 :         if (r < 0)
     544           0 :                 return r;
     545             : 
     546           2 :         (void) fchmod_umask(fileno(f), 0644);
     547             : 
     548          17 :         STRV_FOREACH(i, l)
     549          15 :                 write_env_var(f, *i);
     550             : 
     551           2 :         r = fflush_and_check(f);
     552           2 :         if (r >= 0) {
     553           2 :                 if (rename(p, fname) >= 0)
     554           2 :                         return 0;
     555             : 
     556           0 :                 r = -errno;
     557             :         }
     558             : 
     559           0 :         (void) unlink(p);
     560           0 :         return r;
     561             : }

Generated by: LCOV version 1.14