2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/prctl.h>
23 #include <sys/types.h>
27 #include "alloc-util.h"
28 #include "conf-files.h"
30 #include "exec-util.h"
35 #include "process-util.h"
37 #include "signal-util.h"
38 #include "stat-util.h"
39 #include "string-util.h"
41 #include "terminal-util.h"
44 /* Put this test here for a lack of better place */
45 assert_cc(EAGAIN == EWOULDBLOCK);
47 static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
51 if (null_or_empty_path(path)) {
52 log_debug("%s is empty (a mask).", path);
58 return log_error_errno(errno, "Failed to fork: %m");
62 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
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 (void) reset_all_signal_handlers();
110 (void) reset_signal_mask();
112 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
114 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
119 pids = hashmap_new(NULL);
124 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
125 * default action terminating the process, and turn on alarm(). */
127 if (timeout != USEC_INFINITY)
128 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
130 STRV_FOREACH(path, paths) {
131 _cleanup_free_ char *t = NULL;
132 _cleanup_close_ int fd = -1;
133 #if 0 /// No "maybe uninitialized" warning in elogind
144 fd = open_serialization_fd(basename(*path));
146 return log_error_errno(fd, "Failed to open serialization file: %m");
149 r = do_spawn(t, argv, fd, &pid);
154 r = hashmap_put(pids, PID_TO_PTR(pid), t);
159 r = wait_for_terminate_and_warn(t, pid, true);
163 if (lseek(fd, 0, SEEK_SET) < 0)
164 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
166 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
169 return log_error_errno(r, "Failed to process output from %s: %m", *path);
174 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
176 return log_error_errno(r, "Callback two failed: %m");
179 while (!hashmap_isempty(pids)) {
180 _cleanup_free_ char *t = NULL;
183 pid = PTR_TO_PID(hashmap_first_key(pids));
186 t = hashmap_remove(pids, PID_TO_PTR(pid));
189 wait_for_terminate_and_warn(t, pid, true);
195 int execute_directories(
196 const char* const* directories,
198 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
199 void* const callback_args[_STDOUT_CONSUME_MAX],
204 char **dirs = (char**) directories;
205 _cleanup_close_ int fd = -1;
208 assert(!strv_isempty(dirs));
210 name = basename(dirs[0]);
211 assert(!isempty(name));
214 assert(callback_args);
215 assert(callbacks[STDOUT_GENERATE]);
216 assert(callbacks[STDOUT_COLLECT]);
217 assert(callbacks[STDOUT_CONSUME]);
219 fd = open_serialization_fd(name);
221 return log_error_errno(fd, "Failed to open serialization file: %m");
224 /* Executes all binaries in the directories serially or in parallel and waits for
225 * them to finish. Optionally a timeout is applied. If a file with the same name
226 * exists in more than one directory, the earliest one wins. */
228 executor_pid = fork();
229 if (executor_pid < 0)
230 return log_error_errno(errno, "Failed to fork: %m");
232 if (executor_pid == 0) {
233 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
234 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
237 r = wait_for_terminate_and_warn(name, executor_pid, true);
239 return log_error_errno(r, "Execution failed: %m");
241 /* non-zero return code from child */
242 log_error("Forker process failed.");
249 if (lseek(fd, 0, SEEK_SET) < 0)
250 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
252 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
255 return log_error_errno(r, "Failed to parse returned data: %m");
259 #if 0 /// UNNEEDED by elogind
260 static int gather_environment_generate(int fd, void *arg) {
261 char ***env = arg, **x, **y;
262 _cleanup_fclose_ FILE *f = NULL;
263 _cleanup_strv_free_ char **new = NULL;
266 /* Read a series of VAR=value assignments from fd, use them to update the list of
267 * variables in env. Also update the exported environment.
269 * fd is always consumed, even on error.
280 r = load_env_file_pairs(f, NULL, NULL, &new);
284 STRV_FOREACH_PAIR(x, y, new) {
287 if (!env_name_is_valid(*x)) {
288 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
292 p = strjoin(*x, "=", *y);
296 r = strv_env_replace(env, p);
300 if (setenv(*x, *y, true) < 0)
307 static int gather_environment_collect(int fd, void *arg) {
309 _cleanup_fclose_ FILE *f = NULL;
312 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
322 r = serialize_environment(f, *env);
327 return errno > 0 ? -errno : -EIO;
332 static int gather_environment_consume(int fd, void *arg) {
334 _cleanup_fclose_ FILE *f = NULL;
338 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
348 FOREACH_LINE(line, f, return -EIO) {
351 k = deserialize_environment(env, line);
353 log_error_errno(k, "Invalid line \"%s\": %m", line);
361 const gather_stdout_callback_t gather_environment[] = {
362 gather_environment_generate,
363 gather_environment_collect,
364 gather_environment_consume,