1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-02 12:58:35 +03:00

machine: introduce io.systemd.Machine.OpenRootDirectory method (#35880)

This PR introduces io.systemd.Machine.OpenRootDirectory method which is
DBus's alternative to OpenMachineRootDirectory.
This commit is contained in:
Daan De Meyer 2025-01-09 13:06:38 +01:00 committed by GitHub
commit 606034cc1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 201 additions and 85 deletions

View File

@ -2215,3 +2215,26 @@ _noreturn_ void report_errno_and_exit(int errno_fd, int error) {
_exit(EXIT_FAILURE);
}
int read_errno(int errno_fd) {
int r;
assert(errno_fd >= 0);
ssize_t n = loop_read(errno_fd, &r, sizeof(r), /* do_pool = */ 0);
if (n < 0) {
log_debug_errno(n, "Failed to read errno: %m");
return -EIO;
}
if (n == sizeof(r)) {
/* child processes reported a error, return it */
if (r < 0)
return log_debug_errno(r, "Child process failed with errno: %m");
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received a errno, but it's a positive value");
}
if (n != 0)
return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Received unexpected amount of bytes while reading errno");
/* the process exited without reporting a error, assuming success */
return 0;
}

View File

@ -269,3 +269,4 @@ int proc_dir_read(DIR *d, pid_t *ret);
int proc_dir_read_pidref(DIR *d, PidRef *ret);
_noreturn_ void report_errno_and_exit(int errno_fd, int error);
int read_errno(int errno_fd);

View File

@ -595,71 +595,11 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda
if (r == 0)
return 1; /* Will call us back */
switch (m->class) {
case MACHINE_HOST:
fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd < 0)
return -errno;
break;
case MACHINE_CONTAINER: {
_cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
_cleanup_close_pair_ int pair[2] = EBADF_PAIR;
pid_t child;
r = pidref_namespace_open(&m->leader,
/* ret_pidns_fd = */ NULL,
&mntns_fd,
/* ret_netns_fd = */ NULL,
/* ret_userns_fd = */ NULL,
&root_fd);
if (r < 0)
return r;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
r = namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
-1, mntns_fd, -1, -1, root_fd, &child);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {
_cleanup_close_ int dfd = -EBADF;
pair[0] = safe_close(pair[0]);
dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (dfd < 0)
_exit(EXIT_FAILURE);
r = send_one_fd(pair[1], dfd, 0);
dfd = safe_close(dfd);
if (r < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
}
pair[1] = safe_close(pair[1]);
r = wait_for_terminate_and_check("(sd-openrootns)", child, 0);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to wait for child: %m");
if (r != EXIT_SUCCESS)
return sd_bus_error_set(error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
fd = receive_one_fd(pair[0], MSG_DONTWAIT);
if (fd < 0)
return fd;
break;
}
default:
fd = machine_open_root_directory(m);
if (ERRNO_IS_NEG_NOT_SUPPORTED(fd))
return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening the root directory is only supported on container machines.");
}
if (fd < 0)
return sd_bus_error_set_errnof(error, fd, "Failed to open root directory of machine '%s': %m", m->name);
return sd_bus_reply_method_return(message, "h", fd);
}

View File

@ -479,10 +479,6 @@ int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
assert(link);
assert(parameters);
r = sd_varlink_set_allow_fd_passing_output(link, true);
if (r < 0)
return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
@ -559,7 +555,8 @@ int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met
}
ptmx_fd_idx = sd_varlink_push_fd(link, ptmx_fd);
/* no need to handle -EPERM because we do sd_varlink_set_allow_fd_passing_output() above */
if (ERRNO_IS_PRIVILEGE(ptmx_fd_idx))
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL);
if (ptmx_fd_idx < 0)
return log_debug_errno(ptmx_fd_idx, "Failed to push file descriptor over varlink: %m");
@ -919,3 +916,41 @@ int vl_method_copy_internal(sd_varlink *link, sd_json_variant *parameters, sd_va
op->done = copy_done;
return 1;
}
int vl_method_open_root_directory_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
_cleanup_close_ int fd = -EBADF;
Machine *machine = ASSERT_PTR(userdata);
Manager *manager = ASSERT_PTR(machine->manager);
int r;
r = varlink_verify_polkit_async(
link,
manager->bus,
"org.freedesktop.machine1.manage-machines",
(const char**) STRV_MAKE("name", machine->name,
"verb", "open_root_directory"),
&manager->polkit_registry);
if (r <= 0)
return r;
fd = machine_open_root_directory(machine);
if (ERRNO_IS_NEG_NOT_SUPPORTED(fd))
return sd_varlink_error(link, "io.systemd.Machine.NotSupported", NULL);
if (fd < 0)
return log_debug_errno(fd, "Failed to open root directory of machine '%s': %m", machine->name);
int fd_idx = sd_varlink_push_fd(link, fd);
if (ERRNO_IS_PRIVILEGE(fd_idx))
return sd_varlink_error(link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL);
if (fd_idx < 0)
return log_debug_errno(fd_idx, "Failed to push file descriptor over varlink: %m");
TAKE_FD(fd);
r = sd_json_buildo(&v, SD_JSON_BUILD_PAIR_INTEGER("fileDescriptor", fd_idx));
if (r < 0)
return r;
return sd_varlink_reply(link, v);
}

