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>
29 #include "bus-label.h"
32 #include "rtnl-util.h"
33 #include "bus-errors.h"
35 static int property_get_id(
38 const char *interface,
40 sd_bus_message *reply,
42 sd_bus_error *error) {
44 Machine *m = userdata;
51 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
58 static int property_get_state(
61 const char *interface,
63 sd_bus_message *reply,
65 sd_bus_error *error) {
67 Machine *m = userdata;
75 state = machine_state_to_string(machine_get_state(m));
77 r = sd_bus_message_append_basic(reply, 's', state);
84 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
86 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
87 Machine *m = userdata;
98 return sd_bus_reply_method_return(message, NULL);
101 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
102 Machine *m = userdata;
112 r = sd_bus_message_read(message, "si", &swho, &signo);
119 who = kill_who_from_string(swho);
121 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
124 if (signo <= 0 || signo >= _NSIG)
125 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
127 r = machine_kill(m, who, signo);
131 return sd_bus_reply_method_return(message, NULL);
134 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
135 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
136 _cleanup_close_pair_ int pair[2] = { -1, -1 };
137 _cleanup_free_ char *us = NULL, *them = NULL;
138 _cleanup_close_ int netns_fd = -1;
139 Machine *m = userdata;
149 r = readlink_malloc("/proc/self/ns/net", &us);
151 return sd_bus_error_set_errno(error, r);
153 p = procfs_file_alloca(m->leader, "ns/net");
154 r = readlink_malloc(p, &them);
156 return sd_bus_error_set_errno(error, r);
159 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
161 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
163 return sd_bus_error_set_errno(error, r);
165 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
166 return sd_bus_error_set_errno(error, -errno);
170 return sd_bus_error_set_errno(error, -errno);
173 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *resp = NULL;
174 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
175 sd_rtnl_message *addr;
177 pair[0] = safe_close(pair[0]);
179 r = namespace_enter(-1, -1, netns_fd, -1);
183 r = sd_rtnl_open(&rtnl, 0);
187 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
191 r = sd_rtnl_message_request_dump(req, true);
195 r = sd_rtnl_call(rtnl, req, 0, &resp);
199 for (addr = resp; addr; addr = sd_rtnl_message_next(addr)) {
201 unsigned char family;
208 r = sd_rtnl_message_get_type(addr, &type);
212 if (type != RTM_NEWADDR)
215 r = sd_rtnl_message_addr_get_family(addr, &family);
219 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
225 r = sd_rtnl_message_read_in_addr(addr, IFA_LOCAL, &in_addr.in);
229 if (in_addr.in.s_addr == htobe32(INADDR_LOOPBACK))
232 iov[1] = (struct iovec) { .iov_base = &in_addr.in, .iov_len = sizeof(in_addr.in) };
237 r = sd_rtnl_message_read_in6_addr(addr, IFA_ADDRESS, &in_addr.in6);
241 if (IN6_IS_ADDR_LOOPBACK(&in_addr.in6))
244 iov[1] = (struct iovec) { .iov_base = &in_addr.in6, .iov_len = sizeof(in_addr.in6) };
251 r = writev(pair[1], iov, 2);
259 pair[1] = safe_close(pair[1]);
261 r = sd_bus_message_new_method_return(message, &reply);
263 return sd_bus_error_set_errno(error, r);
265 r = sd_bus_message_open_container(reply, 'a', "(yay)");
267 return sd_bus_error_set_errno(error, r);
270 unsigned char family;
282 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
283 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
285 n = recvmsg(pair[0], &mh, 0);
287 return sd_bus_error_set_errno(error, -errno);
288 if ((size_t) n < sizeof(family))
291 r = sd_bus_message_open_container(reply, 'r', "yay");
293 return sd_bus_error_set_errno(error, r);
295 r = sd_bus_message_append(reply, "y", family);
297 return sd_bus_error_set_errno(error, r);
302 if (n != sizeof(struct in_addr) + sizeof(family))
303 return sd_bus_error_set_errno(error, EIO);
305 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
309 if (n != sizeof(struct in6_addr) + sizeof(family))
310 return sd_bus_error_set_errno(error, EIO);
312 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
316 return sd_bus_error_set_errno(error, r);
318 r = sd_bus_message_close_container(reply);
320 return sd_bus_error_set_errno(error, r);
323 r = wait_for_terminate(child, &si);
325 return sd_bus_error_set_errno(error, r);
326 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
327 return sd_bus_error_set_errno(error, EIO);
329 r = sd_bus_message_close_container(reply);
331 return sd_bus_error_set_errno(error, r);
333 return sd_bus_send(bus, reply, NULL);
336 const sd_bus_vtable machine_vtable[] = {
337 SD_BUS_VTABLE_START(0),
338 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
339 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
340 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
341 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
342 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
343 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
344 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
345 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
346 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
347 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
348 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
349 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
350 SD_BUS_METHOD("GetAddresses", NULL, "a(yay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
354 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
355 Manager *m = userdata;
365 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
366 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
367 sd_bus_message *message;
370 message = sd_bus_get_current_message(bus);
374 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
378 r = sd_bus_creds_get_pid(creds, &pid);
382 r = manager_get_machine_by_pid(m, pid, &machine);
386 _cleanup_free_ char *e = NULL;
389 p = startswith(path, "/org/freedesktop/machine1/machine/");
393 e = bus_label_unescape(p);
397 machine = hashmap_get(m->machines, e);
406 char *machine_bus_path(Machine *m) {
407 _cleanup_free_ char *e = NULL;
411 e = bus_label_escape(m->name);
415 return strappend("/org/freedesktop/machine1/machine/", e);
418 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
419 _cleanup_strv_free_ char **l = NULL;
420 Machine *machine = NULL;
421 Manager *m = userdata;
429 HASHMAP_FOREACH(machine, m->machines, i) {
432 p = machine_bus_path(machine);
436 r = strv_consume(&l, p);
447 int machine_send_signal(Machine *m, bool new_machine) {
448 _cleanup_free_ char *p = NULL;
452 p = machine_bus_path(m);
456 return sd_bus_emit_signal(
458 "/org/freedesktop/machine1",
459 "org.freedesktop.machine1.Manager",
460 new_machine ? "MachineNew" : "MachineRemoved",
464 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
465 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
466 _cleanup_free_ char *p = NULL;
470 if (!m->create_message)
473 c = m->create_message;
474 m->create_message = NULL;
477 return sd_bus_reply_method_error(c, error);
479 /* Update the machine state file before we notify the client
480 * about the result. */
483 p = machine_bus_path(m);
487 return sd_bus_reply_method_return(c, "o", p);