udev-spawn: search executed command in build directory (#36985)

This makes pin_callout_binary() optionally provides the path of the pinned
binary, and makes it used in udev-spawn.c, to allow easy debugging of
program invocations requested by RUN{program} and friends.
This commit is contained in:
Yu Watanabe
2025-04-05 00:11:15 +09:00
committed by GitHub
4 changed files with 47 additions and 9 deletions

View File

@@ -7,6 +7,7 @@
#include "build-path.h"
#include "errno-list.h"
#include "errno-util.h"
#include "fd-util.h"
#include "macro.h"
#include "path-util.h"
#include "process-util.h"
@@ -245,12 +246,39 @@ int invoke_callout_binary(const char *path, char *const argv[]) {
return -errno;
}
int pin_callout_binary(const char *path) {
static int open_executable(const char *path, char **ret_path) {
int r;
assert(path);
/* Similar to invoke_callout_binary(), but pins (i.e. O_PATH opens) the binary instead of executing it. */
_cleanup_close_ int fd = RET_NERRNO(open(path, O_CLOEXEC|O_PATH));
if (fd < 0)
return fd;
r = fd_verify_regular(fd);
if (r < 0)
return r;
r = access_fd(fd, X_OK);
if (r < 0)
return r;
if (ret_path) {
r = fd_get_path(fd, ret_path);
if (r < 0)
return r;
}
return TAKE_FD(fd);
}
int pin_callout_binary(const char *path, char **ret_path) {
int r;
assert(path);
/* Similar to invoke_callout_binary(), but pins (i.e. O_PATH opens) the binary instead of executing
* it, also optionally provides the path to the binary. */
_cleanup_free_ char *fn = NULL;
r = path_extract_filename(path, &fn);
@@ -261,14 +289,14 @@ int pin_callout_binary(const char *path) {
const char *e;
if (find_environment_binary(fn, &e) >= 0)
return RET_NERRNO(open(e, O_CLOEXEC|O_PATH));
return open_executable(e, ret_path);
_cleanup_free_ char *np = NULL;
if (find_build_dir_binary(fn, &np) >= 0) {
r = RET_NERRNO(open(np, O_CLOEXEC|O_PATH));
r = open_executable(np, ret_path);
if (r >= 0)
return r;
}
return RET_NERRNO(open(path, O_CLOEXEC|O_PATH));
return open_executable(path, ret_path);
}

View File

@@ -5,4 +5,4 @@ int get_build_exec_dir(char **ret);
int invoke_callout_binary(const char *path, char *const argv[]);
int pin_callout_binary(const char *path);
int pin_callout_binary(const char *path, char **ret_path);

View File

@@ -1048,7 +1048,7 @@ int manager_new(RuntimeScope runtime_scope, ManagerTestRunFlags test_run_flags,
}
if (!FLAGS_SET(test_run_flags, MANAGER_TEST_DONT_OPEN_EXECUTOR)) {
m->executor_fd = pin_callout_binary(SYSTEMD_EXECUTOR_BINARY_PATH);
m->executor_fd = pin_callout_binary(SYSTEMD_EXECUTOR_BINARY_PATH, /* ret_path = */ NULL);
if (m->executor_fd < 0)
return log_debug_errno(m->executor_fd, "Failed to pin executor binary: %m");

View File

@@ -2,9 +2,11 @@
#include "sd-event.h"
#include "build-path.h"
#include "device-private.h"
#include "device-util.h"
#include "event-util.h"
#include "exec-util.h"
#include "fd-util.h"
#include "path-util.h"
#include "process-util.h"
@@ -279,6 +281,14 @@ int udev_event_spawn(
free_and_replace(argv[0], program);
}
char *found;
_cleanup_close_ int fd_executable = r = pin_callout_binary(argv[0], &found);
if (r < 0)
return log_device_error_errno(event->dev, r, "Failed to find and pin callout binary \"%s\": %m", argv[0]);
log_device_debug(event->dev, "Found callout binary: \"%s\".", found);
free_and_replace(argv[0], found);
char **envp;
r = device_get_properties_strv(event->dev, &envp);
if (r < 0)
@@ -290,7 +300,7 @@ int udev_event_spawn(
r = pidref_safe_fork_full(
"(spawn)",
(int[]) { -EBADF, outpipe[WRITE_END], errpipe[WRITE_END] },
NULL, 0,
&fd_executable, 1,
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE,
&pidref);
if (r < 0)
@@ -298,7 +308,7 @@ int udev_event_spawn(
"Failed to fork() to execute command '%s': %m", cmd);
if (r == 0) {
DEVICE_TRACE_POINT(spawn_exec, event->dev, cmd);
execve(argv[0], argv, envp);
(void) fexecve_or_execve(fd_executable, argv[0], argv, envp);
_exit(EXIT_FAILURE);
}