1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 08:55:40 +03:00
This commit is contained in:
Ivan Kruglov 2024-10-26 02:03:48 +08:00 committed by GitHub
commit fc91836ebe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 511 additions and 218 deletions

View File

@ -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_strv_free_ char **env = NULL, **args_wire = NULL, **args = NULL;
_cleanup_free_ char *command_line = NULL;
Machine *m = ASSERT_PTR(userdata);
const char *p, *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,145 +408,11 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
if (master < 0)
return master;
p = path_startswith(pty_name, "/dev/pts/");
assert(p);
slave = machine_open_terminal(m, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (slave < 0)
return slave;
return log_debug_errno(slave, "Failed to open terminal: %m");
utmp_id = path_startswith(pty_name, "/dev/");
assert(utmp_id);
r = container_bus_new(m, error, &allocated_bus);
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 */
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);
r = machine_start_shell(m, slave, pty_name, user, path, args, env, error);
if (r < 0)
return r;

View File

@ -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);
@ -377,3 +380,188 @@ 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;
PidRef pidref;
MachineOpenMode mode;
const char *user, *path;
char **args, **env;
} MachineOpenParameters;
static void machine_open_paramaters_done(MachineOpenParameters *p) {
assert(p);
pidref_done(&p->pidref);
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 const 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 (const char**) STRV_MAKE("machine", machine_name);
case MACHINE_OPEN_MODE_LOGIN:
return (const char**) STRV_MAKE("machine", machine_name, "verb", "login");
case MACHINE_OPEN_MODE_SHELL:
assert(user);
assert(path);
assert(command_line);
return (const char**) STRV_MAKE(
"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), 0 },
{ "user", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MachineOpenParameters, user), 0 },
{ "path", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MachineOpenParameters, path), 0 },
{ "args", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(MachineOpenParameters, args), 0 },
{ "env", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(MachineOpenParameters, env), 0 },
VARLINK_DISPATCH_POLKIT_FIELD,
{}
};
Manager *manager = ASSERT_PTR(userdata);
_cleanup_close_ int master = -EBADF;
_cleanup_(machine_open_paramaters_done) MachineOpenParameters p = { .pidref = PIDREF_NULL };
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
_cleanup_free_ char *pty_name = NULL, *command_line = NULL;
_cleanup_strv_free_ char **args = NULL;
const char *user = NULL, *path = NULL; /* gcc complains about uninitialized variables */
Machine *machine;
int r;
assert(link);
assert(parameters);
r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
if (r != 0)
return r;
if (p.mode == MACHINE_OPEN_MODE_SHELL) {
if (!isempty(p.user) && !valid_user_group_name(p.user, VALID_USER_RELAX))
return sd_varlink_error_invalid_parameter_name(link, "user");
if (!isempty(p.path) && !path_is_absolute(p.path))
return sd_varlink_error_invalid_parameter_name(link, "path");
// TODO(ikruglov): args validation?
if (!strv_isempty(p.env) && !strv_env_is_valid(p.env))
return sd_varlink_error_invalid_parameter_name(link, "env");
user = isempty(p.user) ? "root" : p.user;
path = isempty(p.path) ? machine_default_shell_path() : p.path;
args = isempty(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;
r = varlink_verify_polkit_async(
link,
manager->bus,
machine_open_polkit_action(p.mode, machine->class),
machine_open_polkit_details(p.mode, machine->name, user, path, command_line),
&manager->polkit_registry);
if (r <= 0)
return r;
master = machine_openpt(machine, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
if (ERRNO_IS_NEG_NOT_SUPPORTED(master))
return sd_varlink_error(link, "io.systemd.Machine.NotSupported", NULL);
if (master < 0)
return log_debug_errno(master, "Failed to open pseudo terminal: %m");
switch (p.mode) {
case MACHINE_OPEN_MODE_LOGIN:
r = machine_start_getty(machine, pty_name, /* error = */ NULL);
if (r == -ENOENT)
return sd_varlink_error(link, "io.systemd.Machine.NoSDBus", 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: {
_cleanup_close_ int slave = -EBADF;
slave = machine_open_terminal(machine, pty_name, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (slave < 0)
return log_debug_errno(slave, "Failed to open terminal: %m");
assert(user && path && args); /* to avoid gcc complaining about possible uninitialized variables */
r = machine_start_shell(machine, slave, pty_name, user, path, args, p.env, /* error = */ NULL);
if (r == -ENOENT)
return sd_varlink_error(link, "io.systemd.Machine.NoSDBus", 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);
slave = safe_close(slave);
break;
}
default:
break;
}
r = sd_json_buildo(
&v,
SD_JSON_BUILD_PAIR_UNSIGNED("ptyFileDescriptor", master),
JSON_BUILD_PAIR_STRING_NON_EMPTY("ptyPath", pty_name));
if (r < 0)
return r;
return sd_varlink_reply(link, v);
}

View File

@ -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);

View File

@ -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,266 @@ 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 *pty_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(pty_name);
p = path_startswith(pty_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 slave,
const char *pty_name,
const char *user,
const char *path,
char **args,
char **env,
sd_bus_error *error) {
_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(pty_name);
if (isempty(user) || isempty(path) || strv_isempty(args))
return -EINVAL;
p = path_startswith(pty_name, "/dev/pts/");
utmp_id = path_startswith(pty_name, "/dev/");
if (!p || !utmp_id)
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;
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", 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;
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);

View File

@ -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 *pty_name, sd_bus_error *error);
int machine_start_shell(Machine *m, int slave, const char *pty_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);

View File

@ -781,6 +781,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);
if (r < 0)

View File

@ -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. Default value is 'tty'."),
SD_VARLINK_DEFINE_INPUT_BY_TYPE(mode, MachineOpenMode, SD_VARLINK_NULLABLE),
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(env, 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(NoSDBus);
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 system bus in container"),
&vl_error_NoSDBus);

View File

@ -318,6 +318,17 @@ varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machin
(! varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": ".host"}' | grep 'acquireUIDShift')
varlinkctl --more call /run/systemd/machine/io.systemd.Machine io.systemd.Machine.List '{"name": ".host", "acquireMetadata": "yes"}' | grep 'UIDShift'
# test io.systemd.Machine.Open
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"}'
# TODO(ikruglov): how to really test other modes?
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"], "env": ["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'