]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/exec-util.c
tree-wide: Remove O_CLOEXEC from fdopen
[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
DJL
80 char *argv[],
81 char *envp[]) {
c6e47247 82
89711996 83 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65 84 _cleanup_strv_free_ char **paths = NULL;
78ec1bb4 85 char **path, **e;
2e4cfe65 86 int r;
89711996 87
c6e47247
ZJS
88 /* We fork this all off from a child process so that we can somewhat cleanly make
89 * use of SIGALRM to set a time limit.
90 *
91 * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
92 */
89711996 93
3e36211b 94 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
2e4cfe65 95 if (r < 0)
653d4695 96 return log_error_errno(r, "Failed to enumerate executables: %m");
2e4cfe65 97
c6e47247
ZJS
98 if (!callbacks) {
99 pids = hashmap_new(NULL);
100 if (!pids)
101 return log_oom();
102 }
103
104 /* Abort execution of this process after the timout. We simply rely on SIGALRM as
105 * default action terminating the process, and turn on alarm(). */
106
107 if (timeout != USEC_INFINITY)
be6b0c21 108 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
89711996 109
78ec1bb4 110 STRV_FOREACH(e, envp)
8f7329ac 111 if (putenv(*e) != 0)
ed689f78 112 return log_error_errno(errno, "Failed to set environment variable: %m");
78ec1bb4 113
2e4cfe65
ZJS
114 STRV_FOREACH(path, paths) {
115 _cleanup_free_ char *t = NULL;
c6e47247 116 _cleanup_close_ int fd = -1;
2e4cfe65 117 pid_t pid;
89711996 118
2e4cfe65
ZJS
119 t = strdup(*path);
120 if (!t)
121 return log_oom();
89711996 122
c6e47247
ZJS
123 if (callbacks) {
124 fd = open_serialization_fd(basename(*path));
125 if (fd < 0)
126 return log_error_errno(fd, "Failed to open serialization file: %m");
127 }
128
129 r = do_spawn(t, argv, fd, &pid);
2e4cfe65
ZJS
130 if (r <= 0)
131 continue;
89711996 132
c6e47247
ZJS
133 if (pids) {
134 r = hashmap_put(pids, PID_TO_PTR(pid), t);
135 if (r < 0)
136 return log_oom();
137 t = NULL;
138 } else {
7d4904fe 139 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
c6e47247
ZJS
140 if (r < 0)
141 continue;
142
143 if (lseek(fd, 0, SEEK_SET) < 0)
144 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
145
146 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
147 fd = -1;
148 if (r < 0)
149 return log_error_errno(r, "Failed to process output from %s: %m", *path);
150 }
89711996
ZJS
151 }
152
c6e47247
ZJS
153 if (callbacks) {
154 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
155 if (r < 0)
156 return log_error_errno(r, "Callback two failed: %m");
157 }
89711996
ZJS
158
159 while (!hashmap_isempty(pids)) {
2e4cfe65 160 _cleanup_free_ char *t = NULL;
89711996
ZJS
161 pid_t pid;
162
163 pid = PTR_TO_PID(hashmap_first_key(pids));
164 assert(pid > 0);
165
2e4cfe65
ZJS
166 t = hashmap_remove(pids, PID_TO_PTR(pid));
167 assert(t);
89711996 168
7d4904fe 169 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
89711996
ZJS
170 }
171
172 return 0;
173}
174
c6e47247
ZJS
175int execute_directories(
176 const char* const* directories,
177 usec_t timeout,
178 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
179 void* const callback_args[_STDOUT_CONSUME_MAX],
78ec1bb4
DJL
180 char *argv[],
181 char *envp[]) {
c6e47247 182
89711996 183 char **dirs = (char**) directories;
c6e47247 184 _cleanup_close_ int fd = -1;
1f5d1e02 185 char *name;
c6e47247 186 int r;
89711996
ZJS
187
188 assert(!strv_isempty(dirs));
189
190 name = basename(dirs[0]);
191 assert(!isempty(name));
192
c6e47247
ZJS
193 if (callbacks) {
194 assert(callback_args);
195 assert(callbacks[STDOUT_GENERATE]);
196 assert(callbacks[STDOUT_COLLECT]);
197 assert(callbacks[STDOUT_CONSUME]);
198
199 fd = open_serialization_fd(name);
200 if (fd < 0)
201 return log_error_errno(fd, "Failed to open serialization file: %m");
202 }
203
204 /* Executes all binaries in the directories serially or in parallel and waits for
205 * them to finish. Optionally a timeout is applied. If a file with the same name
206 * exists in more than one directory, the earliest one wins. */
89711996 207
1f5d1e02 208 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
4c253ed1 209 if (r < 0)
b6e1fff1 210 return r;
4c253ed1 211 if (r == 0) {
78ec1bb4 212 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp);
89711996
ZJS
213 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
214 }
215
c6e47247
ZJS
216 if (!callbacks)
217 return 0;
218
219 if (lseek(fd, 0, SEEK_SET) < 0)
220 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
221
222 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
223 fd = -1;
224 if (r < 0)
225 return log_error_errno(r, "Failed to parse returned data: %m");
226 return 0;
89711996 227}
3303d1b2
ZJS
228
229static int gather_environment_generate(int fd, void *arg) {
230 char ***env = arg, **x, **y;
231 _cleanup_fclose_ FILE *f = NULL;
3f199740 232 _cleanup_strv_free_ char **new = NULL;
3303d1b2
ZJS
233 int r;
234
235 /* Read a series of VAR=value assignments from fd, use them to update the list of
236 * variables in env. Also update the exported environment.
237 *
238 * fd is always consumed, even on error.
239 */
240
241 assert(env);
242
243 f = fdopen(fd, "r");
244 if (!f) {
245 safe_close(fd);
246 return -errno;
247 }
248
aa8fbc74 249 r = load_env_file_pairs(f, NULL, &new);
3303d1b2
ZJS
250 if (r < 0)
251 return r;
252
253 STRV_FOREACH_PAIR(x, y, new) {
254 char *p;
255
184d1904
ZJS
256 if (!env_name_is_valid(*x)) {
257 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
258 continue;
259 }
260
3303d1b2
ZJS
261 p = strjoin(*x, "=", *y);
262 if (!p)
263 return -ENOMEM;
264
265 r = strv_env_replace(env, p);
266 if (r < 0)
267 return r;
268
269 if (setenv(*x, *y, true) < 0)
270 return -errno;
271 }
272
273 return r;
274}
275
276static int gather_environment_collect(int fd, void *arg) {
3303d1b2 277 _cleanup_fclose_ FILE *f = NULL;
c93d527f 278 char ***env = arg;
3303d1b2
ZJS
279 int r;
280
281 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
282
283 assert(env);
284
285 f = fdopen(fd, "w");
286 if (!f) {
287 safe_close(fd);
288 return -errno;
289 }
290
d68c645b 291 r = serialize_strv(f, "env", *env);
3303d1b2
ZJS
292 if (r < 0)
293 return r;
294
c93d527f
LP
295 r = fflush_and_check(f);
296 if (r < 0)
297 return r;
3303d1b2
ZJS
298
299 return 0;
300}
301
302static int gather_environment_consume(int fd, void *arg) {
3303d1b2 303 _cleanup_fclose_ FILE *f = NULL;
d68c645b
LP
304 char ***env = arg;
305 int r = 0;
3303d1b2
ZJS
306
307 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
308
309 assert(env);
310
e92aaed3 311 f = fdopen(fd, "r");
3303d1b2
ZJS
312 if (!f) {
313 safe_close(fd);
314 return -errno;
315 }
316
d68c645b
LP
317 for (;;) {
318 _cleanup_free_ char *line = NULL;
319 const char *v;
320 int k;
3303d1b2 321
d68c645b 322 k = read_line(f, LONG_LINE_MAX, &line);
3303d1b2 323 if (k < 0)
d68c645b
LP
324 return k;
325 if (k == 0)
326 break;
327
328 v = startswith(line, "env=");
329 if (!v) {
330 log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
331 if (r == 0)
332 r = -EINVAL;
333
334 continue;
335 }
336
337 k = deserialize_environment(v, env);
338 if (k < 0) {
339 log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
340
341 if (r == 0)
342 r = k;
343 }
3303d1b2
ZJS
344 }
345
346 return r;
347}
348
349const gather_stdout_callback_t gather_environment[] = {
350 gather_environment_generate,
351 gather_environment_collect,
352 gather_environment_consume,
353};