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"
38 static int property_get_id(
41 const char *interface,
43 sd_bus_message *reply,
45 sd_bus_error *error) {
47 Machine *m = userdata;
54 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
61 static int property_get_state(
64 const char *interface,
66 sd_bus_message *reply,
68 sd_bus_error *error) {
70 Machine *m = userdata;
78 state = machine_state_to_string(machine_get_state(m));
80 r = sd_bus_message_append_basic(reply, 's', state);
87 static int property_get_netif(
90 const char *interface,
92 sd_bus_message *reply,
94 sd_bus_error *error) {
96 Machine *m = userdata;
103 assert_cc(sizeof(int) == sizeof(int32_t));
105 r = sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
112 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
114 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
115 Machine *m = userdata;
126 return sd_bus_reply_method_return(message, NULL);
129 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
130 Machine *m = userdata;
140 r = sd_bus_message_read(message, "si", &swho, &signo);
147 who = kill_who_from_string(swho);
149 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
152 if (signo <= 0 || signo >= _NSIG)
153 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
155 r = machine_kill(m, who, signo);
159 return sd_bus_reply_method_return(message, NULL);
162 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
163 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
164 _cleanup_close_pair_ int pair[2] = { -1, -1 };
165 _cleanup_free_ char *us = NULL, *them = NULL;
166 _cleanup_close_ int netns_fd = -1;
167 Machine *m = userdata;
177 r = readlink_malloc("/proc/self/ns/net", &us);
179 return sd_bus_error_set_errno(error, r);
181 p = procfs_file_alloca(m->leader, "ns/net");
182 r = readlink_malloc(p, &them);
184 return sd_bus_error_set_errno(error, r);
187 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
189 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
191 return sd_bus_error_set_errno(error, r);
193 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
194 return sd_bus_error_set_errno(error, -errno);
198 return sd_bus_error_set_errno(error, -errno);
201 _cleanup_free_ struct local_address *addresses = NULL;
202 struct local_address *a;
205 pair[0] = safe_close(pair[0]);
207 r = namespace_enter(-1, -1, netns_fd, -1);
211 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
215 for (a = addresses, i = 0; i < n; a++, i++) {
216 struct iovec iov[2] = {
217 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
218 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
221 r = writev(pair[1], iov, 2);
226 pair[1] = safe_close(pair[1]);
231 pair[1] = safe_close(pair[1]);
233 r = sd_bus_message_new_method_return(message, &reply);
235 return sd_bus_error_set_errno(error, r);
237 r = sd_bus_message_open_container(reply, 'a', "(iay)");
239 return sd_bus_error_set_errno(error, r);
244 union in_addr_union in_addr;
251 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
252 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
254 n = recvmsg(pair[0], &mh, 0);
256 return sd_bus_error_set_errno(error, -errno);
257 if ((size_t) n < sizeof(family))
260 r = sd_bus_message_open_container(reply, 'r', "iay");
262 return sd_bus_error_set_errno(error, r);
264 r = sd_bus_message_append(reply, "i", family);
266 return sd_bus_error_set_errno(error, r);
271 if (n != sizeof(struct in_addr) + sizeof(family))
272 return sd_bus_error_set_errno(error, EIO);
274 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
278 if (n != sizeof(struct in6_addr) + sizeof(family))
279 return sd_bus_error_set_errno(error, EIO);
281 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
285 return sd_bus_error_set_errno(error, r);
287 r = sd_bus_message_close_container(reply);
289 return sd_bus_error_set_errno(error, r);
292 r = wait_for_terminate(child, &si);
294 return sd_bus_error_set_errno(error, r);
295 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
296 return sd_bus_error_set_errno(error, EIO);
298 r = sd_bus_message_close_container(reply);
300 return sd_bus_error_set_errno(error, r);
302 return sd_bus_send(bus, reply, NULL);
305 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
306 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
307 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
308 _cleanup_close_pair_ int pair[2] = { -1, -1 };
309 _cleanup_strv_free_ char **l = NULL;
310 _cleanup_fclose_ FILE *f = NULL;
311 Machine *m = userdata;
321 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
325 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
333 _cleanup_close_ int fd = -1;
335 pair[0] = safe_close(pair[0]);
337 r = namespace_enter(-1, mntns_fd, -1, root_fd);
341 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
343 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
348 r = copy_bytes(fd, pair[1], (off_t) -1, false);
355 pair[1] = safe_close(pair[1]);
357 f = fdopen(pair[0], "re");
363 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
367 r = wait_for_terminate(child, &si);
370 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
373 r = sd_bus_message_new_method_return(message, &reply);
377 r = sd_bus_message_open_container(reply, 'a', "{ss}");
381 STRV_FOREACH_PAIR(k, v, l) {
382 r = sd_bus_message_append(reply, "{ss}", *k, *v);
387 r = sd_bus_message_close_container(reply);
391 return sd_bus_send(bus, reply, NULL);
394 int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
395 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
396 _cleanup_free_ char *pty_name = NULL;
397 _cleanup_close_ int master = -1;
398 Machine *m = userdata;
405 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
409 r = ptsname_malloc(master, &pty_name);
413 r = sd_bus_message_new_method_return(message, &reply);
417 r = sd_bus_message_append(reply, "hs", master, pty_name);
421 return sd_bus_send(bus, reply, NULL);
424 int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
425 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
426 _cleanup_free_ char *pty_name = NULL, *getty = NULL;
427 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
428 _cleanup_close_ int master = -1;
429 Machine *m = userdata;
433 r = bus_verify_polkit_async(
436 "org.freedesktop.machine1.login",
438 &m->manager->polkit_registry,
443 return 1; /* Will call us back */
445 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
449 r = ptsname_malloc(master, &pty_name);
453 p = path_startswith(pty_name, "/dev/pts/");
455 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
457 if (unlockpt(master) < 0)
460 r = sd_bus_new(&container_bus);
465 asprintf(&container_bus->address, "x-container-kernel:pid=" PID_FMT ";x-container-unix:pid=" PID_FMT, m->leader, m->leader);
467 asprintf(&container_bus->address, "x-container-kernel:pid=" PID_FMT, m->leader);
469 if (!container_bus->address)
472 container_bus->bus_client = true;
473 container_bus->trusted = false;
474 container_bus->is_system = true;
476 r = sd_bus_start(container_bus);
480 getty = strjoin("container-getty@", p, ".service", NULL);
484 r = sd_bus_call_method(
486 "org.freedesktop.systemd1",
487 "/org/freedesktop/systemd1",
488 "org.freedesktop.systemd1.Manager",
491 "ss", getty, "replace");
495 container_bus = sd_bus_unref(container_bus);
497 r = sd_bus_message_new_method_return(message, &reply);
501 r = sd_bus_message_append(reply, "hs", master, pty_name);
505 return sd_bus_send(bus, reply, NULL);
508 const sd_bus_vtable machine_vtable[] = {
509 SD_BUS_VTABLE_START(0),
510 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
511 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
512 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
513 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
514 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
515 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
516 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
517 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
518 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
519 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
520 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
521 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
522 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
523 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
524 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
525 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
526 SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
530 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
531 Manager *m = userdata;
541 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
542 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
543 sd_bus_message *message;
546 message = sd_bus_get_current_message(bus);
550 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
554 r = sd_bus_creds_get_pid(creds, &pid);
558 r = manager_get_machine_by_pid(m, pid, &machine);
562 _cleanup_free_ char *e = NULL;
565 p = startswith(path, "/org/freedesktop/machine1/machine/");
569 e = bus_label_unescape(p);
573 machine = hashmap_get(m->machines, e);
582 char *machine_bus_path(Machine *m) {
583 _cleanup_free_ char *e = NULL;
587 e = bus_label_escape(m->name);
591 return strappend("/org/freedesktop/machine1/machine/", e);
594 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
595 _cleanup_strv_free_ char **l = NULL;
596 Machine *machine = NULL;
597 Manager *m = userdata;
605 HASHMAP_FOREACH(machine, m->machines, i) {
608 p = machine_bus_path(machine);
612 r = strv_consume(&l, p);
623 int machine_send_signal(Machine *m, bool new_machine) {
624 _cleanup_free_ char *p = NULL;
628 p = machine_bus_path(m);
632 return sd_bus_emit_signal(
634 "/org/freedesktop/machine1",
635 "org.freedesktop.machine1.Manager",
636 new_machine ? "MachineNew" : "MachineRemoved",
640 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
641 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
642 _cleanup_free_ char *p = NULL;
646 if (!m->create_message)
649 c = m->create_message;
650 m->create_message = NULL;
653 return sd_bus_reply_method_error(c, error);
655 /* Update the machine state file before we notify the client
656 * about the result. */
659 p = machine_bus_path(m);
663 return sd_bus_reply_method_return(c, "o", p);