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"
31 #include "rtnl-util.h"
32 #include "bus-errors.h"
37 static int property_get_id(
40 const char *interface,
42 sd_bus_message *reply,
44 sd_bus_error *error) {
46 Machine *m = userdata;
53 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
60 static int property_get_state(
63 const char *interface,
65 sd_bus_message *reply,
67 sd_bus_error *error) {
69 Machine *m = userdata;
77 state = machine_state_to_string(machine_get_state(m));
79 r = sd_bus_message_append_basic(reply, 's', state);
86 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
88 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
89 Machine *m = userdata;
100 return sd_bus_reply_method_return(message, NULL);
103 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
104 Machine *m = userdata;
114 r = sd_bus_message_read(message, "si", &swho, &signo);
121 who = kill_who_from_string(swho);
123 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
126 if (signo <= 0 || signo >= _NSIG)
127 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
129 r = machine_kill(m, who, signo);
133 return sd_bus_reply_method_return(message, NULL);
136 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
137 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
138 _cleanup_close_pair_ int pair[2] = { -1, -1 };
139 _cleanup_free_ char *us = NULL, *them = NULL;
140 _cleanup_close_ int netns_fd = -1;
141 Machine *m = userdata;
151 r = readlink_malloc("/proc/self/ns/net", &us);
153 return sd_bus_error_set_errno(error, r);
155 p = procfs_file_alloca(m->leader, "ns/net");
156 r = readlink_malloc(p, &them);
158 return sd_bus_error_set_errno(error, r);
161 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
163 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
165 return sd_bus_error_set_errno(error, r);
167 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
168 return sd_bus_error_set_errno(error, -errno);
172 return sd_bus_error_set_errno(error, -errno);
175 _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *resp = NULL;
176 _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
177 sd_rtnl_message *addr;
179 pair[0] = safe_close(pair[0]);
181 r = namespace_enter(-1, -1, netns_fd, -1);
185 r = sd_rtnl_open(&rtnl, 0);
189 r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC);
193 r = sd_rtnl_message_request_dump(req, true);
197 r = sd_rtnl_call(rtnl, req, 0, &resp);
201 for (addr = resp; addr; addr = sd_rtnl_message_next(addr)) {
203 unsigned char family;
210 r = sd_rtnl_message_get_type(addr, &type);
214 if (type != RTM_NEWADDR)
217 r = sd_rtnl_message_addr_get_family(addr, &family);
221 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
227 r = sd_rtnl_message_read_in_addr(addr, IFA_LOCAL, &in_addr.in);
231 if (in_addr.in.s_addr == htobe32(INADDR_LOOPBACK))
234 iov[1] = (struct iovec) { .iov_base = &in_addr.in, .iov_len = sizeof(in_addr.in) };
239 r = sd_rtnl_message_read_in6_addr(addr, IFA_ADDRESS, &in_addr.in6);
243 if (IN6_IS_ADDR_LOOPBACK(&in_addr.in6))
246 iov[1] = (struct iovec) { .iov_base = &in_addr.in6, .iov_len = sizeof(in_addr.in6) };
253 r = writev(pair[1], iov, 2);
261 pair[1] = safe_close(pair[1]);
263 r = sd_bus_message_new_method_return(message, &reply);
265 return sd_bus_error_set_errno(error, r);
267 r = sd_bus_message_open_container(reply, 'a', "(yay)");
269 return sd_bus_error_set_errno(error, r);
272 unsigned char family;
284 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
285 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
287 n = recvmsg(pair[0], &mh, 0);
289 return sd_bus_error_set_errno(error, -errno);
290 if ((size_t) n < sizeof(family))
293 r = sd_bus_message_open_container(reply, 'r', "yay");
295 return sd_bus_error_set_errno(error, r);
297 r = sd_bus_message_append(reply, "y", family);
299 return sd_bus_error_set_errno(error, r);
304 if (n != sizeof(struct in_addr) + sizeof(family))
305 return sd_bus_error_set_errno(error, EIO);
307 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
311 if (n != sizeof(struct in6_addr) + sizeof(family))
312 return sd_bus_error_set_errno(error, EIO);
314 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
318 return sd_bus_error_set_errno(error, r);
320 r = sd_bus_message_close_container(reply);
322 return sd_bus_error_set_errno(error, r);
325 r = wait_for_terminate(child, &si);
327 return sd_bus_error_set_errno(error, r);
328 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
329 return sd_bus_error_set_errno(error, EIO);
331 r = sd_bus_message_close_container(reply);
333 return sd_bus_error_set_errno(error, r);
335 return sd_bus_send(bus, reply, NULL);
338 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
339 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
340 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
341 _cleanup_close_pair_ int pair[2] = { -1, -1 };
342 _cleanup_strv_free_ char **l = NULL;
343 _cleanup_fclose_ FILE *f = NULL;
344 Machine *m = userdata;
354 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
356 return sd_bus_error_set_errno(error, r);
358 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
359 return sd_bus_error_set_errno(error, -errno);
363 return sd_bus_error_set_errno(error, -errno);
366 _cleanup_close_ int fd = -1;
368 pair[0] = safe_close(pair[0]);
370 r = namespace_enter(-1, mntns_fd, -1, root_fd);
374 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
376 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
381 r = copy_bytes(fd, pair[1], (off_t) -1);
388 pair[1] = safe_close(pair[1]);
390 f = fdopen(pair[0], "re");
392 return sd_bus_error_set_errno(error, -errno);
396 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
398 return sd_bus_error_set_errno(error, r);
400 r = wait_for_terminate(child, &si);
402 return sd_bus_error_set_errno(error, r);
403 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
404 return sd_bus_error_set_errno(error, EIO);
406 r = sd_bus_message_new_method_return(message, &reply);
408 return sd_bus_error_set_errno(error, r);
410 r = sd_bus_message_open_container(reply, 'a', "{ss}");
412 return sd_bus_error_set_errno(error, r);
414 STRV_FOREACH_PAIR(k, v, l) {
415 r = sd_bus_message_append(reply, "{ss}", *k, *v);
417 return sd_bus_error_set_errno(error, r);
420 r = sd_bus_message_close_container(reply);
422 return sd_bus_error_set_errno(error, r);
424 return sd_bus_send(bus, reply, NULL);
427 const sd_bus_vtable machine_vtable[] = {
428 SD_BUS_VTABLE_START(0),
429 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
430 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
431 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
432 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
433 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
434 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
435 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
436 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
437 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
438 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
439 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
440 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
441 SD_BUS_METHOD("GetAddresses", NULL, "a(yay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
442 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
446 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
447 Manager *m = userdata;
457 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
458 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
459 sd_bus_message *message;
462 message = sd_bus_get_current_message(bus);
466 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
470 r = sd_bus_creds_get_pid(creds, &pid);
474 r = manager_get_machine_by_pid(m, pid, &machine);
478 _cleanup_free_ char *e = NULL;
481 p = startswith(path, "/org/freedesktop/machine1/machine/");
485 e = bus_label_unescape(p);
489 machine = hashmap_get(m->machines, e);
498 char *machine_bus_path(Machine *m) {
499 _cleanup_free_ char *e = NULL;
503 e = bus_label_escape(m->name);
507 return strappend("/org/freedesktop/machine1/machine/", e);
510 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
511 _cleanup_strv_free_ char **l = NULL;
512 Machine *machine = NULL;
513 Manager *m = userdata;
521 HASHMAP_FOREACH(machine, m->machines, i) {
524 p = machine_bus_path(machine);
528 r = strv_consume(&l, p);
539 int machine_send_signal(Machine *m, bool new_machine) {
540 _cleanup_free_ char *p = NULL;
544 p = machine_bus_path(m);
548 return sd_bus_emit_signal(
550 "/org/freedesktop/machine1",
551 "org.freedesktop.machine1.Manager",
552 new_machine ? "MachineNew" : "MachineRemoved",
556 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
557 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
558 _cleanup_free_ char *p = NULL;
562 if (!m->create_message)
565 c = m->create_message;
566 m->create_message = NULL;
569 return sd_bus_reply_method_error(c, error);
571 /* Update the machine state file before we notify the client
572 * about the result. */
575 p = machine_bus_path(m);
579 return sd_bus_reply_method_return(c, "o", p);