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>
25 #include <sys/mount.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"
37 #include "bus-internal.h"
39 #include "machine-dbus.h"
41 static int property_get_id(
44 const char *interface,
46 sd_bus_message *reply,
48 sd_bus_error *error) {
50 Machine *m = userdata;
57 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
64 static int property_get_state(
67 const char *interface,
69 sd_bus_message *reply,
71 sd_bus_error *error) {
73 Machine *m = userdata;
81 state = machine_state_to_string(machine_get_state(m));
83 r = sd_bus_message_append_basic(reply, 's', state);
90 static int property_get_netif(
93 const char *interface,
95 sd_bus_message *reply,
97 sd_bus_error *error) {
99 Machine *m = userdata;
106 assert_cc(sizeof(int) == sizeof(int32_t));
108 r = sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
115 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
117 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
118 Machine *m = userdata;
129 return sd_bus_reply_method_return(message, NULL);
132 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
133 Machine *m = userdata;
143 r = sd_bus_message_read(message, "si", &swho, &signo);
150 who = kill_who_from_string(swho);
152 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
155 if (signo <= 0 || signo >= _NSIG)
156 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
158 r = machine_kill(m, who, signo);
162 return sd_bus_reply_method_return(message, NULL);
165 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
166 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
167 _cleanup_close_pair_ int pair[2] = { -1, -1 };
168 _cleanup_free_ char *us = NULL, *them = NULL;
169 _cleanup_close_ int netns_fd = -1;
170 Machine *m = userdata;
180 if (m->class != MACHINE_CONTAINER)
181 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
183 r = readlink_malloc("/proc/self/ns/net", &us);
185 return sd_bus_error_set_errno(error, r);
187 p = procfs_file_alloca(m->leader, "ns/net");
188 r = readlink_malloc(p, &them);
190 return sd_bus_error_set_errno(error, r);
193 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
195 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
197 return sd_bus_error_set_errno(error, r);
199 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
200 return sd_bus_error_set_errno(error, -errno);
204 return sd_bus_error_set_errno(error, -errno);
207 _cleanup_free_ struct local_address *addresses = NULL;
208 struct local_address *a;
211 pair[0] = safe_close(pair[0]);
213 r = namespace_enter(-1, -1, netns_fd, -1);
217 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
221 for (a = addresses, i = 0; i < n; a++, i++) {
222 struct iovec iov[2] = {
223 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
224 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
227 r = writev(pair[1], iov, 2);
232 pair[1] = safe_close(pair[1]);
237 pair[1] = safe_close(pair[1]);
239 r = sd_bus_message_new_method_return(message, &reply);
241 return sd_bus_error_set_errno(error, r);
243 r = sd_bus_message_open_container(reply, 'a', "(iay)");
245 return sd_bus_error_set_errno(error, r);
250 union in_addr_union in_addr;
257 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
258 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
260 n = recvmsg(pair[0], &mh, 0);
262 return sd_bus_error_set_errno(error, -errno);
263 if ((size_t) n < sizeof(family))
266 r = sd_bus_message_open_container(reply, 'r', "iay");
268 return sd_bus_error_set_errno(error, r);
270 r = sd_bus_message_append(reply, "i", family);
272 return sd_bus_error_set_errno(error, r);
277 if (n != sizeof(struct in_addr) + sizeof(family))
278 return sd_bus_error_set_errno(error, EIO);
280 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
284 if (n != sizeof(struct in6_addr) + sizeof(family))
285 return sd_bus_error_set_errno(error, EIO);
287 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
291 return sd_bus_error_set_errno(error, r);
293 r = sd_bus_message_close_container(reply);
295 return sd_bus_error_set_errno(error, r);
298 r = wait_for_terminate(child, &si);
300 return sd_bus_error_set_errno(error, r);
301 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
302 return sd_bus_error_set_errno(error, EIO);
304 r = sd_bus_message_close_container(reply);
306 return sd_bus_error_set_errno(error, r);
308 return sd_bus_send(bus, reply, NULL);
311 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
312 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
313 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
314 _cleanup_close_pair_ int pair[2] = { -1, -1 };
315 _cleanup_strv_free_ char **l = NULL;
316 _cleanup_fclose_ FILE *f = NULL;
317 Machine *m = userdata;
327 if (m->class != MACHINE_CONTAINER)
328 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
330 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
334 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
342 _cleanup_close_ int fd = -1;
344 pair[0] = safe_close(pair[0]);
346 r = namespace_enter(-1, mntns_fd, -1, root_fd);
350 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
352 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
357 r = copy_bytes(fd, pair[1], (off_t) -1, false);
364 pair[1] = safe_close(pair[1]);
366 f = fdopen(pair[0], "re");
372 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
376 r = wait_for_terminate(child, &si);
379 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
382 r = sd_bus_message_new_method_return(message, &reply);
386 r = sd_bus_message_open_container(reply, 'a', "{ss}");
390 STRV_FOREACH_PAIR(k, v, l) {
391 r = sd_bus_message_append(reply, "{ss}", *k, *v);
396 r = sd_bus_message_close_container(reply);
400 return sd_bus_send(bus, reply, NULL);
403 int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
404 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
405 _cleanup_free_ char *pty_name = NULL;
406 _cleanup_close_ int master = -1;
407 Machine *m = userdata;
414 if (m->class != MACHINE_CONTAINER)
415 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines.");
417 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
421 r = ptsname_malloc(master, &pty_name);
425 r = sd_bus_message_new_method_return(message, &reply);
429 r = sd_bus_message_append(reply, "hs", master, pty_name);
433 return sd_bus_send(bus, reply, NULL);
436 int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
437 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
438 _cleanup_free_ char *pty_name = NULL, *getty = NULL;
439 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
440 _cleanup_close_ int master = -1;
441 Machine *m = userdata;
445 if (m->class != MACHINE_CONTAINER)
446 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines.");
448 r = bus_verify_polkit_async(
451 "org.freedesktop.machine1.login",
453 &m->manager->polkit_registry,
458 return 1; /* Will call us back */
460 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
464 r = ptsname_malloc(master, &pty_name);
468 p = path_startswith(pty_name, "/dev/pts/");
470 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
472 if (unlockpt(master) < 0)
475 r = sd_bus_new(&container_bus);
480 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT ";x-machine-unix:pid=" PID_FMT, m->leader, m->leader);
482 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT, m->leader);
484 if (!container_bus->address)
487 container_bus->bus_client = true;
488 container_bus->trusted = false;
489 container_bus->is_system = true;
491 r = sd_bus_start(container_bus);
495 getty = strjoin("container-getty@", p, ".service", NULL);
499 r = sd_bus_call_method(
501 "org.freedesktop.systemd1",
502 "/org/freedesktop/systemd1",
503 "org.freedesktop.systemd1.Manager",
506 "ss", getty, "replace");
510 container_bus = sd_bus_unref(container_bus);
512 r = sd_bus_message_new_method_return(message, &reply);
516 r = sd_bus_message_append(reply, "hs", master, pty_name);
520 return sd_bus_send(bus, reply, NULL);
523 int bus_machine_method_bind_mount(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
524 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
525 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
526 bool mount_slave_created = false, mount_slave_mounted = false,
527 mount_tmp_created = false, mount_tmp_mounted = false,
528 mount_outside_created = false, mount_outside_mounted = false;
529 const char *dest, *src;
530 Machine *m = userdata;
531 int read_only, make_directory;
536 if (m->class != MACHINE_CONTAINER)
537 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
539 r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_directory);
543 if (!path_is_absolute(src) || !path_is_safe(src))
544 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
548 else if (!path_is_absolute(dest) || !path_is_safe(dest))
549 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
551 /* One day, when bind mounting /proc/self/fd/n works across
552 * namespace boundaries we should rework this logic to make
555 p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/");
556 if (laccess(p, F_OK) < 0)
557 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points.");
559 /* Our goal is to install a new bind mount into the container,
560 possibly read-only. This is irritatingly complex
561 unfortunately, currently.
563 First, we start by creating a private playground in /tmp,
564 that we can mount MS_SLAVE. (Which is necessary, since
565 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
568 if (!mkdtemp(mount_slave))
569 return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave);
571 mount_slave_created = true;
573 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
574 r = sd_bus_error_set_errnof(error, errno, "Failed to make bind mount %s: %m", mount_slave);
578 mount_slave_mounted = true;
580 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
581 r = sd_bus_error_set_errnof(error, errno, "Failed to remount slave %s: %m", mount_slave);
585 /* Second, we mount the source directory to a directory inside
586 of our MS_SLAVE playground. */
587 mount_tmp = strjoina(mount_slave, "/mount");
588 if (mkdir(mount_tmp, 0700) < 0) {
589 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp);
593 mount_tmp_created = true;
595 if (mount(src, mount_tmp, NULL, MS_BIND, NULL) < 0) {
596 r = sd_bus_error_set_errnof(error, errno, "Failed to overmount %s: %m", mount_tmp);
600 mount_tmp_mounted = true;
602 /* Third, we remount the new bind mount read-only if requested. */
604 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
605 r = sd_bus_error_set_errnof(error, errno, "Failed to remount read-only %s: %m", mount_tmp);
609 /* Fourth, we move the new bind mount into the propagation
610 * directory. This way it will appear there read-only
613 mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX");
614 if (!mkdtemp(mount_outside)) {
615 r = sd_bus_error_set_errnof(error, errno, "Cannot create propagation directory %s: %m", mount_outside);
619 mount_outside_created = true;
621 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
622 r = sd_bus_error_set_errnof(error, errno, "Failed to move %s to %s: %m", mount_tmp, mount_outside);
626 mount_outside_mounted = true;
627 mount_tmp_mounted = false;
629 (void) rmdir(mount_tmp);
630 mount_tmp_created = false;
632 (void) umount(mount_slave);
633 mount_slave_mounted = false;
635 (void) rmdir(mount_slave);
636 mount_slave_created = false;
638 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) {
639 r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
645 r = sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
650 const char *mount_inside;
654 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
656 q = procfs_file_alloca(m->leader, "ns/mnt");
657 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
659 r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
663 if (setns(mntfd, CLONE_NEWNS) < 0) {
664 r = log_error_errno(errno, "Failed to join namespace of leader: %m");
669 (void) mkdir_p(dest, 0755);
671 /* Fifth, move the mount to the right place inside */
672 mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside));
673 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
674 r = log_error_errno(errno, "Failed to mount: %m");
681 (void) write(errno_pipe_fd[1], &r, sizeof(r));
682 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
687 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
689 r = wait_for_terminate(child, &si);
691 r = sd_bus_error_set_errnof(error, errno, "Failed to wait for client: %m");
694 if (si.si_code != CLD_EXITED) {
695 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
698 if (si.si_status != EXIT_SUCCESS) {
700 if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
701 r = sd_bus_error_set_errnof(error, r, "Failed to mount in container: %m");
703 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client failed.");
707 r = sd_bus_reply_method_return(message, NULL);
710 if (mount_outside_mounted)
711 umount(mount_outside);
712 if (mount_outside_created)
713 rmdir(mount_outside);
715 if (mount_tmp_mounted)
717 if (mount_tmp_created)
720 if (mount_slave_mounted)
722 if (mount_slave_created)
728 const sd_bus_vtable machine_vtable[] = {
729 SD_BUS_VTABLE_START(0),
730 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
731 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
732 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
733 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
734 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
735 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
736 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
737 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
738 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
739 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
740 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
741 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
742 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
743 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
744 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
745 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
746 SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
747 SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, 0),
751 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
752 Manager *m = userdata;
762 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
763 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
764 sd_bus_message *message;
767 message = sd_bus_get_current_message(bus);
771 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
775 r = sd_bus_creds_get_pid(creds, &pid);
779 r = manager_get_machine_by_pid(m, pid, &machine);
783 _cleanup_free_ char *e = NULL;
786 p = startswith(path, "/org/freedesktop/machine1/machine/");
790 e = bus_label_unescape(p);
794 machine = hashmap_get(m->machines, e);
803 char *machine_bus_path(Machine *m) {
804 _cleanup_free_ char *e = NULL;
808 e = bus_label_escape(m->name);
812 return strappend("/org/freedesktop/machine1/machine/", e);
815 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
816 _cleanup_strv_free_ char **l = NULL;
817 Machine *machine = NULL;
818 Manager *m = userdata;
826 HASHMAP_FOREACH(machine, m->machines, i) {
829 p = machine_bus_path(machine);
833 r = strv_consume(&l, p);
844 int machine_send_signal(Machine *m, bool new_machine) {
845 _cleanup_free_ char *p = NULL;
849 p = machine_bus_path(m);
853 return sd_bus_emit_signal(
855 "/org/freedesktop/machine1",
856 "org.freedesktop.machine1.Manager",
857 new_machine ? "MachineNew" : "MachineRemoved",
861 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
862 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
863 _cleanup_free_ char *p = NULL;
867 if (!m->create_message)
870 c = m->create_message;
871 m->create_message = NULL;
874 return sd_bus_reply_method_error(c, error);
876 /* Update the machine state file before we notify the client
877 * about the result. */
880 p = machine_bus_path(m);
884 return sd_bus_reply_method_return(c, "o", p);