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/mount.h>
26 /* When we include libgen.h because we need dirname() we immediately
27 * undefine basename() since libgen.h defines it as a macro to the POSIX
28 * version which is really broken. We prefer GNU basename(). */
33 #include "bus-label.h"
35 #include "bus-common-errors.h"
38 #include "in-addr-util.h"
39 #include "local-addresses.h"
40 #include "path-util.h"
42 #include "bus-internal.h"
44 #include "machine-dbus.h"
45 #include "formats-util.h"
46 #include "process-util.h"
48 #include "terminal-util.h"
50 static int property_get_id(
53 const char *interface
,
55 sd_bus_message
*reply
,
57 sd_bus_error
*error
) {
59 Machine
*m
= userdata
;
65 return sd_bus_message_append_array(reply
, 'y', &m
->id
, 16);
68 static int property_get_state(
71 const char *interface
,
73 sd_bus_message
*reply
,
75 sd_bus_error
*error
) {
77 Machine
*m
= userdata
;
85 state
= machine_state_to_string(machine_get_state(m
));
87 r
= sd_bus_message_append_basic(reply
, 's', state
);
94 static int property_get_netif(
97 const char *interface
,
99 sd_bus_message
*reply
,
101 sd_bus_error
*error
) {
103 Machine
*m
= userdata
;
109 assert_cc(sizeof(int) == sizeof(int32_t));
111 return sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
114 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
116 int bus_machine_method_terminate(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
117 Machine
*m
= userdata
;
123 r
= bus_verify_polkit_async(
126 "org.freedesktop.machine1.manage-machines",
130 &m
->manager
->polkit_registry
,
135 return 1; /* Will call us back */
141 return sd_bus_reply_method_return(message
, NULL
);
144 int bus_machine_method_kill(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
145 Machine
*m
= userdata
;
154 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
161 who
= kill_who_from_string(swho
);
163 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
166 if (signo
<= 0 || signo
>= _NSIG
)
167 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
169 r
= bus_verify_polkit_async(
172 "org.freedesktop.machine1.manage-machines",
176 &m
->manager
->polkit_registry
,
181 return 1; /* Will call us back */
183 r
= machine_kill(m
, who
, signo
);
187 return sd_bus_reply_method_return(message
, NULL
);
190 int bus_machine_method_get_addresses(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
191 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
192 Machine
*m
= userdata
;
198 r
= sd_bus_message_new_method_return(message
, &reply
);
202 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
209 _cleanup_free_
struct local_address
*addresses
= NULL
;
210 struct local_address
*a
;
213 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
217 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
219 r
= sd_bus_message_open_container(reply
, 'r', "iay");
223 r
= sd_bus_message_append(reply
, "i", addresses
[i
].family
);
227 r
= sd_bus_message_append_array(reply
, 'y', &addresses
[i
].address
, FAMILY_ADDRESS_SIZE(addresses
[i
].family
));
231 r
= sd_bus_message_close_container(reply
);
239 case MACHINE_CONTAINER
: {
240 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
241 _cleanup_free_
char *us
= NULL
, *them
= NULL
;
242 _cleanup_close_
int netns_fd
= -1;
247 r
= readlink_malloc("/proc/self/ns/net", &us
);
251 p
= procfs_file_alloca(m
->leader
, "ns/net");
252 r
= readlink_malloc(p
, &them
);
257 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
259 r
= namespace_open(m
->leader
, NULL
, NULL
, &netns_fd
, NULL
, NULL
);
263 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
268 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
271 _cleanup_free_
struct local_address
*addresses
= NULL
;
272 struct local_address
*a
;
275 pair
[0] = safe_close(pair
[0]);
277 r
= namespace_enter(-1, -1, netns_fd
, -1, -1);
281 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
285 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
286 struct iovec iov
[2] = {
287 { .iov_base
= &a
->family
, .iov_len
= sizeof(a
->family
) },
288 { .iov_base
= &a
->address
, .iov_len
= FAMILY_ADDRESS_SIZE(a
->family
) },
291 r
= writev(pair
[1], iov
, 2);
296 pair
[1] = safe_close(pair
[1]);
301 pair
[1] = safe_close(pair
[1]);
306 union in_addr_union in_addr
;
313 iov
[0] = (struct iovec
) { .iov_base
= &family
, .iov_len
= sizeof(family
) };
314 iov
[1] = (struct iovec
) { .iov_base
= &in_addr
, .iov_len
= sizeof(in_addr
) };
316 n
= recvmsg(pair
[0], &mh
, 0);
319 if ((size_t) n
< sizeof(family
))
322 r
= sd_bus_message_open_container(reply
, 'r', "iay");
326 r
= sd_bus_message_append(reply
, "i", family
);
333 if (n
!= sizeof(struct in_addr
) + sizeof(family
))
336 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in
, sizeof(in_addr
.in
));
340 if (n
!= sizeof(struct in6_addr
) + sizeof(family
))
343 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in6
, sizeof(in_addr
.in6
));
349 r
= sd_bus_message_close_container(reply
);
354 r
= wait_for_terminate(child
, &si
);
356 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
357 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
358 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
363 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting IP address data is only supported on container machines.");
366 r
= sd_bus_message_close_container(reply
);
370 return sd_bus_send(NULL
, reply
, NULL
);
373 int bus_machine_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
374 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
375 _cleanup_strv_free_
char **l
= NULL
;
376 Machine
*m
= userdata
;
386 r
= load_env_file_pairs(NULL
, "/etc/os-release", NULL
, &l
);
392 case MACHINE_CONTAINER
: {
393 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
394 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
395 _cleanup_fclose_
FILE *f
= NULL
;
399 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, NULL
, &root_fd
);
403 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
408 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
411 _cleanup_close_
int fd
= -1;
413 pair
[0] = safe_close(pair
[0]);
415 r
= namespace_enter(-1, mntns_fd
, -1, -1, root_fd
);
419 fd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
);
421 fd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
);
426 r
= copy_bytes(fd
, pair
[1], (uint64_t) -1, false);
433 pair
[1] = safe_close(pair
[1]);
435 f
= fdopen(pair
[0], "re");
441 r
= load_env_file_pairs(f
, "/etc/os-release", NULL
, &l
);
445 r
= wait_for_terminate(child
, &si
);
447 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
448 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
449 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
455 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting OS release data is only supported on container machines.");
458 r
= sd_bus_message_new_method_return(message
, &reply
);
462 r
= sd_bus_message_open_container(reply
, 'a', "{ss}");
466 STRV_FOREACH_PAIR(k
, v
, l
) {
467 r
= sd_bus_message_append(reply
, "{ss}", *k
, *v
);
472 r
= sd_bus_message_close_container(reply
);
476 return sd_bus_send(NULL
, reply
, NULL
);
479 int bus_machine_method_open_pty(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
480 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
481 _cleanup_free_
char *pty_name
= NULL
;
482 _cleanup_close_
int master
= -1;
483 Machine
*m
= userdata
;
489 r
= bus_verify_polkit_async(
492 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
496 &m
->manager
->polkit_registry
,
501 return 1; /* Will call us back */
503 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
507 r
= ptsname_namespace(master
, &pty_name
);
511 r
= sd_bus_message_new_method_return(message
, &reply
);
515 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
519 return sd_bus_send(NULL
, reply
, NULL
);
522 static int container_bus_new(Machine
*m
, sd_bus_error
*error
, sd_bus
**ret
) {
534 case MACHINE_CONTAINER
: {
535 _cleanup_bus_unref_ sd_bus
*bus
= NULL
;
538 r
= sd_bus_new(&bus
);
542 if (asprintf(&address
, "x-machine-kernel:pid=%1$" PID_PRI
";x-machine-unix:pid=%1$" PID_PRI
, m
->leader
) < 0)
545 bus
->address
= address
;
546 bus
->bus_client
= true;
547 bus
->trusted
= false;
548 bus
->is_system
= true;
550 r
= sd_bus_start(bus
);
552 return sd_bus_error_set_errnof(error
, r
, "There is no system bus in container %s.", m
->name
);
568 int bus_machine_method_open_login(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
569 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
570 _cleanup_free_
char *pty_name
= NULL
;
571 _cleanup_bus_flush_close_unref_ sd_bus
*allocated_bus
= NULL
;
572 _cleanup_close_
int master
= -1;
573 sd_bus
*container_bus
= NULL
;
574 Machine
*m
= userdata
;
575 const char *p
, *getty
;
581 r
= bus_verify_polkit_async(
584 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
588 &m
->manager
->polkit_registry
,
593 return 1; /* Will call us back */
595 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
599 r
= ptsname_namespace(master
, &pty_name
);
603 p
= path_startswith(pty_name
, "/dev/pts/");
605 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "PTS name %s is invalid", pty_name
);
607 r
= container_bus_new(m
, error
, &allocated_bus
);
611 container_bus
= allocated_bus
?: m
->manager
->bus
;
613 getty
= strjoina("container-getty@", p
, ".service");
615 r
= sd_bus_call_method(
617 "org.freedesktop.systemd1",
618 "/org/freedesktop/systemd1",
619 "org.freedesktop.systemd1.Manager",
622 "ss", getty
, "replace");
626 r
= sd_bus_message_new_method_return(message
, &reply
);
630 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
634 return sd_bus_send(NULL
, reply
, NULL
);
637 int bus_machine_method_open_shell(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
638 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
, *tm
= NULL
;
639 _cleanup_free_
char *pty_name
= NULL
;
640 _cleanup_bus_flush_close_unref_ sd_bus
*allocated_bus
= NULL
;
641 sd_bus
*container_bus
= NULL
;
642 _cleanup_close_
int master
= -1;
643 _cleanup_strv_free_
char **env
= NULL
, **args
= NULL
;
644 Machine
*m
= userdata
;
645 const char *p
, *unit
, *user
, *path
, *description
, *utmp_id
;
651 r
= sd_bus_message_read(message
, "ss", &user
, &path
);
658 if (!path_is_absolute(path
))
659 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Specified path '%s' is not absolute", path
);
661 r
= sd_bus_message_read_strv(message
, &args
);
664 if (strv_isempty(args
)) {
665 args
= strv_free(args
);
667 args
= strv_new(path
, NULL
);
671 args
[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */
674 r
= sd_bus_message_read_strv(message
, &env
);
677 if (!strv_env_is_valid(env
))
678 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid environment assignments");
680 r
= bus_verify_polkit_async(
683 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
687 &m
->manager
->polkit_registry
,
692 return 1; /* Will call us back */
694 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
698 r
= ptsname_namespace(master
, &pty_name
);
702 p
= path_startswith(pty_name
, "/dev/pts/");
704 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "PTS name %s is invalid", pty_name
);
706 utmp_id
= path_startswith(pty_name
, "/dev/");
709 r
= container_bus_new(m
, error
, &allocated_bus
);
713 container_bus
= allocated_bus
?: m
->manager
->bus
;
715 r
= sd_bus_message_new_method_call(
718 "org.freedesktop.systemd1",
719 "/org/freedesktop/systemd1",
720 "org.freedesktop.systemd1.Manager",
721 "StartTransientUnit");
726 unit
= strjoina("container-shell@", p
, ".service", NULL
);
727 r
= sd_bus_message_append(tm
, "ss", unit
, "fail");
732 r
= sd_bus_message_open_container(tm
, 'a', "(sv)");
736 description
= strjoina("Shell for User ", isempty(user
) ? "root" : user
);
737 r
= sd_bus_message_append(tm
,
738 "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
739 "Description", "s", description
,
740 "StandardInput", "s", "tty",
741 "StandardOutput", "s", "tty",
742 "StandardError", "s", "tty",
743 "TTYPath", "s", pty_name
,
744 "SendSIGHUP", "b", true,
745 "IgnoreSIGPIPE", "b", false,
746 "KillMode", "s", "mixed",
747 "TTYVHangup", "b", true,
748 "TTYReset", "b", true,
749 "UtmpIdentifier", "s", utmp_id
,
750 "UtmpMode", "s", "user",
751 "PAMName", "s", "login");
755 r
= sd_bus_message_append(tm
, "(sv)", "User", "s", isempty(user
) ? "root" : user
);
759 if (!strv_isempty(env
)) {
760 r
= sd_bus_message_open_container(tm
, 'r', "sv");
764 r
= sd_bus_message_append(tm
, "s", "Environment");
768 r
= sd_bus_message_open_container(tm
, 'v', "as");
772 r
= sd_bus_message_append_strv(tm
, env
);
776 r
= sd_bus_message_close_container(tm
);
780 r
= sd_bus_message_close_container(tm
);
786 r
= sd_bus_message_open_container(tm
, 'r', "sv");
790 r
= sd_bus_message_append(tm
, "s", "ExecStart");
794 r
= sd_bus_message_open_container(tm
, 'v', "a(sasb)");
798 r
= sd_bus_message_open_container(tm
, 'a', "(sasb)");
802 r
= sd_bus_message_open_container(tm
, 'r', "sasb");
806 r
= sd_bus_message_append(tm
, "s", path
);
810 r
= sd_bus_message_append_strv(tm
, args
);
814 r
= sd_bus_message_append(tm
, "b", true);
818 r
= sd_bus_message_close_container(tm
);
822 r
= sd_bus_message_close_container(tm
);
826 r
= sd_bus_message_close_container(tm
);
830 r
= sd_bus_message_close_container(tm
);
834 r
= sd_bus_message_close_container(tm
);
838 /* Auxiliary units */
839 r
= sd_bus_message_append(tm
, "a(sa(sv))", 0);
843 r
= sd_bus_call(container_bus
, tm
, 0, error
, NULL
);
847 r
= sd_bus_message_new_method_return(message
, &reply
);
851 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
855 return sd_bus_send(NULL
, reply
, NULL
);
858 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
859 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
860 char mount_slave
[] = "/tmp/propagate.XXXXXX", *mount_tmp
, *mount_outside
, *p
;
861 bool mount_slave_created
= false, mount_slave_mounted
= false,
862 mount_tmp_created
= false, mount_tmp_mounted
= false,
863 mount_outside_created
= false, mount_outside_mounted
= false;
864 const char *dest
, *src
;
865 Machine
*m
= userdata
;
866 int read_only
, make_directory
;
874 if (m
->class != MACHINE_CONTAINER
)
875 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
877 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_directory
);
881 if (!path_is_absolute(src
) || !path_is_safe(src
))
882 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and not contain ../.");
886 else if (!path_is_absolute(dest
) || !path_is_safe(dest
))
887 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and not contain ../.");
889 r
= bus_verify_polkit_async(
892 "org.freedesktop.machine1.manage-machines",
896 &m
->manager
->polkit_registry
,
901 return 1; /* Will call us back */
903 /* One day, when bind mounting /proc/self/fd/n works across
904 * namespace boundaries we should rework this logic to make
907 p
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/");
908 if (laccess(p
, F_OK
) < 0)
909 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Container does not allow propagation of mount points.");
911 /* Our goal is to install a new bind mount into the container,
912 possibly read-only. This is irritatingly complex
913 unfortunately, currently.
915 First, we start by creating a private playground in /tmp,
916 that we can mount MS_SLAVE. (Which is necessary, since
917 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
920 if (!mkdtemp(mount_slave
))
921 return sd_bus_error_set_errnof(error
, errno
, "Failed to create playground %s: %m", mount_slave
);
923 mount_slave_created
= true;
925 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
926 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to make bind mount %s: %m", mount_slave
);
930 mount_slave_mounted
= true;
932 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
933 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount slave %s: %m", mount_slave
);
937 /* Second, we mount the source directory to a directory inside
938 of our MS_SLAVE playground. */
939 mount_tmp
= strjoina(mount_slave
, "/mount");
940 if (mkdir(mount_tmp
, 0700) < 0) {
941 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount point %s: %m", mount_tmp
);
945 mount_tmp_created
= true;
947 if (mount(src
, mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
948 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to overmount %s: %m", mount_tmp
);
952 mount_tmp_mounted
= true;
954 /* Third, we remount the new bind mount read-only if requested. */
956 if (mount(NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
) < 0) {
957 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount read-only %s: %m", mount_tmp
);
961 /* Fourth, we move the new bind mount into the propagation
962 * directory. This way it will appear there read-only
965 mount_outside
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/XXXXXX");
966 if (!mkdtemp(mount_outside
)) {
967 r
= sd_bus_error_set_errnof(error
, errno
, "Cannot create propagation directory %s: %m", mount_outside
);
971 mount_outside_created
= true;
973 if (mount(mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
) < 0) {
974 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to move %s to %s: %m", mount_tmp
, mount_outside
);
978 mount_outside_mounted
= true;
979 mount_tmp_mounted
= false;
981 (void) rmdir(mount_tmp
);
982 mount_tmp_created
= false;
984 (void) umount(mount_slave
);
985 mount_slave_mounted
= false;
987 (void) rmdir(mount_slave
);
988 mount_slave_created
= false;
990 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0) {
991 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
997 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1002 const char *mount_inside
;
1006 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1008 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1009 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1011 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1015 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1016 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1021 (void) mkdir_p(dest
, 0755);
1023 /* Fifth, move the mount to the right place inside */
1024 mount_inside
= strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside
));
1025 if (mount(mount_inside
, dest
, NULL
, MS_MOVE
, NULL
) < 0) {
1026 r
= log_error_errno(errno
, "Failed to mount: %m");
1030 _exit(EXIT_SUCCESS
);
1033 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1034 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1036 _exit(EXIT_FAILURE
);
1039 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1041 r
= wait_for_terminate(child
, &si
);
1043 r
= sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1046 if (si
.si_code
!= CLD_EXITED
) {
1047 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1050 if (si
.si_status
!= EXIT_SUCCESS
) {
1052 if (read(errno_pipe_fd
[0], &r
, sizeof(r
)) == sizeof(r
))
1053 r
= sd_bus_error_set_errnof(error
, r
, "Failed to mount: %m");
1055 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1059 r
= sd_bus_reply_method_return(message
, NULL
);
1062 if (mount_outside_mounted
)
1063 umount(mount_outside
);
1064 if (mount_outside_created
)
1065 rmdir(mount_outside
);
1067 if (mount_tmp_mounted
)
1069 if (mount_tmp_created
)
1072 if (mount_slave_mounted
)
1073 umount(mount_slave
);
1074 if (mount_slave_created
)
1080 static int machine_operation_done(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
1081 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1082 MachineOperation
*o
= userdata
;
1090 if (si
->si_code
!= CLD_EXITED
) {
1091 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1095 if (si
->si_status
!= EXIT_SUCCESS
) {
1096 if (read(o
->errno_fd
, &r
, sizeof(r
)) == sizeof(r
))
1097 r
= sd_bus_error_set_errnof(&error
, r
, "%m");
1099 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1104 r
= sd_bus_reply_method_return(o
->message
, NULL
);
1106 log_error_errno(r
, "Failed to reply to message: %m");
1108 machine_operation_unref(o
);
1112 r
= sd_bus_reply_method_error(o
->message
, &error
);
1114 log_error_errno(r
, "Failed to reply to message: %m");
1116 machine_operation_unref(o
);
1120 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1121 const char *src
, *dest
, *host_path
, *container_path
, *host_basename
, *host_dirname
, *container_basename
, *container_dirname
;
1122 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
1123 _cleanup_close_
int hostfd
= -1;
1124 Machine
*m
= userdata
;
1125 MachineOperation
*o
;
1134 if (m
->n_operations
>= MACHINE_OPERATIONS_MAX
)
1135 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
1137 if (m
->class != MACHINE_CONTAINER
)
1138 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
1140 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
1144 if (!path_is_absolute(src
))
1145 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
1149 else if (!path_is_absolute(dest
))
1150 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
1152 r
= bus_verify_polkit_async(
1155 "org.freedesktop.machine1.manage-machines",
1159 &m
->manager
->polkit_registry
,
1164 return 1; /* Will call us back */
1166 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
1169 container_path
= src
;
1173 container_path
= dest
;
1176 host_basename
= basename(host_path
);
1177 t
= strdupa(host_path
);
1178 host_dirname
= dirname(t
);
1180 container_basename
= basename(container_path
);
1181 t
= strdupa(container_path
);
1182 container_dirname
= dirname(t
);
1184 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1186 return sd_bus_error_set_errnof(error
, errno
, "Failed to open host directory %s: %m", host_dirname
);
1188 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
1189 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1193 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1200 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1202 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1203 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1205 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1209 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1210 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1214 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1215 if (containerfd
< 0) {
1216 r
= log_error_errno(errno
, "Failed top open destination directory: %m");
1221 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, true);
1223 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, true);
1225 hostfd
= safe_close(hostfd
);
1226 containerfd
= safe_close(containerfd
);
1229 r
= log_error_errno(r
, "Failed to copy tree: %m");
1233 _exit(EXIT_SUCCESS
);
1236 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1237 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1239 _exit(EXIT_FAILURE
);
1242 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1244 /* Copying might take a while, hence install a watch the
1245 * child, and return */
1247 o
= new0(MachineOperation
, 1);
1252 o
->message
= sd_bus_message_ref(message
);
1253 o
->errno_fd
= errno_pipe_fd
[0];
1254 errno_pipe_fd
[0] = -1;
1256 r
= sd_event_add_child(m
->manager
->event
, &o
->event_source
, child
, WEXITED
, machine_operation_done
, o
);
1258 machine_operation_unref(o
);
1262 LIST_PREPEND(operations
, m
->operations
, o
);
1269 const sd_bus_vtable machine_vtable
[] = {
1270 SD_BUS_VTABLE_START(0),
1271 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1272 SD_BUS_PROPERTY("Id", "ay", property_get_id
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1273 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1274 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1275 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1276 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1277 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
1278 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1279 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1280 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1281 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1282 SD_BUS_METHOD("Terminate", NULL
, NULL
, bus_machine_method_terminate
, SD_BUS_VTABLE_UNPRIVILEGED
),
1283 SD_BUS_METHOD("Kill", "si", NULL
, bus_machine_method_kill
, SD_BUS_VTABLE_UNPRIVILEGED
),
1284 SD_BUS_METHOD("GetAddresses", NULL
, "a(iay)", bus_machine_method_get_addresses
, SD_BUS_VTABLE_UNPRIVILEGED
),
1285 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_machine_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
1286 SD_BUS_METHOD("OpenPTY", NULL
, "hs", bus_machine_method_open_pty
, SD_BUS_VTABLE_UNPRIVILEGED
),
1287 SD_BUS_METHOD("OpenLogin", NULL
, "hs", bus_machine_method_open_login
, SD_BUS_VTABLE_UNPRIVILEGED
),
1288 SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell
, SD_BUS_VTABLE_UNPRIVILEGED
),
1289 SD_BUS_METHOD("BindMount", "ssbb", NULL
, bus_machine_method_bind_mount
, SD_BUS_VTABLE_UNPRIVILEGED
),
1290 SD_BUS_METHOD("CopyFrom", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1291 SD_BUS_METHOD("CopyTo", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1295 int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1296 Manager
*m
= userdata
;
1306 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1307 _cleanup_bus_creds_unref_ sd_bus_creds
*creds
= NULL
;
1308 sd_bus_message
*message
;
1311 message
= sd_bus_get_current_message(bus
);
1315 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1319 r
= sd_bus_creds_get_pid(creds
, &pid
);
1323 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1327 _cleanup_free_
char *e
= NULL
;
1330 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1334 e
= bus_label_unescape(p
);
1338 machine
= hashmap_get(m
->machines
, e
);
1347 char *machine_bus_path(Machine
*m
) {
1348 _cleanup_free_
char *e
= NULL
;
1352 e
= bus_label_escape(m
->name
);
1356 return strappend("/org/freedesktop/machine1/machine/", e
);
1359 int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1360 _cleanup_strv_free_
char **l
= NULL
;
1361 Machine
*machine
= NULL
;
1362 Manager
*m
= userdata
;
1370 HASHMAP_FOREACH(machine
, m
->machines
, i
) {
1373 p
= machine_bus_path(machine
);
1377 r
= strv_consume(&l
, p
);
1388 int machine_send_signal(Machine
*m
, bool new_machine
) {
1389 _cleanup_free_
char *p
= NULL
;
1393 p
= machine_bus_path(m
);
1397 return sd_bus_emit_signal(
1399 "/org/freedesktop/machine1",
1400 "org.freedesktop.machine1.Manager",
1401 new_machine
? "MachineNew" : "MachineRemoved",
1405 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1406 _cleanup_bus_message_unref_ sd_bus_message
*c
= NULL
;
1407 _cleanup_free_
char *p
= NULL
;
1411 if (!m
->create_message
)
1414 c
= m
->create_message
;
1415 m
->create_message
= NULL
;
1418 return sd_bus_reply_method_error(c
, error
);
1420 /* Update the machine state file before we notify the client
1421 * about the result. */
1424 p
= machine_bus_path(m
);
1428 return sd_bus_reply_method_return(c
, "o", p
);