From bf4066ece0d40ebf59c70dcf5c6448930ca14667 Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Wed, 6 Nov 2024 14:31:29 +0100 Subject: [PATCH 1/7] machine: adjust operation callback logic for varlink This is to simplyfy varlink callback. There is no use of this logic atm. So, no harm. --- src/machine/operation.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/machine/operation.c b/src/machine/operation.c index 7d7bfa50627..00b9e092499 100644 --- a/src/machine/operation.c +++ b/src/machine/operation.c @@ -46,10 +46,12 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat if (r < 0) log_debug_errno(r, "Operation failed: %m"); - /* If a completion routine (o->done) is set for this operation, call it. It sends a response, but can return an error in which case it expect us to reply. - * Otherwise, the default action is to simply return an error on failure or an empty success message on success. */ - if (o->message) { + /* If o->done set, call it. It sends a response, but can return + * an error in which case it expect this code to reply. + * If o->done is not set, the default action is to simply return + * an error on failure or an empty success message on success.*/ + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; if (o->done) r = o->done(o, r, &error); @@ -68,13 +70,16 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat log_error_errno(r, "Failed to reply to dbus message: %m"); } } else if (o->link) { - if (o->done) - r = o->done(o, r, /* error = */ NULL); + /* If o->done set, call it. Unlike o->message case above, this + * code expect o->done to reply in all cases. + * If o->done is not set, the default action is to simply return + * an error on failure or an empty success message on success.*/ - if (r < 0) + if (o->done) + (void) o->done(o, r, /* error = */ NULL); + else if (r < 0) (void) sd_varlink_error_errno(o->link, r); - else if (!o->done) - /* when o->done set it's responsible for sending reply in a happy-path case */ + else (void) sd_varlink_reply(o->link, NULL); } else assert_not_reached(); From 22cd741ff16104ec3426327b6ae872c6b6a751de Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Tue, 17 Dec 2024 12:24:51 +0100 Subject: [PATCH 2/7] machine: split operation initialization into two steps --- src/machine/operation.c | 13 +++++------ src/machine/operation.h | 48 ++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/machine/operation.c b/src/machine/operation.c index 00b9e092499..e8b297dce08 100644 --- a/src/machine/operation.c +++ b/src/machine/operation.c @@ -88,15 +88,14 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat return 0; } -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, sd_varlink *link, int errno_fd, Operation **ret) { +int operation_new(Manager *manager, Machine *machine, pid_t child, int errno_fd, Operation **ret) { Operation *o; int r; assert(manager); assert(child > 1); assert(errno_fd >= 0); - assert(message || link); - assert(!(message && link)); + assert(ret); o = new0(Operation, 1); if (!o) @@ -111,8 +110,8 @@ int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_messag } o->pid = child; - o->message = sd_bus_message_ref(message); - o->link = sd_varlink_ref(link); + o->message = NULL; + o->link = NULL; o->errno_fd = errno_fd; LIST_PREPEND(operations, manager->operations, o); @@ -128,9 +127,7 @@ int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_messag /* At this point we took ownership of both the child and the errno file descriptor! */ - if (ret) - *ret = o; - + *ret = o; return 0; } diff --git a/src/machine/operation.h b/src/machine/operation.h index 75bf918c2b1..4501cab6f09 100644 --- a/src/machine/operation.h +++ b/src/machine/operation.h @@ -31,11 +31,53 @@ struct Operation { LIST_FIELDS(Operation, operations_by_machine); }; -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, sd_varlink *link, int errno_fd, Operation **ret); +int operation_new(Manager *manager, Machine *machine, pid_t child, int errno_fd, Operation **ret); Operation *operation_free(Operation *o); + +static inline void operation_with_bus_reply(Operation *op, sd_bus_message *message) { + assert(op); + assert(!op->message); + assert(!op->link); + assert(message); + op->message = sd_bus_message_ref(message); +} + +static inline void operation_with_varlink_reply(Operation *op, sd_varlink *link) { + assert(op); + assert(!op->message); + assert(!op->link); + assert(link); + op->link = sd_varlink_ref(link); +} + static inline int operation_new_with_bus_reply(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret) { - return operation_new(manager, machine, child, message, /* link = */ NULL, errno_fd, ret); + Operation *op; + int r; + + r = operation_new(manager, machine, child, errno_fd, &op); + if (r < 0) + return r; + + operation_with_bus_reply(op, message); + + if (ret) + *ret = op; + + return 0; } + static inline int operation_new_with_varlink_reply(Manager *manager, Machine *machine, pid_t child, sd_varlink *link, int errno_fd, Operation **ret) { - return operation_new(manager, machine, child, /* message = */ NULL, link, errno_fd, ret); + Operation *op; + int r; + + r = operation_new(manager, machine, child, errno_fd, &op); + if (r < 0) + return r; + + operation_with_varlink_reply(op, link); + + if (ret) + *ret = op; + + return 0; } From 2b9c7a8bd0ac58e6a3f89e352c9f2aa2faa51c6b Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Tue, 17 Dec 2024 12:25:34 +0100 Subject: [PATCH 3/7] machine: introduce machine_copy_from_to() helper --- src/machine/machine.c | 109 ++++++++++++++++++++++++++++++++++++++++++ src/machine/machine.h | 2 + 2 files changed, 111 insertions(+) diff --git a/src/machine/machine.c b/src/machine/machine.c index 587c2267b69..15959db0aeb 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -973,6 +973,115 @@ char** machine_default_shell_args(const char *user) { return TAKE_PTR(args); } +int machine_copy_from_to(Manager *manager, Machine *machine, const char *host_path, const char *container_path, bool copy_from, CopyFlags copy_flags, Operation **ret) { + _cleanup_close_ int hostfd = -EBADF, mntns_fd = -EBADF; + _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR; + _cleanup_free_ char *host_basename = NULL, *container_basename = NULL; + uid_t uid_shift; + pid_t child; + int r; + + assert(manager); + assert(machine); + assert(ret); + + if (isempty(host_path) || isempty(container_path)) + return -EINVAL; + + r = path_extract_filename(host_path, &host_basename); + if (r < 0) + return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", host_path); + + r = path_extract_filename(container_path, &container_basename); + if (r < 0) + return log_debug_errno(r, "Failed to extract file name of '%s' path: %m", container_path); + + hostfd = open_parent(host_path, O_CLOEXEC, 0); + if (hostfd < 0) + return log_debug_errno(hostfd, "Failed to open host directory %s: %m", host_path); + + r = machine_get_uid_shift(machine, &uid_shift); + if (r < 0) + return log_debug_errno(r, "Failed to get machine '%s' UID shift: %m", machine->name); + + r = pidref_namespace_open(&machine->leader, + /* ret_pidns_fd = */ NULL, + &mntns_fd, + /* ret_netns_fd = */ NULL, + /* ret_userns_fd = */ NULL, + /* ret_root_fd = */ NULL); + if (r < 0) + return log_debug_errno(r, "Failed to open namespace for machine '%s': %m", machine->name); + + if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) + return log_debug_errno(errno, "Failed to create pipe: %m"); + + r = namespace_fork("(sd-copyns)", + "(sd-copy)", + /* except_fds = */ NULL, + /* n_except_fds = */ 0, + FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL, + /* pidns_fd = */ -1, + mntns_fd, + /* netns_fd = */ -1, + /* userns_fd = */ -1, + /* root_fd = */ -1, + &child); + if (r < 0) + return log_debug_errno(r, "Failed to fork() for machine '%s': %m", machine->name); + if (r == 0) { + errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); + + _cleanup_close_ int containerfd = -EBADF; + containerfd = open_parent(container_path, O_CLOEXEC, 0); + if (containerfd < 0) { + log_error_errno(containerfd, "Failed to open destination directory: %m"); + report_errno_and_exit(errno_pipe_fd[1], containerfd); + } + + /* Run the actual copy operation. Note that when a UID shift is set we'll either clamp the UID/GID to */ + /* 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy */ + /* the UID/GIDs as they are. */ + r = copy_from ? copy_tree_at( + containerfd, + container_basename, + hostfd, + host_basename, + uid_shift == 0 ? UID_INVALID : 0, + uid_shift == 0 ? GID_INVALID : 0, + copy_flags, + /* denylist = */ NULL, + /* subvolumes = */ NULL) + : copy_tree_at( + hostfd, + host_basename, + containerfd, + container_basename, + uid_shift == 0 ? UID_INVALID : uid_shift, + uid_shift == 0 ? GID_INVALID : uid_shift, + copy_flags, + /* denylist = */ NULL, + /* subvolumes = */ NULL); + if (r < 0) + log_error_errno(r, "Failed to copy tree: %m"); + + report_errno_and_exit(errno_pipe_fd[1], r); + } + + errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + + Operation *operation; + r = operation_new(manager, machine, child, errno_pipe_fd[0], &operation); + if (r < 0) { + sigkill_wait(child); + return r; + } + + TAKE_FD(errno_pipe_fd[0]); + *ret = operation; + return 0; +} + void machine_release_unit(Machine *m) { assert(m); diff --git a/src/machine/machine.h b/src/machine/machine.h index bf265143f03..5587c5556e9 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -4,6 +4,7 @@ typedef struct Machine Machine; typedef enum KillWhom KillWhom; +#include "copy.h" #include "list.h" #include "machined.h" #include "operation.h" @@ -107,6 +108,7 @@ int machine_start_shell(Machine *m, int ptmx_fd, const char *ptmx_name, const ch #define machine_default_shell_path() ("/bin/sh") char** machine_default_shell_args(const char *user); +int machine_copy_from_to(Manager *manager, Machine *machine, const char *host_path, const char *container_path, bool copy_from, CopyFlags copy_flags, Operation **ret); int machine_get_uid_shift(Machine *m, uid_t *ret); int machine_owns_uid(Machine *m, uid_t host_uid, uid_t *ret_internal_uid); From 9f309f9893fe2663ddbfd6ce82ba57d133b9a44e Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Tue, 17 Dec 2024 12:53:28 +0100 Subject: [PATCH 4/7] machine: use machine_copy_from_to() in dbus implementation --- src/machine/machine-dbus.c | 91 +++----------------------------------- 1 file changed, 6 insertions(+), 85 deletions(-) diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 2ed779d117f..923b04d843b 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -492,15 +492,11 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu } int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *host_basename = NULL, *container_basename = NULL; const char *src, *dest, *host_path, *container_path; - _cleanup_close_pair_ int errno_pipe_fd[2] = EBADF_PAIR; CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS; - _cleanup_close_ int hostfd = -EBADF; Machine *m = ASSERT_PTR(userdata); + Manager *manager = m->manager; bool copy_from; - pid_t child; - uid_t uid_shift; int r; assert(message); @@ -549,17 +545,13 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro message, "org.freedesktop.machine1.manage-machines", details, - &m->manager->polkit_registry, + &manager->polkit_registry, error); if (r < 0) return r; if (r == 0) return 1; /* Will call us back */ - r = machine_get_uid_shift(m, &uid_shift); - if (r < 0) - return r; - copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom"); if (copy_from) { @@ -570,83 +562,12 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro container_path = dest; } - r = path_extract_filename(host_path, &host_basename); + Operation *op; + r = machine_copy_from_to(manager, m, host_path, container_path, copy_from, copy_flags, &op); if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to extract file name of '%s' path: %m", host_path); - - r = path_extract_filename(container_path, &container_basename); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to extract file name of '%s' path: %m", container_path); - - hostfd = open_parent(host_path, O_CLOEXEC, 0); - if (hostfd < 0) - return sd_bus_error_set_errnof(error, hostfd, "Failed to open host directory %s: %m", host_path); - - if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); - - r = safe_fork("(sd-copy)", FORK_RESET_SIGNALS, &child); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); - if (r == 0) { - int containerfd; - const char *q; - int mntfd; - - errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); - - q = procfs_file_alloca(m->leader.pid, "ns/mnt"); - mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntfd < 0) { - r = log_error_errno(errno, "Failed to open mount namespace of leader: %m"); - goto child_fail; - } - - if (setns(mntfd, CLONE_NEWNS) < 0) { - r = log_error_errno(errno, "Failed to join namespace of leader: %m"); - goto child_fail; - } - - containerfd = open_parent(container_path, O_CLOEXEC, 0); - if (containerfd < 0) { - r = log_error_errno(containerfd, "Failed to open destination directory: %m"); - goto child_fail; - } - - /* Run the actual copy operation. Note that when a UID shift is set we'll either clamp the UID/GID to - * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy - * the UID/GIDs as they are. */ - if (copy_from) - r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, uid_shift == 0 ? UID_INVALID : 0, uid_shift == 0 ? GID_INVALID : 0, copy_flags, NULL, NULL); - else - r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, uid_shift == 0 ? UID_INVALID : uid_shift, uid_shift == 0 ? GID_INVALID : uid_shift, copy_flags, NULL, NULL); - - hostfd = safe_close(hostfd); - containerfd = safe_close(containerfd); - - if (r < 0) { - r = log_error_errno(r, "Failed to copy tree: %m"); - goto child_fail; - } - - _exit(EXIT_SUCCESS); - - child_fail: - (void) write(errno_pipe_fd[1], &r, sizeof(r)); - _exit(EXIT_FAILURE); - } - - errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); - - /* Copying might take a while, hence install a watch on the child, and return */ - - r = operation_new_with_bus_reply(m->manager, m, child, message, errno_pipe_fd[0], /* ret= */ NULL); - if (r < 0) { - (void) sigkill_wait(child); - return r; - } - errno_pipe_fd[0] = -EBADF; + return sd_bus_error_set_errnof(error, r, "Failed to copy from/to machine '%s': %m", m->name); + operation_with_bus_reply(op, message); return 1; } From 9616d7eb446ddf2c551a42bf61c32e95816d615b Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Thu, 24 Oct 2024 12:19:56 +0200 Subject: [PATCH 5/7] machine: introduce io.system.Machine.{CopyFrom, CopyTo} methods --- src/machine/machine-varlink.c | 100 ++++++++++++++++++++++++ src/machine/machine-varlink.h | 1 + src/machine/machined-varlink.c | 9 +++ src/shared/varlink-io.systemd.Machine.c | 29 +++++++ 4 files changed, 139 insertions(+) diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index 1d2814b26a3..e8c10312f3f 100644 --- a/src/machine/machine-varlink.c +++ b/src/machine/machine-varlink.c @@ -7,6 +7,7 @@ #include "sd-varlink.h" #include "bus-polkit.h" +#include "copy.h" #include "fd-util.h" #include "hostname-util.h" #include "json-util.h" @@ -728,3 +729,102 @@ int vl_method_map_to(sd_varlink *link, sd_json_variant *parameters, sd_varlink_m return sd_varlink_reply(link, v); } + +typedef struct MachineCopyParameters { + const char *name; + PidRef pidref; + char *src, *dest; + bool replace; +} MachineCopyParameters; + +static void machine_copy_paramaters_done(MachineCopyParameters *p) { + assert(p); + + pidref_done(&p->pidref); + free(p->src); + free(p->dest); +} + +static int copy_done(Operation *operation, int ret, sd_bus_error *error) { + assert(operation); + assert(operation->link); + + // TODO(ikruglov): maybe just leaving a plain errno in response? + if (ret == -EPERM || ret == -EACCES) + return sd_varlink_error(operation->link, SD_VARLINK_ERROR_PERMISSION_DENIED, NULL); + if (ERRNO_IS_NEG_NOT_SUPPORTED(ret)) + return sd_varlink_error(operation->link, "io.systemd.Machine.NotSupported", NULL); + if (ret == -ENOENT) + return sd_varlink_error(operation->link, "io.systemd.Machine.NoSuchFile", NULL); + if (ret == -EEXIST) + return sd_varlink_error(operation->link, "io.systemd.Machine.FileExists", NULL); + if (ret < 0) + return sd_varlink_error_errno(operation->link, ret); + + return sd_varlink_reply(operation->link, NULL); +} + +int vl_method_copy_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata, bool copy_from) { + static const sd_json_dispatch_field dispatch_table[] = { + VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineCopyParameters), + { "source", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(MachineCopyParameters, src), SD_JSON_MANDATORY }, + { "destination", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(MachineCopyParameters, dest), 0 }, + { "replace", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(MachineCopyParameters, replace), 0 }, + VARLINK_DISPATCH_POLKIT_FIELD, + {} + }; + + int r; + Manager *manager = ASSERT_PTR(userdata); + _cleanup_(machine_copy_paramaters_done) MachineCopyParameters p = { + .pidref = PIDREF_NULL + }; + + assert(link); + assert(parameters); + + if (manager->n_operations >= OPERATIONS_MAX) + return sd_varlink_error(link, "io.systemd.MachineImage.TooManyOperations", NULL); + + r = sd_varlink_dispatch(link, parameters, dispatch_table, &p); + if (r != 0) + return r; + + /* There is no need for extra validation since path_is_absolute() does path_is_valid() and path_is_absolute().*/ + const char *dest = p.dest ?: p.src; + const char *container_path = copy_from ? p.src : dest; + const char *host_path = copy_from ? dest : p.src; + CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE|COPY_HARDLINKS; + copy_flags |= p.replace ? COPY_REPLACE : 0; + + Machine *machine; + r = lookup_machine_by_name_or_pidref(link, manager, p.name, &p.pidref, &machine); + if (r == -ESRCH) + return sd_varlink_error(link, "io.systemd.Machine.NoSuchMachine", NULL); + if (r != 0) + return r; + + if (machine->class != MACHINE_CONTAINER) + return sd_varlink_error(link, "io.systemd.Machine.NotSupported", NULL); + + r = varlink_verify_polkit_async( + link, + manager->bus, + "org.freedesktop.machine1.manage-machines", + (const char**) STRV_MAKE("name", machine->name, + "verb", "copy", + "src", p.src, + "dest", dest), + &manager->polkit_registry); + if (r <= 0) + return r; + + Operation *op; + r = machine_copy_from_to(manager, machine, host_path, container_path, copy_from, copy_flags, &op); + if (r < 0) + return log_debug_errno(r, "Failed to copy from/to machine '%s': %m", machine->name); + + operation_with_varlink_reply(op, link); + op->done = copy_done; + return 1; +} diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h index 984a8d8f3ed..84185d2fe2d 100644 --- a/src/machine/machine-varlink.h +++ b/src/machine/machine-varlink.h @@ -27,3 +27,4 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_map_from(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_map_to(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); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index e0e27c44964..db9f8d87ade 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -591,6 +591,13 @@ static int vl_method_terminate(sd_varlink *link, sd_json_variant *parameters, sd return lookup_machine_and_call_method(link, parameters, flags, userdata, vl_method_terminate_internal); } +static int vl_method_copy_from(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return vl_method_copy_internal(link, parameters, flags, userdata, /* copy_from = */ true); +} +static int vl_method_copy_to(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + return vl_method_copy_internal(link, parameters, flags, userdata, /* copy_from = */ false); +} + static int list_image_one_and_maybe_read_metadata(sd_varlink *link, Image *image, bool more, AcquireMetadata am) { int r; @@ -773,6 +780,8 @@ static int manager_varlink_init_machine(Manager *m) { "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.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, diff --git a/src/shared/varlink-io.systemd.Machine.c b/src/shared/varlink-io.systemd.Machine.c index 696d4020022..4886069e905 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -121,6 +121,25 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_DEFINE_OUTPUT(ptyFileDescriptor, SD_VARLINK_INT, 0), SD_VARLINK_FIELD_COMMENT("Path to the allocated pseudo TTY"), SD_VARLINK_DEFINE_OUTPUT(ptyPath, SD_VARLINK_STRING, 0)); +static SD_VARLINK_DEFINE_METHOD( + CopyFrom, + VARLINK_DEFINE_MACHINE_LOOKUP_AND_POLKIT_INPUT_FIELDS, + SD_VARLINK_FIELD_COMMENT("A source directory/file in the container"), + SD_VARLINK_DEFINE_INPUT(source, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("A destination directory/file in the container. If null, it's equal to 'source'"), + SD_VARLINK_DEFINE_INPUT(destination, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + 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( + CopyTo, + VARLINK_DEFINE_MACHINE_LOOKUP_AND_POLKIT_INPUT_FIELDS, + SD_VARLINK_FIELD_COMMENT("A source directory/file on the host"), + SD_VARLINK_DEFINE_INPUT(source, SD_VARLINK_STRING, 0), + SD_VARLINK_FIELD_COMMENT("A destination directory/file in the container. If null, it's equal to 'source'"), + SD_VARLINK_DEFINE_INPUT(destination, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + 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( MapFrom, @@ -149,6 +168,8 @@ static SD_VARLINK_DEFINE_METHOD( static SD_VARLINK_DEFINE_ERROR(NoSuchMachine); static SD_VARLINK_DEFINE_ERROR(MachineExists); +static SD_VARLINK_DEFINE_ERROR(NoSuchFile); +static SD_VARLINK_DEFINE_ERROR(FileExists); static SD_VARLINK_DEFINE_ERROR(NoPrivateNetworking); static SD_VARLINK_DEFINE_ERROR(NoOSReleaseInformation); static SD_VARLINK_DEFINE_ERROR(NoUIDShift); @@ -187,9 +208,17 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_method_MapFrom, SD_VARLINK_SYMBOL_COMMENT("Maps given host's UID/GID to a machine and corresponding UID/GID"), &vl_method_MapTo, + SD_VARLINK_SYMBOL_COMMENT("Copy files or directories from a container into the host"), + &vl_method_CopyFrom, + SD_VARLINK_SYMBOL_COMMENT("Copy files or directories from the host into a container"), + &vl_method_CopyTo, SD_VARLINK_SYMBOL_COMMENT("No matching machine currently running"), &vl_error_NoSuchMachine, &vl_error_MachineExists, + SD_VARLINK_SYMBOL_COMMENT("No such file"), + &vl_error_NoSuchFile, + SD_VARLINK_SYMBOL_COMMENT("File exists"), + &vl_error_FileExists, SD_VARLINK_SYMBOL_COMMENT("Machine does not use private networking"), &vl_error_NoPrivateNetworking, SD_VARLINK_SYMBOL_COMMENT("Machine does not contain OS release information"), From f1ce905e94087b10105c0c7b7e4a2d332c84b60e Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Thu, 24 Oct 2024 12:32:08 +0200 Subject: [PATCH 6/7] machine: tests for io.systemd.Machine.{CopyFrom, CopyTo} methods --- test/units/TEST-13-NSPAWN.machined.sh | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/test/units/TEST-13-NSPAWN.machined.sh b/test/units/TEST-13-NSPAWN.machined.sh index 85eb89b14d8..f36557f2b84 100755 --- a/test/units/TEST-13-NSPAWN.machined.sh +++ b/test/units/TEST-13-NSPAWN.machined.sh @@ -257,7 +257,7 @@ done #################### # varlinkctl tests # -# ################## +#################### long_running_machine_start @@ -395,7 +395,26 @@ varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open timeout 30 bash -c "until test -e /tmp/none-existent-file; do sleep .5; done" grep -q "BAR" /tmp/none-existent-file -# terminate machines +# test io.systemd.Machine.CopyTo +rm -f /tmp/foo /var/lib/machines/long-running/root/foo +cp /etc/machine-id /tmp/foo +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.CopyTo '{"name": "long-running", "source": "/tmp/foo", "destination": "/root/foo"}' +diff /tmp/foo /var/lib/machines/long-running/root/foo +(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.CopyTo '{"name": "long-running", "source": "/tmp/foo", "destination": "/root/foo"}') 2>&1 | grep "FileExists" +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.CopyTo '{"name": "long-running", "source": "/tmp/foo", "destination": "/root/foo", "replace": true}' + +echo "sample-test-output" > /tmp/foo +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.CopyTo '{"name": "long-running", "source": "/tmp/foo", "destination": "/root/foo", "replace": true}' +diff /tmp/foo /var/lib/machines/long-running/root/foo +rm -f /tmp/foo /var/lib/machines/long-running/root/foo + +# test io.systemd.Machine.CopyFrom +cp /etc/machine-id /var/lib/machines/long-running/foo +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.CopyFrom '{"name": "long-running", "source": "/foo"}' +diff /var/lib/machines/long-running/foo /foo +rm -f /var/lib/machines/long-running/root/foo /foo + +# 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. timeout 10 bash -c "while machinectl status long-running &>/dev/null; do sleep .5; done" From 39a4935e3c1e4e9fac8718920af641c74127c4bd Mon Sep 17 00:00:00 2001 From: Ivan Kruglov Date: Tue, 17 Dec 2024 19:03:05 +0100 Subject: [PATCH 7/7] machine: move io.systemd.Machine.Map* tests into right position --- test/units/TEST-13-NSPAWN.machined.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/units/TEST-13-NSPAWN.machined.sh b/test/units/TEST-13-NSPAWN.machined.sh index f36557f2b84..04949e98eb1 100755 --- a/test/units/TEST-13-NSPAWN.machined.sh +++ b/test/units/TEST-13-NSPAWN.machined.sh @@ -381,6 +381,11 @@ varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": "login"}' varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": "shell"}' +rm -f /tmp/none-existent-file +varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": "shell", "user": "root", "path": "/bin/sh", "args": ["/bin/sh", "-c", "echo $FOO > /tmp/none-existent-file"], "environment": ["FOO=BAR"]}' +timeout 30 bash -c "until test -e /tmp/none-existent-file; do sleep .5; done" +grep -q "BAR" /tmp/none-existent-file + # test io.systemd.Machine.MapFrom varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.MapFrom '{"name": "long-running", "uid":0, "gid": 0}' container_uid=$(varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.MapFrom '{"name": "long-running", "uid":0}' | jq '.uid') @@ -390,11 +395,6 @@ varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.MapTo (! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.MapTo '{"uid": 0}') (! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.MapTo '{"gid": 0}') -rm -f /tmp/none-existent-file -varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": "shell", "user": "root", "path": "/bin/sh", "args": ["/bin/sh", "-c", "echo $FOO > /tmp/none-existent-file"], "environment": ["FOO=BAR"]}' -timeout 30 bash -c "until test -e /tmp/none-existent-file; do sleep .5; done" -grep -q "BAR" /tmp/none-existent-file - # test io.systemd.Machine.CopyTo rm -f /tmp/foo /var/lib/machines/long-running/root/foo cp /etc/machine-id /tmp/foo