]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/exec-util.c
exec-util: tighten variable scope a bit
[thirdparty/systemd.git] / src / shared / exec-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
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"
3f51bbff 14#include "errno-util.h"
89711996 15#include "exec-util.h"
c6e47247
ZJS
16#include "fd-util.h"
17#include "fileio.h"
89711996
ZJS
18#include "hashmap.h"
19#include "macro.h"
8939eeae 20#include "missing_syscall.h"
03469b77 21#include "path-util.h"
89711996 22#include "process-util.h"
d68c645b 23#include "serialize.h"
89711996
ZJS
24#include "set.h"
25#include "signal-util.h"
26#include "stat-util.h"
b3d59367 27#include "string-table.h"
89711996
ZJS
28#include "string-util.h"
29#include "strv.h"
c6e47247 30#include "terminal-util.h"
e4de7287 31#include "tmpfile-util.h"
89711996
ZJS
32
33/* Put this test here for a lack of better place */
34assert_cc(EAGAIN == EWOULDBLOCK);
35
43f565c6 36static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid, bool set_systemd_exec_pid) {
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
f3f2d02e 45 r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE, &_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) {
aedec452 52 r = rearrange_stdio(STDIN_FILENO, TAKE_FD(stdout_fd), STDERR_FILENO);
2b33ab09 53 if (r < 0)
3554ef51 54 _exit(EXIT_FAILURE);
c6e47247
ZJS
55 }
56
43f565c6
YW
57 if (set_systemd_exec_pid) {
58 r = setenv_systemd_exec_pid(false);
59 if (r < 0)
60 log_warning_errno(r, "Failed to set $SYSTEMD_EXEC_PID, ignoring: %m");
61 }
62
cf55fc18
ZJS
63 if (!argv) {
64 _argv[0] = (char*) path;
65 _argv[1] = NULL;
66 argv = _argv;
67 } else
68 argv[0] = (char*) path;
69
70 execv(path, argv);
71 log_error_errno(errno, "Failed to execute %s: %m", path);
72 _exit(EXIT_FAILURE);
73 }
74
cf55fc18
ZJS
75 *pid = _pid;
76 return 1;
77}
78
c6e47247
ZJS
79static int do_execute(
80 char **directories,
81 usec_t timeout,
82 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
83 void* const callback_args[_STDOUT_CONSUME_MAX],
84 int output_fd,
78ec1bb4 85 char *argv[],
4b05f0c9
MK
86 char *envp[],
87 ExecDirFlags flags) {
c6e47247 88
89711996 89 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
2e4cfe65 90 _cleanup_strv_free_ char **paths = NULL;
2e4cfe65 91 int r;
4b05f0c9 92 bool parallel_execution;
89711996 93
c6e47247
ZJS
94 /* We fork this all off from a child process so that we can somewhat cleanly make
95 * use of SIGALRM to set a time limit.
96 *
4b05f0c9
MK
97 * We attempt to perform parallel execution if configured by the user, however
98 * if `callbacks` is nonnull, execution must be serial.
c6e47247 99 */
4b05f0c9 100 parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks;
89711996 101
3e36211b 102 r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
2e4cfe65 103 if (r < 0)
653d4695 104 return log_error_errno(r, "Failed to enumerate executables: %m");
2e4cfe65 105
4b05f0c9 106 if (parallel_execution) {
c6e47247
ZJS
107 pids = hashmap_new(NULL);
108 if (!pids)
109 return log_oom();
110 }
111
5238e957 112 /* Abort execution of this process after the timeout. We simply rely on SIGALRM as
c6e47247
ZJS
113 * default action terminating the process, and turn on alarm(). */
114
115 if (timeout != USEC_INFINITY)
be6b0c21 116 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
89711996 117
78ec1bb4 118 STRV_FOREACH(e, envp)
8f7329ac 119 if (putenv(*e) != 0)
ed689f78 120 return log_error_errno(errno, "Failed to set environment variable: %m");
78ec1bb4 121
2e4cfe65
ZJS
122 STRV_FOREACH(path, paths) {
123 _cleanup_free_ char *t = NULL;
254d1313 124 _cleanup_close_ int fd = -EBADF;
2e4cfe65 125 pid_t pid;
89711996 126
2e4cfe65
ZJS
127 t = strdup(*path);
128 if (!t)
129 return log_oom();
89711996 130
c6e47247 131 if (callbacks) {
03469b77
LP
132 _cleanup_free_ char *bn = NULL;
133
134 r = path_extract_filename(*path, &bn);
135 if (r < 0)
136 return log_error_errno(r, "Failed to extract filename from path '%s': %m", *path);
137
138 fd = open_serialization_fd(bn);
c6e47247
ZJS
139 if (fd < 0)
140 return log_error_errno(fd, "Failed to open serialization file: %m");
141 }
142
43f565c6 143 r = do_spawn(t, argv, fd, &pid, FLAGS_SET(flags, EXEC_DIR_SET_SYSTEMD_EXEC_PID));
2e4cfe65
ZJS
144 if (r <= 0)
145 continue;
89711996 146
4b05f0c9 147 if (parallel_execution) {
c6e47247
ZJS
148 r = hashmap_put(pids, PID_TO_PTR(pid), t);
149 if (r < 0)
150 return log_oom();
151 t = NULL;
152 } else {
7d4904fe 153 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
65cd9c72
YW
154 if (r < 0)
155 return r;
156 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
4b05f0c9
MK
157 return r;
158
159 if (callbacks) {
160 if (lseek(fd, 0, SEEK_SET) < 0)
161 return log_error_errno(errno, "Failed to seek on serialization fd: %m");
162
ced30d69 163 r = callbacks[STDOUT_GENERATE](TAKE_FD(fd), callback_args[STDOUT_GENERATE]);
4b05f0c9
MK
164 if (r < 0)
165 return log_error_errno(r, "Failed to process output from %s: %m", *path);
166 }
c6e47247 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
4b05f0c9 186 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
65cd9c72
YW
187 if (r < 0)
188 return r;
4b05f0c9
MK
189 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
190 return r;
89711996
ZJS
191 }
192
193 return 0;
194}
195
c6e47247
ZJS
196int execute_directories(
197 const char* const* directories,
198 usec_t timeout,
199 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
200 void* const callback_args[_STDOUT_CONSUME_MAX],
78ec1bb4 201 char *argv[],
4b05f0c9
MK
202 char *envp[],
203 ExecDirFlags flags) {
c6e47247 204
89711996 205 char **dirs = (char**) directories;
254d1313 206 _cleanup_close_ int fd = -EBADF;
c6e47247 207 int r;
4b05f0c9 208 pid_t executor_pid;
89711996
ZJS
209
210 assert(!strv_isempty(dirs));
211
c6e47247
ZJS
212 if (callbacks) {
213 assert(callback_args);
214 assert(callbacks[STDOUT_GENERATE]);
215 assert(callbacks[STDOUT_COLLECT]);
216 assert(callbacks[STDOUT_CONSUME]);
217
8e8df829
YW
218 _cleanup_free_ char *name = NULL;
219
220 r = path_extract_filename(dirs[0], &name);
221 if (r < 0)
222 return log_error_errno(r, "Failed to extract file name from '%s': %m", dirs[0]);
223
c6e47247
ZJS
224 fd = open_serialization_fd(name);
225 if (fd < 0)
226 return log_error_errno(fd, "Failed to open serialization file: %m");
227 }
228
229 /* Executes all binaries in the directories serially or in parallel and waits for
230 * them to finish. Optionally a timeout is applied. If a file with the same name
231 * exists in more than one directory, the earliest one wins. */
89711996 232
4b05f0c9 233 r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &executor_pid);
4c253ed1 234 if (r < 0)
b6e1fff1 235 return r;
4c253ed1 236 if (r == 0) {
4b05f0c9
MK
237 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp, flags);
238 _exit(r < 0 ? EXIT_FAILURE : r);
89711996
ZJS
239 }
240
4b05f0c9
MK
241 r = wait_for_terminate_and_check("(sd-executor)", executor_pid, 0);
242 if (r < 0)
243 return r;
244 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
245 return r;
246
c6e47247
ZJS
247 if (!callbacks)
248 return 0;
249
250 if (lseek(fd, 0, SEEK_SET) < 0)
251 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
252
ced30d69 253 r = callbacks[STDOUT_CONSUME](TAKE_FD(fd), callback_args[STDOUT_CONSUME]);
c6e47247
ZJS
254 if (r < 0)
255 return log_error_errno(r, "Failed to parse returned data: %m");
256 return 0;
89711996 257}
3303d1b2
ZJS
258
259static int gather_environment_generate(int fd, void *arg) {
99534007 260 char ***env = ASSERT_PTR(arg);
3303d1b2 261 _cleanup_fclose_ FILE *f = NULL;
3f199740 262 _cleanup_strv_free_ char **new = NULL;
3303d1b2
ZJS
263 int r;
264
265 /* Read a series of VAR=value assignments from fd, use them to update the list of
266 * variables in env. Also update the exported environment.
267 *
268 * fd is always consumed, even on error.
269 */
270
3303d1b2
ZJS
271 f = fdopen(fd, "r");
272 if (!f) {
273 safe_close(fd);
274 return -errno;
275 }
276
aa8fbc74 277 r = load_env_file_pairs(f, NULL, &new);
3303d1b2
ZJS
278 if (r < 0)
279 return r;
280
281 STRV_FOREACH_PAIR(x, y, new) {
184d1904
ZJS
282 if (!env_name_is_valid(*x)) {
283 log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
284 continue;
285 }
286
b230baae 287 r = strv_env_assign(env, *x, *y);
3303d1b2
ZJS
288 if (r < 0)
289 return r;
290
291 if (setenv(*x, *y, true) < 0)
292 return -errno;
293 }
294
b230baae 295 return 0;
3303d1b2
ZJS
296}
297
298static int gather_environment_collect(int fd, void *arg) {
3303d1b2 299 _cleanup_fclose_ FILE *f = NULL;
99534007 300 char ***env = ASSERT_PTR(arg);
3303d1b2
ZJS
301 int r;
302
303 /* Write out a series of env=cescape(VAR=value) assignments to fd. */
304
3303d1b2
ZJS
305 f = fdopen(fd, "w");
306 if (!f) {
307 safe_close(fd);
308 return -errno;
309 }
310
d68c645b 311 r = serialize_strv(f, "env", *env);
3303d1b2
ZJS
312 if (r < 0)
313 return r;
314
c93d527f
LP
315 r = fflush_and_check(f);
316 if (r < 0)
317 return r;
3303d1b2
ZJS
318
319 return 0;
320}
321
322static int gather_environment_consume(int fd, void *arg) {
3303d1b2 323 _cleanup_fclose_ FILE *f = NULL;
99534007 324 char ***env = ASSERT_PTR(arg);
d68c645b 325 int r = 0;
3303d1b2
ZJS
326
327 /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
328
e92aaed3 329 f = fdopen(fd, "r");
3303d1b2
ZJS
330 if (!f) {
331 safe_close(fd);
332 return -errno;
333 }
334
d68c645b
LP
335 for (;;) {
336 _cleanup_free_ char *line = NULL;
337 const char *v;
338 int k;
3303d1b2 339
d68c645b 340 k = read_line(f, LONG_LINE_MAX, &line);
3303d1b2 341 if (k < 0)
d68c645b
LP
342 return k;
343 if (k == 0)
344 break;
345
346 v = startswith(line, "env=");
347 if (!v) {
348 log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
349 if (r == 0)
350 r = -EINVAL;
351
352 continue;
353 }
354
355 k = deserialize_environment(v, env);
356 if (k < 0) {
357 log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
358
359 if (r == 0)
360 r = k;
361 }
3303d1b2
ZJS
362 }
363
364 return r;
365}
366
b3d59367
AZ
367int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) {
368 ExecCommandFlags ex_flag, ret_flags = 0;
b3d59367
AZ
369
370 assert(flags);
371
372 STRV_FOREACH(opt, ex_opts) {
373 ex_flag = exec_command_flags_from_string(*opt);
9de42a6a
BR
374 if (ex_flag < 0)
375 return ex_flag;
376 ret_flags |= ex_flag;
b3d59367
AZ
377 }
378
379 *flags = ret_flags;
380
381 return 0;
382}
383
384int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts) {
385 _cleanup_strv_free_ char **ret_opts = NULL;
386 ExecCommandFlags it = flags;
387 const char *str;
388 int i, r;
389
390 assert(ex_opts);
391
9de42a6a
BR
392 if (flags < 0)
393 return flags;
394
b3d59367
AZ
395 for (i = 0; it != 0; it &= ~(1 << i), i++) {
396 if (FLAGS_SET(flags, (1 << i))) {
397 str = exec_command_flags_to_string(1 << i);
398 if (!str)
399 return -EINVAL;
400
401 r = strv_extend(&ret_opts, str);
402 if (r < 0)
403 return r;
404 }
405 }
406
407 *ex_opts = TAKE_PTR(ret_opts);
408
409 return 0;
410}
411
3303d1b2
ZJS
412const gather_stdout_callback_t gather_environment[] = {
413 gather_environment_generate,
414 gather_environment_collect,
415 gather_environment_consume,
416};
b3d59367
AZ
417
418static const char* const exec_command_strings[] = {
419 "ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */
420 "privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */
421 "no-setuid", /* EXEC_COMMAND_NO_SETUID */
422 "ambient", /* EXEC_COMMAND_AMBIENT_MAGIC */
423 "no-env-expand", /* EXEC_COMMAND_NO_ENV_EXPAND */
424};
425
426const char* exec_command_flags_to_string(ExecCommandFlags i) {
427 size_t idx;
428
429 for (idx = 0; idx < ELEMENTSOF(exec_command_strings); idx++)
430 if (i == (1 << idx))
431 return exec_command_strings[idx];
432
433 return NULL;
434}
435
436ExecCommandFlags exec_command_flags_from_string(const char *s) {
437 ssize_t idx;
438
439 idx = string_table_lookup(exec_command_strings, ELEMENTSOF(exec_command_strings), s);
440
441 if (idx < 0)
442 return _EXEC_COMMAND_FLAGS_INVALID;
443 else
444 return 1 << idx;
445}
a6d9111c
ZJS
446
447int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) {
69339ae9
LP
448 /* Refuse invalid fds, regardless if fexecve() use is enabled or not */
449 if (executable_fd < 0)
450 return -EBADF;
451
452 /* Block any attempts on exploiting Linux' liberal argv[] handling, i.e. CVE-2021-4034 and suchlike */
453 if (isempty(executable) || strv_isempty(argv))
454 return -EINVAL;
455
ceedbf81 456#if ENABLE_FEXECVE
69339ae9 457
8939eeae 458 execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH);
3f51bbff
ZJS
459
460 if (IN_SET(errno, ENOSYS, ENOENT) || ERRNO_IS_PRIVILEGE(errno))
461 /* Old kernel or a script or an overzealous seccomp filter? Let's fall back to execve().
a6d9111c
ZJS
462 *
463 * fexecve(3): "If fd refers to a script (i.e., it is an executable text file that names a
464 * script interpreter with a first line that begins with the characters #!) and the
465 * close-on-exec flag has been set for fd, then fexecve() fails with the error ENOENT. This
466 * error occurs because, by the time the script interpreter is executed, fd has already been
467 * closed because of the close-on-exec flag. Thus, the close-on-exec flag can't be set on fd
468 * if it refers to a script."
469 *
470 * Unfortunately, if we unset close-on-exec, the script will be executed just fine, but (at
471 * least in case of bash) the script name, $0, will be shown as /dev/fd/nnn, which breaks
472 * scripts which make use of $0. Thus, let's fall back to execve() in this case.
473 */
ceedbf81 474#endif
a6d9111c
ZJS
475 execve(executable, argv, envp);
476 return -errno;
477}
3e24e8cd 478
c85cb3bc 479int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) {
3e24e8cd
ZJS
480 bool stdout_is_tty, stderr_is_tty;
481 size_t n, i;
482 va_list ap;
483 char **l;
484 int r;
485
486 assert(path);
487
488 /* Spawns a temporary TTY agent, making sure it goes away when we go away */
489
490 r = safe_fork_full(name,
911f8f01 491 NULL,
3e24e8cd
ZJS
492 except,
493 n_except,
f3f2d02e 494 FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG|FORK_RLIMIT_NOFILE_SAFE,
3e24e8cd
ZJS
495 ret_pid);
496 if (r < 0)
497 return r;
498 if (r > 0)
499 return 0;
500
501 /* In the child: */
502
503 stdout_is_tty = isatty(STDOUT_FILENO);
504 stderr_is_tty = isatty(STDERR_FILENO);
505
506 if (!stdout_is_tty || !stderr_is_tty) {
507 int fd;
508
509 /* Detach from stdout/stderr and reopen /dev/tty for them. This is important to ensure that
510 * when systemctl is started via popen() or a similar call that expects to read EOF we
511 * actually do generate EOF and not delay this indefinitely by keeping an unused copy of
512 * stdin around. */
513 fd = open("/dev/tty", O_WRONLY);
514 if (fd < 0) {
b98416e1 515 if (errno != ENXIO) {
0bcf1679
LP
516 log_error_errno(errno, "Failed to open /dev/tty: %m");
517 _exit(EXIT_FAILURE);
518 }
3e24e8cd 519
0bcf1679
LP
520 /* If we get ENXIO here we have no controlling TTY even though stdout/stderr are
521 * connected to a TTY. That's a weird setup, but let's handle it gracefully: let's
522 * skip the forking of the agents, given the TTY setup is not in order. */
523 } else {
524 if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
525 log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
526 _exit(EXIT_FAILURE);
527 }
3e24e8cd 528
0bcf1679
LP
529 if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
530 log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
531 _exit(EXIT_FAILURE);
532 }
3e24e8cd 533
0bcf1679
LP
534 fd = safe_close_above_stdio(fd);
535 }
3e24e8cd
ZJS
536 }
537
3e24e8cd
ZJS
538 /* Count arguments */
539 va_start(ap, path);
540 for (n = 0; va_arg(ap, char*); n++)
541 ;
542 va_end(ap);
543
544 /* Allocate strv */
545 l = newa(char*, n + 1);
546
547 /* Fill in arguments */
548 va_start(ap, path);
549 for (i = 0; i <= n; i++)
550 l[i] = va_arg(ap, char*);
551 va_end(ap);
552
553 execv(path, l);
554 _exit(EXIT_FAILURE);
555}