View File

@ -29,3 +29,4 @@ int vl_method_map_from(sd_varlink *link, sd_json_variant *parameters, sd_varlink
int vl_method_map_to(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int vl_method_bind_mount(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);
int vl_method_copy_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata, bool copy_from);
int vl_method_open_root_directory_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata);

View File

@ -1347,6 +1347,99 @@ int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
}
int machine_open_root_directory(Machine *machine) {
int r;
assert(machine);
switch (machine->class) {
case MACHINE_HOST: {
int fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (fd < 0)
return log_debug_errno(errno, "Failed to open host root directory: %m");
return fd;
}
case MACHINE_CONTAINER: {
_cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF;
_cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR, fd_pass_socket[2] = EBADF_PAIR;
pid_t child;
r = pidref_namespace_open(&machine->leader,
/* ret_pidns_fd = */ NULL,
&mntns_fd,
/* ret_netns_fd = */ NULL,
/* ret_userns_fd = */ NULL,
&root_fd);
if (r < 0)
return log_debug_errno(r, "Failed to open mount namespace of machine '%s': %m", machine->name);
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return log_debug_errno(errno, "Failed to open pipe: %m");
if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, fd_pass_socket) < 0)
return log_debug_errno(errno, "Failed to create socket pair: %m");
r = namespace_fork(
"(sd-openrootns)",
"(sd-openroot)",
/* except_fds = */ NULL,
/* n_except_fds = */ 0,
FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
/* pidns_fd = */ -EBADF,
mntns_fd,
/* netns_fd = */ -EBADF,
/* userns_fd = */ -EBADF,
root_fd,
&child);
if (r < 0)
return log_debug_errno(r, "Failed to fork into mount namespace of machine '%s': %m", machine->name);
if (r == 0) {
_cleanup_close_ int dfd = -EBADF;
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
fd_pass_socket[0] = safe_close(fd_pass_socket[0]);
dfd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY);
if (dfd < 0) {
log_debug_errno(dfd, "Failed to open root directory of machine '%s': %m", machine->name);
report_errno_and_exit(errno_pipe_fd[1], dfd);
}
r = send_one_fd(fd_pass_socket[1], dfd, /* flags = */ 0);
dfd = safe_close(dfd);
if (r < 0) {
log_debug_errno(r, "Failed to send FD over socket: %m");
report_errno_and_exit(errno_pipe_fd[1], r);
}
_exit(EXIT_SUCCESS);
}
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
fd_pass_socket[1] = safe_close(fd_pass_socket[1]);
r = wait_for_terminate_and_check("(sd-openrootns)", child, /* flags = */ 0);
if (r < 0)
return log_debug_errno(r, "Failed to wait for child: %m");
r = read_errno(errno_pipe_fd[0]); /* the function does debug reporting */
if (r < 0)
return r;
int fd = receive_one_fd(fd_pass_socket[0], MSG_DONTWAIT);
if (fd < 0)
return log_debug_errno(fd, "Failed to receive FD from child: %m");
return fd;
}
default:
return -EOPNOTSUPP;
}
}
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
[MACHINE_VM] = "vm",

View File

