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