1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
10 #include <sys/prctl.h>
11 #include <sys/types.h>
15 #include "alloc-util.h"
16 #include "conf-files.h"
18 #include "exec-util.h"
23 #include "process-util.h"
25 #include "signal-util.h"
26 #include "stat-util.h"
27 #include "string-util.h"
29 #include "terminal-util.h"
32 /* Put this test here for a lack of better place */
33 assert_cc(EAGAIN == EWOULDBLOCK);
35 static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
40 if (null_or_empty_path(path)) {
41 log_debug("%s is empty (a mask).", path);
45 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
52 r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
58 _argv[0] = (char*) path;
62 argv[0] = (char*) path;
65 log_error_errno(errno, "Failed to execute %s: %m", path);
73 static int do_execute(
76 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
77 void* const callback_args[_STDOUT_CONSUME_MAX],
81 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
82 _cleanup_strv_free_ char **paths = NULL;
86 /* We fork this all off from a child process so that we can somewhat cleanly make
87 * use of SIGALRM to set a time limit.
89 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
92 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
97 pids = hashmap_new(NULL);
102 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
103 * default action terminating the process, and turn on alarm(). */
105 if (timeout != USEC_INFINITY)
106 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
108 STRV_FOREACH(path, paths) {
109 _cleanup_free_ char *t = NULL;
110 _cleanup_close_ int fd = -1;
111 #if 0 /// No "maybe uninitialized" warning in elogind
122 fd = open_serialization_fd(basename(*path));
124 return log_error_errno(fd, "Failed to open serialization file: %m");
127 r = do_spawn(t, argv, fd, &pid);
132 r = hashmap_put(pids, PID_TO_PTR(pid), t);
137 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
141 if (lseek(fd, 0, SEEK_SET) < 0)
142 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
144 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
147 return log_error_errno(r, "Failed to process output from %s: %m", *path);
152 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
154 return log_error_errno(r, "Callback two failed: %m");
157 while (!hashmap_isempty(pids)) {
158 _cleanup_free_ char *t = NULL;
161 pid = PTR_TO_PID(hashmap_first_key(pids));
164 t = hashmap_remove(pids, PID_TO_PTR(pid));
167 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
173 int execute_directories(
174 const char* const* directories,
176 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
177 void* const callback_args[_STDOUT_CONSUME_MAX],
180 char **dirs = (char**) directories;
181 _cleanup_close_ int fd = -1;
185 assert(!strv_isempty(dirs));
187 name = basename(dirs[0]);
188 assert(!isempty(name));
191 assert(callback_args);
192 assert(callbacks[STDOUT_GENERATE]);
193 assert(callbacks[STDOUT_COLLECT]);
194 assert(callbacks[STDOUT_CONSUME]);
196 fd = open_serialization_fd(name);
198 return log_error_errno(fd, "Failed to open serialization file: %m");
201 /* Executes all binaries in the directories serially or in parallel and waits for
202 * them to finish. Optionally a timeout is applied. If a file with the same name
203 * exists in more than one directory, the earliest one wins. */
205 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
209 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
210 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
216 if (lseek(fd, 0, SEEK_SET) < 0)
217 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
219 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
222 return log_error_errno(r, "Failed to parse returned data: %m");
226 #if 0 /// UNNEEDED by elogind
227 static int gather_environment_generate(int fd, void *arg) {
228 char ***env = arg, **x, **y;
229 _cleanup_fclose_ FILE *f = NULL;
230 _cleanup_strv_free_ char **new = NULL;
233 /* Read a series of VAR=value assignments from fd, use them to update the list of
234 * variables in env. Also update the exported environment.
236 * fd is always consumed, even on error.
247 r = load_env_file_pairs(f, NULL, NULL, &new);
251 STRV_FOREACH_PAIR(x, y, new) {
254 if (!env_name_is_valid(*x)) {
255 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
259 p = strjoin(*x, "=", *y);
263 r = strv_env_replace(env, p);
267 if (setenv(*x, *y, true) < 0)
274 static int gather_environment_collect(int fd, void *arg) {
276 _cleanup_fclose_ FILE *f = NULL;
279 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
289 r = serialize_environment(f, *env);
294 return errno > 0 ? -errno : -EIO;
299 static int gather_environment_consume(int fd, void *arg) {
301 _cleanup_fclose_ FILE *f = NULL;
305 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
315 FOREACH_LINE(line, f, return -EIO) {
318 k = deserialize_environment(env, line);
320 log_error_errno(k, "Invalid line \"%s\": %m", line);
328 const gather_stdout_callback_t gather_environment[] = {
329 gather_environment_generate,
330 gather_environment_collect,
331 gather_environment_consume,