diff --git a/src/basic/env-file.c b/src/basic/env-file.c new file mode 100644 index 0000000000..6a7d6746a1 --- /dev/null +++ b/src/basic/env-file.c @@ -0,0 +1,574 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "env-file.h" +#include "env-util.h" +#include "escape.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" +#include "string-util.h" +#include "strv.h" +#include "tmpfile-util.h" +#include "utf8.h" + +static int parse_env_file_internal( + FILE *f, + const char *fname, + int (*push) (const char *filename, unsigned line, + const char *key, char *value, void *userdata, int *n_pushed), + void *userdata, + int *n_pushed) { + + 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; + _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; + unsigned line = 1; + char *p; + int r; + + enum { + PRE_KEY, + KEY, + PRE_VALUE, + VALUE, + VALUE_ESCAPE, + SINGLE_QUOTE_VALUE, + SINGLE_QUOTE_VALUE_ESCAPE, + DOUBLE_QUOTE_VALUE, + DOUBLE_QUOTE_VALUE_ESCAPE, + COMMENT, + COMMENT_ESCAPE + } state = PRE_KEY; + + if (f) + r = read_full_stream(f, &contents, NULL); + else + r = read_full_file(fname, &contents, NULL); + if (r < 0) + return r; + + for (p = contents; *p; p++) { + char c = *p; + + switch (state) { + + case PRE_KEY: + if (strchr(COMMENTS, c)) + state = COMMENT; + else if (!strchr(WHITESPACE, c)) { + state = KEY; + last_key_whitespace = (size_t) -1; + + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + return -ENOMEM; + + key[n_key++] = c; + } + break; + + case KEY: + if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + n_key = 0; + } else if (c == '=') { + state = PRE_VALUE; + last_value_whitespace = (size_t) -1; + } else { + if (!strchr(WHITESPACE, c)) + last_key_whitespace = (size_t) -1; + else if (last_key_whitespace == (size_t) -1) + last_key_whitespace = n_key; + + if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) + return -ENOMEM; + + key[n_key++] = c; + } + + break; + + case PRE_VALUE: + if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + return r; + + n_key = 0; + value = NULL; + value_alloc = n_value = 0; + + } else if (c == '\'') + state = SINGLE_QUOTE_VALUE; + else if (c == '\"') + state = DOUBLE_QUOTE_VALUE; + else if (c == '\\') + state = VALUE_ESCAPE; + else if (!strchr(WHITESPACE, c)) { + state = VALUE; + + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case VALUE: + if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + /* Chomp off trailing whitespace from value */ + if (last_value_whitespace != (size_t) -1) + value[last_value_whitespace] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + return r; + + n_key = 0; + value = NULL; + value_alloc = n_value = 0; + + } else if (c == '\\') { + state = VALUE_ESCAPE; + last_value_whitespace = (size_t) -1; + } else { + if (!strchr(WHITESPACE, c)) + last_value_whitespace = (size_t) -1; + else if (last_value_whitespace == (size_t) -1) + last_value_whitespace = n_value; + + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case VALUE_ESCAPE: + state = VALUE; + + if (!strchr(NEWLINE, c)) { + /* Escaped newlines we eat up entirely */ + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + break; + + case SINGLE_QUOTE_VALUE: + if (c == '\'') + state = PRE_VALUE; + else if (c == '\\') + state = SINGLE_QUOTE_VALUE_ESCAPE; + else { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case SINGLE_QUOTE_VALUE_ESCAPE: + state = SINGLE_QUOTE_VALUE; + + if (!strchr(NEWLINE, c)) { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + break; + + case DOUBLE_QUOTE_VALUE: + if (c == '\"') + state = PRE_VALUE; + else if (c == '\\') + state = DOUBLE_QUOTE_VALUE_ESCAPE; + else { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + + break; + + case DOUBLE_QUOTE_VALUE_ESCAPE: + state = DOUBLE_QUOTE_VALUE; + + if (!strchr(NEWLINE, c)) { + if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) + return -ENOMEM; + + value[n_value++] = c; + } + break; + + case COMMENT: + if (c == '\\') + state = COMMENT_ESCAPE; + else if (strchr(NEWLINE, c)) { + state = PRE_KEY; + line++; + } + break; + + case COMMENT_ESCAPE: + state = COMMENT; + break; + } + } + + if (IN_SET(state, + PRE_VALUE, + VALUE, + VALUE_ESCAPE, + SINGLE_QUOTE_VALUE, + SINGLE_QUOTE_VALUE_ESCAPE, + DOUBLE_QUOTE_VALUE, + DOUBLE_QUOTE_VALUE_ESCAPE)) { + + key[n_key] = 0; + + if (value) + value[n_value] = 0; + + if (state == VALUE) + if (last_value_whitespace != (size_t) -1) + value[last_value_whitespace] = 0; + + /* strip trailing whitespace from key */ + if (last_key_whitespace != (size_t) -1) + key[last_key_whitespace] = 0; + + r = push(fname, line, key, value, userdata, n_pushed); + if (r < 0) + return r; + + value = NULL; + } + + return 0; +} + +static int check_utf8ness_and_warn( + const char *filename, unsigned line, + const char *key, char *value) { + + if (!utf8_is_valid(key)) { + _cleanup_free_ char *p = NULL; + + p = utf8_escape_invalid(key); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s:%u: invalid UTF-8 in key '%s', ignoring.", + strna(filename), line, p); + } + + if (value && !utf8_is_valid(value)) { + _cleanup_free_ char *p = NULL; + + p = utf8_escape_invalid(value); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", + strna(filename), line, key, p); + } + + return 0; +} + +static int parse_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + + const char *k; + va_list aq, *ap = userdata; + int r; + + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; + + va_copy(aq, *ap); + + while ((k = va_arg(aq, const char *))) { + char **v; + + v = va_arg(aq, char **); + + if (streq(key, k)) { + va_end(aq); + free(*v); + *v = value; + + if (n_pushed) + (*n_pushed)++; + + return 1; + } + } + + va_end(aq); + free(value); + + return 0; +} + +int parse_env_filev( + FILE *f, + const char *fname, + va_list ap) { + + int r, n_pushed = 0; + va_list aq; + + va_copy(aq, ap); + r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); + va_end(aq); + if (r < 0) + return r; + + return n_pushed; +} + +int parse_env_file_sentinel( + FILE *f, + const char *fname, + ...) { + + va_list ap; + int r; + + va_start(ap, fname); + r = parse_env_filev(f, fname, ap); + va_end(ap); + + return r; +} + +static int load_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + char ***m = userdata; + char *p; + int r; + + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; + + p = strjoin(key, "=", value); + if (!p) + return -ENOMEM; + + r = strv_env_replace(m, p); + if (r < 0) { + free(p); + return r; + } + + if (n_pushed) + (*n_pushed)++; + + free(value); + return 0; +} + +int load_env_file(FILE *f, const char *fname, char ***rl) { + char **m = NULL; + int r; + + r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); + if (r < 0) { + strv_free(m); + return r; + } + + *rl = m; + return 0; +} + +static int load_env_file_push_pairs( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + char ***m = userdata; + int r; + + r = check_utf8ness_and_warn(filename, line, key, value); + if (r < 0) + return r; + + r = strv_extend(m, key); + if (r < 0) + return -ENOMEM; + + if (!value) { + r = strv_extend(m, ""); + if (r < 0) + return -ENOMEM; + } else { + r = strv_push(m, value); + if (r < 0) + return r; + } + + if (n_pushed) + (*n_pushed)++; + + return 0; +} + +int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { + char **m = NULL; + int r; + + r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); + if (r < 0) { + strv_free(m); + return r; + } + + *rl = m; + return 0; +} + +static int merge_env_file_push( + const char *filename, unsigned line, + const char *key, char *value, + void *userdata, + int *n_pushed) { + + char ***env = userdata; + char *expanded_value; + + assert(env); + + if (!value) { + log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); + return 0; + } + + if (!env_name_is_valid(key)) { + log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); + free(value); + return 0; + } + + expanded_value = replace_env(value, *env, + REPLACE_ENV_USE_ENVIRONMENT| + REPLACE_ENV_ALLOW_BRACELESS| + REPLACE_ENV_ALLOW_EXTENDED); + if (!expanded_value) + return -ENOMEM; + + free_and_replace(value, expanded_value); + + return load_env_file_push(filename, line, key, value, env, n_pushed); +} + +int merge_env_file( + char ***env, + FILE *f, + const char *fname) { + + /* NOTE: this function supports braceful and braceless variable expansions, + * plus "extended" substitutions, unlike other exported parsing functions. + */ + + return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); +} + +static void write_env_var(FILE *f, const char *v) { + const char *p; + + p = strchr(v, '='); + if (!p) { + /* Fallback */ + fputs_unlocked(v, f); + fputc_unlocked('\n', f); + return; + } + + p++; + fwrite_unlocked(v, 1, p-v, f); + + if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { + fputc_unlocked('\"', f); + + for (; *p; p++) { + if (strchr(SHELL_NEED_ESCAPE, *p)) + fputc_unlocked('\\', f); + + fputc_unlocked(*p, f); + } + + fputc_unlocked('\"', f); + } else + fputs_unlocked(p, f); + + fputc_unlocked('\n', f); +} + +int write_env_file(const char *fname, char **l) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; + char **i; + int r; + + assert(fname); + + r = fopen_temporary(fname, &f, &p); + if (r < 0) + return r; + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) fchmod_umask(fileno(f), 0644); + + STRV_FOREACH(i, l) + write_env_var(f, *i); + + r = fflush_and_check(f); + if (r >= 0) { + if (rename(p, fname) >= 0) + return 0; + + r = -errno; + } + + unlink(p); + return r; +} diff --git a/src/basic/env-file.h b/src/basic/env-file.h new file mode 100644 index 0000000000..e1ca195ff0 --- /dev/null +++ b/src/basic/env-file.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +#include "macro.h" + +int parse_env_filev(FILE *f, const char *fname, va_list ap); +int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_; +#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL) +int load_env_file(FILE *f, const char *fname, char ***l); +int load_env_file_pairs(FILE *f, const char *fname, char ***l); + +int merge_env_file(char ***env, FILE *f, const char *fname); + +int write_env_file(const char *fname, char **l); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 549f987283..4c200f8393 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -14,8 +14,6 @@ #include #include "alloc-util.h" -#include "env-util.h" -#include "escape.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" @@ -26,9 +24,7 @@ #include "path-util.h" #include "stdio-util.h" #include "string-util.h" -#include "strv.h" #include "tmpfile-util.h" -#include "utf8.h" #define READ_FULL_BYTES_MAX (4U*1024U*1024U) @@ -359,565 +355,6 @@ int read_full_file(const char *fn, char **contents, size_t *size) { return read_full_stream(f, contents, size); } -static int parse_env_file_internal( - FILE *f, - const char *fname, - int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { - - 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; - _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; - unsigned line = 1; - char *p; - int r; - - enum { - PRE_KEY, - KEY, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE, - COMMENT, - COMMENT_ESCAPE - } state = PRE_KEY; - - if (f) - r = read_full_stream(f, &contents, NULL); - else - r = read_full_file(fname, &contents, NULL); - if (r < 0) - return r; - - for (p = contents; *p; p++) { - char c = *p; - - switch (state) { - - case PRE_KEY: - if (strchr(COMMENTS, c)) - state = COMMENT; - else if (!strchr(WHITESPACE, c)) { - state = KEY; - last_key_whitespace = (size_t) -1; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - break; - - case KEY: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - n_key = 0; - } else if (c == '=') { - state = PRE_VALUE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_key_whitespace = (size_t) -1; - else if (last_key_whitespace == (size_t) -1) - last_key_whitespace = n_key; - - if (!GREEDY_REALLOC(key, key_alloc, n_key+2)) - return -ENOMEM; - - key[n_key++] = c; - } - - break; - - case PRE_VALUE: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\'') - state = SINGLE_QUOTE_VALUE; - else if (c == '\"') - state = DOUBLE_QUOTE_VALUE; - else if (c == '\\') - state = VALUE_ESCAPE; - else if (!strchr(WHITESPACE, c)) { - state = VALUE; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE: - if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - /* Chomp off trailing whitespace from value */ - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - n_key = 0; - value = NULL; - value_alloc = n_value = 0; - - } else if (c == '\\') { - state = VALUE_ESCAPE; - last_value_whitespace = (size_t) -1; - } else { - if (!strchr(WHITESPACE, c)) - last_value_whitespace = (size_t) -1; - else if (last_value_whitespace == (size_t) -1) - last_value_whitespace = n_value; - - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case VALUE_ESCAPE: - state = VALUE; - - if (!strchr(NEWLINE, c)) { - /* Escaped newlines we eat up entirely */ - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case SINGLE_QUOTE_VALUE: - if (c == '\'') - state = PRE_VALUE; - else if (c == '\\') - state = SINGLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case SINGLE_QUOTE_VALUE_ESCAPE: - state = SINGLE_QUOTE_VALUE; - - if (!strchr(NEWLINE, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case DOUBLE_QUOTE_VALUE: - if (c == '\"') - state = PRE_VALUE; - else if (c == '\\') - state = DOUBLE_QUOTE_VALUE_ESCAPE; - else { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - - break; - - case DOUBLE_QUOTE_VALUE_ESCAPE: - state = DOUBLE_QUOTE_VALUE; - - if (!strchr(NEWLINE, c)) { - if (!GREEDY_REALLOC(value, value_alloc, n_value+2)) - return -ENOMEM; - - value[n_value++] = c; - } - break; - - case COMMENT: - if (c == '\\') - state = COMMENT_ESCAPE; - else if (strchr(NEWLINE, c)) { - state = PRE_KEY; - line++; - } - break; - - case COMMENT_ESCAPE: - state = COMMENT; - break; - } - } - - if (IN_SET(state, - PRE_VALUE, - VALUE, - VALUE_ESCAPE, - SINGLE_QUOTE_VALUE, - SINGLE_QUOTE_VALUE_ESCAPE, - DOUBLE_QUOTE_VALUE, - DOUBLE_QUOTE_VALUE_ESCAPE)) { - - key[n_key] = 0; - - if (value) - value[n_value] = 0; - - if (state == VALUE) - if (last_value_whitespace != (size_t) -1) - value[last_value_whitespace] = 0; - - /* strip trailing whitespace from key */ - if (last_key_whitespace != (size_t) -1) - key[last_key_whitespace] = 0; - - r = push(fname, line, key, value, userdata, n_pushed); - if (r < 0) - return r; - - value = NULL; - } - - return 0; -} - -static int check_utf8ness_and_warn( - const char *filename, unsigned line, - const char *key, char *value) { - - if (!utf8_is_valid(key)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(key); - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s:%u: invalid UTF-8 in key '%s', ignoring.", - strna(filename), line, p); - } - - if (value && !utf8_is_valid(value)) { - _cleanup_free_ char *p = NULL; - - p = utf8_escape_invalid(value); - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", - strna(filename), line, key, p); - } - - return 0; -} - -static int parse_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - const char *k; - va_list aq, *ap = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - va_copy(aq, *ap); - - while ((k = va_arg(aq, const char *))) { - char **v; - - v = va_arg(aq, char **); - - if (streq(key, k)) { - va_end(aq); - free(*v); - *v = value; - - if (n_pushed) - (*n_pushed)++; - - return 1; - } - } - - va_end(aq); - free(value); - - return 0; -} - -int parse_env_filev( - FILE *f, - const char *fname, - va_list ap) { - - int r, n_pushed = 0; - va_list aq; - - va_copy(aq, ap); - r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); - va_end(aq); - if (r < 0) - return r; - - return n_pushed; -} - -int parse_env_file_sentinel( - FILE *f, - const char *fname, - ...) { - - va_list ap; - int r; - - va_start(ap, fname); - r = parse_env_filev(f, fname, ap); - va_end(ap); - - return r; -} - -static int load_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - char *p; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - p = strjoin(key, "=", value); - if (!p) - return -ENOMEM; - - r = strv_env_replace(m, p); - if (r < 0) { - free(p); - return r; - } - - if (n_pushed) - (*n_pushed)++; - - free(value); - return 0; -} - -int load_env_file(FILE *f, const char *fname, char ***rl) { - char **m = NULL; - int r; - - r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int load_env_file_push_pairs( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; - int r; - - r = check_utf8ness_and_warn(filename, line, key, value); - if (r < 0) - return r; - - r = strv_extend(m, key); - if (r < 0) - return -ENOMEM; - - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; -} - -int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { - char **m = NULL; - int r; - - r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); - return r; - } - - *rl = m; - return 0; -} - -static int merge_env_file_push( - const char *filename, unsigned line, - const char *key, char *value, - void *userdata, - int *n_pushed) { - - char ***env = userdata; - char *expanded_value; - - assert(env); - - if (!value) { - log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key); - return 0; - } - - if (!env_name_is_valid(key)) { - log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key); - free(value); - return 0; - } - - expanded_value = replace_env(value, *env, - REPLACE_ENV_USE_ENVIRONMENT| - REPLACE_ENV_ALLOW_BRACELESS| - REPLACE_ENV_ALLOW_EXTENDED); - if (!expanded_value) - return -ENOMEM; - - free_and_replace(value, expanded_value); - - return load_env_file_push(filename, line, key, value, env, n_pushed); -} - -int merge_env_file( - char ***env, - FILE *f, - const char *fname) { - - /* NOTE: this function supports braceful and braceless variable expansions, - * plus "extended" substitutions, unlike other exported parsing functions. - */ - - return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); -} - -static void write_env_var(FILE *f, const char *v) { - const char *p; - - p = strchr(v, '='); - if (!p) { - /* Fallback */ - fputs_unlocked(v, f); - fputc_unlocked('\n', f); - return; - } - - p++; - fwrite_unlocked(v, 1, p-v, f); - - if (string_has_cc(p, NULL) || chars_intersect(p, WHITESPACE SHELL_NEED_QUOTES)) { - fputc_unlocked('\"', f); - - for (; *p; p++) { - if (strchr(SHELL_NEED_ESCAPE, *p)) - fputc_unlocked('\\', f); - - fputc_unlocked(*p, f); - } - - fputc_unlocked('\"', f); - } else - fputs_unlocked(p, f); - - fputc_unlocked('\n', f); -} - -int write_env_file(const char *fname, char **l) { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *p = NULL; - char **i; - int r; - - assert(fname); - - r = fopen_temporary(fname, &f, &p); - if (r < 0) - return r; - - (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - (void) fchmod_umask(fileno(f), 0644); - - STRV_FOREACH(i, l) - write_env_var(f, *i); - - r = fflush_and_check(f); - if (r >= 0) { - if (rename(p, fname) >= 0) - return 0; - - r = -errno; - } - - unlink(p); - return r; -} - int executable_is_script(const char *path, char **interpreter) { _cleanup_free_ char *line = NULL; size_t len; diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 874cdabd6b..8480246072 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -44,16 +44,6 @@ int read_full_stream(FILE *f, char **contents, size_t *size); int verify_file(const char *fn, const char *blob, bool accept_extra_nl); -int parse_env_filev(FILE *f, const char *fname, va_list ap); -int parse_env_file_sentinel(FILE *f, const char *fname, ...) _sentinel_; -#define parse_env_file(f, fname, ...) parse_env_file_sentinel(f, fname, __VA_ARGS__, NULL) -int load_env_file(FILE *f, const char *fname, char ***l); -int load_env_file_pairs(FILE *f, const char *fname, char ***l); - -int merge_env_file(char ***env, FILE *f, const char *fname); - -int write_env_file(const char *fname, char **l); - int executable_is_script(const char *path, char **interpreter); int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field); diff --git a/src/basic/meson.build b/src/basic/meson.build index d49aa65d8f..880c9b7eb1 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -40,6 +40,8 @@ basic_sources = files(''' device-nodes.h dirent-util.c dirent-util.h + env-file.c + env-file.h env-util.c env-util.h errno-list.c diff --git a/src/basic/util.c b/src/basic/util.c index 00052c5e1d..c4f12a6daa 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -23,6 +23,7 @@ #include "def.h" #include "device-nodes.h" #include "dirent-util.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/core/execute.c b/src/core/execute.c index 33ba2eca56..6136d700a3 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -50,12 +50,12 @@ #include "chown-recursive.h" #include "cpu-set-util.h" #include "def.h" +#include "env-file.h" #include "env-util.h" #include "errno-list.h" #include "execute.h" #include "exit-status.h" #include "fd-util.h" -#include "fileio.h" #include "format-util.h" #include "fs-util.h" #include "glob-util.h" diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c index fff3a798c4..584fb220a1 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -4,8 +4,8 @@ #include #include +#include "env-file.h" #include "env-util.h" -#include "fileio.h" #include "locale-setup.h" #include "locale-util.h" #include "proc-cmdline.h" diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c index 1719b69b98..9d64d95738 100644 --- a/src/environment-d-generator/environment-d-generator.c +++ b/src/environment-d-generator/environment-d-generator.c @@ -4,8 +4,8 @@ #include "conf-files.h" #include "def.h" +#include "env-file.h" #include "escape.h" -#include "fileio.h" #include "log.h" #include "path-lookup.h" diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 8e429481f1..a3f4377a64 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -22,6 +22,7 @@ #include "alloc-util.h" #include "ask-password-api.h" #include "copy.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index bf1c00b4aa..3992b0e744 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -9,6 +9,8 @@ #include "bus-common-errors.h" #include "bus-util.h" #include "def.h" +#include "env-file-label.h" +#include "env-file.h" #include "env-util.h" #include "fileio-label.h" #include "fileio.h" diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index e2cb76141f..7f08809c54 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -11,6 +11,7 @@ #include "alloc-util.h" #include "conf-parser.h" #include "def.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 7283001158..137c8f0446 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -12,6 +12,7 @@ #include "alloc-util.h" #include "dirent-util.h" +#include "env-file.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 952f67a0d9..b5ff5b64f3 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -16,6 +16,7 @@ #include "catalog.h" #include "compress.h" #include "dirent-util.h" +#include "env-file.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 1c6efc5563..8275d2f31a 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -16,6 +16,7 @@ #include "dhcp-lease-internal.h" #include "dhcp-protocol.h" #include "dns-domain.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "hexdecoct.h" diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index e048d2ce18..a904c6b544 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -11,9 +11,9 @@ #include "alloc-util.h" #include "cgroup-util.h" #include "dirent-util.h" +#include "env-file.h" #include "escape.h" #include "fd-util.h" -#include "fileio.h" #include "format-util.h" #include "fs-util.h" #include "hostname-util.h" diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index fc66d41b63..4b66a92203 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -8,8 +8,8 @@ #include "sd-network.h" #include "alloc-util.h" +#include "env-file.h" #include "fd-util.h" -#include "fileio.h" #include "fs-util.h" #include "macro.h" #include "parse-util.h" diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c index 5a84438fbd..6b6b32a591 100644 --- a/src/locale/keymap-util.c +++ b/src/locale/keymap-util.c @@ -7,6 +7,8 @@ #include "bus-util.h" #include "def.h" +#include "env-file.h" +#include "env-file-label.h" #include "env-util.h" #include "fd-util.h" #include "fileio-label.h" diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index dc9e4d93dc..415c26b147 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -6,6 +6,7 @@ #include #include "alloc-util.h" +#include "env-file.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 676ba37db8..4b4dd4c060 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -16,6 +16,7 @@ #include "audit-util.h" #include "bus-error.h" #include "bus-util.h" +#include "env-file.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 9f4835a8df..ae27bfb662 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -11,6 +11,7 @@ #include "bus-util.h" #include "cgroup-util.h" #include "clean-ipc.h" +#include "env-file.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index c9580d99be..95489a3d94 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -17,9 +17,9 @@ #include "bus-label.h" #include "bus-util.h" #include "copy.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" -#include "fileio.h" #include "format-util.h" #include "fs-util.h" #include "in-addr-util.h" diff --git a/src/machine/machine.c b/src/machine/machine.c index 1120cd577b..7157d57683 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" +#include "env-file.h" #include "escape.h" #include "extract-word.h" #include "fd-util.h" diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 45908340ed..2c18bcef1b 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -10,6 +10,7 @@ #include "bus-util.h" #include "dhcp-identifier.h" #include "dhcp-lease-internal.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "netlink-util.h" diff --git a/src/network/networkd-lldp-tx.c b/src/network/networkd-lldp-tx.c index de39568eca..253308e36c 100644 --- a/src/network/networkd-lldp-tx.c +++ b/src/network/networkd-lldp-tx.c @@ -5,8 +5,8 @@ #include #include "alloc-util.h" +#include "env-file.h" #include "fd-util.h" -#include "fileio.h" #include "hostname-util.h" #include "networkd-lldp-tx.h" #include "networkd-manager.h" diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index 9af9744fd0..02d3954897 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -10,8 +10,8 @@ #include "bus-util.h" #include "def.h" #include "dirent-util.h" +#include "env-file.h" #include "fd-util.h" -#include "fileio.h" #include "format-table.h" #include "fs-util.h" #include "locale-util.h" diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index bff5277b2d..2cfb848a8b 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -6,6 +6,7 @@ #include "sd-network.h" #include "alloc-util.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "missing.h" diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 26dcb0f151..988a354ae3 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -12,8 +12,8 @@ #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" +#include "env-file.h" #include "fd-util.h" -#include "fileio.h" #include "format-util.h" #include "locale-util.h" #include "macro.h" diff --git a/src/shared/condition.c b/src/shared/condition.c index 35c9aa4caf..fb77966264 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -31,6 +31,7 @@ #include "list.h" #include "macro.h" #include "mountpoint-util.h" +#include "env-file.h" #include "parse-util.h" #include "path-util.h" #include "proc-cmdline.h" diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index b234ba15a2..4e572ac861 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -17,6 +17,7 @@ #include "device-nodes.h" #include "device-util.h" #include "dissect-image.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" diff --git a/src/shared/env-file-label.c b/src/shared/env-file-label.c new file mode 100644 index 0000000000..add68d224d --- /dev/null +++ b/src/shared/env-file-label.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "env-file-label.h" +#include "env-file.h" +#include "selinux-util.h" + +int write_env_file_label(const char *fname, char **l) { + int r; + + r = mac_selinux_create_file_prepare(fname, S_IFREG); + if (r < 0) + return r; + + r = write_env_file(fname, l); + + mac_selinux_create_file_clear(); + + return r; +} diff --git a/src/shared/env-file-label.h b/src/shared/env-file-label.h new file mode 100644 index 0000000000..158fc4ec0b --- /dev/null +++ b/src/shared/env-file-label.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +/* These functions are split out of fileio.h (and not for example just flags to the functions they wrap) in order to + * optimize linking: This way, -lselinux is needed only for the callers of these functions that need selinux, but not + * for all */ + +int write_env_file_label(const char *fname, char **l); diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 972231d687..7ea8ad351c 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "conf-files.h" +#include "env-file.h" #include "env-util.h" #include "exec-util.h" #include "fd-util.h" diff --git a/src/shared/fileio-label.c b/src/shared/fileio-label.c index 22df9c9379..49ab29720b 100644 --- a/src/shared/fileio-label.c +++ b/src/shared/fileio-label.c @@ -20,20 +20,6 @@ int write_string_file_atomic_label_ts(const char *fn, const char *line, struct t return r; } -int write_env_file_label(const char *fname, char **l) { - int r; - - r = mac_selinux_create_file_prepare(fname, S_IFREG); - if (r < 0) - return r; - - r = write_env_file(fname, l); - - mac_selinux_create_file_clear(); - - return r; -} - int create_shutdown_run_nologin_or_warn(void) { int r; diff --git a/src/shared/fileio-label.h b/src/shared/fileio-label.h index 2ef2c541de..8f88955d81 100644 --- a/src/shared/fileio-label.h +++ b/src/shared/fileio-label.h @@ -11,6 +11,5 @@ int write_string_file_atomic_label_ts(const char *fn, const char *line, struct t static inline int write_string_file_atomic_label(const char *fn, const char *line) { return write_string_file_atomic_label_ts(fn, line, NULL); } -int write_env_file_label(const char *fname, char **l); int create_shutdown_run_nologin_or_warn(void); diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index d5f6ab8529..7919164b27 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -17,9 +17,9 @@ #include "copy.h" #include "dirent-util.h" #include "dissect-image.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" -#include "fileio.h" #include "fs-util.h" #include "hashmap.h" #include "hostname-util.h" diff --git a/src/shared/meson.build b/src/shared/meson.build index 4971ce2d72..c4da082316 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -52,6 +52,8 @@ shared_sources = files(''' efivars.c efivars.h enable-mempool.c + env-file-label.c + env-file-label.h exec-util.c exec-util.h exit-status.c diff --git a/src/shared/os-util.c b/src/shared/os-util.c index 82471a45ba..f7d46d3c47 100644 --- a/src/shared/os-util.c +++ b/src/shared/os-util.c @@ -1,13 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" +#include "env-file.h" #include "fd-util.h" #include "fs-util.h" #include "macro.h" #include "os-util.h" -#include "strv.h" -#include "fileio.h" #include "string-util.h" +#include "strv.h" int path_is_os_tree(const char *path) { int r; diff --git a/src/shared/tests.c b/src/shared/tests.c index 95f22e58b1..11ea12ed69 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -14,8 +14,8 @@ #undef basename #include "alloc-util.h" +#include "env-file.h" #include "env-util.h" -#include "fileio.h" #include "fs-util.h" #include "log.h" #include "path-util.h" diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 87ff6d288f..fdeaf8f613 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -4,7 +4,7 @@ #include #include "alloc-util.h" -#include "fileio.h" +#include "env-file.h" #include "log.h" #include "parse-util.h" #include "string-table.h" diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index da536fe5ab..44047f2e5a 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "ctype.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index b1aa387110..13c289270d 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -11,8 +11,8 @@ #include "alloc-util.h" #include "capability-util.h" #include "conf-parser.h" +#include "env-file.h" #include "fd-util.h" -#include "fileio.h" #include "fs-util.h" #include "hashmap.h" #include "hostname-util.h" diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 7182be4624..71fc59cbfa 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -18,6 +18,7 @@ #include #include "alloc-util.h" +#include "env-file.h" #include "fd-util.h" #include "fileio.h" #include "io-util.h"