]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/exec-util.c
bus-unit-util: use free_and_strdup() where we can
[thirdparty/systemd.git] / src / shared / exec-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
89711996
ZJS
2
3#include <dirent.h>
4#include <errno.h>
5#include <sys/prctl.h>
6#include <sys/types.h>
7#include <unistd.h>
c6e47247 8#include <stdio.h>
89711996
ZJS
9
10#include "alloc-util.h"
2e4cfe65 11#include "conf-files.h"
686d13b9 12#include "env-file.h"
c6e47247 13#include "env-util.h"
89711996 14#include "exec-util.h"
c6e47247
ZJS
15#include "fd-util.h"
16#include "fileio.h"
89711996
ZJS
17#include "hashmap.h"
18#include "macro.h"
19#include "process-util.h"
595225af 20#include "rlimit-util.h"
d68c645b 21#include "serialize.h"
89711996
ZJS
22#include "set.h"
23#include "signal-util.h"
24#include "stat-util.h"
25#include "string-util.h"
26#include "strv.h"
c6e47247 27#include "terminal-util.h"
e4de7287 28#include "tmpfile-util.h"
89711996
ZJS
29#include "util.h"
30
31/* Put this test here for a lack of better place */
32assert_cc(EAGAIN == EWOULDBLOCK);
33
c6e47247
ZJS
34static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
35
cf55fc18 36 pid_t _pid;
4c253ed1 37 int r;
cf55fc18
ZJS
38
39 if (null_or_empty_path(path)) {
40 log_debug("%s is empty (a mask).", path);
41 return 0;
42 }
43
b6e1fff1 44 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
4c253ed1 45 if (r < 0)
b6e1fff1 46 return r;
4c253ed1 47 if (r == 0) {
cf55fc18
ZJS
48 char *_argv[2];
49
c6e47247 50 if (stdout_fd >= 0) {
2b33ab09
LP
51 r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
52 if (r < 0)
3554ef51 53 _exit(EXIT_FAILURE);
c6e47247
ZJS
54 }
55
595225af
LP
56 (void) rlimit_nofile_safe();
57
cf55fc18
ZJS
58 if (!argv) {
59 _argv[0] = (char*) path;
60 _argv[1] = NULL;
61 argv = _argv;
62 } else
63 argv[0] = (char*) path;
64
65 execv(path, argv);
66 log_error_errno(errno, "Failed to execute %s: %m", path);
67 _exit(EXIT_FAILURE);
68 }
69
cf55fc18
ZJS
70 *pid = _pid;
71 return 1;
72}
73
c6e47247
ZJS
74static int do_execute(
75 char **directories,
76 usec_t timeout,
77 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
78 void* const callback_args[_STDOUT_CONSUME_MAX],
79 int output_fd,
78ec1bb4 80 char *argv[],
4b05f0c9
MK
81 char *envp[],
82 ExecDirFlags flags) {
c6e47247 83
89711996 84 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65 85 _cleanup_strv_free_ char **paths = NULL;
78ec1bb4 86 char **path, **e;
2e4cfe65 87 int r;
4b05f0c9 88 bool parallel_execution;
89711996 89
c6e47247
ZJS
90 /* We fork this all off from a child process so that we can somewhat cleanly make
91 * use of SIGALRM to set a time limit.
92 *
4b05f0c9
MK
93 * We attempt to perform parallel execution if configured by the user, however
94 * if `callbacks` is nonnull, execution must be serial.
c6e47247 95 */
4b05f0c9 96 parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks;
89711996 97
3e36211b 98 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
2e4cfe65 99 if (r < 0)
653d4695 100 return log_error_errno(r, "Failed to enumerate executables: %m");
2e4cfe65 101
4b05f0c9 102 if (parallel_execution) {
c6e47247
ZJS
103 pids = hashmap_new(NULL);
104 if (!pids)
105 return log_oom();
106 }
107
108 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
109 * default action terminating the process, and turn on alarm(). */
110
111 if (timeout != USEC_INFINITY)
be6b0c21 112 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
89711996 113
78ec1bb4 114 STRV_FOREACH(e, envp)
8f7329ac 115 if (putenv(*e) != 0)
ed689f78 116 return log_error_errno(errno, "Failed to set environment variable: %m");
78ec1bb4 117
2e4cfe65
ZJS
118 STRV_FOREACH(path, paths) {
119 _cleanup_free_ char *t = NULL;
c6e47247 120 _cleanup_close_ int fd = -1;
2e4cfe65 121 pid_t pid;
89711996 122
2e4cfe65
ZJS
123 t = strdup(*path);
124 if (!t)
125 return log_oom();
89711996 126
c6e47247
ZJS
127 if (callbacks) {
128 fd = open_serialization_fd(basename(*path));
129 if (fd < 0)
130 return log_error_errno(fd, "Failed to open serialization file: %m");
131 }
132
133 r = do_spawn(t, argv, fd, &pid);
2e4cfe65
ZJS
134 if (r <= 0)
135 continue;
89711996 136
4b05f0c9 137 if (parallel_execution) {
c6e47247
ZJS
138 r = hashmap_put(pids, PID_TO_PTR(pid), t);
139 if (r < 0)
140 return log_oom();
141 t = NULL;
142 } else {
7d4904fe 143 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
4b05f0c9
MK
144 if (FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS)) {
145 if (r < 0)
146 continue;
147 } else if (r > 0)
148 return r;
149
150 if (callbacks) {
151 if (lseek(fd, 0, SEEK_SET) < 0)
152 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
153
154 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
155 fd = -1;
156 if (r < 0)
157 return log_error_errno(r, "Failed to process output from %s: %m", *path);
158 }
c6e47247 159 }
89711996
ZJS
160 }
161
c6e47247
ZJS
162 if (callbacks) {
163 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
164 if (r < 0)
165 return log_error_errno(r, "Callback two failed: %m");
166 }
89711996
ZJS
167
168 while (!hashmap_isempty(pids)) {
2e4cfe65 169 _cleanup_free_ char *t = NULL;
89711996
ZJS
170 pid_t pid;
171
172 pid = PTR_TO_PID(hashmap_first_key(pids));
173 assert(pid > 0);
174
2e4cfe65
ZJS
175 t = hashmap_remove(pids, PID_TO_PTR(pid));
176 assert(t);
89711996 177
4b05f0c9
MK
178 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
179 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
180 return r;
89711996
ZJS
181 }
182
183 return 0;
184}
185
c6e47247
ZJS
186int execute_directories(
187 const char* const* directories,
188 usec_t timeout,
189 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
190 void* const callback_args[_STDOUT_CONSUME_MAX],
78ec1bb4 191 char *argv[],
4b05f0c9
MK
192 char *envp[],
193 ExecDirFlags flags) {
c6e47247 194
89711996 195 char **dirs = (char**) directories;
c6e47247 196 _cleanup_close_ int fd = -1;
1f5d1e02 197 char *name;
c6e47247 198 int r;
4b05f0c9 199 pid_t executor_pid;
89711996
ZJS
200
201 assert(!strv_isempty(dirs));
202
203 name = basename(dirs[0]);
204 assert(!isempty(name));
205
c6e47247
ZJS
206 if (callbacks) {
207 assert(callback_args);
208 assert(callbacks[STDOUT_GENERATE]);
209 assert(callbacks[STDOUT_COLLECT]);
210 assert(callbacks[STDOUT_CONSUME]);
211
212 fd = open_serialization_fd(name);
213 if (fd < 0)
214 return log_error_errno(fd, "Failed to open serialization file: %m");
215 }
216
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. */
89711996 220
4b05f0c9 221 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &executor_pid);
4c253ed1 222 if (r < 0)
b6e1fff1 223 return r;
4c253ed1 224 if (r == 0) {
4b05f0c9
MK
225 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp, flags);
226 _exit(r < 0 ? EXIT_FAILURE : r);
89711996
ZJS
227 }
228
4b05f0c9
MK
229 r = wait_for_terminate_and_check("(sd-executor)", executor_pid, 0);
230 if (r < 0)
231 return r;
232 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
233 return r;
234
c6e47247
ZJS
235 if (!callbacks)
236 return 0;
237
238 if (lseek(fd, 0, SEEK_SET) < 0)
239 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
240
241 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
242 fd = -1;
243 if (r < 0)
244 return log_error_errno(r, "Failed to parse returned data: %m");
245 return 0;
89711996 246}
3303d1b2
ZJS
247
248static int gather_environment_generate(int fd, void *arg) {
249 char ***env = arg, **x, **y;
250 _cleanup_fclose_ FILE *f = NULL;
3f199740 251 _cleanup_strv_free_ char **new = NULL;
3303d1b2
ZJS
252 int r;
253
254 /* Read a series of VAR=value assignments from fd, use them to update the list of
255 * variables in env. Also update the exported environment.
256 *
257 * fd is always consumed, even on error.
258 */
259
260 assert(env);
261
262 f = fdopen(fd, "r");
263 if (!f) {
264 safe_close(fd);
265 return -errno;
266 }
267
aa8fbc74 268 r = load_env_file_pairs(f, NULL, &new);
3303d1b2
ZJS
269 if (r < 0)
270 return r;
271
272 STRV_FOREACH_PAIR(x, y, new) {
273 char *p;
274
184d1904
ZJS
275 if (!env_name_is_valid(*x)) {
276 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
277 continue;
278 }
279
3303d1b2
ZJS
280 p = strjoin(*x, "=", *y);
281 if (!p)
282 return -ENOMEM;
283
284 r = strv_env_replace(env, p);
285 if (r < 0)
286 return r;
287
288 if (setenv(*x, *y, true) < 0)
289 return -errno;
290 }
291
292 return r;
293}
294
295static int gather_environment_collect(int fd, void *arg) {
3303d1b2 296 _cleanup_fclose_ FILE *f = NULL;
c93d527f 297 char ***env = arg;
3303d1b2
ZJS
298 int r;
299
300 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
301
302 assert(env);
303
304 f = fdopen(fd, "w");
305 if (!f) {
306 safe_close(fd);
307 return -errno;
308 }
309
d68c645b 310 r = serialize_strv(f, "env", *env);
3303d1b2
ZJS
311 if (r < 0)
312 return r;
313
c93d527f
LP
314 r = fflush_and_check(f);
315 if (r < 0)
316 return r;
3303d1b2
ZJS
317
318 return 0;
319}
320
321static int gather_environment_consume(int fd, void *arg) {
3303d1b2 322 _cleanup_fclose_ FILE *f = NULL;
d68c645b
LP
323 char ***env = arg;
324 int r = 0;
3303d1b2
ZJS
325
326 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
327
328 assert(env);
329
e92aaed3 330 f = fdopen(fd, "r");
3303d1b2
ZJS
331 if (!f) {
332 safe_close(fd);
333 return -errno;
334 }
335
d68c645b
LP
336 for (;;) {
337 _cleanup_free_ char *line = NULL;
338 const char *v;
339 int k;
3303d1b2 340
d68c645b 341 k = read_line(f, LONG_LINE_MAX, &line);
3303d1b2 342 if (k < 0)
d68c645b
LP
343 return k;
344 if (k == 0)
345 break;
346
347 v = startswith(line, "env=");
348 if (!v) {
349 log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
350 if (r == 0)
351 r = -EINVAL;
352
353 continue;
354 }
355
356 k = deserialize_environment(v, env);
357 if (k < 0) {
358 log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
359
360 if (r == 0)
361 r = k;
362 }
3303d1b2
ZJS
363 }
364
365 return r;
366}
367
368const gather_stdout_callback_t gather_environment[] = {
369 gather_environment_generate,
370 gather_environment_collect,
371 gather_environment_consume,
372};