]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/exec-util.c
tree-wide: drop license boilerplate
[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
89711996
ZJS
6***/
7
8#include <dirent.h>
9#include <errno.h>
10#include <sys/prctl.h>
11#include <sys/types.h>
12#include <unistd.h>
c6e47247 13#include <stdio.h>
89711996
ZJS
14
15#include "alloc-util.h"
2e4cfe65 16#include "conf-files.h"
c6e47247 17#include "env-util.h"
89711996 18#include "exec-util.h"
c6e47247
ZJS
19#include "fd-util.h"
20#include "fileio.h"
89711996
ZJS
21#include "hashmap.h"
22#include "macro.h"
23#include "process-util.h"
24#include "set.h"
25#include "signal-util.h"
26#include "stat-util.h"
27#include "string-util.h"
28#include "strv.h"
c6e47247 29#include "terminal-util.h"
89711996
ZJS
30#include "util.h"
31
32/* Put this test here for a lack of better place */
33assert_cc(EAGAIN == EWOULDBLOCK);
34
c6e47247
ZJS
35static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
36
cf55fc18 37 pid_t _pid;
4c253ed1 38 int r;
cf55fc18
ZJS
39
40 if (null_or_empty_path(path)) {
41 log_debug("%s is empty (a mask).", path);
42 return 0;
43 }
44
b6e1fff1 45 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
4c253ed1 46 if (r < 0)
b6e1fff1 47 return r;
4c253ed1 48 if (r == 0) {
cf55fc18
ZJS
49 char *_argv[2];
50
c6e47247 51 if (stdout_fd >= 0) {
2b33ab09
LP
52 r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
53 if (r < 0)
3554ef51 54 _exit(EXIT_FAILURE);
c6e47247
ZJS
55 }
56
cf55fc18
ZJS
57 if (!argv) {
58 _argv[0] = (char*) path;
59 _argv[1] = NULL;
60 argv = _argv;
61 } else
62 argv[0] = (char*) path;
63
64 execv(path, argv);
65 log_error_errno(errno, "Failed to execute %s: %m", path);
66 _exit(EXIT_FAILURE);
67 }
68
cf55fc18
ZJS
69 *pid = _pid;
70 return 1;
71}
72
c6e47247
ZJS
73static int do_execute(
74 char **directories,
75 usec_t timeout,
76 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
77 void* const callback_args[_STDOUT_CONSUME_MAX],
78 int output_fd,
79 char *argv[]) {
80
89711996 81 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65
ZJS
82 _cleanup_strv_free_ char **paths = NULL;
83 char **path;
84 int r;
89711996 85
c6e47247
ZJS
86 /* We fork this all off from a child process so that we can somewhat cleanly make
87 * use of SIGALRM to set a time limit.
88 *
89 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
90 */
89711996 91
b5084605 92 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
2e4cfe65
ZJS
93 if (r < 0)
94 return r;
95
c6e47247
ZJS
96 if (!callbacks) {
97 pids = hashmap_new(NULL);
98 if (!pids)
99 return log_oom();
100 }
101
102 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
103 * default action terminating the process, and turn on alarm(). */
104
105 if (timeout != USEC_INFINITY)
be6b0c21 106 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
89711996 107
2e4cfe65
ZJS
108 STRV_FOREACH(path, paths) {
109 _cleanup_free_ char *t = NULL;
c6e47247 110 _cleanup_close_ int fd = -1;
2e4cfe65 111 pid_t pid;
89711996 112
2e4cfe65
ZJS
113 t = strdup(*path);
114 if (!t)
115 return log_oom();
89711996 116
c6e47247
ZJS
117 if (callbacks) {
118 fd = open_serialization_fd(basename(*path));
119 if (fd < 0)
120 return log_error_errno(fd, "Failed to open serialization file: %m");
121 }
122
123 r = do_spawn(t, argv, fd, &pid);
2e4cfe65
ZJS
124 if (r <= 0)
125 continue;
89711996 126
c6e47247
ZJS
127 if (pids) {
128 r = hashmap_put(pids, PID_TO_PTR(pid), t);
129 if (r < 0)
130 return log_oom();
131 t = NULL;
132 } else {
7d4904fe 133 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
c6e47247
ZJS
134 if (r < 0)
135 continue;
136
137 if (lseek(fd, 0, SEEK_SET) < 0)
138 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
139
140 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
141 fd = -1;
142 if (r < 0)
143 return log_error_errno(r, "Failed to process output from %s: %m", *path);
144 }
89711996
ZJS
145 }
146
c6e47247
ZJS
147 if (callbacks) {
148 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
149 if (r < 0)
150 return log_error_errno(r, "Callback two failed: %m");
151 }
89711996
ZJS
152
153 while (!hashmap_isempty(pids)) {
2e4cfe65 154 _cleanup_free_ char *t = NULL;
89711996
ZJS
155 pid_t pid;
156
157 pid = PTR_TO_PID(hashmap_first_key(pids));
158 assert(pid > 0);
159
2e4cfe65
ZJS
160 t = hashmap_remove(pids, PID_TO_PTR(pid));
161 assert(t);
89711996 162
7d4904fe 163 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
89711996
ZJS
164 }
165
166 return 0;
167}
168
c6e47247
ZJS
169int execute_directories(
170 const char* const* directories,
171 usec_t timeout,
172 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
173 void* const callback_args[_STDOUT_CONSUME_MAX],
174 char *argv[]) {
175
89711996 176 char **dirs = (char**) directories;
c6e47247 177 _cleanup_close_ int fd = -1;
1f5d1e02 178 char *name;
c6e47247 179 int r;
89711996
ZJS
180
181 assert(!strv_isempty(dirs));
182
183 name = basename(dirs[0]);
184 assert(!isempty(name));
185
c6e47247
ZJS
186 if (callbacks) {
187 assert(callback_args);
188 assert(callbacks[STDOUT_GENERATE]);
189 assert(callbacks[STDOUT_COLLECT]);
190 assert(callbacks[STDOUT_CONSUME]);
191
192 fd = open_serialization_fd(name);
193 if (fd < 0)
194 return log_error_errno(fd, "Failed to open serialization file: %m");
195 }
196
197 /* Executes all binaries in the directories serially or in parallel and waits for
198 * them to finish. Optionally a timeout is applied. If a file with the same name
199 * exists in more than one directory, the earliest one wins. */
89711996 200
1f5d1e02 201 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
4c253ed1 202 if (r < 0)
b6e1fff1 203 return r;
4c253ed1 204 if (r == 0) {
c6e47247 205 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
89711996
ZJS
206 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
207 }
208
c6e47247
ZJS
209 if (!callbacks)
210 return 0;
211
212 if (lseek(fd, 0, SEEK_SET) < 0)
213 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
214
215 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
216 fd = -1;
217 if (r < 0)
218 return log_error_errno(r, "Failed to parse returned data: %m");
219 return 0;
89711996 220}
3303d1b2
ZJS
221
222static int gather_environment_generate(int fd, void *arg) {
223 char ***env = arg, **x, **y;
224 _cleanup_fclose_ FILE *f = NULL;
3f199740 225 _cleanup_strv_free_ char **new = NULL;
3303d1b2
ZJS
226 int r;
227
228 /* Read a series of VAR=value assignments from fd, use them to update the list of
229 * variables in env. Also update the exported environment.
230 *
231 * fd is always consumed, even on error.
232 */
233
234 assert(env);
235
236 f = fdopen(fd, "r");
237 if (!f) {
238 safe_close(fd);
239 return -errno;
240 }
241
242 r = load_env_file_pairs(f, NULL, NULL, &new);
243 if (r < 0)
244 return r;
245
246 STRV_FOREACH_PAIR(x, y, new) {
247 char *p;
248
184d1904
ZJS
249 if (!env_name_is_valid(*x)) {
250 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
251 continue;
252 }
253
3303d1b2
ZJS
254 p = strjoin(*x, "=", *y);
255 if (!p)
256 return -ENOMEM;
257
258 r = strv_env_replace(env, p);
259 if (r < 0)
260 return r;
261
262 if (setenv(*x, *y, true) < 0)
263 return -errno;
264 }
265
266 return r;
267}
268
269static int gather_environment_collect(int fd, void *arg) {
270 char ***env = arg;
271 _cleanup_fclose_ FILE *f = NULL;
272 int r;
273
274 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
275
276 assert(env);
277
278 f = fdopen(fd, "w");
279 if (!f) {
280 safe_close(fd);
281 return -errno;
282 }
283
284 r = serialize_environment(f, *env);
285 if (r < 0)
286 return r;
287
288 if (ferror(f))
289 return errno > 0 ? -errno : -EIO;
290
291 return 0;
292}
293
294static int gather_environment_consume(int fd, void *arg) {
295 char ***env = arg;
296 _cleanup_fclose_ FILE *f = NULL;
297 char line[LINE_MAX];
298 int r = 0, k;
299
300 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
301
302 assert(env);
303
304 f = fdopen(fd, "r");
305 if (!f) {
306 safe_close(fd);
307 return -errno;
308 }
309
310 FOREACH_LINE(line, f, return -EIO) {
311 truncate_nl(line);
312
313 k = deserialize_environment(env, line);
314 if (k < 0)
315 log_error_errno(k, "Invalid line \"%s\": %m", line);
316 if (k < 0 && r == 0)
317 r = k;
318 }
319
320 return r;
321}
322
323const gather_stdout_callback_t gather_environment[] = {
324 gather_environment_generate,
325 gather_environment_collect,
326 gather_environment_consume,
327};