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>
27 /* When we include libgen.h because we need dirname() we immediately
28 * undefine basename() since libgen.h defines it as a macro to the XDG
29 * version which is really broken. */
34 #include "bus-label.h"
36 #include "bus-common-errors.h"
39 #include "in-addr-util.h"
40 #include "local-addresses.h"
41 #include "path-util.h"
43 #include "bus-internal.h"
45 #include "machine-dbus.h"
47 static int property_get_id(
50 const char *interface,
52 sd_bus_message *reply,
54 sd_bus_error *error) {
56 Machine *m = userdata;
63 r = sd_bus_message_append_array(reply, 'y', &m->id, 16);
70 static int property_get_state(
73 const char *interface,
75 sd_bus_message *reply,
77 sd_bus_error *error) {
79 Machine *m = userdata;
87 state = machine_state_to_string(machine_get_state(m));
89 r = sd_bus_message_append_basic(reply, 's', state);
96 static int property_get_netif(
99 const char *interface,
100 const char *property,
101 sd_bus_message *reply,
103 sd_bus_error *error) {
105 Machine *m = userdata;
112 assert_cc(sizeof(int) == sizeof(int32_t));
114 r = sd_bus_message_append_array(reply, 'i', m->netif, m->n_netif * sizeof(int));
121 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, machine_class, MachineClass);
123 int bus_machine_method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
124 Machine *m = userdata;
131 r = bus_verify_polkit_async(
134 "org.freedesktop.machine1.manage-machines",
137 &m->manager->polkit_registry,
142 return 1; /* Will call us back */
148 return sd_bus_reply_method_return(message, NULL);
151 int bus_machine_method_kill(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
152 Machine *m = userdata;
162 r = sd_bus_message_read(message, "si", &swho, &signo);
169 who = kill_who_from_string(swho);
171 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
174 if (signo <= 0 || signo >= _NSIG)
175 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
177 r = bus_verify_polkit_async(
180 "org.freedesktop.machine1.manage-machines",
183 &m->manager->polkit_registry,
188 return 1; /* Will call us back */
190 r = machine_kill(m, who, signo);
194 return sd_bus_reply_method_return(message, NULL);
197 int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
198 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
199 _cleanup_close_pair_ int pair[2] = { -1, -1 };
200 _cleanup_free_ char *us = NULL, *them = NULL;
201 _cleanup_close_ int netns_fd = -1;
202 Machine *m = userdata;
212 if (m->class != MACHINE_CONTAINER)
213 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting IP address data is only supported on container machines.");
215 r = readlink_malloc("/proc/self/ns/net", &us);
219 p = procfs_file_alloca(m->leader, "ns/net");
220 r = readlink_malloc(p, &them);
225 return sd_bus_error_setf(error, BUS_ERROR_NO_PRIVATE_NETWORKING, "Machine %s does not use private networking", m->name);
227 r = namespace_open(m->leader, NULL, NULL, &netns_fd, NULL);
231 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
236 return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
239 _cleanup_free_ struct local_address *addresses = NULL;
240 struct local_address *a;
243 pair[0] = safe_close(pair[0]);
245 r = namespace_enter(-1, -1, netns_fd, -1);
249 n = local_addresses(NULL, 0, AF_UNSPEC, &addresses);
253 for (a = addresses, i = 0; i < n; a++, i++) {
254 struct iovec iov[2] = {
255 { .iov_base = &a->family, .iov_len = sizeof(a->family) },
256 { .iov_base = &a->address, .iov_len = FAMILY_ADDRESS_SIZE(a->family) },
259 r = writev(pair[1], iov, 2);
264 pair[1] = safe_close(pair[1]);
269 pair[1] = safe_close(pair[1]);
271 r = sd_bus_message_new_method_return(message, &reply);
275 r = sd_bus_message_open_container(reply, 'a', "(iay)");
282 union in_addr_union in_addr;
289 iov[0] = (struct iovec) { .iov_base = &family, .iov_len = sizeof(family) };
290 iov[1] = (struct iovec) { .iov_base = &in_addr, .iov_len = sizeof(in_addr) };
292 n = recvmsg(pair[0], &mh, 0);
295 if ((size_t) n < sizeof(family))
298 r = sd_bus_message_open_container(reply, 'r', "iay");
302 r = sd_bus_message_append(reply, "i", family);
309 if (n != sizeof(struct in_addr) + sizeof(family))
312 r = sd_bus_message_append_array(reply, 'y', &in_addr.in, sizeof(in_addr.in));
316 if (n != sizeof(struct in6_addr) + sizeof(family))
319 r = sd_bus_message_append_array(reply, 'y', &in_addr.in6, sizeof(in_addr.in6));
325 r = sd_bus_message_close_container(reply);
330 r = wait_for_terminate(child, &si);
332 return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
333 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
334 return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
336 r = sd_bus_message_close_container(reply);
340 return sd_bus_send(bus, reply, NULL);
343 int bus_machine_method_get_os_release(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
344 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
345 _cleanup_close_ int mntns_fd = -1, root_fd = -1;
346 _cleanup_close_pair_ int pair[2] = { -1, -1 };
347 _cleanup_strv_free_ char **l = NULL;
348 _cleanup_fclose_ FILE *f = NULL;
349 Machine *m = userdata;
359 if (m->class != MACHINE_CONTAINER)
360 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Requesting OS release data is only supported on container machines.");
362 r = namespace_open(m->leader, NULL, &mntns_fd, NULL, &root_fd);
366 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
371 return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
374 _cleanup_close_ int fd = -1;
376 pair[0] = safe_close(pair[0]);
378 r = namespace_enter(-1, mntns_fd, -1, root_fd);
382 fd = open("/etc/os-release", O_RDONLY|O_CLOEXEC);
384 fd = open("/usr/lib/os-release", O_RDONLY|O_CLOEXEC);
389 r = copy_bytes(fd, pair[1], (off_t) -1, false);
396 pair[1] = safe_close(pair[1]);
398 f = fdopen(pair[0], "re");
404 r = load_env_file_pairs(f, "/etc/os-release", NULL, &l);
408 r = wait_for_terminate(child, &si);
410 return sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
411 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
412 return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
414 r = sd_bus_message_new_method_return(message, &reply);
418 r = sd_bus_message_open_container(reply, 'a', "{ss}");
422 STRV_FOREACH_PAIR(k, v, l) {
423 r = sd_bus_message_append(reply, "{ss}", *k, *v);
428 r = sd_bus_message_close_container(reply);
432 return sd_bus_send(bus, reply, NULL);
435 int bus_machine_method_open_pty(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
436 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
437 _cleanup_free_ char *pty_name = NULL;
438 _cleanup_close_ int master = -1;
439 Machine *m = userdata;
446 if (m->class != MACHINE_CONTAINER)
447 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening pseudo TTYs is only supported on container machines.");
449 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
453 r = ptsname_malloc(master, &pty_name);
457 r = sd_bus_message_new_method_return(message, &reply);
461 r = sd_bus_message_append(reply, "hs", master, pty_name);
465 return sd_bus_send(bus, reply, NULL);
468 int bus_machine_method_open_login(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
469 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
470 _cleanup_free_ char *pty_name = NULL, *getty = NULL;
471 _cleanup_bus_unref_ sd_bus *container_bus = NULL;
472 _cleanup_close_ int master = -1;
473 Machine *m = userdata;
477 if (m->class != MACHINE_CONTAINER)
478 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Opening logins is only supported on container machines.");
480 r = bus_verify_polkit_async(
483 "org.freedesktop.machine1.login",
486 &m->manager->polkit_registry,
491 return 1; /* Will call us back */
493 master = openpt_in_namespace(m->leader, O_RDWR|O_NOCTTY|O_CLOEXEC);
497 r = ptsname_malloc(master, &pty_name);
501 p = path_startswith(pty_name, "/dev/pts/");
503 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
505 if (unlockpt(master) < 0)
508 r = sd_bus_new(&container_bus);
513 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT ";x-machine-unix:pid=" PID_FMT, m->leader, m->leader);
515 asprintf(&container_bus->address, "x-machine-kernel:pid=" PID_FMT, m->leader);
517 if (!container_bus->address)
520 container_bus->bus_client = true;
521 container_bus->trusted = false;
522 container_bus->is_system = true;
524 r = sd_bus_start(container_bus);
528 getty = strjoin("container-getty@", p, ".service", NULL);
532 r = sd_bus_call_method(
534 "org.freedesktop.systemd1",
535 "/org/freedesktop/systemd1",
536 "org.freedesktop.systemd1.Manager",
539 "ss", getty, "replace");
543 container_bus = sd_bus_unref(container_bus);
545 r = sd_bus_message_new_method_return(message, &reply);
549 r = sd_bus_message_append(reply, "hs", master, pty_name);
553 return sd_bus_send(bus, reply, NULL);
556 int bus_machine_method_bind_mount(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
557 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
558 char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
559 bool mount_slave_created = false, mount_slave_mounted = false,
560 mount_tmp_created = false, mount_tmp_mounted = false,
561 mount_outside_created = false, mount_outside_mounted = false;
562 const char *dest, *src;
563 Machine *m = userdata;
564 int read_only, make_directory;
569 if (m->class != MACHINE_CONTAINER)
570 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Bind mounting is only supported on container machines.");
572 r = sd_bus_message_read(message, "ssbb", &src, &dest, &read_only, &make_directory);
576 if (!path_is_absolute(src) || !path_is_safe(src))
577 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
581 else if (!path_is_absolute(dest) || !path_is_safe(dest))
582 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
584 r = bus_verify_polkit_async(
587 "org.freedesktop.machine1.manage-machines",
590 &m->manager->polkit_registry,
595 return 1; /* Will call us back */
597 /* One day, when bind mounting /proc/self/fd/n works across
598 * namespace boundaries we should rework this logic to make
601 p = strjoina("/run/systemd/nspawn/propagate/", m->name, "/");
602 if (laccess(p, F_OK) < 0)
603 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Container does not allow propagation of mount points.");
605 /* Our goal is to install a new bind mount into the container,
606 possibly read-only. This is irritatingly complex
607 unfortunately, currently.
609 First, we start by creating a private playground in /tmp,
610 that we can mount MS_SLAVE. (Which is necessary, since
611 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
614 if (!mkdtemp(mount_slave))
615 return sd_bus_error_set_errnof(error, errno, "Failed to create playground %s: %m", mount_slave);
617 mount_slave_created = true;
619 if (mount(mount_slave, mount_slave, NULL, MS_BIND, NULL) < 0) {
620 r = sd_bus_error_set_errnof(error, errno, "Failed to make bind mount %s: %m", mount_slave);
624 mount_slave_mounted = true;
626 if (mount(NULL, mount_slave, NULL, MS_SLAVE, NULL) < 0) {
627 r = sd_bus_error_set_errnof(error, errno, "Failed to remount slave %s: %m", mount_slave);
631 /* Second, we mount the source directory to a directory inside
632 of our MS_SLAVE playground. */
633 mount_tmp = strjoina(mount_slave, "/mount");
634 if (mkdir(mount_tmp, 0700) < 0) {
635 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount point %s: %m", mount_tmp);
639 mount_tmp_created = true;
641 if (mount(src, mount_tmp, NULL, MS_BIND, NULL) < 0) {
642 r = sd_bus_error_set_errnof(error, errno, "Failed to overmount %s: %m", mount_tmp);
646 mount_tmp_mounted = true;
648 /* Third, we remount the new bind mount read-only if requested. */
650 if (mount(NULL, mount_tmp, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL) < 0) {
651 r = sd_bus_error_set_errnof(error, errno, "Failed to remount read-only %s: %m", mount_tmp);
655 /* Fourth, we move the new bind mount into the propagation
656 * directory. This way it will appear there read-only
659 mount_outside = strjoina("/run/systemd/nspawn/propagate/", m->name, "/XXXXXX");
660 if (!mkdtemp(mount_outside)) {
661 r = sd_bus_error_set_errnof(error, errno, "Cannot create propagation directory %s: %m", mount_outside);
665 mount_outside_created = true;
667 if (mount(mount_tmp, mount_outside, NULL, MS_MOVE, NULL) < 0) {
668 r = sd_bus_error_set_errnof(error, errno, "Failed to move %s to %s: %m", mount_tmp, mount_outside);
672 mount_outside_mounted = true;
673 mount_tmp_mounted = false;
675 (void) rmdir(mount_tmp);
676 mount_tmp_created = false;
678 (void) umount(mount_slave);
679 mount_slave_mounted = false;
681 (void) rmdir(mount_slave);
682 mount_slave_created = false;
684 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) {
685 r = sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
691 r = sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
696 const char *mount_inside;
700 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
702 q = procfs_file_alloca(m->leader, "ns/mnt");
703 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
705 r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
709 if (setns(mntfd, CLONE_NEWNS) < 0) {
710 r = log_error_errno(errno, "Failed to join namespace of leader: %m");
715 (void) mkdir_p(dest, 0755);
717 /* Fifth, move the mount to the right place inside */
718 mount_inside = strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside));
719 if (mount(mount_inside, dest, NULL, MS_MOVE, NULL) < 0) {
720 r = log_error_errno(errno, "Failed to mount: %m");
727 (void) write(errno_pipe_fd[1], &r, sizeof(r));
728 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
733 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
735 r = wait_for_terminate(child, &si);
737 r = sd_bus_error_set_errnof(error, r, "Failed to wait for client: %m");
740 if (si.si_code != CLD_EXITED) {
741 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
744 if (si.si_status != EXIT_SUCCESS) {
746 if (read(errno_pipe_fd[0], &r, sizeof(r)) == sizeof(r))
747 r = sd_bus_error_set_errnof(error, r, "Failed to mount: %m");
749 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Client failed.");
753 r = sd_bus_reply_method_return(message, NULL);
756 if (mount_outside_mounted)
757 umount(mount_outside);
758 if (mount_outside_created)
759 rmdir(mount_outside);
761 if (mount_tmp_mounted)
763 if (mount_tmp_created)
766 if (mount_slave_mounted)
768 if (mount_slave_created)
774 static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
775 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
776 MachineOperation *o = userdata;
784 if (si->si_code != CLD_EXITED) {
785 r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Client died abnormally.");
789 if (si->si_status != EXIT_SUCCESS) {
790 if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r))
791 r = sd_bus_error_set_errnof(&error, r, "%m");
793 r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Client failed.");
798 r = sd_bus_reply_method_return(o->message, NULL);
800 log_error_errno(r, "Failed to reply to message: %m");
802 machine_operation_unref(o);
806 r = sd_bus_reply_method_error(o->message, &error);
808 log_error_errno(r, "Failed to reply to message: %m");
810 machine_operation_unref(o);
814 int bus_machine_method_copy(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
815 const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
816 _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
817 _cleanup_close_ int hostfd = -1;
818 Machine *m = userdata;
825 if (m->n_operations >= MACHINE_OPERATIONS_MAX)
826 return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
828 if (m->class != MACHINE_CONTAINER)
829 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Copying files is only supported on container machines.");
831 r = sd_bus_message_read(message, "ss", &src, &dest);
835 if (!path_is_absolute(src) || !path_is_safe(src))
836 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Source path must be absolute and not contain ../.");
840 else if (!path_is_absolute(dest) || !path_is_safe(dest))
841 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and not contain ../.");
843 r = bus_verify_polkit_async(
846 "org.freedesktop.machine1.manage-machines",
849 &m->manager->polkit_registry,
854 return 1; /* Will call us back */
856 copy_from = strstr(sd_bus_message_get_member(message), "CopyFrom");
859 container_path = src;
863 container_path = dest;
866 host_basename = basename(host_path);
867 t = strdupa(host_path);
868 host_dirname = dirname(t);
870 container_basename = basename(container_path);
871 t = strdupa(container_path);
872 container_dirname = dirname(t);
874 hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
876 return sd_bus_error_set_errnof(error, errno, "Failed to open host directory %s: %m", host_dirname);
878 if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
879 return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
883 return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
890 errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
892 q = procfs_file_alloca(m->leader, "ns/mnt");
893 mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
895 r = log_error_errno(errno, "Failed to open mount namespace of leader: %m");
899 if (setns(mntfd, CLONE_NEWNS) < 0) {
900 r = log_error_errno(errno, "Failed to join namespace of leader: %m");
904 containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
905 if (containerfd < 0) {
906 r = log_error_errno(errno, "Failed top open destination directory: %m");
911 r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
913 r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
915 hostfd = safe_close(hostfd);
916 containerfd = safe_close(containerfd);
919 r = log_error_errno(r, "Failed to copy tree: %m");
926 (void) write(errno_pipe_fd[1], &r, sizeof(r));
927 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
932 errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
934 /* Copying might take a while, hence install a watch the
935 * child, and return */
937 o = new0(MachineOperation, 1);
942 o->message = sd_bus_message_ref(message);
943 o->errno_fd = errno_pipe_fd[0];
944 errno_pipe_fd[0] = -1;
946 r = sd_event_add_child(m->manager->event, &o->event_source, child, WEXITED, machine_operation_done, o);
948 machine_operation_unref(o);
952 LIST_PREPEND(operations, m->operations, o);
959 const sd_bus_vtable machine_vtable[] = {
960 SD_BUS_VTABLE_START(0),
961 SD_BUS_PROPERTY("Name", "s", NULL, offsetof(Machine, name), SD_BUS_VTABLE_PROPERTY_CONST),
962 SD_BUS_PROPERTY("Id", "ay", property_get_id, 0, SD_BUS_VTABLE_PROPERTY_CONST),
963 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine, timestamp), SD_BUS_VTABLE_PROPERTY_CONST),
964 SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Machine, service), SD_BUS_VTABLE_PROPERTY_CONST),
965 SD_BUS_PROPERTY("Unit", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST),
966 SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Machine, unit), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN),
967 SD_BUS_PROPERTY("Leader", "u", NULL, offsetof(Machine, leader), SD_BUS_VTABLE_PROPERTY_CONST),
968 SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Machine, class), SD_BUS_VTABLE_PROPERTY_CONST),
969 SD_BUS_PROPERTY("RootDirectory", "s", NULL, offsetof(Machine, root_directory), SD_BUS_VTABLE_PROPERTY_CONST),
970 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif, 0, SD_BUS_VTABLE_PROPERTY_CONST),
971 SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
972 SD_BUS_METHOD("Terminate", NULL, NULL, bus_machine_method_terminate, SD_BUS_VTABLE_UNPRIVILEGED),
973 SD_BUS_METHOD("Kill", "si", NULL, bus_machine_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
974 SD_BUS_METHOD("GetAddresses", NULL, "a(iay)", bus_machine_method_get_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
975 SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_machine_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED),
976 SD_BUS_METHOD("OpenPTY", NULL, "hs", bus_machine_method_open_pty, 0),
977 SD_BUS_METHOD("OpenLogin", NULL, "hs", bus_machine_method_open_login, SD_BUS_VTABLE_UNPRIVILEGED),
978 SD_BUS_METHOD("BindMount", "ssbb", NULL, bus_machine_method_bind_mount, SD_BUS_VTABLE_UNPRIVILEGED),
979 SD_BUS_METHOD("CopyFrom", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
980 SD_BUS_METHOD("CopyTo", "ss", NULL, bus_machine_method_copy, SD_BUS_VTABLE_UNPRIVILEGED),
984 int machine_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
985 Manager *m = userdata;
995 if (streq(path, "/org/freedesktop/machine1/machine/self")) {
996 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
997 sd_bus_message *message;
1000 message = sd_bus_get_current_message(bus);
1004 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
1008 r = sd_bus_creds_get_pid(creds, &pid);
1012 r = manager_get_machine_by_pid(m, pid, &machine);
1016 _cleanup_free_ char *e = NULL;
1019 p = startswith(path, "/org/freedesktop/machine1/machine/");
1023 e = bus_label_unescape(p);
1027 machine = hashmap_get(m->machines, e);
1036 char *machine_bus_path(Machine *m) {
1037 _cleanup_free_ char *e = NULL;
1041 e = bus_label_escape(m->name);
1045 return strappend("/org/freedesktop/machine1/machine/", e);
1048 int machine_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1049 _cleanup_strv_free_ char **l = NULL;
1050 Machine *machine = NULL;
1051 Manager *m = userdata;
1059 HASHMAP_FOREACH(machine, m->machines, i) {
1062 p = machine_bus_path(machine);
1066 r = strv_consume(&l, p);
1077 int machine_send_signal(Machine *m, bool new_machine) {
1078 _cleanup_free_ char *p = NULL;
1082 p = machine_bus_path(m);
1086 return sd_bus_emit_signal(
1088 "/org/freedesktop/machine1",
1089 "org.freedesktop.machine1.Manager",
1090 new_machine ? "MachineNew" : "MachineRemoved",
1094 int machine_send_create_reply(Machine *m, sd_bus_error *error) {
1095 _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
1096 _cleanup_free_ char *p = NULL;
1100 if (!m->create_message)
1103 c = m->create_message;
1104 m->create_message = NULL;
1107 return sd_bus_reply_method_error(c, error);
1109 /* Update the machine state file before we notify the client
1110 * about the result. */
1113 p = machine_bus_path(m);
1117 return sd_bus_reply_method_return(c, "o", p);