@ -125,6 +125,8 @@ int machine_owns_gid(Machine *m, gid_t host_gid, gid_t *ret_internal_gid);
int machine_translate_uid(Machine *m, uid_t internal_uid, uid_t *ret_host_uid);
int machine_translate_gid(Machine *m, gid_t internal_gid, gid_t *ret_host_gid);
int machine_open_root_directory(Machine *machine);
typedef enum AcquireMetadata {
ACQUIRE_METADATA_NO,
ACQUIRE_METADATA_YES,

View File

@ -599,6 +599,10 @@ static int vl_method_copy_to(sd_varlink *link, sd_json_variant *parameters, sd_v
return vl_method_copy_internal(link, parameters, flags, userdata, /* copy_from = */ false);
}
static int vl_method_open_root_directory(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_open_root_directory_internal);
}
static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image, bool more, AcquireMetadata am) {
int r;
@ -761,7 +765,11 @@ static int manager_varlink_init_machine(Manager *m) {
if (m->varlink_machine_server)
return 0;
r = varlink_server_new(&s, SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA, m);
r = varlink_server_new(
&s,
SD_VARLINK_SERVER_ACCOUNT_UID|SD_VARLINK_SERVER_INHERIT_USERDATA|
SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT,
m);
if (r < 0)
return log_error_errno(r, "Failed to allocate varlink server object: %m");
@ -774,21 +782,22 @@ static int manager_varlink_init_machine(Manager *m) {
r = sd_varlink_server_bind_method_many(
s,
"io.systemd.Machine.Register", vl_method_register,
"io.systemd.Machine.List", vl_method_list,
"io.systemd.Machine.Unregister", vl_method_unregister,
"io.systemd.Machine.Terminate", vl_method_terminate,
"io.systemd.Machine.Kill", vl_method_kill,
"io.systemd.Machine.Open", vl_method_open,
"io.systemd.Machine.MapFrom", vl_method_map_from,
"io.systemd.Machine.MapTo", vl_method_map_to,
"io.systemd.Machine.BindMount", vl_method_bind_mount,
"io.systemd.Machine.CopyFrom", vl_method_copy_from,
"io.systemd.Machine.CopyTo", vl_method_copy_to,
"io.systemd.MachineImage.List", vl_method_list_images,
"io.systemd.MachineImage.Update", vl_method_update_image,
"io.systemd.MachineImage.Clone", vl_method_clone_image,
"io.systemd.MachineImage.Remove", vl_method_remove_image);
"io.systemd.Machine.Register", vl_method_register,
"io.systemd.Machine.List", vl_method_list,
"io.systemd.Machine.Unregister", vl_method_unregister,
"io.systemd.Machine.Terminate", vl_method_terminate,
"io.systemd.Machine.Kill", vl_method_kill,
"io.systemd.Machine.Open", vl_method_open,
"io.systemd.Machine.OpenRootDirectory", vl_method_open_root_directory,
"io.systemd.Machine.MapFrom", vl_method_map_from,
"io.systemd.Machine.MapTo", vl_method_map_to,
"io.systemd.Machine.BindMount", vl_method_bind_mount,
"io.systemd.Machine.CopyFrom", vl_method_copy_from,
"io.systemd.Machine.CopyTo", vl_method_copy_to,
"io.systemd.MachineImage.List", vl_method_list_images,
"io.systemd.MachineImage.Update", vl_method_update_image,
"io.systemd.MachineImage.Clone", vl_method_clone_image,
"io.systemd.MachineImage.Remove", vl_method_remove_image);
if (r < 0)
return log_error_errno(r, "Failed to register varlink methods: %m");

View File

@ -179,6 +179,12 @@ static SD_VARLINK_DEFINE_METHOD(
SD_VARLINK_FIELD_COMMENT("If true the destination will be replaced"),
SD_VARLINK_DEFINE_INPUT(replace, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
static SD_VARLINK_DEFINE_METHOD(
OpenRootDirectory,
VARLINK_DEFINE_MACHINE_LOOKUP_AND_POLKIT_INPUT_FIELDS,
SD_VARLINK_FIELD_COMMENT("File descriptor of opened root directory"),
SD_VARLINK_DEFINE_OUTPUT(fileDescriptor, SD_VARLINK_INT, 0));
static SD_VARLINK_DEFINE_ERROR(NoSuchMachine);
static SD_VARLINK_DEFINE_ERROR(MachineExists);
static SD_VARLINK_DEFINE_ERROR(NoPrivateNetworking);
@ -225,6 +231,8 @@ SD_VARLINK_DEFINE_INTERFACE(
&vl_method_CopyFrom,
SD_VARLINK_SYMBOL_COMMENT("Copy files or directories from the host into a container"),
&vl_method_CopyTo,
SD_VARLINK_SYMBOL_COMMENT("Opens machine's root directory"),
&vl_method_OpenRootDirectory,
SD_VARLINK_SYMBOL_COMMENT("No matching machine currently running"),
&vl_error_NoSuchMachine,
&vl_error_MachineExists,

View File

@ -422,6 +422,10 @@ varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.CopyF
diff /var/lib/machines/long-running/foo /foo
rm -f /var/lib/machines/long-running/root/foo /foo
# test io.systemd.Machine.OpenRootDirectory
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.OpenRootDirectory '{"name": ".host"}'
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.OpenRootDirectory '{"name": "long-running"}'
# Terminating machine, otherwise acquiring image metadata by io.systemd.MachineImage.List may fail in the below.
machinectl terminate long-running
# wait for the container being stopped, otherwise acquiring image metadata by io.systemd.MachineImage.List may fail in the below.