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|FORK_LOG, &_pid);
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 (void) 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);
89 static int do_execute(
92 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
93 void* const callback_args[_STDOUT_CONSUME_MAX],
97 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
98 _cleanup_strv_free_ char **paths = NULL;
102 /* We fork this all off from a child process so that we can somewhat cleanly make
103 * use of SIGALRM to set a time limit.
105 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
108 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
113 pids = hashmap_new(NULL);
118 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
119 * default action terminating the process, and turn on alarm(). */
121 if (timeout != USEC_INFINITY)
122 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
124 STRV_FOREACH(path, paths) {
125 _cleanup_free_ char *t = NULL;
126 _cleanup_close_ int fd = -1;
127 #if 0 /// No "maybe uninitialized" warning in elogind
138 fd = open_serialization_fd(basename(*path));
140 return log_error_errno(fd, "Failed to open serialization file: %m");
143 r = do_spawn(t, argv, fd, &pid);
148 r = hashmap_put(pids, PID_TO_PTR(pid), t);
153 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
157 if (lseek(fd, 0, SEEK_SET) < 0)
158 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
160 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
163 return log_error_errno(r, "Failed to process output from %s: %m", *path);
168 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
170 return log_error_errno(r, "Callback two failed: %m");
173 while (!hashmap_isempty(pids)) {
174 _cleanup_free_ char *t = NULL;
177 pid = PTR_TO_PID(hashmap_first_key(pids));
180 t = hashmap_remove(pids, PID_TO_PTR(pid));
183 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
189 int execute_directories(
190 const char* const* directories,
192 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
193 void* const callback_args[_STDOUT_CONSUME_MAX],
196 char **dirs = (char**) directories;
197 _cleanup_close_ int fd = -1;
201 assert(!strv_isempty(dirs));
203 name = basename(dirs[0]);
204 assert(!isempty(name));
207 assert(callback_args);
208 assert(callbacks[STDOUT_GENERATE]);
209 assert(callbacks[STDOUT_COLLECT]);
210 assert(callbacks[STDOUT_CONSUME]);
212 fd = open_serialization_fd(name);
214 return log_error_errno(fd, "Failed to open serialization file: %m");
217 /* Executes all binaries in the directories serially or in parallel and waits for
218 * them to finish. Optionally a timeout is applied. If a file with the same name
219 * exists in more than one directory, the earliest one wins. */
221 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
225 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
226 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
232 if (lseek(fd, 0, SEEK_SET) < 0)
233 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
235 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
238 return log_error_errno(r, "Failed to parse returned data: %m");
242 #if 0 /// UNNEEDED by elogind
243 static int gather_environment_generate(int fd, void *arg) {
244 char ***env = arg, **x, **y;
245 _cleanup_fclose_ FILE *f = NULL;
246 _cleanup_strv_free_ char **new = NULL;
249 /* Read a series of VAR=value assignments from fd, use them to update the list of
250 * variables in env. Also update the exported environment.
252 * fd is always consumed, even on error.
263 r = load_env_file_pairs(f, NULL, NULL, &new);
267 STRV_FOREACH_PAIR(x, y, new) {
270 if (!env_name_is_valid(*x)) {
271 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
275 p = strjoin(*x, "=", *y);
279 r = strv_env_replace(env, p);
283 if (setenv(*x, *y, true) < 0)
290 static int gather_environment_collect(int fd, void *arg) {
292 _cleanup_fclose_ FILE *f = NULL;
295 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
305 r = serialize_environment(f, *env);
310 return errno > 0 ? -errno : -EIO;
315 static int gather_environment_consume(int fd, void *arg) {
317 _cleanup_fclose_ FILE *f = NULL;
321 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
331 FOREACH_LINE(line, f, return -EIO) {
334 k = deserialize_environment(env, line);
336 log_error_errno(k, "Invalid line \"%s\": %m", line);
344 const gather_stdout_callback_t gather_environment[] = {
345 gather_environment_generate,
346 gather_environment_collect,
347 gather_environment_consume,