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, (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;
140 fd = open_serialization_fd(basename(*path));
142 return log_error_errno(fd, "Failed to open serialization file: %m");
145 r = do_spawn(t, argv, fd, &pid);
150 r = hashmap_put(pids, PID_TO_PTR(pid), t);
155 r = wait_for_terminate_and_warn(t, pid, true);
159 if (lseek(fd, 0, SEEK_SET) < 0)
160 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
162 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
165 return log_error_errno(r, "Failed to process output from %s: %m", *path);
170 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
172 return log_error_errno(r, "Callback two failed: %m");
175 while (!hashmap_isempty(pids)) {
176 _cleanup_free_ char *t = NULL;
179 pid = PTR_TO_PID(hashmap_first_key(pids));
182 t = hashmap_remove(pids, PID_TO_PTR(pid));
185 wait_for_terminate_and_warn(t, pid, true);
191 int execute_directories(
192 const char* const* directories,
194 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
195 void* const callback_args[_STDOUT_CONSUME_MAX],
200 char **dirs = (char**) directories;
201 _cleanup_close_ int fd = -1;
204 assert(!strv_isempty(dirs));
206 name = basename(dirs[0]);
207 assert(!isempty(name));
210 assert(callback_args);
211 assert(callbacks[STDOUT_GENERATE]);
212 assert(callbacks[STDOUT_COLLECT]);
213 assert(callbacks[STDOUT_CONSUME]);
215 fd = open_serialization_fd(name);
217 return log_error_errno(fd, "Failed to open serialization file: %m");
220 /* Executes all binaries in the directories serially or in parallel and waits for
221 * them to finish. Optionally a timeout is applied. If a file with the same name
222 * exists in more than one directory, the earliest one wins. */
224 executor_pid = fork();
225 if (executor_pid < 0)
226 return log_error_errno(errno, "Failed to fork: %m");
228 if (executor_pid == 0) {
229 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
230 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
233 r = wait_for_terminate_and_warn(name, executor_pid, true);
235 return log_error_errno(r, "Execution failed: %m");
237 /* non-zero return code from child */
238 log_error("Forker process failed.");
245 if (lseek(fd, 0, SEEK_SET) < 0)
246 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
248 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
251 return log_error_errno(r, "Failed to parse returned data: %m");
255 #if 0 /// UNNEEDED by elogind
256 static int gather_environment_generate(int fd, void *arg) {
257 char ***env = arg, **x, **y;
258 _cleanup_fclose_ FILE *f = NULL;
259 _cleanup_strv_free_ char **new;
262 /* Read a series of VAR=value assignments from fd, use them to update the list of
263 * variables in env. Also update the exported environment.
265 * fd is always consumed, even on error.
276 r = load_env_file_pairs(f, NULL, NULL, &new);
280 STRV_FOREACH_PAIR(x, y, new) {
283 if (!env_name_is_valid(*x)) {
284 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
288 p = strjoin(*x, "=", *y);
292 r = strv_env_replace(env, p);
296 if (setenv(*x, *y, true) < 0)
303 static int gather_environment_collect(int fd, void *arg) {
305 _cleanup_fclose_ FILE *f = NULL;
308 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
318 r = serialize_environment(f, *env);
323 return errno > 0 ? -errno : -EIO;
328 static int gather_environment_consume(int fd, void *arg) {
330 _cleanup_fclose_ FILE *f = NULL;
334 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
344 FOREACH_LINE(line, f, return -EIO) {
347 k = deserialize_environment(env, line);
349 log_error_errno(k, "Invalid line \"%s\": %m", line);
357 const gather_stdout_callback_t gather_environment[] = {
358 gather_environment_generate,
359 gather_environment_collect,
360 gather_environment_consume,