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