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
) {
52 if (null_or_empty_path(path
)) {
53 log_debug("%s is empty (a mask).", path
);
59 return log_error_errno(errno
, "Failed to fork: %m");
63 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
66 /* If the fd happens to be in the right place, go along with that */
67 if (stdout_fd
!= STDOUT_FILENO
&&
68 dup2(stdout_fd
, STDOUT_FILENO
) < 0)
71 fd_cloexec(STDOUT_FILENO
, false);
75 _argv
[0] = (char*) path
;
79 argv
[0] = (char*) path
;
82 log_error_errno(errno
, "Failed to execute %s: %m", path
);
86 log_debug("Spawned %s as " PID_FMT
".", path
, _pid
);
91 static int do_execute(
94 gather_stdout_callback_t
const callbacks
[_STDOUT_CONSUME_MAX
],
95 void* const callback_args
[_STDOUT_CONSUME_MAX
],
99 _cleanup_hashmap_free_free_ Hashmap
*pids
= NULL
;
100 _cleanup_strv_free_
char **paths
= NULL
;
104 /* We fork this all off from a child process so that we can somewhat cleanly make
105 * use of SIGALRM to set a time limit.
107 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
110 (void) reset_all_signal_handlers();
111 (void) reset_signal_mask();
113 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
115 r
= conf_files_list_strv(&paths
, NULL
, NULL
, CONF_FILES_EXECUTABLE
, (const char* const*) directories
);
120 pids
= hashmap_new(NULL
);
125 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
126 * default action terminating the process, and turn on alarm(). */
128 if (timeout
!= USEC_INFINITY
)
129 alarm((timeout
+ USEC_PER_SEC
- 1) / USEC_PER_SEC
);
131 STRV_FOREACH(path
, paths
) {
132 _cleanup_free_
char *t
= NULL
;
133 _cleanup_close_
int fd
= -1;
141 fd
= open_serialization_fd(basename(*path
));
143 return log_error_errno(fd
, "Failed to open serialization file: %m");
146 r
= do_spawn(t
, argv
, fd
, &pid
);
151 r
= hashmap_put(pids
, PID_TO_PTR(pid
), t
);
156 r
= wait_for_terminate_and_warn(t
, pid
, true);
160 if (lseek(fd
, 0, SEEK_SET
) < 0)
161 return log_error_errno(errno
, "Failed to seek on serialization fd: %m");
163 r
= callbacks
[STDOUT_GENERATE
](fd
, callback_args
[STDOUT_GENERATE
]);
166 return log_error_errno(r
, "Failed to process output from %s: %m", *path
);
171 r
= callbacks
[STDOUT_COLLECT
](output_fd
, callback_args
[STDOUT_COLLECT
]);
173 return log_error_errno(r
, "Callback two failed: %m");
176 while (!hashmap_isempty(pids
)) {
177 _cleanup_free_
char *t
= NULL
;
180 pid
= PTR_TO_PID(hashmap_first_key(pids
));
183 t
= hashmap_remove(pids
, PID_TO_PTR(pid
));
186 wait_for_terminate_and_warn(t
, pid
, true);
192 int execute_directories(
193 const char* const* directories
,
195 gather_stdout_callback_t
const callbacks
[_STDOUT_CONSUME_MAX
],
196 void* const callback_args
[_STDOUT_CONSUME_MAX
],
201 char **dirs
= (char**) directories
;
202 _cleanup_close_
int fd
= -1;
205 assert(!strv_isempty(dirs
));
207 name
= basename(dirs
[0]);
208 assert(!isempty(name
));
211 assert(callback_args
);
212 assert(callbacks
[STDOUT_GENERATE
]);
213 assert(callbacks
[STDOUT_COLLECT
]);
214 assert(callbacks
[STDOUT_CONSUME
]);
216 fd
= open_serialization_fd(name
);
218 return log_error_errno(fd
, "Failed to open serialization file: %m");
221 /* Executes all binaries in the directories serially or in parallel and waits for
222 * them to finish. Optionally a timeout is applied. If a file with the same name
223 * exists in more than one directory, the earliest one wins. */
225 executor_pid
= fork();
226 if (executor_pid
< 0)
227 return log_error_errno(errno
, "Failed to fork: %m");
229 if (executor_pid
== 0) {
230 r
= do_execute(dirs
, timeout
, callbacks
, callback_args
, fd
, argv
);
231 _exit(r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
);
234 r
= wait_for_terminate_and_warn(name
, executor_pid
, true);
236 return log_error_errno(r
, "Execution failed: %m");
238 /* non-zero return code from child */
239 log_error("Forker process failed.");
246 if (lseek(fd
, 0, SEEK_SET
) < 0)
247 return log_error_errno(errno
, "Failed to rewind serialization fd: %m");
249 r
= callbacks
[STDOUT_CONSUME
](fd
, callback_args
[STDOUT_CONSUME
]);
252 return log_error_errno(r
, "Failed to parse returned data: %m");
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
,