Merge pull request #5131 from keszybz/environment-generators

Environment generators
This commit is contained in:
Lennart Poettering
2017-02-21 11:11:44 +01:00
committed by GitHub
35 changed files with 1990 additions and 341 deletions
-2
View File
@@ -137,7 +137,6 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const ch
va_list ap;
assert(strv);
assert(suffix);
va_start(ap, dir);
dirs = strv_new_ap(dir, ap);
@@ -153,7 +152,6 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, c
_cleanup_strv_free_ char **dirs = NULL;
assert(strv);
assert(suffix);
dirs = strv_split_nulstr(d);
if (!dirs)
+7 -7
View File
@@ -73,18 +73,18 @@
#define NOTIFY_BUFFER_MAX PIPE_BUF
#ifdef HAVE_SPLIT_USR
#define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
# define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
#else
#define _CONF_PATHS_SPLIT_USR(n)
# define _CONF_PATHS_SPLIT_USR(n)
#endif
/* Return a nulstr for a standard cascade of configuration paths,
* suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
* to implement drop-in directories for extending configuration
* files. */
#define CONF_PATHS_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" \
#define CONF_PATHS_NULSTR(n) \
"/etc/" n "\0" \
"/run/" n "\0" \
"/usr/local/lib/" n "\0" \
"/usr/lib/" n "\0" \
_CONF_PATHS_SPLIT_USR(n)
+186 -26
View File
@@ -26,6 +26,7 @@
#include "alloc-util.h"
#include "env-util.h"
#include "escape.h"
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
@@ -247,7 +248,7 @@ fail:
return NULL;
}
_pure_ static bool env_match(const char *t, const char *pattern) {
static bool env_match(const char *t, const char *pattern) {
assert(t);
assert(pattern);
@@ -273,6 +274,19 @@ _pure_ static bool env_match(const char *t, const char *pattern) {
return false;
}
static bool env_entry_has_name(const char *entry, const char *name) {
const char *t;
assert(entry);
assert(name);
t = startswith(entry, name);
if (!t)
return false;
return *t == '=';
}
char **strv_env_delete(char **x, unsigned n_lists, ...) {
size_t n, i = 0;
char **k, **r;
@@ -386,18 +400,24 @@ char **strv_env_unset_many(char **l, ...) {
int strv_env_replace(char ***l, char *p) {
char **f;
const char *t, *name;
assert(p);
/* Replace first occurrence of the env var or add a new one in the
* string list. Drop other occurences. Edits in-place. Does not copy p.
* p must be a valid key=value assignment.
*/
t = strchr(p, '=');
assert(t);
name = strndupa(p, t - p);
for (f = *l; f && *f; f++)
if (env_match(*f, p)) {
free(*f);
*f = p;
strv_env_unset(f + 1, p);
if (env_entry_has_name(*f, name)) {
free_and_replace(*f, p);
strv_env_unset(f + 1, *f);
return 0;
}
@@ -434,7 +454,7 @@ fail:
return NULL;
}
char *strv_env_get_n(char **l, const char *name, size_t k) {
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
char **i;
assert(name);
@@ -442,18 +462,25 @@ char *strv_env_get_n(char **l, const char *name, size_t k) {
if (k <= 0)
return NULL;
STRV_FOREACH(i, l)
STRV_FOREACH_BACKWARDS(i, l)
if (strneq(*i, name, k) &&
(*i)[k] == '=')
return *i + k + 1;
if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
const char *t;
t = strndupa(name, k);
return getenv(t);
};
return NULL;
}
char *strv_env_get(char **l, const char *name) {
assert(name);
return strv_env_get_n(l, name, strlen(name));
return strv_env_get_n(l, name, strlen(name), 0);
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
@@ -492,19 +519,26 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
char *replace_env(const char *format, char **env) {
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
enum {
WORD,
CURLY,
VARIABLE
VARIABLE,
VARIABLE_RAW,
TEST,
DEFAULT_VALUE,
ALTERNATE_VALUE,
} state = WORD;
const char *e, *word = format;
char *r = NULL, *k;
const char *e, *word = format, *test_value;
char *k;
_cleanup_free_ char *r = NULL;
size_t i, len;
int nest = 0;
assert(format);
for (e = format; *e; e ++) {
for (e = format, i = 0; *e && i < n; e ++, i ++) {
switch (state) {
@@ -517,24 +551,36 @@ char *replace_env(const char *format, char **env) {
if (*e == '{') {
k = strnappend(r, word, e-word-1);
if (!k)
goto fail;
return NULL;
free(r);
r = k;
word = e-1;
state = VARIABLE;
nest++;
} else if (*e == '$') {
k = strnappend(r, word, e-word);
if (!k)
goto fail;
return NULL;
free(r);
r = k;
word = e+1;
state = WORD;
} else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
k = strnappend(r, word, e-word-1);
if (!k)
return NULL;
free(r);
r = k;
word = e-1;
state = VARIABLE_RAW;
} else
state = WORD;
break;
@@ -543,11 +589,68 @@ char *replace_env(const char *format, char **env) {
if (*e == '}') {
const char *t;
t = strempty(strv_env_get_n(env, word+2, e-word-2));
t = strv_env_get_n(env, word+2, e-word-2, flags);
k = strappend(r, t);
if (!k)
goto fail;
return NULL;
free(r);
r = k;
word = e+1;
state = WORD;
} else if (*e == ':') {
if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
/* Treat this as unsupported syntax, i.e. do no replacement */
state = WORD;
else {
len = e-word-2;
state = TEST;
}
}
break;
case TEST:
if (*e == '-')
state = DEFAULT_VALUE;
else if (*e == '+')
state = ALTERNATE_VALUE;
else {
state = WORD;
break;
}
test_value = e+1;
break;
case DEFAULT_VALUE: /* fall through */
case ALTERNATE_VALUE:
assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
if (*e == '{') {
nest++;
break;
}
if (*e != '}')
break;
nest--;
if (nest == 0) { // || !strchr(e+1, '}')) {
const char *t;
_cleanup_free_ char *v = NULL;
t = strv_env_get_n(env, word+2, len, flags);
if (t && state == ALTERNATE_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
else if (!t && state == DEFAULT_VALUE)
t = v = replace_env_n(test_value, e-test_value, env, flags);
k = strappend(r, t);
if (!k)
return NULL;
free(r);
r = k;
@@ -556,18 +659,39 @@ char *replace_env(const char *format, char **env) {
state = WORD;
}
break;
case VARIABLE_RAW:
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
const char *t;
t = strv_env_get_n(env, word+1, e-word-1, flags);
k = strappend(r, t);
if (!k)
return NULL;
free(r);
r = k;
word = e--;
i--;
state = WORD;
}
break;
}
}
k = strnappend(r, word, e-word);
if (!k)
goto fail;
if (state == VARIABLE_RAW) {
const char *t;
free(r);
return k;
assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
fail:
return mfree(r);
t = strv_env_get_n(env, word+1, e-word-1, flags);
return strappend(r, t);
} else
return strnappend(r, word, e-word);
}
char **replace_env_argv(char **argv, char **env) {
@@ -623,7 +747,7 @@ char **replace_env_argv(char **argv, char **env) {
}
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
ret[k] = replace_env(*i, env);
ret[k] = replace_env(*i, env, 0);
if (!ret[k]) {
strv_free(ret);
return NULL;
@@ -644,3 +768,39 @@ int getenv_bool(const char *p) {
return parse_boolean(e);
}
int serialize_environment(FILE *f, char **environment) {
char **e;
STRV_FOREACH(e, environment) {
_cleanup_free_ char *ce;
ce = cescape(*e);
if (!ce)
return -ENOMEM;
fprintf(f, "env=%s\n", *e);
}
/* caller should call ferror() */
return 0;
}
int deserialize_environment(char ***environment, const char *line) {
char *uce = NULL;
int r;
assert(line);
assert(environment);
assert(startswith(line, "env="));
r = cunescape(line + 4, UNESCAPE_RELAX, &uce);
if (r < 0)
return r;
if (!env_assignment_is_valid(uce))
return -EINVAL;
return strv_env_replace(environment, uce);
}
+16 -2
View File
@@ -21,6 +21,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include "macro.h"
@@ -28,9 +29,19 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
char *replace_env(const char *format, char **env);
enum {
REPLACE_ENV_USE_ENVIRONMENT = 1u,
REPLACE_ENV_ALLOW_BRACELESS = 2u,
REPLACE_ENV_ALLOW_EXTENDED = 4u,
};
char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
char **replace_env_argv(char **argv, char **env);
static inline char *replace_env(const char *format, char **env, unsigned flags) {
return replace_env_n(format, strlen(format), env, flags);
}
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
@@ -46,7 +57,10 @@ char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
int strv_env_replace(char ***l, char *p); /* In place ... */
char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
int getenv_bool(const char *p);
int serialize_environment(FILE *f, char **environment);
int deserialize_environment(char ***environment, const char *line);
+360
View File
@@ -0,0 +1,360 @@
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <dirent.h>
#include <errno.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include "alloc-util.h"
#include "conf-files.h"
#include "env-util.h"
#include "exec-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hashmap.h"
#include "macro.h"
#include "process-util.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
#include "util.h"
/* Put this test here for a lack of better place */
assert_cc(EAGAIN == EWOULDBLOCK);
static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
pid_t _pid;
if (null_or_empty_path(path)) {
log_debug("%s is empty (a mask).", path);
return 0;
}
_pid = fork();
if (_pid < 0)
return log_error_errno(errno, "Failed to fork: %m");
if (_pid == 0) {
char *_argv[2];
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
if (stdout_fd >= 0) {
/* If the fd happens to be in the right place, go along with that */
if (stdout_fd != STDOUT_FILENO &&
dup2(stdout_fd, STDOUT_FILENO) < 0)
return -errno;
fd_cloexec(STDOUT_FILENO, false);
}
if (!argv) {
_argv[0] = (char*) path;
_argv[1] = NULL;
argv = _argv;
} else
argv[0] = (char*) path;
execv(path, argv);
log_error_errno(errno, "Failed to execute %s: %m", path);
_exit(EXIT_FAILURE);
}
log_debug("Spawned %s as " PID_FMT ".", path, _pid);
*pid = _pid;
return 1;
}
static int do_execute(
char **directories,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
int output_fd,
char *argv[]) {
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_strv_free_ char **paths = NULL;
char **path;
int r;
/* We fork this all off from a child process so that we can somewhat cleanly make
* use of SIGALRM to set a time limit.
*
* If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
*/
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories);
if (r < 0)
return r;
if (!callbacks) {
pids = hashmap_new(NULL);
if (!pids)
return log_oom();
}
/* Abort execution of this process after the timout. We simply rely on SIGALRM as
* default action terminating the process, and turn on alarm(). */
if (timeout != USEC_INFINITY)
alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
STRV_FOREACH(path, paths) {
_cleanup_free_ char *t = NULL;
_cleanup_close_ int fd = -1;
pid_t pid;
t = strdup(*path);
if (!t)
return log_oom();
if (callbacks) {
fd = open_serialization_fd(basename(*path));
if (fd < 0)
return log_error_errno(fd, "Failed to open serialization file: %m");
}
r = do_spawn(t, argv, fd, &pid);
if (r <= 0)
continue;
if (pids) {
r = hashmap_put(pids, PID_TO_PTR(pid), t);
if (r < 0)
return log_oom();
t = NULL;
} else {
r = wait_for_terminate_and_warn(t, pid, true);
if (r < 0)
continue;
if (lseek(fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek on serialization fd: %m");
r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
fd = -1;
if (r < 0)
return log_error_errno(r, "Failed to process output from %s: %m", *path);
}
}
if (callbacks) {
r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
if (r < 0)
return log_error_errno(r, "Callback two failed: %m");
}
while (!hashmap_isempty(pids)) {
_cleanup_free_ char *t = NULL;
pid_t pid;
pid = PTR_TO_PID(hashmap_first_key(pids));
assert(pid > 0);
t = hashmap_remove(pids, PID_TO_PTR(pid));
assert(t);
wait_for_terminate_and_warn(t, pid, true);
}
return 0;
}
int execute_directories(
const char* const* directories,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
char *argv[]) {
pid_t executor_pid;
char *name;
char **dirs = (char**) directories;
_cleanup_close_ int fd = -1;
int r;
assert(!strv_isempty(dirs));
name = basename(dirs[0]);
assert(!isempty(name));
if (callbacks) {
assert(callback_args);
assert(callbacks[STDOUT_GENERATE]);
assert(callbacks[STDOUT_COLLECT]);
assert(callbacks[STDOUT_CONSUME]);
fd = open_serialization_fd(name);
if (fd < 0)
return log_error_errno(fd, "Failed to open serialization file: %m");
}
/* Executes all binaries in the directories serially or in parallel and waits for
* them to finish. Optionally a timeout is applied. If a file with the same name
* exists in more than one directory, the earliest one wins. */
executor_pid = fork();
if (executor_pid < 0)
return log_error_errno(errno, "Failed to fork: %m");
if (executor_pid == 0) {
r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
r = wait_for_terminate_and_warn(name, executor_pid, true);
if (r < 0)
return log_error_errno(r, "Execution failed: %m");
if (r > 0) {
/* non-zero return code from child */
log_error("Forker process failed.");
return -EREMOTEIO;
}
if (!callbacks)
return 0;
if (lseek(fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to rewind serialization fd: %m");
r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
fd = -1;
if (r < 0)
return log_error_errno(r, "Failed to parse returned data: %m");
return 0;
}
static int gather_environment_generate(int fd, void *arg) {
char ***env = arg, **x, **y;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_strv_free_ char **new;
int r;
/* Read a series of VAR=value assignments from fd, use them to update the list of
* variables in env. Also update the exported environment.
*
* fd is always consumed, even on error.
*/
assert(env);
f = fdopen(fd, "r");
if (!f) {
safe_close(fd);
return -errno;
}
r = load_env_file_pairs(f, NULL, NULL, &new);
if (r < 0)
return r;
STRV_FOREACH_PAIR(x, y, new) {
char *p;
if (!env_name_is_valid(*x)) {
log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
continue;
}
p = strjoin(*x, "=", *y);
if (!p)
return -ENOMEM;
r = strv_env_replace(env, p);
if (r < 0)
return r;
if (setenv(*x, *y, true) < 0)
return -errno;
}
return r;
}
static int gather_environment_collect(int fd, void *arg) {
char ***env = arg;
_cleanup_fclose_ FILE *f = NULL;
int r;
/* Write out a series of env=cescape(VAR=value) assignments to fd. */
assert(env);
f = fdopen(fd, "w");
if (!f) {
safe_close(fd);
return -errno;
}
r = serialize_environment(f, *env);
if (r < 0)
return r;
if (ferror(f))
return errno > 0 ? -errno : -EIO;
return 0;
}
static int gather_environment_consume(int fd, void *arg) {
char ***env = arg;
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
int r = 0, k;
/* Read a series of env=cescape(VAR=value) assignments from fd into env. */
assert(env);
f = fdopen(fd, "r");
if (!f) {
safe_close(fd);
return -errno;
}
FOREACH_LINE(line, f, return -EIO) {
truncate_nl(line);
k = deserialize_environment(env, line);
if (k < 0)
log_error_errno(k, "Invalid line \"%s\": %m", line);
if (k < 0 && r == 0)
r = k;
}
return r;
}
const gather_stdout_callback_t gather_environment[] = {
gather_environment_generate,
gather_environment_collect,
gather_environment_consume,
};
+40
View File
@@ -0,0 +1,40 @@
/***
This file is part of systemd.
Copyright 2017 Zbigniew Jędrzejewski-Szmek
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include "time-util.h"
typedef int (*gather_stdout_callback_t) (int fd, void *arg);
enum {
STDOUT_GENERATE, /* from generators to helper process */
STDOUT_COLLECT, /* from helper process to main process */
STDOUT_CONSUME, /* process data in main process */
_STDOUT_CONSUME_MAX,
};
int execute_directories(
const char* const* directories,
usec_t timeout,
gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
void* const callback_args[_STDOUT_CONSUME_MAX],
char *argv[]);
extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
+95 -36
View File
@@ -30,6 +30,7 @@
#include "alloc-util.h"
#include "ctype.h"
#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -586,14 +587,9 @@ fail:
return r;
}
static int parse_env_file_push(
static int check_utf8ness_and_warn(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
const char *k;
va_list aq, *ap = userdata;
const char *key, char *value) {
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p = NULL;
@@ -611,6 +607,23 @@ static int parse_env_file_push(
return -EINVAL;
}
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 *))) {
@@ -662,27 +675,19 @@ static int load_env_file_push(
char *p;
int r;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *t = utf8_escape_invalid(key);
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *t = utf8_escape_invalid(value);
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
return -EINVAL;
}
p = strjoin(key, "=", strempty(value));
p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
r = strv_consume(m, p);
if (r < 0)
r = strv_env_replace(m, p);
if (r < 0) {
free(p);
return r;
}
if (n_pushed)
(*n_pushed)++;
@@ -716,19 +721,9 @@ static int load_env_file_push_pairs(
char ***m = userdata;
int r;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *t = utf8_escape_invalid(key);
log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
return -EINVAL;
}
if (value && !utf8_is_valid(value)) {
_cleanup_free_ char *t = utf8_escape_invalid(value);
log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
return -EINVAL;
}
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
r = strv_extend(m, key);
if (r < 0)
@@ -767,6 +762,51 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
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);
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, NEWLINE, merge_env_file_push, env, NULL);
}
static void write_env_var(FILE *f, const char *v) {
const char *p;
@@ -1342,6 +1382,25 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
return fd;
}
int open_serialization_fd(const char *ident) {
int fd = -1;
fd = memfd_create(ident, MFD_CLOEXEC);
if (fd < 0) {
const char *path;
path = getpid() == 1 ? "/run/systemd" : "/tmp";
fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
if (fd < 0)
return fd;
log_debug("Serializing %s to %s.", ident, path);
} else
log_debug("Serializing %s to memfd.", ident);
return fd;
}
int link_tmpfile(int fd, const char *path, const char *target) {
assert(fd >= 0);
+3
View File
@@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, 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);
@@ -84,6 +86,7 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
int open_tmpfile_unlinkable(const char *directory, int flags);
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
int open_serialization_fd(const char *ident);
int link_tmpfile(int fd, const char *path, const char *target);
+6 -6
View File
@@ -564,9 +564,6 @@ int strv_extend_front(char ***l, const char *value) {
/* Like strv_extend(), but prepends rather than appends the new entry */
if (!value)
return 0;
n = strv_length(*l);
/* Increase and overflow check. */
@@ -574,9 +571,12 @@ int strv_extend_front(char ***l, const char *value) {
if (m < n)
return -ENOMEM;
v = strdup(value);
if (!v)
return -ENOMEM;
if (value) {
v = strdup(value);
if (!v)
return -ENOMEM;
} else
v = NULL;
c = realloc_multiply(*l, sizeof(char*), m);
if (!c) {
-143
View File
@@ -59,9 +59,6 @@
#include "user-util.h"
#include "util.h"
/* Put this test here for a lack of better place */
assert_cc(EAGAIN == EWOULDBLOCK);
int saved_argc = 0;
char **saved_argv = NULL;
static int saved_in_initrd = -1;
@@ -80,146 +77,6 @@ size_t page_size(void) {
return pgsz;
}
static int do_execute(char **directories, usec_t timeout, char *argv[]) {
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_set_free_free_ Set *seen = NULL;
char **directory;
/* We fork this all off from a child process so that we can
* somewhat cleanly make use of SIGALRM to set a time limit */
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
pids = hashmap_new(NULL);
if (!pids)
return log_oom();
seen = set_new(&string_hash_ops);
if (!seen)
return log_oom();
STRV_FOREACH(directory, directories) {
_cleanup_closedir_ DIR *d;
struct dirent *de;
d = opendir(*directory);
if (!d) {
if (errno == ENOENT)
continue;
return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
}
FOREACH_DIRENT(de, d, break) {
_cleanup_free_ char *path = NULL;
pid_t pid;
int r;
if (!dirent_is_file(de))
continue;
if (set_contains(seen, de->d_name)) {
log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
continue;
}
r = set_put_strdup(seen, de->d_name);
if (r < 0)
return log_oom();
path = strjoin(*directory, "/", de->d_name);
if (!path)
return log_oom();
if (null_or_empty_path(path)) {
log_debug("%s is empty (a mask).", path);
continue;
}
pid = fork();
if (pid < 0) {
log_error_errno(errno, "Failed to fork: %m");
continue;
} else if (pid == 0) {
char *_argv[2];
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
if (!argv) {
_argv[0] = path;
_argv[1] = NULL;
argv = _argv;
} else
argv[0] = path;
execv(path, argv);
return log_error_errno(errno, "Failed to execute %s: %m", path);
}
log_debug("Spawned %s as " PID_FMT ".", path, pid);
r = hashmap_put(pids, PID_TO_PTR(pid), path);
if (r < 0)
return log_oom();
path = NULL;
}
}
/* Abort execution of this process after the timout. We simply
* rely on SIGALRM as default action terminating the process,
* and turn on alarm(). */
if (timeout != USEC_INFINITY)
alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
while (!hashmap_isempty(pids)) {
_cleanup_free_ char *path = NULL;
pid_t pid;
pid = PTR_TO_PID(hashmap_first_key(pids));
assert(pid > 0);
path = hashmap_remove(pids, PID_TO_PTR(pid));
assert(path);
wait_for_terminate_and_warn(path, pid, true);
}
return 0;
}
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
pid_t executor_pid;
int r;
char *name;
char **dirs = (char**) directories;
assert(!strv_isempty(dirs));
name = basename(dirs[0]);
assert(!isempty(name));
/* Executes all binaries in the directories in parallel and waits
* for them to finish. Optionally a timeout is applied. If a file
* with the same name exists in more than one directory, the
* earliest one wins. */
executor_pid = fork();
if (executor_pid < 0) {
log_error_errno(errno, "Failed to fork: %m");
return;
} else if (executor_pid == 0) {
r = do_execute(dirs, timeout, argv);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
wait_for_terminate_and_warn(name, executor_pid, true);
}
bool plymouth_running(void) {
return access("/run/plymouth/pid", F_OK) >= 0;
}
-2
View File
@@ -65,8 +65,6 @@ static inline const char* enable_disable(bool b) {
return b ? "enable" : "disable";
}
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
bool plymouth_running(void);
bool display_is_local(const char *display) _pure_;