From 023a4f67011f24d4b085995a4a3a02661c4794a2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 13:14:04 +0200 Subject: core: optionally create LOGIN_PROCESS or USER_PROCESS utmp entries When generating utmp/wtmp entries, optionally add both LOGIN_PROCESS and INIT_PROCESS entries or even all three of LOGIN_PROCESS, INIT_PROCESS and USER_PROCESS entries, instead of just a single INIT_PROCESS entry. With this change systemd may be used to not only invoke a getty directly in a SysV-compliant way but alternatively also a login(1) implementation or even forego getty and login entirely, and invoke arbitrary shells in a way that they appear in who(1) or w(1). This is preparation for a later commit that adds a "machinectl shell" operation to invoke a shell in a container, in a way that is compatible with who(1) and w(1). --- src/core/dbus-execute.c | 3 +++ src/core/execute.c | 15 ++++++++++++++- src/core/execute.h | 12 ++++++++++++ src/core/load-fragment-gperf.gperf.m4 | 1 + src/core/load-fragment.c | 2 ++ src/core/load-fragment.h | 1 + src/shared/utmp-wtmp.c | 24 ++++++++++++++++++++++-- src/shared/utmp-wtmp.h | 4 ++-- 8 files changed, 57 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index a9f7971cde..21d2b79678 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -46,6 +46,8 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutp static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput); +static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode); + static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome); static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem); @@ -653,6 +655,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("UtmpMode", "s", property_get_exec_utmp_mode, offsetof(ExecContext, utmp_mode), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/execute.c b/src/core/execute.c index 3820165241..28eeeaad18 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #ifdef HAVE_PAM @@ -1504,7 +1505,11 @@ static int exec_child( } if (context->utmp_id) - utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path); + utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path, + context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS : + context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS : + USER_PROCESS, + username ? "root" : context->user); if (context->user && is_terminal_input(context->std_input)) { r = chown_terminal(STDIN_FILENO, uid); @@ -2968,3 +2973,11 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); + +static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = { + [EXEC_UTMP_INIT] = "init", + [EXEC_UTMP_LOGIN] = "login", + [EXEC_UTMP_USER] = "user", +}; + +DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode); diff --git a/src/core/execute.h b/src/core/execute.h index f5d5c1dee7..5d46bf154c 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -38,6 +38,14 @@ typedef struct ExecParameters ExecParameters; #include "namespace.h" #include "bus-endpoint.h" +typedef enum ExecUtmpMode { + EXEC_UTMP_INIT, + EXEC_UTMP_LOGIN, + EXEC_UTMP_USER, + _EXEC_UTMP_MODE_MAX, + _EXEC_UTMP_MODE_INVALID, +} ExecUtmpMode; + typedef enum ExecInput { EXEC_INPUT_NULL, EXEC_INPUT_TTY, @@ -131,6 +139,7 @@ struct ExecContext { char *pam_name; char *utmp_id; + ExecUtmpMode utmp_mode; bool selinux_context_ignore; char *selinux_context; @@ -265,3 +274,6 @@ ExecOutput exec_output_from_string(const char *s) _pure_; const char* exec_input_to_string(ExecInput i) _const_; ExecInput exec_input_from_string(const char *s) _pure_; + +const char* exec_utmp_mode_to_string(ExecUtmpMode i) _const_; +ExecUtmpMode exec_utmp_mode_from_string(const char *s) _pure_; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index aae81c80cb..60b97722db 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -91,6 +91,7 @@ m4_ifdef(`HAVE_PAM', `$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') $1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) $1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) +$1.UtmpMode, config_parse_exec_utmp_mode, 0, offsetof($1, exec_context.utmp_mode) m4_ifdef(`HAVE_SELINUX', `$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)', `$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e1e3a5ffb7..b3bf8bdb40 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1142,6 +1142,8 @@ int config_parse_sysv_priority(const char *unit, } #endif +DEFINE_CONFIG_PARSE_ENUM(config_parse_exec_utmp_mode, exec_utmp_mode, ExecUtmpMode, "Failed to parse utmp mode"); + DEFINE_CONFIG_PARSE_ENUM(config_parse_kill_mode, kill_mode, KillMode, "Failed to parse kill mode"); int config_parse_kill_signal(const char *unit, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index ce10d03c3f..fcca2b0221 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -104,6 +104,7 @@ int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line int config_parse_protect_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bus_name(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_utmp_mode(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 8f66df7718..63f1e4ca6f 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -204,12 +204,13 @@ _pure_ static const char *sanitize_id(const char *id) { return id + l - sizeof(((struct utmpx*) NULL)->ut_id); } -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { struct utmpx store = { .ut_type = INIT_PROCESS, .ut_pid = pid, .ut_session = sid, }; + int r; assert(id); @@ -221,7 +222,26 @@ int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line if (line) strncpy(store.ut_line, basename(line), sizeof(store.ut_line)); - return write_entry_both(&store); + r = write_entry_both(&store); + if (r < 0) + return r; + + if (ut_type == LOGIN_PROCESS || ut_type == USER_PROCESS) { + store.ut_type = LOGIN_PROCESS; + r = write_entry_both(&store); + if (r < 0) + return r; + } + + if (ut_type == USER_PROCESS) { + store.ut_type = USER_PROCESS; + strncpy(store.ut_user, user, sizeof(store.ut_user)-1); + r = write_entry_both(&store); + if (r < 0) + return r; + } + + return 0; } int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { diff --git a/src/shared/utmp-wtmp.h b/src/shared/utmp-wtmp.h index 5d26ba6fb1..e0ceb873ac 100644 --- a/src/shared/utmp-wtmp.h +++ b/src/shared/utmp-wtmp.h @@ -31,7 +31,7 @@ int utmp_put_reboot(usec_t timestamp); int utmp_put_runlevel(int runlevel, int previous); int utmp_put_dead_process(const char *id, pid_t pid, int code, int status); -int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line); +int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user); int utmp_wall( const char *message, @@ -57,7 +57,7 @@ static inline int utmp_put_runlevel(int runlevel, int previous) { static inline int utmp_put_dead_process(const char *id, pid_t pid, int code, int status) { return 0; } -static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line) { +static inline int utmp_put_init_process(const char *id, pid_t pid, pid_t sid, const char *line, int ut_type, const char *user) { return 0; } static inline int utmp_wall( -- cgit v1.2.3-54-g00ecf From 506711fddd432aff2aacadfe0e43b0ff5411b106 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 13:19:21 +0200 Subject: core: open up more executable properties via the bus This is preparation for a later commit that makes use of these properties for spawning an interactive shell in a container. --- src/core/dbus-execute.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) (limited to 'src') diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 21d2b79678..b60025c48a 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -935,6 +935,108 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "IgnoreSIGPIPE")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->ignore_sigpipe = b; + + unit_write_drop_in_private_format(u, mode, name, "IgnoreSIGPIPE=%s\n", yes_no(b)); + } + + return 1; + + } else if (streq(name, "TTYVHangup")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tty_vhangup = b; + + unit_write_drop_in_private_format(u, mode, name, "TTYVHangup=%s\n", yes_no(b)); + } + + return 1; + + + } else if (streq(name, "TTYReset")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tty_reset = b; + + unit_write_drop_in_private_format(u, mode, name, "TTYReset=%s\n", yes_no(b)); + } + + return 1; + + } else if (streq(name, "UtmpIdentifier")) { + const char *id; + + r = sd_bus_message_read(message, "s", &id); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(id)) + c->utmp_id = mfree(c->utmp_id); + else if (free_and_strdup(&c->utmp_id, id) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "UtmpIdentifier=%s\n", strempty(id)); + } + + return 1; + + } else if (streq(name, "UtmpMode")) { + const char *s; + ExecUtmpMode m; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + m = exec_utmp_mode_from_string(s); + if (m < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid utmp mode"); + + if (mode != UNIT_CHECK) { + c->utmp_mode = m; + + unit_write_drop_in_private_format(u, mode, name, "UtmpMode=%s\n", exec_utmp_mode_to_string(m)); + } + + return 1; + + } else if (streq(name, "PAMName")) { + const char *n; + + r = sd_bus_message_read(message, "s", &n); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (isempty(n)) + c->pam_name = mfree(c->pam_name); + else if (free_and_strdup(&c->pam_name, n) < 0) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "PAMName=%s\n", strempty(n)); + } + + return 1; + } else if (streq(name, "Environment")) { _cleanup_strv_free_ char **l = NULL; -- cgit v1.2.3-54-g00ecf From 49af9e1368571f4e423cde0fd45ee284451434d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 13:20:58 +0200 Subject: machined: add new OpenShell() bus call This new bus call opens an interactive shell in a container. It works like the existing OpenLogin() call, but does not involve getty, and instead opens an arbitrary command line. This is similar to "systemd-run -t -M" but is controlled by a specific PolicyKit privilege. --- src/machine/machine-dbus.c | 280 +++++++++++++++++++++++-- src/machine/machine-dbus.h | 1 + src/machine/machined-dbus.c | 22 ++ src/machine/org.freedesktop.machine1.conf | 8 + src/machine/org.freedesktop.machine1.policy.in | 10 + 5 files changed, 302 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index b62a9bd813..ad3dd8facf 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -44,6 +44,7 @@ #include "machine-dbus.h" #include "formats-util.h" #include "process-util.h" +#include "env-util.h" static int property_get_id( sd_bus *bus, @@ -451,14 +452,43 @@ 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 **ret) { + _cleanup_bus_unref_ sd_bus *bus = NULL; + char *address; + int r; + + assert(m); + assert(ret); + + r = sd_bus_new(&bus); + if (r < 0) + return r; + + if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) + return -ENOMEM; + + bus->address = address; + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + + return 0; +} + int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_free_ char *pty_name = NULL, *getty = NULL; + _cleanup_free_ char *pty_name = NULL; _cleanup_bus_unref_ sd_bus *container_bus = NULL; _cleanup_close_ int master = -1; Machine *m = userdata; - const char *p; - char *address; + const char *p, *getty; int r; assert(message); @@ -495,35 +525,246 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = sd_bus_new(&container_bus); + r = container_bus_new(m, &container_bus); if (r < 0) return r; -# define ADDRESS_FMT "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI - if (asprintf(&address, ADDRESS_FMT, m->leader) < 0) - return log_oom(); + getty = strjoina("container-getty@", p, ".service"); - container_bus->address = address; - container_bus->bus_client = true; - container_bus->trusted = false; - container_bus->is_system = true; + r = sd_bus_call_method( + container_bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartUnit", + error, NULL, + "ss", getty, "replace"); + if (r < 0) + return r; + + container_bus = sd_bus_unref(container_bus); - r = sd_bus_start(container_bus); + r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; - getty = strjoin("container-getty@", p, ".service", NULL); - if (!getty) - return log_oom(); + r = sd_bus_message_append(reply, "hs", master, pty_name); + if (r < 0) + return r; - r = sd_bus_call_method( + return sd_bus_send(NULL, reply, NULL); +} + +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL; + _cleanup_free_ char *pty_name = NULL; + _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_close_ int master = -1; + _cleanup_strv_free_ char **env = NULL, **args = NULL; + Machine *m = userdata; + const char *p, *unit, *user, *path, *description, *utmp_id; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "ss", &user, &path); + if (r < 0) + return r; + if (isempty(user)) + user = NULL; + if (isempty(path)) + path = "/bin/sh"; + if (!path_is_absolute(path)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified path '%s' is not absolute", path); + + r = sd_bus_message_read_strv(message, &args); + if (r < 0) + return r; + if (strv_isempty(args)) { + args = strv_free(args); + + args = strv_new(path, NULL); + if (!args) + return -ENOMEM; + + args[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */ + } + + r = sd_bus_message_read_strv(message, &env); + if (r < 0) + return r; + if (!strv_env_is_valid(env)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); + + if (m->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening shells is only supported on container machines."); + + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.shell", + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + if (master < 0) + return master; + + r = ptsname_malloc(master, &pty_name); + if (r < 0) + return r; + + p = path_startswith(pty_name, "/dev/pts/"); + if (!p) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name); + + utmp_id = path_startswith(pty_name, "/dev/"); + assert(utmp_id); + + if (unlockpt(master) < 0) + return -errno; + + r = container_bus_new(m, &container_bus); + if (r < 0) + return r; + + r = sd_bus_message_new_method_call( container_bus, + &tm, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "StartUnit", - error, NULL, - "ss", getty, "replace"); + "StartTransientUnit"); + if (r < 0) + return r; + + unit = strjoina("container-shell@", p, ".service", NULL); + + /* Name and mode */ + 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 ", isempty(user) ? "root" : user); + r = sd_bus_message_append(tm, + "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)", + "Description", "s", description, + "StandardInput", "s", "tty", + "StandardOutput", "s", "tty", + "StandardError", "s", "tty", + "TTYPath", "s", pty_name, + "SendSIGHUP", "b", true, + "IgnoreSIGPIPE", "b", false, + "KillMode", "s", "mixed", + "TTYVHangup", "b", true, + "TTYReset", "b", true, + "UtmpIdentifier", "s", utmp_id, + "UtmpMode", "s", "user", + "PAMName", "s", "login"); + if (r < 0) + return r; + + r = sd_bus_message_append(tm, "(sv)", "User", "s", isempty(user) ? "root" : 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; @@ -968,6 +1209,7 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0), SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machine-dbus.h b/src/machine/machine-dbus.h index d309131860..38b46ad936 100644 --- a/src/machine/machine-dbus.h +++ b/src/machine/machine-dbus.h @@ -35,6 +35,7 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 08a7f58ef5..da3ab26e89 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -637,6 +637,27 @@ static int method_open_machine_login(sd_bus_message *message, void *userdata, sd return bus_machine_method_open_login(message, machine, error); } +static int method_open_machine_shell(sd_bus_message *message, void *userdata, sd_bus_error *error) { + Manager *m = userdata; + Machine *machine; + const char *name; + + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + machine = hashmap_get(m->machines, name); + if (!machine) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + + return bus_machine_method_open_shell(message, machine, error); +} + static int method_bind_mount_machine(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; Machine *machine; @@ -1085,6 +1106,7 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetMachineOSRelease", "s", "a{ss}", method_get_machine_os_release, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenMachinePTY", "s", "hs", method_open_machine_pty, 0), SD_BUS_METHOD("OpenMachineLogin", "s", "hs", method_open_machine_login, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("OpenMachineShell", "sssasas", "hs", method_open_machine_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMountMachine", "sssbb", NULL, method_bind_mount_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyFromMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CopyToMachine", "sss", NULL, method_copy_machine, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index d58f01507b..9d40b90151 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -68,6 +68,10 @@ send_interface="org.freedesktop.machine1.Manager" send_member="OpenMachineLogin"/> + + @@ -140,6 +144,10 @@ send_interface="org.freedesktop.machine1.Machine" send_member="OpenLogin"/> + + diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in index 02714e83ae..b3b2fa29c1 100644 --- a/src/machine/org.freedesktop.machine1.policy.in +++ b/src/machine/org.freedesktop.machine1.policy.in @@ -26,6 +26,16 @@ + + <_description>Acquire a shell in a local container + <_message>Authentication is required to acquire a shell in a local container. + + auth_admin + auth_admin + auth_admin_keep + + + <_description>Manage local virtual machines and containers <_message>Authentication is required to manage local virtual machines and containers. -- cgit v1.2.3-54-g00ecf From c454426c54c9beb274f415a80c64a4f1580700e7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 13:24:10 +0200 Subject: machinectl: add new "machinectl shell" command This makes use of machined's new OpenShell() command and allows opening a new interactive shell in any container. --- man/machinectl.xml | 44 ++++++++++- src/machine/machinectl.c | 185 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 194 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/man/machinectl.xml b/man/machinectl.xml index a5eb3f08e4..43dcbbdb18 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -1,4 +1,4 @@ - + @@ -137,6 +137,30 @@ SIGTERM. + + + + When used with the shell + command, chooses the user ID to open the interactive shell + session as. If this switch is not specified, defaults to + root. Note that this switch is not + supported for the login command (see + below). + + + + + + When used with the shell + command, sets an environment variable to pass to the executed + shell. Takes a pair of environment variable name and value, + separated by = as argument. This switch + may be used multiple times to set multiple environment + variables. Note that this switch is not supported for the + login command (see + below). + + @@ -145,7 +169,6 @@ mount. - @@ -318,7 +341,7 @@ login NAME - Open an interactive terminal login session to + Open an interactive terminal login session in a container. This will create a TTY connection to a specific container and asks for the execution of a getty on it. Note that this is only supported for containers running @@ -327,12 +350,27 @@ This command will open a full login prompt on the container, which then asks for username and password. Use + shell (see below) or systemd-run1 with the switch to invoke a single command, either interactively or in the background within a local container. + + shell NAME [PATH [ARGUMENTS...]] + + Open an interactive shell session in a + container. This works similar to login but + immediately invokes a user process. Invokes the specified + executable with the specified arguments, or + /bin/sh if none is specified. By default + opens a root shell, but using + a different user may be selected. Use + to set environment variables for + the executed process. + + enable NAME... disable NAME... diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 66ed41087c..8e75508444 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -55,6 +55,7 @@ #include "process-util.h" #include "terminal-util.h" #include "signal-util.h" +#include "env-util.h" static char **arg_property = NULL; static bool arg_all = false; @@ -75,6 +76,8 @@ static bool arg_force = false; static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE; static const char* arg_dkr_index_url = NULL; static const char* arg_format = NULL; +static const char *arg_uid = NULL; +static char **arg_setenv = NULL; static void pager_open_if_enabled(void) { @@ -1170,20 +1173,67 @@ static int on_machine_removed(sd_bus_message *m, void *userdata, sd_bus_error *r return 0; } +static int process_forward(sd_event *event, PTYForward **forward, int master, bool ignore_vhangup, const char *name) { + char last_char = 0; + bool machine_died; + int ret = 0, r; + + assert(event); + assert(master >= 0); + assert(name); + + assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + + log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + + r = pty_forward_new(event, master, ignore_vhangup, false, forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + pty_forward_get_last_char(*forward, &last_char); + + machine_died = + ignore_vhangup && + pty_forward_get_ignore_vhangup(*forward) == 0; + + *forward = pty_forward_free(*forward); + + if (last_char != '\n') + fputc('\n', stdout); + + if (machine_died) + log_info("Machine %s terminated.", name); + else + log_info("Connection to machine %s terminated.", name); + + sd_event_get_exit_code(event, &ret); + return ret; +} + static int login_machine(int argc, char *argv[], void *userdata) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; _cleanup_event_unref_ sd_event *event = NULL; - int master = -1, r, ret = 0; + int master = -1, r; sd_bus *bus = userdata; const char *pty, *match; - char last_char = 0; - bool machine_died; assert(bus); + if (!strv_isempty(arg_setenv) || arg_uid) { + log_error("--setenv= and --uid= are not supported for 'login'. Use 'shell' instead."); + return -EINVAL; + } + if (arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_MACHINE) { log_error("Login only supported on local machines."); @@ -1201,13 +1251,13 @@ static int login_machine(int argc, char *argv[], void *userdata) { return log_error_errno(r, "Failed to attach bus to event loop: %m"); match = strjoina("type='signal'," - "sender='org.freedesktop.machine1'," - "path='/org/freedesktop/machine1',", - "interface='org.freedesktop.machine1.Manager'," - "member='MachineRemoved'," - "arg0='", - argv[1], - "'"); + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", + argv[1], + "'"); r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); if (r < 0) @@ -1223,7 +1273,7 @@ static int login_machine(int argc, char *argv[], void *userdata) { &reply, "s", argv[1]); if (r < 0) { - log_error("Failed to get machine PTY: %s", bus_error_message(&error, -r)); + log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); return r; } @@ -1231,36 +1281,83 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); + return process_forward(event, &forward, master, true, argv[1]); +} - log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", argv[1]); +static int shell_machine(int argc, char *argv[], void *userdata) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL; + _cleanup_event_unref_ sd_event *event = NULL; + int master = -1, r; + sd_bus *bus = userdata; + const char *pty, *match; - sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); - sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + assert(bus); + + if (arg_transport != BUS_TRANSPORT_LOCAL && + arg_transport != BUS_TRANSPORT_MACHINE) { + log_error("Shell only supported on local machines."); + return -EOPNOTSUPP; + } + + polkit_agent_open_if_enabled(); - r = pty_forward_new(event, master, true, false, &forward); + r = sd_event_default(&event); if (r < 0) - return log_error_errno(r, "Failed to create PTY forwarder: %m"); + return log_error_errno(r, "Failed to get event loop: %m"); - r = sd_event_loop(event); + r = sd_bus_attach_event(bus, event, 0); if (r < 0) - return log_error_errno(r, "Failed to run event loop: %m"); + return log_error_errno(r, "Failed to attach bus to event loop: %m"); - pty_forward_get_last_char(forward, &last_char); - machine_died = pty_forward_get_ignore_vhangup(forward) == 0; + match = strjoina("type='signal'," + "sender='org.freedesktop.machine1'," + "path='/org/freedesktop/machine1',", + "interface='org.freedesktop.machine1.Manager'," + "member='MachineRemoved'," + "arg0='", + argv[1], + "'"); - forward = pty_forward_free(forward); + r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); + if (r < 0) + return log_error_errno(r, "Failed to add machine removal match: %m"); - if (last_char != '\n') - fputc('\n', stdout); + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.machine1", + "/org/freedesktop/machine1", + "org.freedesktop.machine1.Manager", + "OpenMachineShell"); + if (r < 0) + return bus_log_create_error(r); - if (machine_died) - log_info("Machine %s terminated.", argv[1]); - else - log_info("Connection to machine %s terminated.", argv[1]); + r = sd_bus_message_append(m, "sss", argv[1], arg_uid, argv[2]); + if (r < 0) + return bus_log_create_error(r); - sd_event_get_exit_code(event, &ret); - return ret; + r = sd_bus_message_append_strv(m, strv_length(argv + 2) <= 1 ? NULL : argv + 2); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, arg_setenv); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + log_error("Failed to get shell PTY: %s", bus_error_message(&error, -r)); + return r; + } + + r = sd_bus_message_read(reply, "hs", &master, &pty); + if (r < 0) + return bus_log_parse_error(r); + + return process_forward(event, &forward, master, false, argv[1]); } static int remove_image(int argc, char *argv[], void *userdata) { @@ -2314,6 +2411,8 @@ static int help(int argc, char *argv[], void *userdata) { " -l --full Do not ellipsize output\n" " --kill-who=WHO Who to send signal to\n" " -s --signal=SIGNAL Which signal to send\n" + " --uid=USER Specify user ID to invoke shell as\n" + " --setenv=VAR=VALUE Add an environment variable for shell\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before bind mounting, if missing\n" " -n --lines=INTEGER Number of journal entries to show\n" @@ -2331,6 +2430,7 @@ static int help(int argc, char *argv[], void *userdata) { " show NAME... Show properties of one or more VMs/containers\n" " start NAME... Start container as a service\n" " login NAME Get a login prompt on a container\n" + " shell NAME [COMMAND...] Invoke a shell (or other command) in a container\n" " enable NAME... Enable automatic container start at boot\n" " disable NAME... Disable automatic container start at boot\n" " poweroff NAME... Power off one or more containers\n" @@ -2378,6 +2478,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_FORCE, ARG_DKR_INDEX_URL, ARG_FORMAT, + ARG_UID, + ARG_SETENV, }; static const struct option options[] = { @@ -2402,6 +2504,8 @@ static int parse_argv(int argc, char *argv[]) { { "force", no_argument, NULL, ARG_FORCE }, { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL }, { "format", required_argument, NULL, ARG_FORMAT }, + { "uid", required_argument, NULL, ARG_UID }, + { "setenv", required_argument, NULL, ARG_SETENV }, {} }; @@ -2532,6 +2636,21 @@ static int parse_argv(int argc, char *argv[]) { arg_format = optarg; break; + case ARG_UID: + arg_uid = optarg; + break; + + case ARG_SETENV: + if (!env_assignment_is_valid(optarg)) { + log_error("Environment assignment invalid: %s", optarg); + return -EINVAL; + } + + r = strv_extend(&arg_setenv, optarg); + if (r < 0) + return log_oom(); + break; + case '?': return -EINVAL; @@ -2557,6 +2676,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, { "kill", 2, VERB_ANY, 0, kill_machine }, { "login", 2, 2, 0, login_machine }, + { "shell", 2, VERB_ANY, 0, shell_machine }, { "bind", 3, 4, 0, bind_mount }, { "copy-to", 3, 4, 0, copy_files }, { "copy-from", 3, 4, 0, copy_files }, @@ -2610,6 +2730,7 @@ finish: polkit_agent_close(); strv_free(arg_property); + strv_free(arg_setenv); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } -- cgit v1.2.3-54-g00ecf From 077c8c366b58222629ed953abf2faa74ebadb769 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 14:04:31 +0200 Subject: machined: always look for leader PID first When looking for the machine belonging to a PID, always look for the leader first, only then fall back to a cgroup check. We keep direct track of the leader PID, but only indirectly of the cgroup, hence prefer the PID. --- src/machine/machined-dbus.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index da3ab26e89..93514986f3 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -1477,7 +1477,6 @@ int manager_job_is_active(Manager *manager, const char *path) { } int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { - _cleanup_free_ char *unit = NULL; Machine *mm; int r; @@ -1485,12 +1484,14 @@ int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) { assert(pid >= 1); assert(machine); - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); - else - mm = hashmap_get(m->machine_units, unit); + mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid)); + if (!mm) { + _cleanup_free_ char *unit = NULL; + r = cg_pid_get_unit(pid, &unit); + if (r >= 0) + mm = hashmap_get(m->machine_units, unit); + } if (!mm) return 0; -- cgit v1.2.3-54-g00ecf From b59abc4d1e5c8e99323f404ef4a09bd6da68064f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 14:29:59 +0200 Subject: util: make hostname_is_valid() easier to read Add more comments, and rename some parameters and variables to be more expressive. --- src/basic/hostname-util.c | 19 ++++++++++++------- src/basic/hostname-util.h | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index dc5434fcd1..1b816fb77a 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -62,16 +62,19 @@ static bool hostname_valid_char(char c) { } /** - * Check if s looks like a valid host name or fqdn. This does not do + * Check if s looks like a valid host name or FQDN. This does not do * full DNS validation, but only checks if the name is composed of * allowed characters and the length is not above the maximum allowed * by Linux (c.f. dns_name_is_valid()). Trailing dot is allowed if - * relax is true and at least two components are present in the name. + * allow_trailing_dot is true and at least two components are present + * in the name. Note that due to the restricted charset and length + * this call is substantially more conservative than + * dns_domain_is_valid(). */ -bool hostname_is_valid(const char *s, bool relax) { +bool hostname_is_valid(const char *s, bool allow_trailing_dot) { + unsigned n_dots = 0; const char *p; bool dot; - unsigned dots = 0; if (isempty(s)) return false; @@ -87,7 +90,7 @@ bool hostname_is_valid(const char *s, bool relax) { return false; dot = true; - dots ++; + n_dots ++; } else { if (!hostname_valid_char(*p)) return false; @@ -96,10 +99,12 @@ bool hostname_is_valid(const char *s, bool relax) { } } - if (dot && (dots < 2 || !relax)) + if (dot && (n_dots < 2 || !allow_trailing_dot)) return false; - if (p-s > HOST_NAME_MAX) + if (p-s > HOST_NAME_MAX) /* Note that HOST_NAME_MAX is 64 on + * Linux, but DNS allows domain names + * up to 255 characters */ return false; return true; diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index a1ca94980d..7c50260d73 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -29,7 +29,7 @@ bool hostname_is_set(void); char* gethostname_malloc(void); -bool hostname_is_valid(const char *s, bool relax) _pure_; +bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; char* hostname_cleanup(char *s); bool is_localhost(const char *hostname); -- cgit v1.2.3-54-g00ecf From 25300b5a1fcf54674a69d0f4ab08925be00b0227 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 14:30:52 +0200 Subject: util: make machine_name_is_valid() a macro and move it to hostname-util.h As it turns out machine_name_is_valid() does the exact same thing as hostname_is_valid() these days, as it just invoked that and checked the name length was < 64. However, hostname_is_valid() checks the length against HOST_NAME_MAX anyway (which is 64 on Linux), hence any additional check is redundant. We hence replace machine_name_is_valid() by a macro that simply maps it to hostname_is_valid() but sets the allow_trailing_dot parameter to false. We also move this this call to hostname-util.h, to the same place as the hostname_is_valid() declaration. --- src/basic/hostname-util.h | 2 ++ src/basic/util.c | 15 --------------- src/basic/util.h | 2 -- src/import/export.c | 1 + src/import/import-raw.c | 1 + src/import/import-tar.c | 1 + src/import/import.c | 1 + src/import/importd.c | 1 + src/import/pull-raw.c | 1 + src/import/pull-tar.c | 3 ++- src/import/pull.c | 1 + src/journal/sd-journal.c | 1 + src/libsystemd/sd-bus/sd-bus.c | 1 + src/libsystemd/sd-login/sd-login.c | 1 + src/machine/machinectl.c | 1 + src/machine/machined-dbus.c | 1 + src/machine/machined.c | 1 + src/nss-mymachines/nss-mymachines.c | 1 + src/shared/logs-show.c | 1 + 19 files changed, 19 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index 7c50260d73..d4f5bfe45e 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -32,6 +32,8 @@ char* gethostname_malloc(void); bool hostname_is_valid(const char *s, bool allow_trailing_dot) _pure_; char* hostname_cleanup(char *s); +#define machine_name_is_valid(s) hostname_is_valid(s, false) + bool is_localhost(const char *hostname); bool is_gateway_hostname(const char *hostname); diff --git a/src/basic/util.c b/src/basic/util.c index 9571f0ab12..deff68073c 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -3006,21 +3006,6 @@ char* strshorten(char *s, size_t l) { return s; } -bool machine_name_is_valid(const char *s) { - - if (!hostname_is_valid(s, false)) - return false; - - /* Machine names should be useful hostnames, but also be - * useful in unit names, hence we enforce a stricter length - * limitation. */ - - if (strlen(s) > 64) - return false; - - return true; -} - int pipe_eof(int fd) { struct pollfd pollfd = { .fd = fd, diff --git a/src/basic/util.h b/src/basic/util.h index 8f21d56ed8..1ead7b5419 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -394,8 +394,6 @@ bool nulstr_contains(const char*nulstr, const char *needle); bool plymouth_running(void); -bool machine_name_is_valid(const char *s) _pure_; - char* strshorten(char *s, size_t l); int symlink_idempotent(const char *from, const char *to); diff --git a/src/import/export.c b/src/import/export.c index ec7dbe210a..b88d71fec6 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -24,6 +24,7 @@ #include "sd-event.h" #include "event-util.h" #include "signal-util.h" +#include "hostname-util.h" #include "verbs.h" #include "build.h" #include "machine-image.h" diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 43cd413042..a27e81bbd4 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -26,6 +26,7 @@ #include "util.h" #include "path-util.h" #include "btrfs-util.h" +#include "hostname-util.h" #include "copy.h" #include "mkdir.h" #include "rm-rf.h" diff --git a/src/import/import-tar.c b/src/import/import-tar.c index 2bf0b0680c..7ffe83cc33 100644 --- a/src/import/import-tar.c +++ b/src/import/import-tar.c @@ -26,6 +26,7 @@ #include "util.h" #include "path-util.h" #include "btrfs-util.h" +#include "hostname-util.h" #include "copy.h" #include "mkdir.h" #include "rm-rf.h" diff --git a/src/import/import.c b/src/import/import.c index b7772390e9..929a840298 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -26,6 +26,7 @@ #include "verbs.h" #include "build.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "import-util.h" #include "import-tar.h" diff --git a/src/import/importd.c b/src/import/importd.c index dd314f5b00..8b508eaeec 100644 --- a/src/import/importd.c +++ b/src/import/importd.c @@ -35,6 +35,7 @@ #include "import-util.h" #include "process-util.h" #include "signal-util.h" +#include "hostname-util.h" typedef struct Transfer Transfer; typedef struct Manager Manager; diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 5bfaf012c0..d0e0faa261 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -33,6 +33,7 @@ #include "mkdir.h" #include "rm-rf.h" #include "path-util.h" +#include "hostname-util.h" #include "import-util.h" #include "import-common.h" #include "curl-util.h" diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index 71b8908a58..d38a2158c4 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -32,13 +32,14 @@ #include "mkdir.h" #include "rm-rf.h" #include "path-util.h" +#include "process-util.h" +#include "hostname-util.h" #include "import-util.h" #include "import-common.h" #include "curl-util.h" #include "pull-job.h" #include "pull-common.h" #include "pull-tar.h" -#include "process-util.h" typedef enum TarProgress { TAR_DOWNLOADING, diff --git a/src/import/pull.c b/src/import/pull.c index ca7be6be85..e13cd6af97 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -26,6 +26,7 @@ #include "verbs.h" #include "build.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "import-util.h" #include "pull-tar.h" diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index bfd1901e16..13fa9b52fc 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -43,6 +43,7 @@ #include "replace-var.h" #include "fileio.h" #include "formats-util.h" +#include "hostname-util.h" #define JOURNAL_FILES_MAX 7168 diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index db4f21e9ff..31cdcb4e5b 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -33,6 +33,7 @@ #include "missing.h" #include "def.h" #include "cgroup-util.h" +#include "hostname-util.h" #include "bus-label.h" #include "sd-bus.h" diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 9bbf8974dd..5f290573cb 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -32,6 +32,7 @@ #include "fileio.h" #include "login-util.h" #include "formats-util.h" +#include "hostname-util.h" #include "sd-login.h" _public_ int sd_pid_get_session(pid_t pid, char **session) { diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 8e75508444..72b9a619d2 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -56,6 +56,7 @@ #include "terminal-util.h" #include "signal-util.h" #include "env-util.h" +#include "hostname-util.h" static char **arg_property = NULL; static bool arg_all = false; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 93514986f3..03625ba4cd 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -33,6 +33,7 @@ #include "btrfs-util.h" #include "formats-util.h" #include "process-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machine-pool.h" #include "image-dbus.h" diff --git a/src/machine/machined.c b/src/machine/machined.c index c8ad157326..109bab76c5 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -30,6 +30,7 @@ #include "label.h" #include "formats-util.h" #include "signal-util.h" +#include "hostname-util.h" #include "machine-image.h" #include "machined.h" diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index cdec83d074..5758ea1569 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -30,6 +30,7 @@ #include "bus-util.h" #include "bus-common-errors.h" #include "in-addr-util.h" +#include "hostname-util.h" NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); NSS_GETPW_PROTOTYPES(mymachines); diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 60144ab8a6..9a5ffb7a6c 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -34,6 +34,7 @@ #include "formats-util.h" #include "process-util.h" #include "terminal-util.h" +#include "hostname-util.h" /* up to three lines (each up to 100 characters), or 300 characters, whichever is less */ -- cgit v1.2.3-54-g00ecf From b9a8d250810d4803bc9bf6b36932b528cb991d1e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 23 Aug 2015 14:33:50 +0200 Subject: machined: validate machine names at more places When enumerating machines from /run, and when accepting machine names for operations, be more strict and always validate. Note that these checks are strictly speaking unnecessary, since enumeration happens only on the trusted /run... --- src/basic/util.c | 3 +++ src/libsystemd/sd-login/sd-login.c | 2 +- src/machine/machined.c | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/basic/util.c b/src/basic/util.c index deff68073c..f752595ca1 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -4913,6 +4913,9 @@ int container_get_leader(const char *machine, pid_t *pid) { assert(machine); assert(pid); + if (!machine_name_is_valid(machine)) + return -EINVAL; + p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); if (r == -ENOENT) diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 5f290573cb..0eadc8c747 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -791,7 +791,7 @@ _public_ int sd_get_machine_names(char ***machines) { /* Filter out the unit: symlinks */ for (a = l, b = l; *a; a++) { - if (startswith(*a, "unit:")) + if (startswith(*a, "unit:") || !machine_name_is_valid(*a)) free(*a); else { *b = *a; diff --git a/src/machine/machined.c b/src/machine/machined.c index 109bab76c5..9b9a334838 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -118,6 +118,9 @@ int manager_enumerate_machines(Manager *m) { if (startswith(de->d_name, "unit:")) continue; + if (!machine_name_is_valid(de->d_name)) + continue; + k = manager_add_machine(m, de->d_name, &machine); if (k < 0) { log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); -- cgit v1.2.3-54-g00ecf From fbe550738d03b178bb004a1390e74115e904118a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 21:05:09 +0200 Subject: machined: introduce pseudo-machine ".host" refererring to the host system Some of the operations machined/machinectl implement are also very useful when applied to the host system (such as machinectl login, machinectl shell or machinectl status), hence introduce a pseudo-machine by the name of ".host" in machined that refers to the host system, and may be used top execute operations on the host system with. This copies the pseudo-image ".host" machined already implements for image related commands. (This commit also adds a PK privilege for opening a PTY in a container, which was previously not accessible for non-root.) --- src/basic/time-util.c | 26 ++ src/basic/time-util.h | 1 + src/machine/machine-dbus.c | 416 +++++++++++++++---------- src/machine/machine.c | 67 +++- src/machine/machine.h | 5 +- src/machine/machinectl.c | 6 +- src/machine/machined-dbus.c | 2 +- src/machine/machined.c | 47 ++- src/machine/machined.h | 2 + src/machine/org.freedesktop.machine1.policy.in | 10 + 10 files changed, 394 insertions(+), 188 deletions(-) (limited to 'src') diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 12f1b193be..e278196c90 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -88,6 +88,32 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { return ts; } +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { + int64_t delta; + + if (u == USEC_INFINITY) { + ts->realtime = ts->monotonic = USEC_INFINITY; + return ts; + } + ts->realtime = now(CLOCK_REALTIME); + ts->monotonic = now(CLOCK_MONOTONIC); + + delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; + + if ((int64_t) ts->realtime > delta) + ts->realtime -= delta; + else + ts->realtime = 0; + + if ((int64_t) ts->monotonic > delta) + ts->monotonic -= delta; + else + ts->monotonic = 0; + + return ts; +} + + usec_t timespec_load(const struct timespec *ts) { assert(ts); diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 7a64d454a0..2aba042217 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -74,6 +74,7 @@ usec_t now(clockid_t clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); static inline bool dual_timestamp_is_set(dual_timestamp *ts) { return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) || diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index ad3dd8facf..b89bb2cba1 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -186,141 +186,179 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; - _cleanup_free_ char *us = NULL, *them = NULL; - _cleanup_close_ int netns_fd = -1; Machine *m = userdata; - const char *p; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); - - r = readlink_malloc("/proc/self/ns/net", &us); - if (r < 0) - return r; - - p = procfs_file_alloca(m->leader, "ns/net"); - r = readlink_malloc(p, &them); + r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; - if (streq(us, them)) - return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); - - r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); + r = sd_bus_message_open_container(reply, 'a', "(iay)"); if (r < 0) return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; - - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + switch (m->class) { - if (child == 0) { + case MACHINE_HOST: { _cleanup_free_ struct local_address *addresses = NULL; struct local_address *a; - int i, n; - - pair[0] = safe_close(pair[0]); - - r = namespace_enter(-1, -1, netns_fd, -1, -1); - if (r < 0) - _exit(EXIT_FAILURE); + int n, i; n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n < 0) - _exit(EXIT_FAILURE); + return n; for (a = addresses, i = 0; i < n; a++, i++) { - struct iovec iov[2] = { - { .iov_base = &a->family, .iov_len = sizeof(a->family) }, - { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, - }; - r = writev(pair[1], iov, 2); + r = sd_bus_message_open_container(reply, 'r', "iay"); if (r < 0) - _exit(EXIT_FAILURE); - } - - pair[1] = safe_close(pair[1]); + return r; - _exit(EXIT_SUCCESS); - } - - pair[1] = safe_close(pair[1]); + r = sd_bus_message_append(reply, "i", addresses[i].family); + if (r < 0) + return r; - r = sd_bus_message_new_method_return(message, &reply); - if (r < 0) - return r; + r = sd_bus_message_append_array(reply, 'y', &addresses[i].address, FAMILY_ADDRESS_SIZE(addresses[i].family)); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'a', "(iay)"); - if (r < 0) - return r; + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } - for (;;) { - int family; - ssize_t n; - union in_addr_union in_addr; - struct iovec iov[2]; - struct msghdr mh = { - .msg_iov = iov, - .msg_iovlen = 2, - }; + break; + } - iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; - iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + case MACHINE_CONTAINER: { + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_free_ char *us = NULL, *them = NULL; + _cleanup_close_ int netns_fd = -1; + const char *p; + siginfo_t si; + pid_t child; - n = recvmsg(pair[0], &mh, 0); - if (n < 0) - return -errno; - if ((size_t) n < sizeof(family)) - break; + r = readlink_malloc("/proc/self/ns/net", &us); + if (r < 0) + return r; - r = sd_bus_message_open_container(reply, 'r', "iay"); + p = procfs_file_alloca(m->leader, "ns/net"); + r = readlink_malloc(p, &them); if (r < 0) return r; - r = sd_bus_message_append(reply, "i", family); + if (streq(us, them)) + return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name); + + r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL, NULL); if (r < 0) return r; - switch (family) { + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; + + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_free_ struct local_address *addresses = NULL; + struct local_address *a; + int i, n; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, -1, netns_fd, -1, -1); + if (r < 0) + _exit(EXIT_FAILURE); - case AF_INET: - if (n != sizeof(struct in_addr) + sizeof(family)) - return -EIO; + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n < 0) + _exit(EXIT_FAILURE); - r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); - break; + for (a = addresses, i = 0; i < n; a++, i++) { + struct iovec iov[2] = { + { .iov_base = &a->family, .iov_len = sizeof(a->family) }, + { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) }, + }; - case AF_INET6: - if (n != sizeof(struct in6_addr) + sizeof(family)) - return -EIO; + r = writev(pair[1], iov, 2); + if (r < 0) + _exit(EXIT_FAILURE); + } - r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); - break; + pair[1] = safe_close(pair[1]); + + _exit(EXIT_SUCCESS); } - if (r < 0) - return r; - r = sd_bus_message_close_container(reply); + pair[1] = safe_close(pair[1]); + + for (;;) { + int family; + ssize_t n; + union in_addr_union in_addr; + struct iovec iov[2]; + struct msghdr mh = { + .msg_iov = iov, + .msg_iovlen = 2, + }; + + iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) }; + iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) }; + + n = recvmsg(pair[0], &mh, 0); + if (n < 0) + return -errno; + if ((size_t) n < sizeof(family)) + break; + + r = sd_bus_message_open_container(reply, 'r', "iay"); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "i", family); + if (r < 0) + return r; + + switch (family) { + + case AF_INET: + if (n != sizeof(struct in_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in)); + break; + + case AF_INET6: + if (n != sizeof(struct in6_addr) + sizeof(family)) + return -EIO; + + r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6)); + break; + } + if (r < 0) + return r; + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + } + + r = wait_for_terminate(child, &si); if (r < 0) - return r; + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + break; } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines."); + } r = sd_bus_message_close_container(reply); if (r < 0) @@ -331,73 +369,88 @@ int bus_machine_method_get_addresses(sd_bus_message *message, void *userdata, sd int bus_machine_method_get_os_release(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - _cleanup_close_ int mntns_fd = -1, root_fd = -1; - _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_strv_free_ char **l = NULL; - _cleanup_fclose_ FILE *f = NULL; Machine *m = userdata; char **k, **v; - siginfo_t si; - pid_t child; int r; assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + switch (m->class) { - r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); - if (r < 0) - return r; + case MACHINE_HOST: + r = load_env_file_pairs(NULL, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) - return -errno; + break; - child = fork(); - if (child < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + case MACHINE_CONTAINER: { + _cleanup_close_ int mntns_fd = -1, root_fd = -1; + _cleanup_close_pair_ int pair[2] = { -1, -1 }; + _cleanup_fclose_ FILE *f = NULL; + siginfo_t si; + pid_t child; - if (child == 0) { - _cleanup_close_ int fd = -1; + r = namespace_open(m->leader, NULL, &mntns_fd, NULL, NULL, &root_fd); + if (r < 0) + return r; - pair[0] = safe_close(pair[0]); + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0) + return -errno; - r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); - if (r < 0) - _exit(EXIT_FAILURE); + child = fork(); + if (child < 0) + return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); + + if (child == 0) { + _cleanup_close_ int fd = -1; + + pair[0] = safe_close(pair[0]); + + r = namespace_enter(-1, mntns_fd, -1, -1, root_fd); + if (r < 0) + _exit(EXIT_FAILURE); + + fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) { + fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); + if (fd < 0) + _exit(EXIT_FAILURE); + } - fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) { - fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC); - if (fd < 0) + r = copy_bytes(fd, pair[1], (off_t) -1, false); + if (r < 0) _exit(EXIT_FAILURE); + + _exit(EXIT_SUCCESS); } - r = copy_bytes(fd, pair[1], (off_t) -1, false); - if (r < 0) - _exit(EXIT_FAILURE); + pair[1] = safe_close(pair[1]); - _exit(EXIT_SUCCESS); - } + f = fdopen(pair[0], "re"); + if (!f) + return -errno; - pair[1] = safe_close(pair[1]); + pair[0] = -1; - f = fdopen(pair[0], "re"); - if (!f) - return -errno; + r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); + if (r < 0) + return r; - pair[0] = -1; + r = wait_for_terminate(child, &si); + if (r < 0) + return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); + if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) + return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); - r = load_env_file_pairs(f, "/etc/os-release", NULL, &l); - if (r < 0) - return r; + break; + } - r = wait_for_terminate(child, &si); - if (r < 0) - return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m"); - if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) - return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally."); + default: + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines."); + } r = sd_bus_message_new_method_return(message, &reply); if (r < 0) @@ -430,10 +483,20 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines."); + r = bus_verify_polkit_async( + message, + CAP_SYS_ADMIN, + "org.freedesktop.machine1.open-pty", + false, + UID_INVALID, + &m->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -453,31 +516,45 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ } static int container_bus_new(Machine *m, sd_bus **ret) { - _cleanup_bus_unref_ sd_bus *bus = NULL; - char *address; int r; assert(m); assert(ret); - r = sd_bus_new(&bus); - if (r < 0) - return r; + switch (m->class) { - if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) - return -ENOMEM; + case MACHINE_HOST: + *ret = NULL; + break; - bus->address = address; - bus->bus_client = true; - bus->trusted = false; - bus->is_system = true; + case MACHINE_CONTAINER: { + _cleanup_bus_unref_ sd_bus *bus = NULL; + char *address; - r = sd_bus_start(bus); - if (r < 0) - return r; + r = sd_bus_new(&bus); + if (r < 0) + return r; - *ret = bus; - bus = NULL; + if (asprintf(&address, "x-machine-kernel:pid=%1$" PID_PRI ";x-machine-unix:pid=%1$" PID_PRI, m->leader) < 0) + return -ENOMEM; + + bus->address = address; + bus->bus_client = true; + bus->trusted = false; + bus->is_system = true; + + r = sd_bus_start(bus); + if (r < 0) + return r; + + *ret = bus; + bus = NULL; + break; + } + + default: + return -EOPNOTSUPP; + } return 0; } @@ -485,8 +562,9 @@ static int container_bus_new(Machine *m, sd_bus **ret) { int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_free_ char *pty_name = NULL; - _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; _cleanup_close_ int master = -1; + sd_bus *container_bus = NULL; Machine *m = userdata; const char *p, *getty; int r; @@ -494,9 +572,6 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu assert(message); assert(m); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines."); - r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, @@ -510,7 +585,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -525,10 +600,12 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = container_bus_new(m, &container_bus); + r = container_bus_new(m, &allocated_bus); if (r < 0) return r; + container_bus = allocated_bus ?: m->manager->bus; + getty = strjoina("container-getty@", p, ".service"); r = sd_bus_call_method( @@ -542,8 +619,6 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - container_bus = sd_bus_unref(container_bus); - r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; @@ -558,7 +633,8 @@ 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_bus_message_unref_ sd_bus_message *reply = NULL, *tm = NULL; _cleanup_free_ char *pty_name = NULL; - _cleanup_bus_unref_ sd_bus *container_bus = NULL; + _cleanup_bus_flush_close_unref_ sd_bus *allocated_bus = NULL; + sd_bus *container_bus = NULL; _cleanup_close_ int master = -1; _cleanup_strv_free_ char **env = NULL, **args = NULL; Machine *m = userdata; @@ -597,9 +673,6 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (!strv_env_is_valid(env)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments"); - if (m->class != MACHINE_CONTAINER) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening shells is only supported on container machines."); - r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, @@ -613,7 +686,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (r == 0) return 1; /* Will call us back */ - master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC); + master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC); if (master < 0) return master; @@ -631,10 +704,12 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (unlockpt(master) < 0) return -errno; - r = container_bus_new(m, &container_bus); + r = container_bus_new(m, &allocated_bus); if (r < 0) return r; + container_bus = allocated_bus ?: m->manager->bus; + r = sd_bus_message_new_method_call( container_bus, &tm, @@ -645,9 +720,8 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - unit = strjoina("container-shell@", p, ".service", NULL); - /* Name and mode */ + unit = strjoina("container-shell@", p, ".service", NULL); r = sd_bus_message_append(tm, "ss", unit, "fail"); if (r < 0) return r; @@ -768,8 +842,6 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; - container_bus = sd_bus_unref(container_bus); - r = sd_bus_message_new_method_return(message, &reply); if (r < 0) return r; @@ -1207,7 +1279,7 @@ const sd_bus_vtable machine_vtable[] = { SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0), + SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/machine/machine.c b/src/machine/machine.c index f045159d41..0d1b119dc1 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -37,12 +37,17 @@ #include "machine-dbus.h" #include "formats-util.h" -Machine* machine_new(Manager *manager, const char *name) { +Machine* machine_new(Manager *manager, MachineClass class, const char *name) { Machine *m; assert(manager); + assert(class < _MACHINE_CLASS_MAX); assert(name); + /* Passing class == _MACHINE_CLASS_INVALID here is fine. It + * means as much as "we don't know yet", and that we'll figure + * it out later when loading the state file. */ + m = new0(Machine, 1); if (!m) return NULL; @@ -51,14 +56,17 @@ Machine* machine_new(Manager *manager, const char *name) { if (!m->name) goto fail; - m->state_file = strappend("/run/systemd/machines/", m->name); - if (!m->state_file) - goto fail; + if (class != MACHINE_HOST) { + m->state_file = strappend("/run/systemd/machines/", m->name); + if (!m->state_file) + goto fail; + } + + m->class = class; if (hashmap_put(manager->machines, m->name, m) < 0) goto fail; - m->class = _MACHINE_CLASS_INVALID; m->manager = manager; return m; @@ -86,6 +94,9 @@ void machine_free(Machine *m) { (void) hashmap_remove(m->manager->machines, m->name); + if (m->manager->host_machine == m) + m->manager->host_machine = NULL; + if (m->leader > 0) (void) hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m); @@ -105,7 +116,9 @@ int machine_save(Machine *m) { int r; assert(m); - assert(m->state_file); + + if (!m->state_file) + return 0; if (!m->started) return 0; @@ -244,6 +257,9 @@ int machine_load(Machine *m) { assert(m); + if (!m->state_file) + return 0; + r = parse_env_file(m->state_file, NEWLINE, "SCOPE", &m->unit, "SCOPE_JOB", &m->scope_job, @@ -325,6 +341,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er int r = 0; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) { _cleanup_free_ char *escaped = NULL; @@ -364,6 +381,9 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { assert(m); + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + if (m->started) return 0; @@ -402,6 +422,7 @@ static int machine_stop_scope(Machine *m) { int r; assert(m); + assert(m->class != MACHINE_HOST); if (!m->unit) return 0; @@ -422,6 +443,9 @@ int machine_stop(Machine *m) { int r; assert(m); + if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM)) + return -EOPNOTSUPP; + r = machine_stop_scope(m); m->stopping = true; @@ -456,6 +480,9 @@ int machine_finalize(Machine *m) { bool machine_check_gc(Machine *m, bool drop_not_started) { assert(m); + if (m->class == MACHINE_HOST) + return true; + if (drop_not_started && !m->started) return false; @@ -481,6 +508,9 @@ void machine_add_to_gc_queue(Machine *m) { MachineState machine_get_state(Machine *s) { assert(s); + if (s->class == MACHINE_HOST) + return MACHINE_RUNNING; + if (s->stopping) return MACHINE_CLOSING; @@ -493,6 +523,9 @@ MachineState machine_get_state(Machine *s) { int machine_kill(Machine *m, KillWho who, int signo) { assert(m); + if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER)) + return -EOPNOTSUPP; + if (!m->unit) return -ESRCH; @@ -509,6 +542,25 @@ int machine_kill(Machine *m, KillWho who, int signo) { return manager_kill_unit(m->manager, m->unit, signo, NULL); } +int machine_openpt(Machine *m, int flags) { + assert(m); + + switch (m->class) { + + case MACHINE_HOST: + return posix_openpt(flags); + + case MACHINE_CONTAINER: + if (m->leader <= 0) + return -EINVAL; + + return openpt_in_namespace(m->leader, flags); + + default: + return -EOPNOTSUPP; + } +} + MachineOperation *machine_operation_unref(MachineOperation *o) { if (!o) return NULL; @@ -544,7 +596,8 @@ void machine_release_unit(Machine *m) { static const char* const machine_class_table[_MACHINE_CLASS_MAX] = { [MACHINE_CONTAINER] = "container", - [MACHINE_VM] = "vm" + [MACHINE_VM] = "vm", + [MACHINE_HOST] = "host", }; DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass); diff --git a/src/machine/machine.h b/src/machine/machine.h index 0132b65a97..5f978289f2 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -39,6 +39,7 @@ typedef enum MachineState { typedef enum MachineClass { MACHINE_CONTAINER, MACHINE_VM, + MACHINE_HOST, _MACHINE_CLASS_MAX, _MACHINE_CLASS_INVALID = -1 } MachineClass; @@ -95,7 +96,7 @@ struct Machine { unsigned n_operations; }; -Machine* machine_new(Manager *manager, const char *name); +Machine* machine_new(Manager *manager, MachineClass class, const char *name); void machine_free(Machine *m); bool machine_check_gc(Machine *m, bool drop_not_started); void machine_add_to_gc_queue(Machine *m); @@ -120,3 +121,5 @@ MachineState machine_state_from_string(const char *s) _pure_; const char *kill_who_to_string(KillWho k) _const_; KillWho kill_who_from_string(const char *s) _pure_; + +int machine_openpt(Machine *m, int flags); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 72b9a619d2..c84deba1c0 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -358,7 +358,8 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { bus, "org.freedesktop.systemd1", path, - endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : "org.freedesktop.systemd1.Service", + endswith(unit, ".scope") ? "org.freedesktop.systemd1.Scope" : + endswith(unit, ".slice") ? "org.freedesktop.systemd1.Slice" : "org.freedesktop.systemd1.Service", "ControlGroup", &error, &reply, @@ -372,9 +373,6 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) { if (r < 0) return bus_log_parse_error(r); - if (isempty(cgroup)) - return 0; - if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0) return 0; diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 03625ba4cd..29649899ed 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -1508,7 +1508,7 @@ int manager_add_machine(Manager *m, const char *name, Machine **_machine) { machine = hashmap_get(m->machines, name); if (!machine) { - machine = machine_new(m, name); + machine = machine_new(m, _MACHINE_CLASS_INVALID, name); if (!machine) return -ENOMEM; } diff --git a/src/machine/machined.c b/src/machine/machined.c index 9b9a334838..df3cc9972a 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -90,6 +90,45 @@ void manager_free(Manager *m) { free(m); } +static int manager_add_host_machine(Manager *m) { + _cleanup_free_ char *rd = NULL, *unit = NULL; + sd_id128_t mid; + Machine *t; + int r; + + if (m->host_machine) + return 0; + + r = sd_id128_get_machine(&mid); + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); + + rd = strdup("/"); + if (!rd) + return log_oom(); + + unit = strdup("-.slice"); + if (!unit) + return log_oom(); + + t = machine_new(m, MACHINE_HOST, ".host"); + if (!t) + return log_oom(); + + t->leader = 1; + t->id = mid; + + t->root_directory = rd; + t->unit = unit; + rd = unit = NULL; + + dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0); + + m->host_machine = t; + + return 0; +} + int manager_enumerate_machines(Manager *m) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -97,6 +136,10 @@ int manager_enumerate_machines(Manager *m) { assert(m); + r = manager_add_host_machine(m); + if (r < 0) + return r; + /* Read in machine data stored on disk */ d = opendir("/run/systemd/machines"); if (!d) { @@ -123,9 +166,7 @@ int manager_enumerate_machines(Manager *m) { k = manager_add_machine(m, de->d_name, &machine); if (k < 0) { - log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); - - r = k; + r = log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); continue; } diff --git a/src/machine/machined.h b/src/machine/machined.h index 61dbefb5f1..b3e59bf998 100644 --- a/src/machine/machined.h +++ b/src/machine/machined.h @@ -48,6 +48,8 @@ struct Manager { sd_event_source *image_cache_defer_event; LIST_HEAD(Machine, machine_gc_queue); + + Machine *host_machine; }; Manager *manager_new(void); diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in index b3b2fa29c1..f1557806d1 100644 --- a/src/machine/org.freedesktop.machine1.policy.in +++ b/src/machine/org.freedesktop.machine1.policy.in @@ -26,6 +26,16 @@ + + <_description>Acquire a pseudo TTY in a local container + <_message>Authentication is acquire a pseudo TTY in a local container. + + auth_admin + auth_admin + auth_admin_keep + + + <_description>Acquire a shell in a local container <_message>Authentication is required to acquire a shell in a local container. -- cgit v1.2.3-54-g00ecf From fee6d013d859bc66f5c993530898fece53fab06d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 21:08:04 +0200 Subject: machinectl: don't show ".host" pseudo-machine in list by default Let's hide all machines whose name begins with "." by default, thus hiding the ".host" pseudo-machine, unless --all is specified. This takes inspiration from the ".host" image handling in "machinectl list-images" which also hides all images whose name starts with ".". --- src/machine/machinectl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index c84deba1c0..0f9e732ada 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -158,6 +158,9 @@ static int list_machines(int argc, char *argv[], void *userdata) { while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { size_t l; + if (name[0] == '.' && !arg_all) + continue; + if (!GREEDY_REALLOC(machines, n_allocated, n_machines + 1)) return log_oom(); -- cgit v1.2.3-54-g00ecf From a79366e22a43ed81017e2d8c2426431d369f3cfa Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 21:09:16 +0200 Subject: machined: userns is only supported for container-class machines We do not support userns for VM machines or for the host itself. --- src/machine/machined-dbus.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 29649899ed..0d52c693e4 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -882,6 +882,9 @@ static int method_map_from_machine_user(sd_bus_message *message, void *userdata, if (!machine) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + p = procfs_file_alloca(machine->leader, "uid_map"); f = fopen(p, "re"); if (!f) @@ -934,6 +937,9 @@ static int method_map_to_machine_user(sd_bus_message *message, void *userdata, s _cleanup_fclose_ FILE *f = NULL; char p[strlen("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + if (machine->class != MACHINE_CONTAINER) + continue; + xsprintf(p, "/proc/" UID_FMT "/uid_map", machine->leader); f = fopen(p, "re"); if (!f) { @@ -994,6 +1000,9 @@ static int method_map_from_machine_group(sd_bus_message *message, void *groupdat if (!machine) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name); + if (machine->class != MACHINE_CONTAINER) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not supported for non-container machines."); + p = procfs_file_alloca(machine->leader, "gid_map"); f = fopen(p, "re"); if (!f) @@ -1046,6 +1055,9 @@ static int method_map_to_machine_group(sd_bus_message *message, void *groupdata, _cleanup_fclose_ FILE *f = NULL; char p[strlen("/proc//gid_map") + DECIMAL_STR_MAX(pid_t) + 1]; + if (machine->class != MACHINE_CONTAINER) + continue; + xsprintf(p, "/proc/" GID_FMT "/gid_map", machine->leader); f = fopen(p, "re"); if (!f) { -- cgit v1.2.3-54-g00ecf From b04c25f9ef6359ed0ae403bdbfe4df840aba0f58 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 21:09:49 +0200 Subject: systemctl: properly handle empty control group paths in "status" When showing the status of the "-.slice" slice root unit (whose reported cgroup path is ""), we suppressed the cgroup tree so far, because skipped it for all unit with an empty cgroup path. Let's fix that, and properly handle the empty cgroup path. --- src/systemctl/systemctl.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 1a9dbadbe1..3cd267ec5a 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3608,7 +3608,14 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * if (r < 0) return bus_log_parse_error(r); - if (!isempty(s)) { + if (streq(name, "ControlGroup")) + i->control_group = s; + else if (!isempty(s)) { + /* For all but the cgroup path (see above) we + * consider the empty string as unset. For the + * cgroup path the empty string refers to the + * root of the cgroup tree. */ + if (streq(name, "Id")) i->id = s; else if (streq(name, "LoadState")) @@ -3631,8 +3638,6 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->control_group = e; } #endif - else if (streq(name, "ControlGroup")) - i->control_group = s; else if (streq(name, "StatusText")) i->status_text = s; else if (streq(name, "PIDFile")) -- cgit v1.2.3-54-g00ecf From 4289c3a725062e2750da0baaf67fc53ba90e4739 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 21:27:37 +0200 Subject: machined: beef up PolicyKit actions Introduce separate actions for creating login or shell sessions for the local host or a local container. By default allow local unprivileged clients to create new login sessions (which is safe, since getty will ask for username and authentication). Also, imply login privs from shell privs, as well as shell and login privs from manage privs. --- src/machine/machine-dbus.c | 6 ++-- src/machine/org.freedesktop.machine1.policy.in | 39 ++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index b89bb2cba1..af2b8eff06 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -486,7 +486,7 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "org.freedesktop.machine1.open-pty", + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", false, UID_INVALID, &m->manager->polkit_registry, @@ -575,7 +575,7 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "org.freedesktop.machine1.login", + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", false, UID_INVALID, &m->manager->polkit_registry, @@ -676,7 +676,7 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, - "org.freedesktop.machine1.shell", + m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell", false, UID_INVALID, &m->manager->polkit_registry, diff --git a/src/machine/org.freedesktop.machine1.policy.in b/src/machine/org.freedesktop.machine1.policy.in index f1557806d1..6e35c5c045 100644 --- a/src/machine/org.freedesktop.machine1.policy.in +++ b/src/machine/org.freedesktop.machine1.policy.in @@ -26,6 +26,38 @@ + + <_description>Log into the local host + <_message>Authentication is required to log into the local host. + + auth_admin + auth_admin + yes + + + + + <_description>Acquire a shell in a local container + <_message>Authentication is required to acquire a shell in a local container. + + auth_admin + auth_admin + auth_admin_keep + + org.freedesktop.login1.login + + + + <_description>Acquire a shell on the local host + <_message>Authentication is required to acquire a shell on the local host. + + auth_admin + auth_admin + auth_admin_keep + + org.freedesktop.login1.host-login + + <_description>Acquire a pseudo TTY in a local container <_message>Authentication is acquire a pseudo TTY in a local container. @@ -36,9 +68,9 @@ - - <_description>Acquire a shell in a local container - <_message>Authentication is required to acquire a shell in a local container. + + <_description>Acquire a pseudo TTY on the local host + <_message>Authentication is acquire a pseudo TTY on the local host. auth_admin auth_admin @@ -54,6 +86,7 @@ auth_admin auth_admin_keep + org.freedesktop.login1.shell org.freedesktop.login1.login -- cgit v1.2.3-54-g00ecf From 91913f584af38b29a816cca959ba648acd60ac9f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 22:17:52 +0200 Subject: machinectl: make machine name parameters for "shell" and "login" optional If no machine name is specified, imply that we connect to ".host", i.e. the local host. --- man/machinectl.xml | 123 +++++++++++++++++++++++++++++++++++++++-------- src/machine/machinectl.c | 51 ++++++++++++-------- 2 files changed, 134 insertions(+), 40 deletions(-) (limited to 'src') diff --git a/man/machinectl.xml b/man/machinectl.xml index 43dcbbdb18..2f68f91b93 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -65,6 +65,43 @@ systemd1 virtual machine and container registration manager systemd-machined.service8. + + machinectl may be used to execute + operations on machines and images. Machines in this sense are + considered running instances of: + + + Virtual Machines (VMs) that virtualize hardware + to run full operating system (OS) instances (including their kernels) + in a virtualized environment on top of the host OS. + + Containers that share the hardware and + OS kernel with the host OS, in order to run + OS userspace instances on top the host OS. + + The host system itself + + + Machines are identified by names that follow the same rules + as UNIX and DNS host names, for details see below. Machines are + instantiated from disk or file system images, that frequently but not + necessarily carry the same name as machines running from + them. Images in this sense are considered: + + + Directory trees containing an OS, including its + top-level directories /usr, + /etc, and so on. + + btrfs subvolumes containing OS trees, similar to + normal directory trees. + + Binary "raw" disk images containing MBR or GPT + partition tables and Linux file system partitions. + + The file system tree of the host OS itself. + + @@ -270,9 +307,11 @@ list List currently running (online) virtual - machines and containers. To enumerate container images that - can be started, use list-images (see - below). + machines and containers. To enumerate machine images that can + be started, use list-images (see + below). Note that this command hides the special + .host machine by default. Use the + switch to show it. @@ -290,7 +329,7 @@ - show NAME... + show [NAME...] Show properties of one or more registered virtual machines or containers or the manager itself. If no @@ -339,31 +378,42 @@ - login NAME + login [NAME] Open an interactive terminal login session in - a container. This will create a TTY connection to a specific - container and asks for the execution of a getty on it. Note - that this is only supported for containers running + a container or on the local host. If an argument is supplied + it refers to the container machine to connect to. If none is + specified, or the container name is specified as the empty + string, or the special machine name .host + (see below) is specified, the connection is made to the local + host instead. This will create a TTY connection to a specific + container or the local host and asks for the execution of a + getty on it. Note that this is only supported for containers + running systemd1 as init system. This command will open a full login prompt on the - container, which then asks for username and password. Use - shell (see below) or + container or the local host, which then asks for username and + password. Use shell (see below) or systemd-run1 - with the switch to invoke a single - command, either interactively or in the background within a - local container. + with the switch to directly invoke + a single command, either interactively or in the + background. - shell NAME [PATH [ARGUMENTS...]] + shell [NAME [PATH [ARGUMENTS...]]] Open an interactive shell session in a - container. This works similar to login but - immediately invokes a user process. Invokes the specified - executable with the specified arguments, or + container or on the local host. The first argument refers to + the container machine to connect to. If none is specified, or + the machine name is specified as the empty string, or the + special machine name .host (see below) is + specified, the connection is made to the local host + instead. This works similar to login but + immediately invokes a user process. This command runs the + specified executable with the specified arguments, or /bin/sh if none is specified. By default opens a root shell, but using a different user may be selected. Use @@ -491,7 +541,7 @@ - image-status NAME... + image-status [NAME...] Show terse status information about one or more container or VM images. This function is intended to @@ -501,7 +551,7 @@ - show-image NAME... + show-image [NAME...] Show properties of one or more registered virtual machine or container images, or the manager itself. If @@ -803,6 +853,41 @@ + + Machine and Image Names + + The machinectl tool operates on machines + and images, whose names must be chosen following strict + rules. Machine names must be suitable for use as host names + following a conservative subset of DNS and UNIX/Linux + semantics. Specifically, they must consist of one or more + non-empty label strings, separated by dots. No leading or trailing + dots are allowed. No sequences of multiple dots are allowed. The + label strings may only consists of alphanumeric characters as well + as the dash and underscore. The maximum length of a machine name + is 64 characters. + + A special machine with the name .host + refers to the running host system itself. This is useful for execution + operations or inspecting the host system as well. Not that + machinectl list will not show this special + machine unless the switch is specified. + + Requirements on image names are less strict, however must be + valid UTF-8, must be suitable as file names (hence not be the + single or double dot, and not include a slash), and may not + contain control characters. Since many operations search for an + image by the name of a requested machine it is recommended to name + images in the same strict fashion as machines. + + A special image with the name .host + refers to the image of the running host system. It is hence + conceptually maps to the special .host machine + name described above. Note that machinectl + list-images won't show this special image either, unless + is specified. + + Files and Directories diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 0f9e732ada..08a2d3fe4d 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1186,7 +1186,10 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, bo assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGWINCH, SIGTERM, SIGINT, -1) >= 0); - log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); + if (streq(name, ".host")) + log_info("Connected to the local host. Press ^] three times within 1s to exit session."); + else + log_info("Connected to machine %s. Press ^] three times within 1s to exit session.", name); sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); @@ -1212,6 +1215,8 @@ static int process_forward(sd_event *event, PTYForward **forward, int master, bo if (machine_died) log_info("Machine %s terminated.", name); + else if (streq(name, ".host")) + log_info("Connection to the local host terminated."); else log_info("Connection to machine %s terminated.", name); @@ -1227,7 +1232,7 @@ static int login_machine(int argc, char *argv[], void *userdata) { _cleanup_event_unref_ sd_event *event = NULL; int master = -1, r; sd_bus *bus = userdata; - const char *pty, *match; + const char *pty, *match, *machine; assert(bus); @@ -1252,14 +1257,14 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); + machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; + match = strjoina("type='signal'," "sender='org.freedesktop.machine1'," "path='/org/freedesktop/machine1',", "interface='org.freedesktop.machine1.Manager'," "member='MachineRemoved'," - "arg0='", - argv[1], - "'"); + "arg0='", machine, "'"); r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); if (r < 0) @@ -1273,7 +1278,7 @@ static int login_machine(int argc, char *argv[], void *userdata) { "OpenMachineLogin", &error, &reply, - "s", argv[1]); + "s", machine); if (r < 0) { log_error("Failed to get login PTY: %s", bus_error_message(&error, -r)); return r; @@ -1283,7 +1288,7 @@ static int login_machine(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - return process_forward(event, &forward, master, true, argv[1]); + return process_forward(event, &forward, master, true, machine); } static int shell_machine(int argc, char *argv[], void *userdata) { @@ -1294,7 +1299,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) { _cleanup_event_unref_ sd_event *event = NULL; int master = -1, r; sd_bus *bus = userdata; - const char *pty, *match; + const char *pty, *match, *machine, *path; assert(bus); @@ -1314,14 +1319,14 @@ static int shell_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); + machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; + match = strjoina("type='signal'," "sender='org.freedesktop.machine1'," "path='/org/freedesktop/machine1',", "interface='org.freedesktop.machine1.Manager'," "member='MachineRemoved'," - "arg0='", - argv[1], - "'"); + "arg0='", machine, "'"); r = sd_bus_add_match(bus, &slot, match, on_machine_removed, &forward); if (r < 0) @@ -1337,11 +1342,13 @@ static int shell_machine(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append(m, "sss", argv[1], arg_uid, argv[2]); + path = argc < 3 || isempty(argv[2]) ? NULL : argv[2]; + + r = sd_bus_message_append(m, "sss", machine, arg_uid, path); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, strv_length(argv + 2) <= 1 ? NULL : argv + 2); + r = sd_bus_message_append_strv(m, strv_length(argv) <= 3 ? NULL : argv + 2); if (r < 0) return bus_log_create_error(r); @@ -1359,7 +1366,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - return process_forward(event, &forward, master, false, argv[1]); + return process_forward(event, &forward, master, false, machine); } static int remove_image(int argc, char *argv[], void *userdata) { @@ -2429,10 +2436,12 @@ static int help(int argc, char *argv[], void *userdata) { "Machine Commands:\n" " list List running VMs and containers\n" " status NAME... Show VM/container details\n" - " show NAME... Show properties of one or more VMs/containers\n" + " show [NAME...] Show properties of one or more VMs/containers\n" " start NAME... Start container as a service\n" - " login NAME Get a login prompt on a container\n" - " shell NAME [COMMAND...] Invoke a shell (or other command) in a container\n" + " login [NAME] Get a login prompt in a container or on the\n" + " local host\n" + " shell [NAME] [COMMAND...] Invoke a shell (or other command) in a container\n" + " or the local host\n" " enable NAME... Enable automatic container start at boot\n" " disable NAME... Disable automatic container start at boot\n" " poweroff NAME... Power off one or more containers\n" @@ -2444,8 +2453,8 @@ static int help(int argc, char *argv[], void *userdata) { " bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n" "Image Commands:\n" " list-images Show available container and VM images\n" - " image-status NAME... Show image details\n" - " show-image NAME... Show properties of image\n" + " image-status [NAME...] Show image details\n" + " show-image [NAME...] Show properties of image\n" " clone NAME NAME Clone an image\n" " rename NAME NAME Rename an image\n" " read-only NAME [BOOL] Mark or unmark image read-only\n" @@ -2677,8 +2686,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) { { "reboot", 2, VERB_ANY, 0, reboot_machine }, { "poweroff", 2, VERB_ANY, 0, poweroff_machine }, { "kill", 2, VERB_ANY, 0, kill_machine }, - { "login", 2, 2, 0, login_machine }, - { "shell", 2, VERB_ANY, 0, shell_machine }, + { "login", VERB_ANY, 2, 0, login_machine }, + { "shell", VERB_ANY, VERB_ANY, 0, shell_machine }, { "bind", 3, 4, 0, bind_mount }, { "copy-to", 3, 4, 0, copy_files }, { "copy-from", 3, 4, 0, copy_files }, -- cgit v1.2.3-54-g00ecf From ef3100e9637adda26fa19e7ee8606788320dcde3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Aug 2015 22:44:54 +0200 Subject: machinectl: extend the "shell" syntax to take user@container names In order to make "machinectl shell" more similar to ssh, allow the following syntax to connect to a container under a specific username: machinectl shell lennart@fedora Also beefs up related man page documentation. --- man/machinectl.xml | 40 +++++++++++++++++++++++++++++++++++----- src/machine/machinectl.c | 26 +++++++++++++++++++++----- 2 files changed, 56 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/man/machinectl.xml b/man/machinectl.xml index 2f68f91b93..6cf405ed29 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -403,7 +403,7 @@ - shell [NAME [PATH [ARGUMENTS...]]] + shell [[NAME@]NAME [PATH [ARGUMENTS...]]] Open an interactive shell session in a container or on the local host. The first argument refers to @@ -415,10 +415,29 @@ immediately invokes a user process. This command runs the specified executable with the specified arguments, or /bin/sh if none is specified. By default - opens a root shell, but using - a different user may be selected. Use - to set environment variables for - the executed process. + opens a root shell, but by using + , or by prefixing the machine name with + a username and an @ character, a different + user may be selected. Use to set + environment variables for the executed process. + + When using the shell command without + arguments (thus invoking the executed shell or command on the + local host) it is similar in many ways to a su1 + session, but unlike su completely isolates + the new session from the originating session, so that it + shares no process or session properties, and is in a clean and + well-defined state. It will be tracked in a new utmp, login, + audit and keyring session, and will not inherit an environment + variables or resource limits, among other properties. + + Note that the + systemd-run1 + may be used in place of the shell command, + and allows more detailed, low-level configuration of the + invoked unit. However, it is frequently more privileged than + the shell command. @@ -995,6 +1014,17 @@ current directory. + + Create a new shell session + + # machinectl shell --uid=lennart + + This creates a new shell session on the local host, for + the user ID lennart, in a su1-like + fashion. + + diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 08a2d3fe4d..6b29e61642 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -1299,7 +1299,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) { _cleanup_event_unref_ sd_event *event = NULL; int master = -1, r; sd_bus *bus = userdata; - const char *pty, *match, *machine, *path; + const char *pty, *match, *machine, *path, *uid = NULL; assert(bus); @@ -1319,7 +1319,22 @@ static int shell_machine(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); - machine = argc < 2 || isempty(argv[1]) ? ".host" : argv[1]; + machine = argc < 2 || isempty(argv[1]) ? NULL : argv[1]; + + if (arg_uid) + uid = arg_uid; + else if (machine) { + const char *at; + + at = strchr(machine, '@'); + if (at) { + uid = strndupa(machine, at - machine); + machine = at + 1; + } + } + + if (isempty(machine)) + machine = ".host"; match = strjoina("type='signal'," "sender='org.freedesktop.machine1'," @@ -1344,7 +1359,7 @@ static int shell_machine(int argc, char *argv[], void *userdata) { path = argc < 3 || isempty(argv[2]) ? NULL : argv[2]; - r = sd_bus_message_append(m, "sss", machine, arg_uid, path); + r = sd_bus_message_append(m, "sss", machine, uid, path); if (r < 0) return bus_log_create_error(r); @@ -2440,8 +2455,9 @@ static int help(int argc, char *argv[], void *userdata) { " start NAME... Start container as a service\n" " login [NAME] Get a login prompt in a container or on the\n" " local host\n" - " shell [NAME] [COMMAND...] Invoke a shell (or other command) in a container\n" - " or the local host\n" + " shell [[USER@]NAME [COMMAND...]]\n" + " Invoke a shell (or other command) in a container\n" + " or on the local host\n" " enable NAME... Enable automatic container start at boot\n" " disable NAME... Disable automatic container start at boot\n" " poweroff NAME... Power off one or more containers\n" -- cgit v1.2.3-54-g00ecf