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 r = readlink_malloc("/proc/self/ns/net", &us);
180 return sd_bus_error_set_errno(error, r);
182 p = procfs_file_alloca(m->leader, "ns/net");
183 r = readlink_malloc(p, &them);
185 return sd_bus_error_set_errno(error, r);
188 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
190 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
192 return sd_bus_error_set_errno(error, r);
194 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
195 return sd_bus_error_set_errno(error, -errno);
199 return sd_bus_error_set_errno(error, -errno);
202 _cleanup_free_ struct local_address *addresses = NULL;
203 struct local_address *a;
206 pair[0] = safe_close(pair[0]);
208 r = namespace_enter(-1, -1, netns_fd, -1);
212 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
216 for (a = addresses, i = 0; i < n; a++, i++) {
217 struct iovec iov[2] = {
218 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
219 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
222 r = writev(pair[1], iov, 2);
227 pair[1] = safe_close(pair[1]);
232 pair[1] = safe_close(pair[1]);
234 r = sd_bus_message_new_method_return(message, &reply);
236 return sd_bus_error_set_errno(error, r);
238 r = sd_bus_message_open_container(reply, 'a', "(iay)");
240 return sd_bus_error_set_errno(error, r);
245 union in_addr_union in_addr;
252 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
253 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
255 n = recvmsg(pair[0], &mh, 0);
257 return sd_bus_error_set_errno(error, -errno);
258 if ((size_t) n < sizeof(family))
261 r = sd_bus_message_open_container(reply, 'r', "iay");
263 return sd_bus_error_set_errno(error, r);
265 r = sd_bus_message_append(reply, "i", family);
267 return sd_bus_error_set_errno(error, r);
272 if (n != sizeof(struct in_addr) + sizeof(family))
273 return sd_bus_error_set_errno(error, EIO);
275 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
279 if (n != sizeof(struct in6_addr) + sizeof(family))
280 return sd_bus_error_set_errno(error, EIO);
282 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
286 return sd_bus_error_set_errno(error, r);
288 r = sd_bus_message_close_container(reply);
290 return sd_bus_error_set_errno(error, r);
293 r = wait_for_terminate(child, &si);
295 return sd_bus_error_set_errno(error, r);
296 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
297 return sd_bus_error_set_errno(error, EIO);
299 r = sd_bus_message_close_container(reply);
301 return sd_bus_error_set_errno(error, r);
303 return sd_bus_send(bus, reply, NULL);
306 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
307 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
308 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
309 _cleanup_close_pair_ int pair[2] = { -1, -1 };
310 _cleanup_strv_free_ char **l = NULL;
311 _cleanup_fclose_ FILE *f = NULL;
312 Machine *m = userdata;
322 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
326 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
334 _cleanup_close_ int fd = -1;
336 pair[0] = safe_close(pair[0]);
338 r = namespace_enter(-1, mntns_fd, -1, root_fd);
342 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
344 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
349 r = copy_bytes(fd, pair[1], (off_t) -1, false);
356 pair[1] = safe_close(pair[1]);
358 f = fdopen(pair[0], "re");
364 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
368 r = wait_for_terminate(child, &si);
371 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
374 r = sd_bus_message_new_method_return(message, &reply);
378 r = sd_bus_message_open_container(reply, 'a', "{ss}");
382 STRV_FOREACH_PAIR(k, v, l) {
383 r = sd_bus_message_append(reply, "{ss}", *k, *v);
388 r = sd_bus_message_close_container(reply);
392 return sd_bus_send(bus, reply, NULL);
395 int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
396 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
397 _cleanup_free_ char *pty_name = NULL;
398 _cleanup_close_ int master = -1;
399 Machine *m = userdata;
406 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
410 r = ptsname_malloc(master, &pty_name);
414 r = sd_bus_message_new_method_return(message, &reply);
418 r = sd_bus_message_append(reply, "hs", master, pty_name);
422 return sd_bus_send(bus, reply, NULL);
425 int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
426 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
427 _cleanup_free_ char *pty_name = NULL, *getty = NULL;
428 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
429 _cleanup_close_ int master = -1;
430 Machine *m = userdata;
434 r = bus_verify_polkit_async(
437 "org.freedesktop.machine1.login",
439 &m->manager->polkit_registry,
444 return 1; /* Will call us back */
446 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
450 r = ptsname_malloc(master, &pty_name);
454 p = path_startswith(pty_name, "/dev/pts/");
456 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
458 if (unlockpt(master) < 0)
461 r = sd_bus_new(&container_bus);
466 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT ";x-machine-unix:pid=" PID_FMT, m->leader, m->leader);
468 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT, m->leader);
470 if (!container_bus->address)
473 container_bus->bus_client = true;
474 container_bus->trusted = false;
475 container_bus->is_system = true;
477 r = sd_bus_start(container_bus);
481 getty = strjoin("container-getty@", p, ".service", NULL);
485 r = sd_bus_call_method(
487 "org.freedesktop.systemd1",
488 "/org/freedesktop/systemd1",
489 "org.freedesktop.systemd1.Manager",
492 "ss", getty, "replace");
496 container_bus = sd_bus_unref(container_bus);
498 r = sd_bus_message_new_method_return(message, &reply);
502 r = sd_bus_message_append(reply, "hs", master, pty_name);
506 return sd_bus_send(bus, reply, NULL);
509 const sd_bus_vtable machine_vtable[] = {
510 SD_BUS_VTABLE_START(0),
511 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
512 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
513 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
514 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
515 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
516 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
517 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
518 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
519 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
520 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
521 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
522 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
523 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
524 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
525 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
526 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
527 SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
531 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
532 Manager *m = userdata;
542 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
543 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
544 sd_bus_message *message;
547 message = sd_bus_get_current_message(bus);
551 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
555 r = sd_bus_creds_get_pid(creds, &pid);
559 r = manager_get_machine_by_pid(m, pid, &machine);
563 _cleanup_free_ char *e = NULL;
566 p = startswith(path, "/org/freedesktop/machine1/machine/");
570 e = bus_label_unescape(p);
574 machine = hashmap_get(m->machines, e);
583 char *machine_bus_path(Machine *m) {
584 _cleanup_free_ char *e = NULL;
588 e = bus_label_escape(m->name);
592 return strappend("/org/freedesktop/machine1/machine/", e);
595 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
596 _cleanup_strv_free_ char **l = NULL;
597 Machine *machine = NULL;
598 Manager *m = userdata;
606 HASHMAP_FOREACH(machine, m->machines, i) {
609 p = machine_bus_path(machine);
613 r = strv_consume(&l, p);
624 int machine_send_signal(Machine *m, bool new_machine) {
625 _cleanup_free_ char *p = NULL;
629 p = machine_bus_path(m);
633 return sd_bus_emit_signal(
635 "/org/freedesktop/machine1",
636 "org.freedesktop.machine1.Manager",
637 new_machine ? "MachineNew" : "MachineRemoved",
641 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
642 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
643 _cleanup_free_ char *p = NULL;
647 if (!m->create_message)
650 c = m->create_message;
651 m->create_message = NULL;
654 return sd_bus_reply_method_error(c, error);
656 /* Update the machine state file before we notify the client
657 * about the result. */
660 p = machine_bus_path(m);
664 return sd_bus_reply_method_return(c, "o", p);