]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/exec-util.c
tree-wide: invoke rlimit_nofile_safe() before various exec{v,ve,l}() invocations
[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"
c6e47247 12#include "env-util.h"
89711996 13#include "exec-util.h"
c6e47247
ZJS
14#include "fd-util.h"
15#include "fileio.h"
89711996
ZJS
16#include "hashmap.h"
17#include "macro.h"
18#include "process-util.h"
595225af 19#include "rlimit-util.h"
d68c645b 20#include "serialize.h"
89711996
ZJS
21#include "set.h"
22#include "signal-util.h"
23#include "stat-util.h"
24#include "string-util.h"
25#include "strv.h"
c6e47247 26#include "terminal-util.h"
89711996
ZJS
27#include "util.h"
28
29/* Put this test here for a lack of better place */
30assert_cc(EAGAIN == EWOULDBLOCK);
31
c6e47247
ZJS
32static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
33
cf55fc18 34 pid_t _pid;
4c253ed1 35 int r;
cf55fc18
ZJS
36
37 if (null_or_empty_path(path)) {
38 log_debug("%s is empty (a mask).", path);
39 return 0;
40 }
41
b6e1fff1 42 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
4c253ed1 43 if (r < 0)
b6e1fff1 44 return r;
4c253ed1 45 if (r == 0) {
cf55fc18
ZJS
46 char *_argv[2];
47
c6e47247 48 if (stdout_fd >= 0) {
2b33ab09
LP
49 r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
50 if (r < 0)
3554ef51 51 _exit(EXIT_FAILURE);
c6e47247
ZJS
52 }
53
595225af
LP
54 (void) rlimit_nofile_safe();
55
cf55fc18
ZJS
56 if (!argv) {
57 _argv[0] = (char*) path;
58 _argv[1] = NULL;
59 argv = _argv;
60 } else
61 argv[0] = (char*) path;
62
63 execv(path, argv);
64 log_error_errno(errno, "Failed to execute %s: %m", path);
65 _exit(EXIT_FAILURE);
66 }
67
cf55fc18
ZJS
68 *pid = _pid;
69 return 1;
70}
71
c6e47247
ZJS
72static int do_execute(
73 char **directories,
74 usec_t timeout,
75 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
76 void* const callback_args[_STDOUT_CONSUME_MAX],
77 int output_fd,
78ec1bb4
DJL
78 char *argv[],
79 char *envp[]) {
c6e47247 80
89711996 81 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65 82 _cleanup_strv_free_ char **paths = NULL;
78ec1bb4 83 char **path, **e;
2e4cfe65 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
3e36211b 92 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
2e4cfe65 93 if (r < 0)
653d4695 94 return log_error_errno(r, "Failed to enumerate executables: %m");
2e4cfe65 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
78ec1bb4 108 STRV_FOREACH(e, envp)
8f7329ac 109 if (putenv(*e) != 0)
ed689f78 110 return log_error_errno(errno, "Failed to set environment variable: %m");
78ec1bb4 111
2e4cfe65
ZJS
112 STRV_FOREACH(path, paths) {
113 _cleanup_free_ char *t = NULL;
c6e47247 114 _cleanup_close_ int fd = -1;
2e4cfe65 115 pid_t pid;
89711996 116
2e4cfe65
ZJS
117 t = strdup(*path);
118 if (!t)
119 return log_oom();
89711996 120
c6e47247
ZJS
121 if (callbacks) {
122 fd = open_serialization_fd(basename(*path));
123 if (fd < 0)
124 return log_error_errno(fd, "Failed to open serialization file: %m");
125 }
126
127 r = do_spawn(t, argv, fd, &pid);
2e4cfe65
ZJS
128 if (r <= 0)
129 continue;
89711996 130
c6e47247
ZJS
131 if (pids) {
132 r = hashmap_put(pids, PID_TO_PTR(pid), t);
133 if (r < 0)
134 return log_oom();
135 t = NULL;
136 } else {
7d4904fe 137 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
c6e47247
ZJS
138 if (r < 0)
139 continue;
140
141 if (lseek(fd, 0, SEEK_SET) < 0)
142 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
143
144 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
145 fd = -1;
146 if (r < 0)
147 return log_error_errno(r, "Failed to process output from %s: %m", *path);
148 }
89711996
ZJS
149 }
150
c6e47247
ZJS
151 if (callbacks) {
152 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
153 if (r < 0)
154 return log_error_errno(r, "Callback two failed: %m");
155 }
89711996
ZJS
156
157 while (!hashmap_isempty(pids)) {
2e4cfe65 158 _cleanup_free_ char *t = NULL;
89711996
ZJS
159 pid_t pid;
160
161 pid = PTR_TO_PID(hashmap_first_key(pids));
162 assert(pid > 0);
163
2e4cfe65
ZJS
164 t = hashmap_remove(pids, PID_TO_PTR(pid));
165 assert(t);
89711996 166
7d4904fe 167 (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
89711996
ZJS
168 }
169
170 return 0;
171}
172
c6e47247
ZJS
173int execute_directories(
174 const char* const* directories,
175 usec_t timeout,
176 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
177 void* const callback_args[_STDOUT_CONSUME_MAX],
78ec1bb4
DJL
178 char *argv[],
179 char *envp[]) {
c6e47247 180
89711996 181 char **dirs = (char**) directories;
c6e47247 182 _cleanup_close_ int fd = -1;
1f5d1e02 183 char *name;
c6e47247 184 int r;
89711996
ZJS
185
186 assert(!strv_isempty(dirs));
187
188 name = basename(dirs[0]);
189 assert(!isempty(name));
190
c6e47247
ZJS
191 if (callbacks) {
192 assert(callback_args);
193 assert(callbacks[STDOUT_GENERATE]);
194 assert(callbacks[STDOUT_COLLECT]);
195 assert(callbacks[STDOUT_CONSUME]);
196
197 fd = open_serialization_fd(name);
198 if (fd < 0)
199 return log_error_errno(fd, "Failed to open serialization file: %m");
200 }
201
202 /* Executes all binaries in the directories serially or in parallel and waits for
203 * them to finish. Optionally a timeout is applied. If a file with the same name
204 * exists in more than one directory, the earliest one wins. */
89711996 205
1f5d1e02 206 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
4c253ed1 207 if (r < 0)
b6e1fff1 208 return r;
4c253ed1 209 if (r == 0) {
78ec1bb4 210 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp);
89711996
ZJS
211 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
212 }
213
c6e47247
ZJS
214 if (!callbacks)
215 return 0;
216
217 if (lseek(fd, 0, SEEK_SET) < 0)
218 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
219
220 r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
221 fd = -1;
222 if (r < 0)
223 return log_error_errno(r, "Failed to parse returned data: %m");
224 return 0;
89711996 225}
3303d1b2
ZJS
226
227static int gather_environment_generate(int fd, void *arg) {
228 char ***env = arg, **x, **y;
229 _cleanup_fclose_ FILE *f = NULL;
3f199740 230 _cleanup_strv_free_ char **new = NULL;
3303d1b2
ZJS
231 int r;
232
233 /* Read a series of VAR=value assignments from fd, use them to update the list of
234 * variables in env. Also update the exported environment.
235 *
236 * fd is always consumed, even on error.
237 */
238
239 assert(env);
240
241 f = fdopen(fd, "r");
242 if (!f) {
243 safe_close(fd);
244 return -errno;
245 }
246
aa8fbc74 247 r = load_env_file_pairs(f, NULL, &new);
3303d1b2
ZJS
248 if (r < 0)
249 return r;
250
251 STRV_FOREACH_PAIR(x, y, new) {
252 char *p;
253
184d1904
ZJS
254 if (!env_name_is_valid(*x)) {
255 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
256 continue;
257 }
258
3303d1b2
ZJS
259 p = strjoin(*x, "=", *y);
260 if (!p)
261 return -ENOMEM;
262
263 r = strv_env_replace(env, p);
264 if (r < 0)
265 return r;
266
267 if (setenv(*x, *y, true) < 0)
268 return -errno;
269 }
270
271 return r;
272}
273
274static int gather_environment_collect(int fd, void *arg) {
3303d1b2 275 _cleanup_fclose_ FILE *f = NULL;
c93d527f 276 char ***env = arg;
3303d1b2
ZJS
277 int r;
278
279 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
280
281 assert(env);
282
283 f = fdopen(fd, "w");
284 if (!f) {
285 safe_close(fd);
286 return -errno;
287 }
288
d68c645b 289 r = serialize_strv(f, "env", *env);
3303d1b2
ZJS
290 if (r < 0)
291 return r;
292
c93d527f
LP
293 r = fflush_and_check(f);
294 if (r < 0)
295 return r;
3303d1b2
ZJS
296
297 return 0;
298}
299
300static int gather_environment_consume(int fd, void *arg) {
3303d1b2 301 _cleanup_fclose_ FILE *f = NULL;
d68c645b
LP
302 char ***env = arg;
303 int r = 0;
3303d1b2
ZJS
304
305 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
306
307 assert(env);
308
d68c645b 309 f = fdopen(fd, "re");
3303d1b2
ZJS
310 if (!f) {
311 safe_close(fd);
312 return -errno;
313 }
314
d68c645b
LP
315 for (;;) {
316 _cleanup_free_ char *line = NULL;
317 const char *v;
318 int k;
3303d1b2 319
d68c645b 320 k = read_line(f, LONG_LINE_MAX, &line);
3303d1b2 321 if (k < 0)
d68c645b
LP
322 return k;
323 if (k == 0)
324 break;
325
326 v = startswith(line, "env=");
327 if (!v) {
328 log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
329 if (r == 0)
330 r = -EINVAL;
331
332 continue;
333 }
334
335 k = deserialize_environment(v, env);
336 if (k < 0) {
337 log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
338
339 if (r == 0)
340 r = k;
341 }
3303d1b2
ZJS
342 }
343
344 return r;
345}
346
347const gather_stdout_callback_t gather_environment[] = {
348 gather_environment_generate,
349 gather_environment_collect,
350 gather_environment_consume,
351};