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 <sys/capability.h>
25 #include <arpa/inet.h>
28 #include "bus-label.h"
30 #include "bus-common-errors.h"
33 #include "in-addr-util.h"
34 #include "local-addresses.h"
35 #include "path-util.h"
36 #include "bus-internal.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 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
438 r = ptsname_malloc(master, &pty_name);
442 p = path_startswith(pty_name, "/dev/pts/");
444 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
446 if (unlockpt(master) < 0)
449 r = sd_bus_new(&container_bus);
454 asprintf(&container_bus->address, "x-container-kernel:pid=" PID_FMT ";x-container-unix:pid=" PID_FMT, m->leader, m->leader);
456 asprintf(&container_bus->address, "x-container-kernel:pid=" PID_FMT, m->leader);
458 if (!container_bus->address)
461 container_bus->bus_client = true;
462 container_bus->trusted = false;
463 container_bus->is_system = true;
465 r = sd_bus_start(container_bus);
469 getty = strjoin("container-getty@", p, ".service", NULL);
473 r = sd_bus_call_method(
475 "org.freedesktop.systemd1",
476 "/org/freedesktop/systemd1",
477 "org.freedesktop.systemd1.Manager",
480 "ss", getty, "replace");
484 container_bus = sd_bus_unref(container_bus);
486 r = sd_bus_message_new_method_return(message, &reply);
490 r = sd_bus_message_append(reply, "hs", master, pty_name);
494 return sd_bus_send(bus, reply, NULL);
497 const sd_bus_vtable machine_vtable[] = {
498 SD_BUS_VTABLE_START(0),
499 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
500 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
501 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
502 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
503 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
504 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
505 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
506 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
507 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
508 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
509 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
510 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
511 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
512 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
513 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
514 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
518 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
519 Manager *m = userdata;
529 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
530 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
531 sd_bus_message *message;
534 message = sd_bus_get_current_message(bus);
538 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
542 r = sd_bus_creds_get_pid(creds, &pid);
546 r = manager_get_machine_by_pid(m, pid, &machine);
550 _cleanup_free_ char *e = NULL;
553 p = startswith(path, "/org/freedesktop/machine1/machine/");
557 e = bus_label_unescape(p);
561 machine = hashmap_get(m->machines, e);
570 char *machine_bus_path(Machine *m) {
571 _cleanup_free_ char *e = NULL;
575 e = bus_label_escape(m->name);
579 return strappend("/org/freedesktop/machine1/machine/", e);
582 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
583 _cleanup_strv_free_ char **l = NULL;
584 Machine *machine = NULL;
585 Manager *m = userdata;
593 HASHMAP_FOREACH(machine, m->machines, i) {
596 p = machine_bus_path(machine);
600 r = strv_consume(&l, p);
611 int machine_send_signal(Machine *m, bool new_machine) {
612 _cleanup_free_ char *p = NULL;
616 p = machine_bus_path(m);
620 return sd_bus_emit_signal(
622 "/org/freedesktop/machine1",
623 "org.freedesktop.machine1.Manager",
624 new_machine ? "MachineNew" : "MachineRemoved",
628 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
629 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
630 _cleanup_free_ char *p = NULL;
634 if (!m->create_message)
637 c = m->create_message;
638 m->create_message = NULL;
641 return sd_bus_reply_method_error(c, error);
643 /* Update the machine state file before we notify the client
644 * about the result. */
647 p = machine_bus_path(m);
651 return sd_bus_reply_method_return(c, "o", p);