mirror of
https://github.com/systemd/systemd.git
synced 2024-11-23 18:23:32 +08:00
machine: introduce io.systemd.Machine.Open method (#34867)
This PR introduces io.systemd.Machine.Open method which combines three DBus alternatives: - OpenMachinePTY - OpenMachineLogin - OpenMachineShell The PR contains basic tests.
This commit is contained in:
commit
4055529003
@ -2,6 +2,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "devnum-util.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "in-addr-util.h"
|
||||
@ -369,6 +370,40 @@ int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dis
|
||||
return 0;
|
||||
}
|
||||
|
||||
int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
_cleanup_strv_free_ char **n = NULL;
|
||||
char ***l = userdata;
|
||||
int r;
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*l = strv_free(*l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sd_json_variant_is_array(variant))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
||||
|
||||
for (size_t i = 0; i < sd_json_variant_elements(variant); i++) {
|
||||
sd_json_variant *e;
|
||||
const char *a;
|
||||
|
||||
e = sd_json_variant_by_index(variant, i);
|
||||
if (!sd_json_variant_is_string(e))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
|
||||
|
||||
assert_se(a = sd_json_variant_string(e));
|
||||
|
||||
if (!env_assignment_is_valid(a))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of environment variables.", strna(name));
|
||||
|
||||
r = strv_env_replace_strdup(&n, a);
|
||||
if (r < 0)
|
||||
return json_log_oom(variant, flags);
|
||||
}
|
||||
|
||||
return strv_free_and_replace(*l, n);
|
||||
}
|
||||
|
||||
static int json_variant_new_stat(sd_json_variant **ret, const struct stat *st) {
|
||||
char mode[STRLEN("0755")+1];
|
||||
|
||||
|
@ -116,6 +116,7 @@ int json_dispatch_path(const char *name, sd_json_variant *variant, sd_json_dispa
|
||||
int json_dispatch_pidref(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_devnum(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_ifindex(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
int json_dispatch_strv_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata);
|
||||
|
||||
static inline int json_variant_unbase64_iovec(sd_json_variant *v, struct iovec *ret) {
|
||||
return sd_json_variant_unbase64(v, ret ? &ret->iov_base : NULL, ret ? &ret->iov_len : NULL);
|
||||
|
@ -294,59 +294,11 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
|
||||
return sd_bus_send(NULL, reply, NULL);
|
||||
}
|
||||
|
||||
static int container_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(ret);
|
||||
|
||||
switch (m->class) {
|
||||
|
||||
case MACHINE_HOST:
|
||||
*ret = NULL;
|
||||
break;
|
||||
|
||||
case MACHINE_CONTAINER: {
|
||||
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
|
||||
char *address;
|
||||
|
||||
r = sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (asprintf(&address, "x-machine-unix:pid=%" PID_PRI, m->leader.pid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
bus->address = address;
|
||||
bus->bus_client = true;
|
||||
bus->trusted = false;
|
||||
bus->runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
|
||||
r = sd_bus_start(bus);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(bus);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_free_ char *pty_name = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
|
||||
_cleanup_close_ int master = -EBADF;
|
||||
sd_bus *container_bus = NULL;
|
||||
Machine *m = ASSERT_PTR(userdata);
|
||||
const char *p, *getty;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
@ -372,18 +324,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (master < 0)
|
||||
return master;
|
||||
|
||||
p = path_startswith(pty_name, "/dev/pts/");
|
||||
assert(p);
|
||||
|
||||
r = container_bus_new(m, error, &allocated_bus);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
container_bus = allocated_bus ?: m->manager->bus;
|
||||
|
||||
getty = strjoina("container-getty@", p, ".service");
|
||||
|
||||
r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, NULL, "ss", getty, "replace");
|
||||
r = machine_start_getty(m, pty_name, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -399,15 +340,13 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
|
||||
}
|
||||
|
||||
int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *tm = NULL;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||
_cleanup_free_ char *pty_name = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
|
||||
sd_bus *container_bus = NULL;
|
||||
_cleanup_close_ int master = -EBADF, slave = -EBADF;
|
||||
_cleanup_close_ int master = -EBADF;
|
||||
_cleanup_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
|
||||
_cleanup_free_ char *command_line = NULL;
|
||||
Machine *m = ASSERT_PTR(userdata);
|
||||
const char *unit, *user, *path, *description, *utmp_id;
|
||||
const char *user, *path;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
@ -420,25 +359,10 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (isempty(path)) {
|
||||
path = "/bin/sh";
|
||||
|
||||
args = new0(char*, 3 + 1);
|
||||
path = machine_default_shell_path();
|
||||
args = machine_default_shell_args(user);
|
||||
if (!args)
|
||||
return -ENOMEM;
|
||||
args[0] = strdup("sh");
|
||||
if (!args[0])
|
||||
return -ENOMEM;
|
||||
args[1] = strdup("-c");
|
||||
if (!args[1])
|
||||
return -ENOMEM;
|
||||
r = asprintf(&args[2],
|
||||
"shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
|
||||
"exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
|
||||
user);
|
||||
if (r < 0) {
|
||||
args[2] = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
} else {
|
||||
if (!path_is_absolute(path))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path);
|
||||
@ -484,153 +408,10 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
|
||||
if (master < 0)
|
||||
return master;
|
||||
|
||||
/* First try to get an fd for the PTY peer via the new racefree ioctl(), directly. Otherwise go via
|
||||
* joining the namespace, because it goes by path */
|
||||
slave = pty_open_peer_racefree(master, O_RDWR|O_NOCTTY|O_CLOEXEC);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(slave))
|
||||
slave = machine_open_terminal(m, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
|
||||
if (slave < 0)
|
||||
return slave;
|
||||
|
||||
utmp_id = path_startswith(pty_name, "/dev/");
|
||||
assert(utmp_id);
|
||||
|
||||
r = container_bus_new(m, error, &allocated_bus);
|
||||
r = machine_start_shell(m, master, pty_name, user, path, args, env, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
container_bus = allocated_bus ?: m->manager->bus;
|
||||
|
||||
r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Name and mode */
|
||||
const char *p = ASSERT_PTR(path_startswith(pty_name, "/dev/pts/"));
|
||||
|
||||
unit = strjoina("container-shell@", p, ".service");
|
||||
r = sd_bus_message_append(tm, "ss", unit, "fail");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Properties */
|
||||
r = sd_bus_message_open_container(tm, 'a', "(sv)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
description = strjoina("Shell for User ", user);
|
||||
r = sd_bus_message_append(tm,
|
||||
"(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
|
||||
"Description", "s", description,
|
||||
"StandardInputFileDescriptor", "h", slave,
|
||||
"StandardOutputFileDescriptor", "h", slave,
|
||||
"StandardErrorFileDescriptor", "h", slave,
|
||||
"SendSIGHUP", "b", true,
|
||||
"IgnoreSIGPIPE", "b", false,
|
||||
"KillMode", "s", "mixed",
|
||||
"TTYPath", "s", pty_name,
|
||||
"TTYReset", "b", true,
|
||||
"UtmpIdentifier", "s", utmp_id,
|
||||
"UtmpMode", "s", "user",
|
||||
"PAMName", "s", "login",
|
||||
"WorkingDirectory", "s", "-~");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strv_isempty(env)) {
|
||||
r = sd_bus_message_open_container(tm, 'r', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "s", "Environment");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'v', "as");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append_strv(tm, env);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Exec container */
|
||||
r = sd_bus_message_open_container(tm, 'r', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "s", "ExecStart");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'a', "(sasb)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'r', "sasb");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "s", path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append_strv(tm, args);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "b", true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Auxiliary units */
|
||||
r = sd_bus_message_append(tm, "a(sa(sv))", 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_call(container_bus, tm, 0, error, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
slave = safe_close(slave);
|
||||
|
||||
r = sd_bus_message_new_method_return(message, &reply);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "bus-error.h"
|
||||
#include "bus-internal.h"
|
||||
#include "bus-locator.h"
|
||||
#include "bus-unit-util.h"
|
||||
#include "bus-util.h"
|
||||
@ -702,6 +703,276 @@ int machine_open_terminal(Machine *m, const char *path, int mode) {
|
||||
}
|
||||
}
|
||||
|
||||
static int machine_bus_new(Machine *m, sd_bus_error *error, sd_bus **ret) {
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(ret);
|
||||
|
||||
switch (m->class) {
|
||||
|
||||
case MACHINE_HOST:
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
|
||||
case MACHINE_CONTAINER: {
|
||||
_cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL;
|
||||
char *address;
|
||||
|
||||
r = sd_bus_new(&bus);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to allocate new DBus: %m");
|
||||
|
||||
if (asprintf(&address, "x-machine-unix:pid=%" PID_PRI, m->leader.pid) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
bus->address = address;
|
||||
bus->bus_client = true;
|
||||
bus->trusted = false;
|
||||
bus->runtime_scope = RUNTIME_SCOPE_SYSTEM;
|
||||
|
||||
r = sd_bus_start(bus);
|
||||
if (r == -ENOENT)
|
||||
return sd_bus_error_set_errnof(error, r, "There is no system bus in container %s.", m->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
int machine_start_getty(Machine *m, const char *ptmx_name, sd_bus_error *error) {
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
|
||||
sd_bus *container_bus = NULL;
|
||||
const char *p, *getty;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(ptmx_name);
|
||||
|
||||
p = path_startswith(ptmx_name, "/dev/pts/");
|
||||
if (!p)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
|
||||
|
||||
r = machine_bus_new(m, error, &allocated_bus);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create DBus to machine: %m");
|
||||
|
||||
container_bus = allocated_bus ?: m->manager->bus;
|
||||
getty = strjoina("container-getty@", p, ".service");
|
||||
|
||||
r = bus_call_method(container_bus, bus_systemd_mgr, "StartUnit", error, /* reply = */ NULL, "ss", getty, "replace");
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to StartUnit '%s' in container '%s': %m", getty, m->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int machine_start_shell(
|
||||
Machine *m,
|
||||
int ptmx_fd,
|
||||
const char *ptmx_name,
|
||||
const char *user,
|
||||
const char *path,
|
||||
char **args,
|
||||
char **env,
|
||||
sd_bus_error *error) {
|
||||
_cleanup_close_ int pty_fd = -EBADF;
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *tm = NULL;
|
||||
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *allocated_bus = NULL;
|
||||
const char *p, *utmp_id, *unit, *description;
|
||||
sd_bus *container_bus = NULL;
|
||||
int r;
|
||||
|
||||
assert(m);
|
||||
assert(ptmx_fd >= 0);
|
||||
assert(ptmx_name);
|
||||
|
||||
if (isempty(user) || isempty(path) || strv_isempty(args))
|
||||
return -EINVAL;
|
||||
|
||||
p = path_startswith(ptmx_name, "/dev/pts/");
|
||||
utmp_id = path_startswith(ptmx_name, "/dev/");
|
||||
if (!p || !utmp_id)
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Path of pseudo TTY has unexpected prefix");
|
||||
|
||||
/* First try to get an fd for the PTY peer via the new racefree ioctl(), directly. Otherwise go via
|
||||
* joining the namespace, because it goes by path */
|
||||
pty_fd = pty_open_peer_racefree(ptmx_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
|
||||
if (ERRNO_IS_NEG_NOT_SUPPORTED(pty_fd))
|
||||
pty_fd = machine_open_terminal(m, ptmx_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
|
||||
if (pty_fd < 0)
|
||||
return log_debug_errno(pty_fd, "Failed to open terminal: %m");
|
||||
|
||||
r = machine_bus_new(m, error, &allocated_bus);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to create DBus to machine: %m");
|
||||
|
||||
container_bus = allocated_bus ?: m->manager->bus;
|
||||
r = bus_message_new_method_call(container_bus, &tm, bus_systemd_mgr, "StartTransientUnit");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Name and mode */
|
||||
unit = strjoina("container-shell@", p, ".service");
|
||||
r = sd_bus_message_append(tm, "ss", unit, "fail");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Properties */
|
||||
r = sd_bus_message_open_container(tm, 'a', "(sv)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
description = strjoina("Shell for User ", user);
|
||||
r = sd_bus_message_append(tm,
|
||||
"(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
|
||||
"Description", "s", description,
|
||||
"StandardInputFileDescriptor", "h", pty_fd,
|
||||
"StandardOutputFileDescriptor", "h", pty_fd,
|
||||
"StandardErrorFileDescriptor", "h", pty_fd,
|
||||
"SendSIGHUP", "b", true,
|
||||
"IgnoreSIGPIPE", "b", false,
|
||||
"KillMode", "s", "mixed",
|
||||
"TTYPath", "s", ptmx_name,
|
||||
"TTYReset", "b", true,
|
||||
"UtmpIdentifier", "s", utmp_id,
|
||||
"UtmpMode", "s", "user",
|
||||
"PAMName", "s", "login",
|
||||
"WorkingDirectory", "s", "-~");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "(sv)", "User", "s", user);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!strv_isempty(env)) {
|
||||
r = sd_bus_message_open_container(tm, 'r', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "s", "Environment");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'v', "as");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append_strv(tm, env);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Exec container */
|
||||
r = sd_bus_message_open_container(tm, 'r', "sv");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "s", "ExecStart");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'v', "a(sasb)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'a', "(sasb)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_open_container(tm, 'r', "sasb");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "s", path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append_strv(tm, args);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_append(tm, "b", true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_close_container(tm);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Auxiliary units */
|
||||
r = sd_bus_message_append(tm, "a(sa(sv))", 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_call(container_bus, tm, 0, error, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char** machine_default_shell_args(const char *user) {
|
||||
_cleanup_strv_free_ char **args = NULL;
|
||||
int r;
|
||||
|
||||
assert(user);
|
||||
|
||||
args = new0(char*, 3 + 1);
|
||||
if (!args)
|
||||
return NULL;
|
||||
|
||||
args[0] = strdup("sh");
|
||||
if (!args[0])
|
||||
return NULL;
|
||||
|
||||
args[1] = strdup("-c");
|
||||
if (!args[1])
|
||||
return NULL;
|
||||
|
||||
r = asprintf(&args[2],
|
||||
"shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
|
||||
"exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
|
||||
user);
|
||||
if (r < 0) {
|
||||
args[2] = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return TAKE_PTR(args);
|
||||
}
|
||||
|
||||
void machine_release_unit(Machine *m) {
|
||||
assert(m);
|
||||
|
||||
|
@ -102,6 +102,10 @@ KillWhom kill_whom_from_string(const char *s) _pure_;
|
||||
|
||||
int machine_openpt(Machine *m, int flags, char **ret_slave);
|
||||
int machine_open_terminal(Machine *m, const char *path, int mode);
|
||||
int machine_start_getty(Machine *m, const char *ptmx_name, sd_bus_error *error);
|
||||
int machine_start_shell(Machine *m, int ptmx_fd, const char *ptmx_name, const char *user, const char *path, char **args, char **env, sd_bus_error *error);
|
||||
#define machine_default_shell_path() ("/bin/sh")
|
||||
char** machine_default_shell_args(const char *user);
|
||||
|
||||
int machine_get_uid_shift(Machine *m, uid_t *ret);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -493,40 +493,6 @@ static int json_dispatch_access_mode(const char *name, sd_json_variant *variant,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int json_dispatch_environment(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
_cleanup_strv_free_ char **n = NULL;
|
||||
char ***l = userdata;
|
||||
int r;
|
||||
|
||||
if (sd_json_variant_is_null(variant)) {
|
||||
*l = strv_free(*l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!sd_json_variant_is_array(variant))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
||||
|
||||
for (size_t i = 0; i < sd_json_variant_elements(variant); i++) {
|
||||
sd_json_variant *e;
|
||||
const char *a;
|
||||
|
||||
e = sd_json_variant_by_index(variant, i);
|
||||
if (!sd_json_variant_is_string(e))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of strings.", strna(name));
|
||||
|
||||
assert_se(a = sd_json_variant_string(e));
|
||||
|
||||
if (!env_assignment_is_valid(a))
|
||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array of environment variables.", strna(name));
|
||||
|
||||
r = strv_env_replace_strdup(&n, a);
|
||||
if (r < 0)
|
||||
return json_log_oom(variant, flags);
|
||||
}
|
||||
|
||||
return strv_free_and_replace(*l, n);
|
||||
}
|
||||
|
||||
static int json_dispatch_locale(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
||||
char **s = userdata;
|
||||
const char *n;
|
||||
@ -1237,7 +1203,7 @@ static int dispatch_per_machine(const char *name, sd_json_variant *variant, sd_j
|
||||
{ "location", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, location), 0 },
|
||||
{ "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
|
||||
{ "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
|
||||
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 },
|
||||
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 },
|
||||
{ "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT },
|
||||
{ "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 },
|
||||
{ "additionalLanguages", SD_JSON_VARIANT_ARRAY, json_dispatch_locales, offsetof(UserRecord, additional_languages), 0 },
|
||||
@ -1583,7 +1549,7 @@ int user_record_load(UserRecord *h, sd_json_variant *v, UserRecordLoadFlags load
|
||||
{ "lastPasswordChangeUSec", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(UserRecord, last_password_change_usec), 0 },
|
||||
{ "shell", SD_JSON_VARIANT_STRING, json_dispatch_filename_or_path, offsetof(UserRecord, shell), 0 },
|
||||
{ "umask", SD_JSON_VARIANT_UNSIGNED, json_dispatch_umask, offsetof(UserRecord, umask), 0 },
|
||||
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_environment, offsetof(UserRecord, environment), 0 },
|
||||
{ "environment", SD_JSON_VARIANT_ARRAY, json_dispatch_strv_environment, offsetof(UserRecord, environment), 0 },
|
||||
{ "timeZone", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(UserRecord, time_zone), SD_JSON_STRICT },
|
||||
{ "preferredLanguage", SD_JSON_VARIANT_STRING, json_dispatch_locale, offsetof(UserRecord, preferred_language), 0 },
|
||||
{ "additionalLanguages", SD_JSON_VARIANT_ARRAY, json_dispatch_locales, offsetof(UserRecord, additional_languages), 0 },
|
||||
|
@ -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);
|
||||
|
@ -367,6 +367,25 @@ varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List
|
||||
(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": ".host"}' | grep 'acquireUIDShift')
|
||||
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": ".host", "acquireMetadata": "yes"}' | grep 'UIDShift'
|
||||
|
||||
# test io.systemd.Machine.Open
|
||||
|
||||
# Reducing log level here is to work-around check in end.service (end.sh). Read https://github.com/systemd/systemd/pull/34867 for more details
|
||||
systemctl service-log-level systemd-machined info
|
||||
(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host"}')
|
||||
(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": ""}')
|
||||
(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": null}')
|
||||
(! varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": "foo"}')
|
||||
systemctl service-log-level systemd-machined debug
|
||||
|
||||
varlinkctl call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.Open '{"name": ".host", "mode": "tty"}'
|
||||
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.MachineImage.List
|
||||
varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}' | grep 'long-running'
|
||||
varlinkctl --more call /run/systemd/machine/io.systemd.MachineImage io.systemd.MachineImage.List '{}' | grep '.host'
|
||||
|
Loading…
Reference in New Issue
Block a user