]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/exec-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / basic / exec-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
89711996
ZJS
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
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.
11
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.
16
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/>.
19***/
20
21#include <dirent.h>
22#include <errno.h>
23#include <sys/prctl.h>
24#include <sys/types.h>
25#include <unistd.h>
c6e47247 26#include <stdio.h>
89711996
ZJS
27
28#include "alloc-util.h"
2e4cfe65 29#include "conf-files.h"
c6e47247 30#include "env-util.h"
89711996 31#include "exec-util.h"
c6e47247
ZJS
32#include "fd-util.h"
33#include "fileio.h"
89711996
ZJS
34#include "hashmap.h"
35#include "macro.h"
36#include "process-util.h"
37#include "set.h"
38#include "signal-util.h"
39#include "stat-util.h"
40#include "string-util.h"
41#include "strv.h"
c6e47247 42#include "terminal-util.h"
89711996
ZJS
43#include "util.h"
44
45/* Put this test here for a lack of better place */
46assert_cc(EAGAIN == EWOULDBLOCK);
47
c6e47247
ZJS
48static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
49
cf55fc18
ZJS
50 pid_t _pid;
51
52 if (null_or_empty_path(path)) {
53 log_debug("%s is empty (a mask).", path);
54 return 0;
55 }
56
57 _pid = fork();
58 if (_pid < 0)
59 return log_error_errno(errno, "Failed to fork: %m");
60 if (_pid == 0) {
61 char *_argv[2];
62
63 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
64
c6e47247
ZJS
65 if (stdout_fd >= 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)
69 return -errno;
70
71 fd_cloexec(STDOUT_FILENO, false);
72 }
73
cf55fc18
ZJS
74 if (!argv) {
75 _argv[0] = (char*) path;
76 _argv[1] = NULL;
77 argv = _argv;
78 } else
79 argv[0] = (char*) path;
80
81 execv(path, argv);
82 log_error_errno(errno, "Failed to execute %s: %m", path);
83 _exit(EXIT_FAILURE);
84 }
85
86 log_debug("Spawned %s as " PID_FMT ".", path, _pid);
87 *pid = _pid;
88 return 1;
89}
90
c6e47247
ZJS
91static int do_execute(
92 char **directories,
93 usec_t timeout,
94 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
95 void* const callback_args[_STDOUT_CONSUME_MAX],
96 int output_fd,
97 char *argv[]) {
98
89711996 99 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65
ZJS
100 _cleanup_strv_free_ char **paths = NULL;
101 char **path;
102 int r;
89711996 103
c6e47247
ZJS
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.
106 *
107 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
108 */
89711996
ZJS
109
110 (void) reset_all_signal_handlers();
111 (void) reset_signal_mask();
112
113 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
114
b5084605 115 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
2e4cfe65
ZJS
116 if (r < 0)
117 return r;
118
c6e47247
ZJS
119 if (!callbacks) {
120 pids = hashmap_new(NULL);
121 if (!pids)
122 return log_oom();
123 }
124
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(). */
127
128 if (timeout != USEC_INFINITY)
129 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
89711996 130
2e4cfe65
ZJS
131 STRV_FOREACH(path, paths) {
132 _cleanup_free_ char *t = NULL;
c6e47247 133 _cleanup_close_ int fd = -1;
2e4cfe65 134 pid_t pid;
89711996 135
2e4cfe65
ZJS
136 t = strdup(*path);
137 if (!t)
138 return log_oom();
89711996 139
c6e47247
ZJS
140 if (callbacks) {
141 fd = open_serialization_fd(basename(*path));
142 if (fd < 0)
143 return log_error_errno(fd, "Failed to open serialization file: %m");
144 }
145
146 r = do_spawn(t, argv, fd, &pid);
2e4cfe65
ZJS
147 if (r <= 0)
148 continue;
89711996 149
c6e47247
ZJS
150 if (pids) {
151 r = hashmap_put(pids, PID_TO_PTR(pid), t);
152 if (r < 0)
153 return log_oom();
154 t = NULL;
155 } else {
156 r = wait_for_terminate_and_warn(t, pid, true);
157 if (r < 0)
158 continue;
159
160 if (lseek(fd, 0, SEEK_SET) < 0)
161 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
162
163 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
164 fd = -1;
165 if (r < 0)
166 return log_error_errno(r, "Failed to process output from %s: %m", *path);
167 }
89711996
ZJS
168 }
169
c6e47247
ZJS
170 if (callbacks) {
171 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
172 if (r < 0)
173 return log_error_errno(r, "Callback two failed: %m");
174 }
89711996
ZJS
175
176 while (!hashmap_isempty(pids)) {
2e4cfe65 177 _cleanup_free_ char *t = NULL;
89711996
ZJS
178 pid_t pid;
179
180 pid = PTR_TO_PID(hashmap_first_key(pids));
181 assert(pid > 0);
182
2e4cfe65
ZJS
183 t = hashmap_remove(pids, PID_TO_PTR(pid));
184 assert(t);
89711996 185
2e4cfe65 186 wait_for_terminate_and_warn(t, pid, true);
89711996
ZJS
187 }
188
189 return 0;
190}
191
c6e47247
ZJS
192int execute_directories(
193 const char* const* directories,
194 usec_t timeout,
195 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
196 void* const callback_args[_STDOUT_CONSUME_MAX],
197 char *argv[]) {
198
89711996 199 pid_t executor_pid;
89711996
ZJS
200 char *name;
201 char **dirs = (char**) directories;
c6e47247
ZJS
202 _cleanup_close_ int fd = -1;
203 int r;
89711996
ZJS
204
205 assert(!strv_isempty(dirs));
206
207 name = basename(dirs[0]);
208 assert(!isempty(name));
209
c6e47247
ZJS
210 if (callbacks) {
211 assert(callback_args);
212 assert(callbacks[STDOUT_GENERATE]);
213 assert(callbacks[STDOUT_COLLECT]);
214 assert(callbacks[STDOUT_CONSUME]);
215
216 fd = open_serialization_fd(name);
217 if (fd < 0)
218 return log_error_errno(fd, "Failed to open serialization file: %m");
219 }
220
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. */
89711996
ZJS
224
225 executor_pid = fork();
c6e47247
ZJS
226 if (executor_pid < 0)
227 return log_error_errno(errno, "Failed to fork: %m");
89711996 228
c6e47247
ZJS
229 if (executor_pid == 0) {
230 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
89711996
ZJS
231 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
232 }
233
c6e47247
ZJS
234 r = wait_for_terminate_and_warn(name, executor_pid, true);
235 if (r < 0)
236 return log_error_errno(r, "Execution failed: %m");
237 if (r > 0) {
238 /* non-zero return code from child */
239 log_error("Forker process failed.");
240 return -EREMOTEIO;
241 }
242
243 if (!callbacks)
244 return 0;
245
246 if (lseek(fd, 0, SEEK_SET) < 0)
247 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
248
249 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
250 fd = -1;
251 if (r < 0)
252 return log_error_errno(r, "Failed to parse returned data: %m");
253 return 0;
89711996 254}
3303d1b2
ZJS
255
256static 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;
260 int r;
261
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.
264 *
265 * fd is always consumed, even on error.
266 */
267
268 assert(env);
269
270 f = fdopen(fd, "r");
271 if (!f) {
272 safe_close(fd);
273 return -errno;
274 }
275
276 r = load_env_file_pairs(f, NULL, NULL, &new);
277 if (r < 0)
278 return r;
279
280 STRV_FOREACH_PAIR(x, y, new) {
281 char *p;
282
184d1904
ZJS
283 if (!env_name_is_valid(*x)) {
284 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
285 continue;
286 }
287
3303d1b2
ZJS
288 p = strjoin(*x, "=", *y);
289 if (!p)
290 return -ENOMEM;
291
292 r = strv_env_replace(env, p);
293 if (r < 0)
294 return r;
295
296 if (setenv(*x, *y, true) < 0)
297 return -errno;
298 }
299
300 return r;
301}
302
303static int gather_environment_collect(int fd, void *arg) {
304 char ***env = arg;
305 _cleanup_fclose_ FILE *f = NULL;
306 int r;
307
308 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
309
310 assert(env);
311
312 f = fdopen(fd, "w");
313 if (!f) {
314 safe_close(fd);
315 return -errno;
316 }
317
318 r = serialize_environment(f, *env);
319 if (r < 0)
320 return r;
321
322 if (ferror(f))
323 return errno > 0 ? -errno : -EIO;
324
325 return 0;
326}
327
328static int gather_environment_consume(int fd, void *arg) {
329 char ***env = arg;
330 _cleanup_fclose_ FILE *f = NULL;
331 char line[LINE_MAX];
332 int r = 0, k;
333
334 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
335
336 assert(env);
337
338 f = fdopen(fd, "r");
339 if (!f) {
340 safe_close(fd);
341 return -errno;
342 }
343
344 FOREACH_LINE(line, f, return -EIO) {
345 truncate_nl(line);
346
347 k = deserialize_environment(env, line);
348 if (k < 0)
349 log_error_errno(k, "Invalid line \"%s\": %m", line);
350 if (k < 0 && r == 0)
351 r = k;
352 }
353
354 return r;
355}
356
357const gather_stdout_callback_t gather_environment[] = {
358 gather_environment_generate,
359 gather_environment_collect,
360 gather_environment_consume,
361};