From: Lennart Poettering Date: Wed, 30 Oct 2013 14:34:50 +0000 (+0100) Subject: bus: add API call to create bus connection to the system bus of local containers X-Git-Tag: v209~1714 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=a7893c6b28772edbc7e1fea3c209caa54d465648 bus: add API call to create bus connection to the system bus of local containers Also, add support for this to machinectl, so that we can enumerate the machines that run inside a container. We must go deeper! --- diff --git a/Makefile.am b/Makefile.am index bd7f577d4..de9a4aad5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1947,6 +1947,8 @@ libsystemd_bus_la_SOURCES = \ src/libsystemd-bus/bus-socket.h \ src/libsystemd-bus/bus-kernel.c \ src/libsystemd-bus/bus-kernel.h \ + src/libsystemd-bus/bus-container.c \ + src/libsystemd-bus/bus-container.h \ src/libsystemd-bus/bus-message.c \ src/libsystemd-bus/bus-message.h \ src/libsystemd-bus/bus-signature.c \ diff --git a/man/machinectl.xml b/man/machinectl.xml index 2ed9f2e8a..f2fa6ce53 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -86,6 +86,42 @@ string and exits. + + + + Do not pipe output into a + pager. + + + + + + Do not query the user + for authentication for privileged + operations. + + + + + + + Execute operation + remotely. Specify a hostname, or + username and hostname separated by @, + to connect to. This will use SSH to + talk to the remote machine manager + instance. + + + + + + + Execute operation on a + local container. Specify a container + name to connect to. + + @@ -122,21 +158,6 @@ - - - - Do not pipe output into a - pager. - - - - - - Do not query the user - for authentication for privileged - operations. - - @@ -167,26 +188,6 @@ SIGTERM. - - - - - Execute operation - remotely. Specify a hostname, or - username and hostname separated by @, - to connect to. This will use SSH to - talk to the remote machine manager - instance. - - - - - - - Acquire privileges via - PolicyKit before executing the - operation. - The following commands are understood: @@ -293,8 +294,8 @@ See Also systemd-machined.service8, - systemd-logind.service8, - systemd.special7. + systemd-nspawn1, + systemd.special7 diff --git a/src/libsystemd-bus/bus-container.c b/src/libsystemd-bus/bus-container.c new file mode 100644 index 000000000..eac186324 --- /dev/null +++ b/src/libsystemd-bus/bus-container.c @@ -0,0 +1,128 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "util.h" +#include "fileio.h" +#include "bus-internal.h" +#include "bus-socket.h" +#include "bus-container.h" + +int bus_container_connect(sd_bus *b) { + _cleanup_free_ char *p = NULL, *s = NULL, *ns = NULL, *root = NULL, *class = NULL; + _cleanup_close_ int nsfd = -1, rootfd = -1; + siginfo_t si; + pid_t leader, child; + int r; + + assert(b); + assert(b->input_fd < 0); + assert(b->output_fd < 0); + + p = strappend("/run/systemd/machines/", b->machine); + if (!p) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL); + if (r < 0) + return r; + if (!s) + return -EIO; + + if (!streq_ptr(class, "container")) + return -EIO; + + r = parse_pid(s, &leader); + if (r < 0) + return r; + if (leader <= 1) + return -EIO; + + r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) leader); + if (r < 0) + return -ENOMEM; + + nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (nsfd < 0) + return -errno; + + r = asprintf(&root, "/proc/%lu/root", (unsigned long) leader); + if (r < 0) + return -ENOMEM; + + rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (rootfd < 0) + return -errno; + + b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (b->input_fd < 0) + return -errno; + + b->output_fd = b->input_fd; + + r = bus_socket_setup(b); + if (r < 0) + return r; + + child = fork(); + if (child < 0) + return -errno; + + if (child == 0) { + r = setns(nsfd, CLONE_NEWNS); + if (r < 0) + _exit(255); + + if (fchdir(rootfd) < 0) + _exit(255); + + if (chroot(".") < 0) + _exit(255); + + + r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size); + if (r < 0) { + if (errno == EINPROGRESS) + _exit(1); + + _exit(255); + } + + _exit(0); + } + + r = wait_for_terminate(child, &si); + if (r < 0) + return r; + + if (si.si_code != CLD_EXITED) + return -EIO; + + if (si.si_status == 1) + return 1; + + if (si.si_status != 0) + return -EIO; + + return bus_socket_start_auth(b); +} diff --git a/src/libsystemd-bus/bus-container.h b/src/libsystemd-bus/bus-container.h new file mode 100644 index 000000000..65f43ab4f --- /dev/null +++ b/src/libsystemd-bus/bus-container.h @@ -0,0 +1,26 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2013 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "sd-bus.h" + +int bus_container_connect(sd_bus *b); diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 2ae796191..913f28131 100644 --- a/src/libsystemd-bus/bus-internal.h +++ b/src/libsystemd-bus/bus-internal.h @@ -196,6 +196,7 @@ struct sd_bus { socklen_t sockaddr_size; char *kernel; + char *machine; sd_id128_t server_id; diff --git a/src/libsystemd-bus/bus-socket.c b/src/libsystemd-bus/bus-socket.c index b60facb20..b7e816ea8 100644 --- a/src/libsystemd-bus/bus-socket.c +++ b/src/libsystemd-bus/bus-socket.c @@ -600,7 +600,7 @@ static int bus_socket_read_auth(sd_bus *b) { return 1; } -static int bus_socket_setup(sd_bus *b) { +int bus_socket_setup(sd_bus *b) { int enable; socklen_t l; @@ -668,7 +668,7 @@ static int bus_socket_start_auth_client(sd_bus *b) { return bus_socket_write_auth(b); } -static int bus_socket_start_auth(sd_bus *b) { +int bus_socket_start_auth(sd_bus *b) { assert(b); b->state = BUS_AUTHENTICATING; diff --git a/src/libsystemd-bus/bus-socket.h b/src/libsystemd-bus/bus-socket.h index a9c43f82d..c61b90f47 100644 --- a/src/libsystemd-bus/bus-socket.h +++ b/src/libsystemd-bus/bus-socket.h @@ -23,9 +23,11 @@ #include "sd-bus.h" +int bus_socket_setup(sd_bus *b); int bus_socket_connect(sd_bus *b); int bus_socket_exec(sd_bus *b); int bus_socket_take_fd(sd_bus *b); +int bus_socket_start_auth(sd_bus *b); int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx); int bus_socket_read_message(sd_bus *bus, sd_bus_message **m); diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 383b035b7..f42d5d0ee 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -46,6 +46,7 @@ #include "bus-signature.h" #include "bus-objects.h" #include "bus-util.h" +#include "bus-container.h" static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); @@ -117,6 +118,7 @@ static void bus_free(sd_bus *b) { free(b->auth_buffer); free(b->address); free(b->kernel); + free(b->machine); free(b->exec_path); strv_free(b->exec_argv); @@ -753,6 +755,45 @@ static int parse_kernel_address(sd_bus *b, const char **p, char **guid) { return 0; } +static int parse_container_address(sd_bus *b, const char **p, char **guid) { + _cleanup_free_ char *machine = NULL; + int r; + + assert(b); + assert(p); + assert(*p); + assert(guid); + + while (**p != 0 && **p != ';') { + r = parse_address_key(p, "guid", guid); + if (r < 0) + return r; + else if (r > 0) + continue; + + r = parse_address_key(p, "machine", &machine); + if (r < 0) + return r; + else if (r > 0) + continue; + + skip_address_key(p); + } + + if (!machine) + return -EINVAL; + + free(b->machine); + b->machine = machine; + machine = NULL; + + b->sockaddr.un.sun_family = AF_UNIX; + strncpy(b->sockaddr.un.sun_path, "/var/run/dbus/system_bus_socket", sizeof(b->sockaddr.un.sun_path)); + b->sockaddr_size = offsetof(struct sockaddr_un, sun_path) + sizeof("/var/run/dbus/system_bus_socket") - 1; + + return 0; +} + static void bus_reset_parsed_address(sd_bus *b) { assert(b); @@ -765,6 +806,8 @@ static void bus_reset_parsed_address(sd_bus *b) { b->server_id = SD_ID128_NULL; free(b->kernel); b->kernel = NULL; + free(b->machine); + b->machine = NULL; } static int bus_parse_next_address(sd_bus *b) { @@ -823,6 +866,14 @@ static int bus_parse_next_address(sd_bus *b) { if (r < 0) return r; + break; + } else if (startswith(a, "x-container:")) { + + a += 12; + r = parse_container_address(b, &a, &guid); + if (r < 0) + return r; + break; } @@ -849,24 +900,32 @@ static int bus_start_address(sd_bus *b) { for (;;) { sd_bus_close(b); - if (b->sockaddr.sa.sa_family != AF_UNSPEC) { + if (b->exec_path) { - r = bus_socket_connect(b); + r = bus_socket_exec(b); if (r >= 0) return r; b->last_connect_error = -r; + } else if (b->kernel) { - } else if (b->exec_path) { + r = bus_kernel_connect(b); + if (r >= 0) + return r; - r = bus_socket_exec(b); + b->last_connect_error = -r; + + } else if (b->machine) { + + r = bus_container_connect(b); if (r >= 0) return r; b->last_connect_error = -r; - } else if (b->kernel) { - r = bus_kernel_connect(b); + } else if (b->sockaddr.sa.sa_family != AF_UNSPEC) { + + r = bus_socket_connect(b); if (r >= 0) return r; @@ -937,7 +996,7 @@ int sd_bus_start(sd_bus *bus) { if (bus->input_fd >= 0) r = bus_start_fd(bus); - else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel) + else if (bus->address || bus->sockaddr.sa.sa_family != AF_UNSPEC || bus->exec_path || bus->kernel || bus->machine) r = bus_start_address(bus); else return -EINVAL; @@ -1069,6 +1128,42 @@ int sd_bus_open_system_remote(const char *host, sd_bus **ret) { return 0; } +int sd_bus_open_system_container(const char *machine, sd_bus **ret) { + _cleanup_free_ char *e = NULL; + sd_bus *bus; + char *p; + int r; + + assert_return(machine, -EINVAL); + assert_return(ret, -EINVAL); + + e = bus_address_escape(machine); + if (!e) + return -ENOMEM; + + p = strjoin("x-container:machine=", e, NULL); + if (!p) + return -ENOMEM; + + r = sd_bus_new(&bus); + if (r < 0) { + free(p); + return r; + } + + bus->address = p; + bus->bus_client = true; + + r = sd_bus_start(bus); + if (r < 0) { + bus_free(bus); + return r; + } + + *ret = bus; + return 0; +} + void sd_bus_close(sd_bus *bus) { if (!bus) return; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 241e360d4..83c5da380 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -47,12 +47,12 @@ static bool arg_no_pager = false; static const char *arg_kill_who = NULL; static int arg_signal = SIGTERM; static enum transport { - TRANSPORT_NORMAL, - TRANSPORT_SSH, -} arg_transport = TRANSPORT_NORMAL; + TRANSPORT_LOCAL, + TRANSPORT_REMOTE, + TRANSPORT_CONTAINER +} arg_transport = TRANSPORT_LOCAL; static bool arg_ask_password = true; static char *arg_host = NULL; -static char *arg_user = NULL; static void pager_open_if_enabled(void) { @@ -94,9 +94,6 @@ static int list_machines(sd_bus *bus, char **args, unsigned n) { goto fail; while ((r = sd_bus_message_read(reply, "(ssso)", &name, &class, &service, &object)) > 0) { - if (r < 0) - goto fail; - printf("%-32s %-9s %-16s\n", name, class, service); k++; @@ -115,7 +112,7 @@ static int list_machines(sd_bus *bus, char **args, unsigned n) { fail: log_error("Failed to parse reply: %s", strerror(-r)); - return -EIO; + return r; } static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) { @@ -129,30 +126,28 @@ static int show_scope_cgroup(sd_bus *bus, const char *unit, pid_t leader) { assert(bus); assert(unit); - if (arg_transport == TRANSPORT_SSH) + if (arg_transport == TRANSPORT_REMOTE) return 0; path = unit_dbus_path_from_name(unit); if (!path) return log_oom(); - r = sd_bus_call_method( + r = sd_bus_get_property( bus, "org.freedesktop.systemd1", path, - "org.freedesktop.DBus.Properties", - "Get", + "org.freedesktop.systemd1.Scope", + "ControlGroup", &error, &reply, - "ss", - "org.freedesktop.systemd1.Scope", - "ControlGroup"); + "s"); if (r < 0) { log_error("Failed to query ControlGroup: %s", bus_error_message(&error, -r)); return r; } - r = sd_bus_message_read(reply, "v", "s", &cgroup); + r = sd_bus_message_read(reply, "s", &cgroup); if (r < 0) { log_error("Failed to parse reply: %s", strerror(-r)); return r; @@ -371,9 +366,6 @@ static int show_one(const char *verb, sd_bus *bus, const char *path, bool show_p const char *name; const char *contents; - if (r < 0) - goto fail; - r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &name); if (r < 0) goto fail; @@ -415,7 +407,7 @@ static int show_one(const char *verb, sd_bus *bus, const char *path, bool show_p fail: log_error("Failed to parse reply: %s", strerror(-r)); - return -EIO; + return r; } static int show(sd_bus *bus, char **args, unsigned n) { @@ -460,7 +452,7 @@ static int show(sd_bus *bus, char **args, unsigned n) { r = sd_bus_message_read(reply, "o", &path); if (r < 0) { log_error("Failed to parse reply: %s", strerror(-r)); - return -EIO; + return r; } r = show_one(args[0], bus, path, show_properties, &new_line); @@ -534,14 +526,15 @@ static int help(void) { "Send control commands to or query the virtual machine and container registration manager.\n\n" " -h --help Show this help\n" " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-ask-password Don't prompt for password\n" + " -H --host=[USER@]HOST Show information for remote host\n" + " -M --machine=CONTAINER Show information for local container\n" " -p --property=NAME Show only properties by this name\n" " -a --all Show all properties, including empty ones\n" - " --kill-who=WHO Who to send signal to\n" " -l --full Do not ellipsize output\n" - " -s --signal=SIGNAL Which signal to send\n" - " --no-ask-password Don't prompt for password\n" - " -H --host=[USER@]HOST Show information for remote host\n" - " --no-pager Do not pipe output into a pager\n\n" + " --kill-who=WHO Who to send signal to\n" + " -s --signal=SIGNAL Which signal to send\n\n" "Commands:\n" " list List running VMs and containers\n" " status [NAME...] Show VM/container status\n" @@ -572,16 +565,17 @@ static int parse_argv(int argc, char *argv[]) { { "kill-who", required_argument, NULL, ARG_KILL_WHO }, { "signal", required_argument, NULL, 's' }, { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD }, { NULL, 0, NULL, 0 } }; - int c; + int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) { switch (c) { @@ -594,22 +588,16 @@ static int parse_argv(int argc, char *argv[]) { puts(SYSTEMD_FEATURES); return 0; - case 'p': { - char **l; - - l = strv_append(arg_property, optarg); - if (!l) - return -ENOMEM; - - strv_free(arg_property); - arg_property = l; + case 'p': + r = strv_extend(&arg_property, optarg); + if (r < 0) + return log_oom(); /* If the user asked for a particular * property, show it to him, even if it is * empty. */ arg_all = true; break; - } case 'a': arg_all = true; @@ -640,8 +628,13 @@ static int parse_argv(int argc, char *argv[]) { break; case 'H': - arg_transport = TRANSPORT_SSH; - parse_user_at_host(optarg, &arg_user, &arg_host); + arg_transport = TRANSPORT_REMOTE; + arg_host = optarg; + break; + + case 'M': + arg_transport = TRANSPORT_CONTAINER; + arg_host = optarg; break; case '?': @@ -741,7 +734,7 @@ static int machinectl_main(sd_bus *bus, int argc, char *argv[], const int r) { } int main(int argc, char*argv[]) { - int r, retval = EXIT_FAILURE; + int r, ret = EXIT_FAILURE; _cleanup_bus_unref_ sd_bus *bus = NULL; setlocale(LC_ALL, ""); @@ -752,28 +745,31 @@ int main(int argc, char*argv[]) { if (r < 0) goto finish; else if (r == 0) { - retval = EXIT_SUCCESS; + ret = EXIT_SUCCESS; goto finish; } - if (arg_transport == TRANSPORT_NORMAL) + if (arg_transport == TRANSPORT_LOCAL) r = sd_bus_open_system(&bus); - else if (arg_transport == TRANSPORT_SSH) + else if (arg_transport == TRANSPORT_REMOTE) r = sd_bus_open_system_remote(arg_host, &bus); + else if (arg_transport == TRANSPORT_CONTAINER) + r = sd_bus_open_system_container(arg_host, &bus); else assert_not_reached("Uh, invalid transport..."); if (r < 0) { - retval = EXIT_FAILURE; + log_error("Failed to connect to machined: %s", strerror(-r)); + ret = EXIT_FAILURE; goto finish; } r = machinectl_main(bus, argc, argv, r); - retval = r < 0 ? EXIT_FAILURE : r; + ret = r < 0 ? EXIT_FAILURE : r; finish: strv_free(arg_property); pager_close(); - return retval; + return ret; } diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 7b6c86dd3..7163b0c40 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -61,6 +61,7 @@ typedef int (*sd_bus_node_enumerator_t) (sd_bus *bus, const char *path, char *** int sd_bus_open_user(sd_bus **ret); int sd_bus_open_system(sd_bus **ret); int sd_bus_open_system_remote(const char *host, sd_bus **ret); +int sd_bus_open_system_container(const char *machine, sd_bus **ret); int sd_bus_new(sd_bus **ret); int sd_bus_set_address(sd_bus *bus, const char *address);