diff options
author | Ivan Kruglov <mail@ikruglov.com> | 2024-10-23 11:53:22 +0200 |
---|---|---|
committer | Ivan Kruglov <mail@ikruglov.com> | 2024-11-06 11:37:51 +0100 |
commit | a686bedb888c4cad0bb9095278ae79b2f11bceec (patch) | |
tree | 076216b73b62592a6835a057d5c5d1755e0e215f | |
parent | json: introduce json_dispatch_strv_environment() (diff) | |
download | systemd-a686bedb888c4cad0bb9095278ae79b2f11bceec.tar.xz systemd-a686bedb888c4cad0bb9095278ae79b2f11bceec.zip |
machine: introduce io.systemd.Machine.Open method
-rw-r--r-- | src/machine/machine-varlink.c | 195 | ||||
-rw-r--r-- | src/machine/machine-varlink.h | 1 | ||||
-rw-r--r-- | src/machine/machined-varlink.c | 1 | ||||
-rw-r--r-- | src/shared/varlink-io.systemd.Machine.c | 39 |
4 files changed, 235 insertions, 1 deletions
diff --git a/src/machine/machine-varlink.c b/src/machine/machine-varlink.c index bfa4095a3b..8ad3c87469 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 "fd-util.h" #include "hostname-util.h" #include "json-util.h" #include "machine-varlink.h" @@ -16,7 +17,9 @@ #include "process-util.h" #include "signal-util.h" #include "socket-util.h" +#include "string-table.h" #include "string-util.h" +#include "user-util.h" #include "varlink-util.h" static JSON_DISPATCH_ENUM_DEFINE(dispatch_machine_class, MachineClass, machine_class_from_string); @@ -375,3 +378,195 @@ int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_met return sd_varlink_reply(link, NULL); } + +typedef enum MachineOpenMode { + MACHINE_OPEN_MODE_TTY, + MACHINE_OPEN_MODE_LOGIN, + MACHINE_OPEN_MODE_SHELL, + _MACHINE_OPEN_MODE_MAX, + _MACHINE_OPEN_MODE_INVALID = -EINVAL, +} MachineOpenMode; + +static const char* const machine_open_mode_table[_MACHINE_OPEN_MODE_MAX] = { + [MACHINE_OPEN_MODE_TTY] = "tty", + [MACHINE_OPEN_MODE_LOGIN] = "login", + [MACHINE_OPEN_MODE_SHELL] = "shell", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(machine_open_mode, MachineOpenMode); +static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_machine_open_mode, MachineOpenMode, machine_open_mode_from_string); + +typedef struct MachineOpenParameters { + const char *name, *user; + PidRef pidref; + MachineOpenMode mode; + char *path, **args, **env; +} MachineOpenParameters; + +static void machine_open_paramaters_done(MachineOpenParameters *p) { + assert(p); + pidref_done(&p->pidref); + free(p->path); + strv_free(p->args); + strv_free(p->env); +} + +inline static const char* machine_open_polkit_action(MachineOpenMode mode, MachineClass class) { + switch (mode) { + case MACHINE_OPEN_MODE_TTY: + return class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty"; + case MACHINE_OPEN_MODE_LOGIN: + return class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login"; + case MACHINE_OPEN_MODE_SHELL: + return class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell"; + default: + assert_not_reached(); + } +} + +inline static char** machine_open_polkit_details(MachineOpenMode mode, const char *machine_name, const char *user, const char *path, const char *command_line) { + assert(machine_name); + + switch (mode) { + case MACHINE_OPEN_MODE_TTY: + return strv_new("machine", machine_name); + case MACHINE_OPEN_MODE_LOGIN: + return strv_new("machine", machine_name, "verb", "login"); + case MACHINE_OPEN_MODE_SHELL: + assert(user); + assert(path); + assert(command_line); + return strv_new( + "machine", machine_name, + "verb", "shell", + "user", user, + "program", path, + "command_line", command_line); + default: + assert_not_reached(); + } +} + +int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) { + static const sd_json_dispatch_field dispatch_table[] = { + VARLINK_DISPATCH_MACHINE_LOOKUP_FIELDS(MachineOpenParameters), + { "mode", SD_JSON_VARIANT_STRING, json_dispatch_machine_open_mode, offsetof(MachineOpenParameters, mode), SD_JSON_MANDATORY }, + { "user", SD_JSON_VARIANT_STRING, json_dispatch_const_user_group_name, offsetof(MachineOpenParameters, user), SD_JSON_RELAX }, + { "path", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(MachineOpenParameters, path), 0 }, + { "args", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(MachineOpenParameters, args), 0 }, + { "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(MachineOpenParameters, env), 0 }, + VARLINK_DISPATCH_POLKIT_FIELD, + {} + }; + + Manager *manager = ASSERT_PTR(userdata); + _cleanup_close_ int ptmx_fd = -EBADF; + _cleanup_(machine_open_paramaters_done) MachineOpenParameters p = { + .pidref = PIDREF_NULL, + .mode = _MACHINE_OPEN_MODE_INVALID, + }; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + _cleanup_free_ char *ptmx_name = NULL, *command_line = NULL; + _cleanup_strv_free_ char **polkit_details = NULL, **args = NULL; + const char *user = NULL, *path = NULL; /* gcc complains about uninitialized variables */ + Machine *machine; + int r, ptmx_fd_idx; + + 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; + + if (p.mode == MACHINE_OPEN_MODE_SHELL) { + /* json_dispatch_const_user_group_name() does valid_user_group_name(p.user) */ + /* json_dispatch_path() does path_is_absolute(p.path) */ + /* json_dispatch_strv_environment() does validation of p.env */ + + user = p.user ?: "root"; + path = p.path ?: machine_default_shell_path(); + args = !p.path ? machine_default_shell_args(user) : strv_isempty(p.args) ? strv_new(path) : TAKE_PTR(p.args); + if (!args) + return -ENOMEM; + + command_line = strv_join(args, " "); + if (!command_line) + return -ENOMEM; + } + + 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; + + polkit_details = machine_open_polkit_details(p.mode, machine->name, user, path, command_line); + r = varlink_verify_polkit_async( + link, + manager->bus, + machine_open_polkit_action(p.mode, machine->class), + (const char**) polkit_details, + &manager->polkit_registry); + if (r <= 0) + return r; + + ptmx_fd = machine_openpt(machine, O_RDWR|O_NOCTTY|O_CLOEXEC, &ptmx_name); + if (ERRNO_IS_NEG_NOT_SUPPORTED(ptmx_fd)) + return sd_varlink_error(link, "io.systemd.Machine.NotSupported", NULL); + if (ptmx_fd < 0) + return log_debug_errno(ptmx_fd, "Failed to open pseudo terminal: %m"); + + switch (p.mode) { + case MACHINE_OPEN_MODE_TTY: + /* noop */ + break; + + case MACHINE_OPEN_MODE_LOGIN: + r = machine_start_getty(machine, ptmx_name, /* error = */ NULL); + if (r == -ENOENT) + return sd_varlink_error(link, "io.systemd.Machine.NoIPC", NULL); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) + return sd_varlink_error(link, "io.systemd.Machine.NotSupported", NULL); + if (r < 0) + return log_debug_errno(r, "Failed to start getty for machine '%s': %m", machine->name); + + break; + + case MACHINE_OPEN_MODE_SHELL: { + assert(user && path && args); /* to avoid gcc complaining about possible uninitialized variables */ + r = machine_start_shell(machine, ptmx_fd, ptmx_name, user, path, args, p.env, /* error = */ NULL); + if (r == -ENOENT) + return sd_varlink_error(link, "io.systemd.Machine.NoIPC", NULL); + if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) + return sd_varlink_error(link, "io.systemd.Machine.NotSupported", NULL); + if (r < 0) + return log_debug_errno(r, "Failed to start shell for machine '%s': %m", machine->name); + + break; + } + + default: + assert_not_reached(); + } + + 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 (ptmx_fd_idx < 0) + return log_debug_errno(ptmx_fd_idx, "Failed to push file descriptor over varlink: %m"); + + TAKE_FD(ptmx_fd); + + r = sd_json_buildo( + &v, + SD_JSON_BUILD_PAIR_INTEGER("ptyFileDescriptor", ptmx_fd_idx), + JSON_BUILD_PAIR_STRING_NON_EMPTY("ptyPath", ptmx_name)); + if (r < 0) + return r; + + return sd_varlink_reply(link, v); +} diff --git a/src/machine/machine-varlink.h b/src/machine/machine-varlink.h index 5b58b35d80..380f011dbe 100644 --- a/src/machine/machine-varlink.h +++ b/src/machine/machine-varlink.h @@ -24,3 +24,4 @@ int vl_method_register(sd_varlink *link, sd_json_variant *parameters, sd_varlink int vl_method_unregister_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_terminate_internal(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); int vl_method_kill(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); +int vl_method_open(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata); diff --git a/src/machine/machined-varlink.c b/src/machine/machined-varlink.c index 85cac71dda..616b972b37 100644 --- a/src/machine/machined-varlink.c +++ b/src/machine/machined-varlink.c @@ -773,6 +773,7 @@ static int manager_varlink_init_machine(Manager *m) { "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.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 3b44f80854..83a20f4f0e 100644 --- a/src/shared/varlink-io.systemd.Machine.c +++ b/src/shared/varlink-io.systemd.Machine.c @@ -95,12 +95,41 @@ static SD_VARLINK_DEFINE_METHOD_FULL( SD_VARLINK_FIELD_COMMENT("Return the base UID/GID of the machine"), SD_VARLINK_DEFINE_OUTPUT(UIDShift, SD_VARLINK_INT, SD_VARLINK_NULLABLE)); +static SD_VARLINK_DEFINE_ENUM_TYPE( + MachineOpenMode, + SD_VARLINK_FIELD_COMMENT("This mode allocates a pseudo TTY in the container and returns a file descriptor and its path. This is equivalent to transitioning into the container and invoking posix_openpt(3)."), + SD_VARLINK_DEFINE_ENUM_VALUE(tty), + SD_VARLINK_FIELD_COMMENT("This mode allocates a pseudo TTY in the container and ensures that a getty login prompt of the container is running on the other end. It returns the file descriptor of the PTY and the PTY path. This is useful for acquiring a pty with a login prompt from the container."), + SD_VARLINK_DEFINE_ENUM_VALUE(login), + SD_VARLINK_FIELD_COMMENT("This mode allocates a pseudo TTY in the container, as the specified user, and invokes the executable at the specified path with a list of arguments (starting from argv[0]) and an environment block. It then returns the file descriptor of the PTY and the PTY path."), + SD_VARLINK_DEFINE_ENUM_VALUE(shell)); + +static SD_VARLINK_DEFINE_METHOD( + Open, + VARLINK_DEFINE_MACHINE_LOOKUP_AND_POLKIT_INPUT_FIELDS, + SD_VARLINK_FIELD_COMMENT("There are three possible values: 'tty', 'login', and 'shell'. Please see description for each of the modes."), + SD_VARLINK_DEFINE_INPUT_BY_TYPE(mode, MachineOpenMode, 0), + SD_VARLINK_FIELD_COMMENT("See description of mode='shell'. Valid only when mode='shell'"), + SD_VARLINK_DEFINE_INPUT(user, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("See description of mode='shell'. Valid only when mode='shell'"), + SD_VARLINK_DEFINE_INPUT(path, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("See description of mode='shell'. Valid only when mode='shell'"), + SD_VARLINK_DEFINE_INPUT(args, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), + SD_VARLINK_FIELD_COMMENT("See description of mode='shell'. Valid only when mode='shell'"), + SD_VARLINK_DEFINE_INPUT(environment, SD_VARLINK_STRING, SD_VARLINK_NULLABLE|SD_VARLINK_ARRAY), + SD_VARLINK_FIELD_COMMENT("File descriptor of the allocated pseudo TTY"), + 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_ERROR(NoSuchMachine); static SD_VARLINK_DEFINE_ERROR(MachineExists); static SD_VARLINK_DEFINE_ERROR(NoPrivateNetworking); static SD_VARLINK_DEFINE_ERROR(NoOSReleaseInformation); static SD_VARLINK_DEFINE_ERROR(NoUIDShift); static SD_VARLINK_DEFINE_ERROR(NotAvailable); +static SD_VARLINK_DEFINE_ERROR(NotSupported); +static SD_VARLINK_DEFINE_ERROR(NoIPC); SD_VARLINK_DEFINE_INTERFACE( io_systemd_Machine, @@ -121,6 +150,10 @@ SD_VARLINK_DEFINE_INTERFACE( &vl_method_Kill, SD_VARLINK_SYMBOL_COMMENT("List running machines"), &vl_method_List, + SD_VARLINK_SYMBOL_COMMENT("A enum field which defines way to open TTY for a machine"), + &vl_type_MachineOpenMode, + SD_VARLINK_SYMBOL_COMMENT("Allocates a pseudo TTY in the container in various modes"), + &vl_method_Open, SD_VARLINK_SYMBOL_COMMENT("No matching machine currently running"), &vl_error_NoSuchMachine, &vl_error_MachineExists, @@ -131,4 +164,8 @@ SD_VARLINK_DEFINE_INTERFACE( SD_VARLINK_SYMBOL_COMMENT("Machine uses a complex UID/GID mapping, cannot determine shift"), &vl_error_NoUIDShift, SD_VARLINK_SYMBOL_COMMENT("Requested information is not available"), - &vl_error_NotAvailable); + &vl_error_NotAvailable, + SD_VARLINK_SYMBOL_COMMENT("Requested operation is not supported"), + &vl_error_NotSupported, + SD_VARLINK_SYMBOL_COMMENT("There is no IPC service (such as system bus or varlink) in the container"), + &vl_error_NoIPC); |