1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <arpa/inet.h>
27 #include "bus-label.h"
29 #include "bus-common-errors.h"
32 #include "in-addr-util.h"
33 #include "local-addresses.h"
34 #include "path-util.h"
35 #include "bus-internal.h"
37 #include "machine-dbus.h"
39 static int property_get_id(
42 const char *interface,
44 sd_bus_message *reply,
46 sd_bus_error *error) {
48 Machine *m = userdata;
55 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
62 static int property_get_state(
65 const char *interface,
67 sd_bus_message *reply,
69 sd_bus_error *error) {
71 Machine *m = userdata;
79 state = machine_state_to_string(machine_get_state(m));
81 r = sd_bus_message_append_basic(reply, 's', state);
88 static int property_get_netif(
91 const char *interface,
93 sd_bus_message *reply,
95 sd_bus_error *error) {
97 Machine *m = userdata;
104 assert_cc(sizeof(int) == sizeof(int32_t));
106 r = sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
113 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
115 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
116 Machine *m = userdata;
127 return sd_bus_reply_method_return(message, NULL);
130 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
131 Machine *m = userdata;
141 r = sd_bus_message_read(message, "si", &swho, &signo);
148 who = kill_who_from_string(swho);
150 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
153 if (signo <= 0 || signo >= _NSIG)
154 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
156 r = machine_kill(m, who, signo);
160 return sd_bus_reply_method_return(message, NULL);
163 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
164 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
165 _cleanup_close_pair_ int pair[2] = { -1, -1 };
166 _cleanup_free_ char *us = NULL, *them = NULL;
167 _cleanup_close_ int netns_fd = -1;
168 Machine *m = userdata;
178 if (m->class != MACHINE_CONTAINER)
179 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
181 r = readlink_malloc("/proc/self/ns/net", &us);
183 return sd_bus_error_set_errno(error, r);
185 p = procfs_file_alloca(m->leader, "ns/net");
186 r = readlink_malloc(p, &them);
188 return sd_bus_error_set_errno(error, r);
191 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
193 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
195 return sd_bus_error_set_errno(error, r);
197 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
198 return sd_bus_error_set_errno(error, -errno);
202 return sd_bus_error_set_errno(error, -errno);
205 _cleanup_free_ struct local_address *addresses = NULL;
206 struct local_address *a;
209 pair[0] = safe_close(pair[0]);
211 r = namespace_enter(-1, -1, netns_fd, -1);
215 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
219 for (a = addresses, i = 0; i < n; a++, i++) {
220 struct iovec iov[2] = {
221 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
222 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
225 r = writev(pair[1], iov, 2);
230 pair[1] = safe_close(pair[1]);
235 pair[1] = safe_close(pair[1]);
237 r = sd_bus_message_new_method_return(message, &reply);
239 return sd_bus_error_set_errno(error, r);
241 r = sd_bus_message_open_container(reply, 'a', "(iay)");
243 return sd_bus_error_set_errno(error, r);
248 union in_addr_union in_addr;
255 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
256 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
258 n = recvmsg(pair[0], &mh, 0);
260 return sd_bus_error_set_errno(error, -errno);
261 if ((size_t) n < sizeof(family))
264 r = sd_bus_message_open_container(reply, 'r', "iay");
266 return sd_bus_error_set_errno(error, r);
268 r = sd_bus_message_append(reply, "i", family);
270 return sd_bus_error_set_errno(error, r);
275 if (n != sizeof(struct in_addr) + sizeof(family))
276 return sd_bus_error_set_errno(error, EIO);
278 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
282 if (n != sizeof(struct in6_addr) + sizeof(family))
283 return sd_bus_error_set_errno(error, EIO);
285 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
289 return sd_bus_error_set_errno(error, r);
291 r = sd_bus_message_close_container(reply);
293 return sd_bus_error_set_errno(error, r);
296 r = wait_for_terminate(child, &si);
298 return sd_bus_error_set_errno(error, r);
299 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
300 return sd_bus_error_set_errno(error, EIO);
302 r = sd_bus_message_close_container(reply);
304 return sd_bus_error_set_errno(error, r);
306 return sd_bus_send(bus, reply, NULL);
309 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
310 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
311 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
312 _cleanup_close_pair_ int pair[2] = { -1, -1 };
313 _cleanup_strv_free_ char **l = NULL;
314 _cleanup_fclose_ FILE *f = NULL;
315 Machine *m = userdata;
325 if (m->class != MACHINE_CONTAINER)
326 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
328 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
332 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
340 _cleanup_close_ int fd = -1;
342 pair[0] = safe_close(pair[0]);
344 r = namespace_enter(-1, mntns_fd, -1, root_fd);
348 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
350 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
355 r = copy_bytes(fd, pair[1], (off_t) -1, false);
362 pair[1] = safe_close(pair[1]);
364 f = fdopen(pair[0], "re");
370 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
374 r = wait_for_terminate(child, &si);
377 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
380 r = sd_bus_message_new_method_return(message, &reply);
384 r = sd_bus_message_open_container(reply, 'a', "{ss}");
388 STRV_FOREACH_PAIR(k, v, l) {
389 r = sd_bus_message_append(reply, "{ss}", *k, *v);
394 r = sd_bus_message_close_container(reply);
398 return sd_bus_send(bus, reply, NULL);
401 int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
402 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
403 _cleanup_free_ char *pty_name = NULL;
404 _cleanup_close_ int master = -1;
405 Machine *m = userdata;
412 if (m->class != MACHINE_CONTAINER)
413 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines.");
415 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
419 r = ptsname_malloc(master, &pty_name);
423 r = sd_bus_message_new_method_return(message, &reply);
427 r = sd_bus_message_append(reply, "hs", master, pty_name);
431 return sd_bus_send(bus, reply, NULL);
434 int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
435 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
436 _cleanup_free_ char *pty_name = NULL, *getty = NULL;
437 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
438 _cleanup_close_ int master = -1;
439 Machine *m = userdata;
443 if (m->class != MACHINE_CONTAINER)
444 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines.");
446 r = bus_verify_polkit_async(
449 "org.freedesktop.machine1.login",
451 &m->manager->polkit_registry,
456 return 1; /* Will call us back */
458 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
462 r = ptsname_malloc(master, &pty_name);
466 p = path_startswith(pty_name, "/dev/pts/");
468 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
470 if (unlockpt(master) < 0)
473 r = sd_bus_new(&container_bus);
478 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT ";x-machine-unix:pid=" PID_FMT, m->leader, m->leader);
480 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT, m->leader);
482 if (!container_bus->address)
485 container_bus->bus_client = true;
486 container_bus->trusted = false;
487 container_bus->is_system = true;
489 r = sd_bus_start(container_bus);
493 getty = strjoin("container-getty@", p, ".service", NULL);
497 r = sd_bus_call_method(
499 "org.freedesktop.systemd1",
500 "/org/freedesktop/systemd1",
501 "org.freedesktop.systemd1.Manager",
504 "ss", getty, "replace");
508 container_bus = sd_bus_unref(container_bus);
510 r = sd_bus_message_new_method_return(message, &reply);
514 r = sd_bus_message_append(reply, "hs", master, pty_name);
518 return sd_bus_send(bus, reply, NULL);
521 const sd_bus_vtable machine_vtable[] = {
522 SD_BUS_VTABLE_START(0),
523 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
524 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
525 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
526 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
527 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
528 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
529 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
530 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
531 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
532 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
533 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
534 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
535 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
536 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
537 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
538 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
539 SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
543 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
544 Manager *m = userdata;
554 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
555 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
556 sd_bus_message *message;
559 message = sd_bus_get_current_message(bus);
563 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
567 r = sd_bus_creds_get_pid(creds, &pid);
571 r = manager_get_machine_by_pid(m, pid, &machine);
575 _cleanup_free_ char *e = NULL;
578 p = startswith(path, "/org/freedesktop/machine1/machine/");
582 e = bus_label_unescape(p);
586 machine = hashmap_get(m->machines, e);
595 char *machine_bus_path(Machine *m) {
596 _cleanup_free_ char *e = NULL;
600 e = bus_label_escape(m->name);
604 return strappend("/org/freedesktop/machine1/machine/", e);
607 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
608 _cleanup_strv_free_ char **l = NULL;
609 Machine *machine = NULL;
610 Manager *m = userdata;
618 HASHMAP_FOREACH(machine, m->machines, i) {
621 p = machine_bus_path(machine);
625 r = strv_consume(&l, p);
636 int machine_send_signal(Machine *m, bool new_machine) {
637 _cleanup_free_ char *p = NULL;
641 p = machine_bus_path(m);
645 return sd_bus_emit_signal(
647 "/org/freedesktop/machine1",
648 "org.freedesktop.machine1.Manager",
649 new_machine ? "MachineNew" : "MachineRemoved",
653 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
654 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
655 _cleanup_free_ char *p = NULL;
659 if (!m->create_message)
662 c = m->create_message;
663 m->create_message = NULL;
666 return sd_bus_reply_method_error(c, error);
668 /* Update the machine state file before we notify the client
669 * about the result. */
672 p = machine_bus_path(m);
676 return sd_bus_reply_method_return(c, "o", p);