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!
This commit is contained in:
Lennart Poettering 2013-10-30 15:34:50 +01:00
parent 0f8bd8debb
commit a7893c6b28
10 changed files with 349 additions and 97 deletions

View File

@ -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 \

View File

@ -86,6 +86,42 @@
string and exits.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--no-pager</option></term>
<listitem><para>Do not pipe output into a
pager.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--no-ask-password</option></term>
<listitem><para>Do not query the user
for authentication for privileged
operations.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-H</option></term>
<term><option>--host=</option></term>
<listitem><para>Execute operation
remotely. Specify a hostname, or
username and hostname separated by <literal>@</literal>,
to connect to. This will use SSH to
talk to the remote machine manager
instance.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-M</option></term>
<term><option>--machine=</option></term>
<listitem><para>Execute operation on a
local container. Specify a container
name to connect to.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-p</option></term>
<term><option>--property=</option></term>
@ -122,21 +158,6 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-pager</option></term>
<listitem><para>Do not pipe output into a
pager.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--no-ask-password</option></term>
<listitem><para>Do not query the user
for authentication for privileged
operations.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--kill-who=</option></term>
@ -167,26 +188,6 @@
<constant>SIGTERM</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-H</option></term>
<term><option>--host</option></term>
<listitem><para>Execute operation
remotely. Specify a hostname, or
username and hostname separated by <literal>@</literal>,
to connect to. This will use SSH to
talk to the remote machine manager
instance.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-P</option></term>
<term><option>--privileged</option></term>
<listitem><para>Acquire privileges via
PolicyKit before executing the
operation.</para></listitem>
</varlistentry>
</variablelist>
<para>The following commands are understood:</para>
@ -293,8 +294,8 @@
<title>See Also</title>
<para>
<citerefentry><refentrytitle>systemd-machined.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-logind.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
<citerefentry><refentrytitle>systemd-nspawn</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry>
</para>
</refsect1>

View File

@ -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 <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include <fcntl.h>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
***/
#include "sd-bus.h"
int bus_container_connect(sd_bus *b);

View File

@ -196,6 +196,7 @@ struct sd_bus {
socklen_t sockaddr_size;
char *kernel;
char *machine;
sd_id128_t server_id;

View File

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

View File

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

View File

@ -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,15 +900,7 @@ static int bus_start_address(sd_bus *b) {
for (;;) {
sd_bus_close(b);
if (b->sockaddr.sa.sa_family != AF_UNSPEC) {
r = bus_socket_connect(b);
if (r >= 0)
return r;
b->last_connect_error = -r;
} else if (b->exec_path) {
if (b->exec_path) {
r = bus_socket_exec(b);
if (r >= 0)
@ -871,6 +914,22 @@ static int bus_start_address(sd_bus *b) {
return r;
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->sockaddr.sa.sa_family != AF_UNSPEC) {
r = bus_socket_connect(b);
if (r >= 0)
return r;
b->last_connect_error = -r;
}
r = bus_parse_next_address(b);
@ -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;

View File

@ -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"
" -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-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"
" --no-pager Do not pipe output into a pager\n\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"
" -l --full Do not ellipsize output\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;
}

View File

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