1 /* SPDX-License-Identifier: LGPL-2.1+ */
10 #include "alloc-util.h"
11 #include "conf-files.h"
13 #include "exec-util.h"
18 #include "process-util.h"
20 #include "signal-util.h"
21 #include "stat-util.h"
22 #include "string-util.h"
24 #include "terminal-util.h"
27 /* Put this test here for a lack of better place */
28 assert_cc(EAGAIN == EWOULDBLOCK);
30 static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
35 if (null_or_empty_path(path)) {
36 log_debug("%s is empty (a mask).", path);
40 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
47 r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
53 _argv[0] = (char*) path;
57 argv[0] = (char*) path;
60 log_error_errno(errno, "Failed to execute %s: %m", path);
68 static int do_execute(
71 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
72 void* const callback_args[_STDOUT_CONSUME_MAX],
76 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
77 _cleanup_strv_free_ char **paths = NULL;
81 /* We fork this all off from a child process so that we can somewhat cleanly make
82 * use of SIGALRM to set a time limit.
84 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
87 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
92 pids = hashmap_new(NULL);
97 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
98 * default action terminating the process, and turn on alarm(). */
100 if (timeout != USEC_INFINITY)
101 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
103 STRV_FOREACH(path, paths) {
104 _cleanup_free_ char *t = NULL;
105 _cleanup_close_ int fd = -1;
106 #if 0 /// No "maybe uninitialized" warning in elogind
117 fd = open_serialization_fd(basename(*path));
119 return log_error_errno(fd, "Failed to open serialization file: %m");
122 r = do_spawn(t, argv, fd, &pid);
127 r = hashmap_put(pids, PID_TO_PTR(pid), t);
132 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
136 if (lseek(fd, 0, SEEK_SET) < 0)
137 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
139 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
142 return log_error_errno(r, "Failed to process output from %s: %m", *path);
147 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
149 return log_error_errno(r, "Callback two failed: %m");
152 while (!hashmap_isempty(pids)) {
153 _cleanup_free_ char *t = NULL;
156 pid = PTR_TO_PID(hashmap_first_key(pids));
159 t = hashmap_remove(pids, PID_TO_PTR(pid));
162 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
168 int execute_directories(
169 const char* const* directories,
171 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
172 void* const callback_args[_STDOUT_CONSUME_MAX],
175 char **dirs = (char**) directories;
176 _cleanup_close_ int fd = -1;
180 assert(!strv_isempty(dirs));
182 name = basename(dirs[0]);
183 assert(!isempty(name));
186 assert(callback_args);
187 assert(callbacks[STDOUT_GENERATE]);
188 assert(callbacks[STDOUT_COLLECT]);
189 assert(callbacks[STDOUT_CONSUME]);
191 fd = open_serialization_fd(name);
193 return log_error_errno(fd, "Failed to open serialization file: %m");
196 /* Executes all binaries in the directories serially or in parallel and waits for
197 * them to finish. Optionally a timeout is applied. If a file with the same name
198 * exists in more than one directory, the earliest one wins. */
200 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
204 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
205 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
211 if (lseek(fd, 0, SEEK_SET) < 0)
212 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
214 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
217 return log_error_errno(r, "Failed to parse returned data: %m");
221 #if 0 /// UNNEEDED by elogind
222 static int gather_environment_generate(int fd, void *arg) {
223 char ***env = arg, **x, **y;
224 _cleanup_fclose_ FILE *f = NULL;
225 _cleanup_strv_free_ char **new = NULL;
228 /* Read a series of VAR=value assignments from fd, use them to update the list of
229 * variables in env. Also update the exported environment.
231 * fd is always consumed, even on error.
242 r = load_env_file_pairs(f, NULL, NULL, &new);
246 STRV_FOREACH_PAIR(x, y, new) {
249 if (!env_name_is_valid(*x)) {
250 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
254 p = strjoin(*x, "=", *y);
258 r = strv_env_replace(env, p);
262 if (setenv(*x, *y, true) < 0)
269 static int gather_environment_collect(int fd, void *arg) {
271 _cleanup_fclose_ FILE *f = NULL;
274 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
284 r = serialize_environment(f, *env);
289 return errno > 0 ? -errno : -EIO;
294 static int gather_environment_consume(int fd, void *arg) {
296 _cleanup_fclose_ FILE *f = NULL;
300 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
310 FOREACH_LINE(line, f, return -EIO) {
313 k = deserialize_environment(env, line);
315 log_error_errno(k, "Invalid line \"%s\": %m", line);
323 const gather_stdout_callback_t gather_environment[] = {
324 gather_environment_generate,
325 gather_environment_collect,
326 gather_environment_consume,