1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/prctl.h>
24 #include <sys/types.h>
28 #include "alloc-util.h"
29 #include "conf-files.h"
31 #include "exec-util.h"
36 #include "process-util.h"
38 #include "signal-util.h"
39 #include "stat-util.h"
40 #include "string-util.h"
42 #include "terminal-util.h"
45 /* Put this test here for a lack of better place */
46 assert_cc(EAGAIN == EWOULDBLOCK);
48 static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
53 if (null_or_empty_path(path)) {
54 log_debug("%s is empty (a mask).", path);
58 r = safe_fork("(direxec)", FORK_DEATHSIG, &_pid);
60 return log_error_errno(r, "Failed to fork: %m");
65 /* If the fd happens to be in the right place, go along with that */
66 if (stdout_fd != STDOUT_FILENO &&
67 dup2(stdout_fd, STDOUT_FILENO) < 0)
70 fd_cloexec(STDOUT_FILENO, false);
74 _argv[0] = (char*) path;
78 argv[0] = (char*) path;
81 log_error_errno(errno, "Failed to execute %s: %m", path);
85 log_debug("Spawned %s as " PID_FMT ".", path, _pid);
90 static int do_execute(
93 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
94 void* const callback_args[_STDOUT_CONSUME_MAX],
98 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
99 _cleanup_strv_free_ char **paths = NULL;
103 /* We fork this all off from a child process so that we can somewhat cleanly make
104 * use of SIGALRM to set a time limit.
106 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
109 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
114 pids = hashmap_new(NULL);
119 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
120 * default action terminating the process, and turn on alarm(). */
122 if (timeout != USEC_INFINITY)
123 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
125 STRV_FOREACH(path, paths) {
126 _cleanup_free_ char *t = NULL;
127 _cleanup_close_ int fd = -1;
128 #if 0 /// No "maybe uninitialized" warning in elogind
139 fd = open_serialization_fd(basename(*path));
141 return log_error_errno(fd, "Failed to open serialization file: %m");
144 r = do_spawn(t, argv, fd, &pid);
149 r = hashmap_put(pids, PID_TO_PTR(pid), t);
154 r = wait_for_terminate_and_warn(t, pid, true);
158 if (lseek(fd, 0, SEEK_SET) < 0)
159 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
161 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
164 return log_error_errno(r, "Failed to process output from %s: %m", *path);
169 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
171 return log_error_errno(r, "Callback two failed: %m");
174 while (!hashmap_isempty(pids)) {
175 _cleanup_free_ char *t = NULL;
178 pid = PTR_TO_PID(hashmap_first_key(pids));
181 t = hashmap_remove(pids, PID_TO_PTR(pid));
184 wait_for_terminate_and_warn(t, pid, true);
190 int execute_directories(
191 const char* const* directories,
193 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
194 void* const callback_args[_STDOUT_CONSUME_MAX],
199 char **dirs = (char**) directories;
200 _cleanup_close_ int fd = -1;
203 assert(!strv_isempty(dirs));
205 name = basename(dirs[0]);
206 assert(!isempty(name));
209 assert(callback_args);
210 assert(callbacks[STDOUT_GENERATE]);
211 assert(callbacks[STDOUT_COLLECT]);
212 assert(callbacks[STDOUT_CONSUME]);
214 fd = open_serialization_fd(name);
216 return log_error_errno(fd, "Failed to open serialization file: %m");
219 /* Executes all binaries in the directories serially or in parallel and waits for
220 * them to finish. Optionally a timeout is applied. If a file with the same name
221 * exists in more than one directory, the earliest one wins. */
223 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &executor_pid);
225 return log_error_errno(r, "Failed to fork: %m");
227 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
228 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
231 r = wait_for_terminate_and_warn(name, executor_pid, true);
233 return log_error_errno(r, "Execution failed: %m");
235 /* non-zero return code from child */
236 log_error("Forker process failed.");
243 if (lseek(fd, 0, SEEK_SET) < 0)
244 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
246 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
249 return log_error_errno(r, "Failed to parse returned data: %m");
253 #if 0 /// UNNEEDED by elogind
254 static int gather_environment_generate(int fd, void *arg) {
255 char ***env = arg, **x, **y;
256 _cleanup_fclose_ FILE *f = NULL;
257 _cleanup_strv_free_ char **new = NULL;
260 /* Read a series of VAR=value assignments from fd, use them to update the list of
261 * variables in env. Also update the exported environment.
263 * fd is always consumed, even on error.
274 r = load_env_file_pairs(f, NULL, NULL, &new);
278 STRV_FOREACH_PAIR(x, y, new) {
281 if (!env_name_is_valid(*x)) {
282 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
286 p = strjoin(*x, "=", *y);
290 r = strv_env_replace(env, p);
294 if (setenv(*x, *y, true) < 0)
301 static int gather_environment_collect(int fd, void *arg) {
303 _cleanup_fclose_ FILE *f = NULL;
306 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
316 r = serialize_environment(f, *env);
321 return errno > 0 ? -errno : -EIO;
326 static int gather_environment_consume(int fd, void *arg) {
328 _cleanup_fclose_ FILE *f = NULL;
332 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
342 FOREACH_LINE(line, f, return -EIO) {
345 k = deserialize_environment(env, line);
347 log_error_errno(k, "Invalid line \"%s\": %m", line);
355 const gather_stdout_callback_t gather_environment[] = {
356 gather_environment_generate,
357 gather_environment_collect,
358 gather_environment_consume,