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:
commit
606034cc1e
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
Loading…
x
Reference in New Issue
Block a user