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(). */
32 #include "alloc-util.h"
33 #include "bus-common-errors.h"
34 #include "bus-internal.h"
35 #include "bus-label.h"
41 #include "formats-util.h"
43 #include "in-addr-util.h"
44 #include "local-addresses.h"
45 #include "machine-dbus.h"
48 #include "path-util.h"
49 #include "process-util.h"
51 #include "terminal-util.h"
52 #include "user-util.h"
54 static int property_get_id(
57 const char *interface
,
59 sd_bus_message
*reply
,
61 sd_bus_error
*error
) {
63 Machine
*m
= userdata
;
69 return sd_bus_message_append_array(reply
, 'y', &m
->id
, 16);
72 static int property_get_state(
75 const char *interface
,
77 sd_bus_message
*reply
,
79 sd_bus_error
*error
) {
81 Machine
*m
= userdata
;
89 state
= machine_state_to_string(machine_get_state(m
));
91 r
= sd_bus_message_append_basic(reply
, 's', state
);
98 static int property_get_netif(
101 const char *interface
,
102 const char *property
,
103 sd_bus_message
*reply
,
105 sd_bus_error
*error
) {
107 Machine
*m
= userdata
;
113 assert_cc(sizeof(int) == sizeof(int32_t));
115 return sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
118 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
120 int bus_machine_method_terminate(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
121 Machine
*m
= userdata
;
127 r
= bus_verify_polkit_async(
130 "org.freedesktop.machine1.manage-machines",
134 &m
->manager
->polkit_registry
,
139 return 1; /* Will call us back */
145 return sd_bus_reply_method_return(message
, NULL
);
148 int bus_machine_method_kill(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
149 Machine
*m
= userdata
;
158 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
165 who
= kill_who_from_string(swho
);
167 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
170 if (signo
<= 0 || signo
>= _NSIG
)
171 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
173 r
= bus_verify_polkit_async(
176 "org.freedesktop.machine1.manage-machines",
180 &m
->manager
->polkit_registry
,
185 return 1; /* Will call us back */
187 r
= machine_kill(m
, who
, signo
);
191 return sd_bus_reply_method_return(message
, NULL
);
194 int bus_machine_method_get_addresses(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
195 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
196 Machine
*m
= userdata
;
202 r
= sd_bus_message_new_method_return(message
, &reply
);
206 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
213 _cleanup_free_
struct local_address
*addresses
= NULL
;
214 struct local_address
*a
;
217 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
221 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
223 r
= sd_bus_message_open_container(reply
, 'r', "iay");
227 r
= sd_bus_message_append(reply
, "i", addresses
[i
].family
);
231 r
= sd_bus_message_append_array(reply
, 'y', &addresses
[i
].address
, FAMILY_ADDRESS_SIZE(addresses
[i
].family
));
235 r
= sd_bus_message_close_container(reply
);
243 case MACHINE_CONTAINER
: {
244 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
245 _cleanup_free_
char *us
= NULL
, *them
= NULL
;
246 _cleanup_close_
int netns_fd
= -1;
251 r
= readlink_malloc("/proc/self/ns/net", &us
);
255 p
= procfs_file_alloca(m
->leader
, "ns/net");
256 r
= readlink_malloc(p
, &them
);
261 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
263 r
= namespace_open(m
->leader
, NULL
, NULL
, &netns_fd
, NULL
, NULL
);
267 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
272 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
275 _cleanup_free_
struct local_address
*addresses
= NULL
;
276 struct local_address
*a
;
279 pair
[0] = safe_close(pair
[0]);
281 r
= namespace_enter(-1, -1, netns_fd
, -1, -1);
285 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
289 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
290 struct iovec iov
[2] = {
291 { .iov_base
= &a
->family
, .iov_len
= sizeof(a
->family
) },
292 { .iov_base
= &a
->address
, .iov_len
= FAMILY_ADDRESS_SIZE(a
->family
) },
295 r
= writev(pair
[1], iov
, 2);
300 pair
[1] = safe_close(pair
[1]);
305 pair
[1] = safe_close(pair
[1]);
310 union in_addr_union in_addr
;
317 iov
[0] = (struct iovec
) { .iov_base
= &family
, .iov_len
= sizeof(family
) };
318 iov
[1] = (struct iovec
) { .iov_base
= &in_addr
, .iov_len
= sizeof(in_addr
) };
320 n
= recvmsg(pair
[0], &mh
, 0);
323 if ((size_t) n
< sizeof(family
))
326 r
= sd_bus_message_open_container(reply
, 'r', "iay");
330 r
= sd_bus_message_append(reply
, "i", family
);
337 if (n
!= sizeof(struct in_addr
) + sizeof(family
))
340 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in
, sizeof(in_addr
.in
));
344 if (n
!= sizeof(struct in6_addr
) + sizeof(family
))
347 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in6
, sizeof(in_addr
.in6
));
353 r
= sd_bus_message_close_container(reply
);
358 r
= wait_for_terminate(child
, &si
);
360 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
361 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
362 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
367 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting IP address data is only supported on container machines.");
370 r
= sd_bus_message_close_container(reply
);
374 return sd_bus_send(NULL
, reply
, NULL
);
377 int bus_machine_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
378 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
379 _cleanup_strv_free_
char **l
= NULL
;
380 Machine
*m
= userdata
;
390 r
= load_env_file_pairs(NULL
, "/etc/os-release", NULL
, &l
);
396 case MACHINE_CONTAINER
: {
397 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
398 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
399 _cleanup_fclose_
FILE *f
= NULL
;
403 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, NULL
, &root_fd
);
407 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
412 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
415 _cleanup_close_
int fd
= -1;
417 pair
[0] = safe_close(pair
[0]);
419 r
= namespace_enter(-1, mntns_fd
, -1, -1, root_fd
);
423 fd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
);
425 fd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
);
430 r
= copy_bytes(fd
, pair
[1], (uint64_t) -1, false);
437 pair
[1] = safe_close(pair
[1]);
439 f
= fdopen(pair
[0], "re");
445 r
= load_env_file_pairs(f
, "/etc/os-release", NULL
, &l
);
449 r
= wait_for_terminate(child
, &si
);
451 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
452 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
453 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
459 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting OS release data is only supported on container machines.");
462 r
= sd_bus_message_new_method_return(message
, &reply
);
466 r
= sd_bus_message_open_container(reply
, 'a', "{ss}");
470 STRV_FOREACH_PAIR(k
, v
, l
) {
471 r
= sd_bus_message_append(reply
, "{ss}", *k
, *v
);
476 r
= sd_bus_message_close_container(reply
);
480 return sd_bus_send(NULL
, reply
, NULL
);
483 int bus_machine_method_open_pty(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
484 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
485 _cleanup_free_
char *pty_name
= NULL
;
486 _cleanup_close_
int master
= -1;
487 Machine
*m
= userdata
;
493 r
= bus_verify_polkit_async(
496 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
500 &m
->manager
->polkit_registry
,
505 return 1; /* Will call us back */
507 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
511 r
= ptsname_namespace(master
, &pty_name
);
515 r
= sd_bus_message_new_method_return(message
, &reply
);
519 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
523 return sd_bus_send(NULL
, reply
, NULL
);
526 static int container_bus_new(Machine
*m
, sd_bus_error
*error
, sd_bus
**ret
) {
538 case MACHINE_CONTAINER
: {
539 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
542 r
= sd_bus_new(&bus
);
546 if (asprintf(&address
, "x-machine-kernel:pid=%1$" PID_PRI
";x-machine-unix:pid=%1$" PID_PRI
, m
->leader
) < 0)
549 bus
->address
= address
;
550 bus
->bus_client
= true;
551 bus
->trusted
= false;
552 bus
->is_system
= true;
554 r
= sd_bus_start(bus
);
556 return sd_bus_error_set_errnof(error
, r
, "There is no system bus in container %s.", m
->name
);
572 int bus_machine_method_open_login(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
573 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
574 _cleanup_free_
char *pty_name
= NULL
;
575 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
576 _cleanup_close_
int master
= -1;
577 sd_bus
*container_bus
= NULL
;
578 Machine
*m
= userdata
;
579 const char *p
, *getty
;
585 r
= bus_verify_polkit_async(
588 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
592 &m
->manager
->polkit_registry
,
597 return 1; /* Will call us back */
599 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
603 r
= ptsname_namespace(master
, &pty_name
);
607 p
= path_startswith(pty_name
, "/dev/pts/");
609 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "PTS name %s is invalid", pty_name
);
611 r
= container_bus_new(m
, error
, &allocated_bus
);
615 container_bus
= allocated_bus
?: m
->manager
->bus
;
617 getty
= strjoina("container-getty@", p
, ".service");
619 r
= sd_bus_call_method(
621 "org.freedesktop.systemd1",
622 "/org/freedesktop/systemd1",
623 "org.freedesktop.systemd1.Manager",
626 "ss", getty
, "replace");
630 r
= sd_bus_message_new_method_return(message
, &reply
);
634 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
638 return sd_bus_send(NULL
, reply
, NULL
);
641 int bus_machine_method_open_shell(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
642 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *tm
= NULL
;
643 _cleanup_free_
char *pty_name
= NULL
;
644 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
645 sd_bus
*container_bus
= NULL
;
646 _cleanup_close_
int master
= -1, slave
= -1;
647 _cleanup_strv_free_
char **env
= NULL
, **args
= NULL
;
648 Machine
*m
= userdata
;
649 const char *p
, *unit
, *user
, *path
, *description
, *utmp_id
;
655 r
= sd_bus_message_read(message
, "ss", &user
, &path
);
662 if (!path_is_absolute(path
))
663 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Specified path '%s' is not absolute", path
);
665 r
= sd_bus_message_read_strv(message
, &args
);
668 if (strv_isempty(args
)) {
669 args
= strv_free(args
);
671 args
= strv_new(path
, NULL
);
675 args
[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */
678 r
= sd_bus_message_read_strv(message
, &env
);
681 if (!strv_env_is_valid(env
))
682 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid environment assignments");
684 r
= bus_verify_polkit_async(
687 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
691 &m
->manager
->polkit_registry
,
696 return 1; /* Will call us back */
698 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
702 r
= ptsname_namespace(master
, &pty_name
);
706 p
= path_startswith(pty_name
, "/dev/pts/");
709 slave
= machine_open_terminal(m
, pty_name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
713 utmp_id
= path_startswith(pty_name
, "/dev/");
716 r
= container_bus_new(m
, error
, &allocated_bus
);
720 container_bus
= allocated_bus
?: m
->manager
->bus
;
722 r
= sd_bus_message_new_method_call(
725 "org.freedesktop.systemd1",
726 "/org/freedesktop/systemd1",
727 "org.freedesktop.systemd1.Manager",
728 "StartTransientUnit");
733 unit
= strjoina("container-shell@", p
, ".service", NULL
);
734 r
= sd_bus_message_append(tm
, "ss", unit
, "fail");
739 r
= sd_bus_message_open_container(tm
, 'a', "(sv)");
743 description
= strjoina("Shell for User ", isempty(user
) ? "root" : user
);
744 r
= sd_bus_message_append(tm
,
745 "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
746 "Description", "s", description
,
747 "StandardInputFileDescriptor", "h", slave
,
748 "StandardOutputFileDescriptor", "h", slave
,
749 "StandardErrorFileDescriptor", "h", slave
,
750 "SendSIGHUP", "b", true,
751 "IgnoreSIGPIPE", "b", false,
752 "KillMode", "s", "mixed",
753 "TTYReset", "b", true,
754 "UtmpIdentifier", "s", utmp_id
,
755 "UtmpMode", "s", "user",
756 "PAMName", "s", "login",
757 "WorkingDirectory", "s", "-~");
761 r
= sd_bus_message_append(tm
, "(sv)", "User", "s", isempty(user
) ? "root" : user
);
765 if (!strv_isempty(env
)) {
766 r
= sd_bus_message_open_container(tm
, 'r', "sv");
770 r
= sd_bus_message_append(tm
, "s", "Environment");
774 r
= sd_bus_message_open_container(tm
, 'v', "as");
778 r
= sd_bus_message_append_strv(tm
, env
);
782 r
= sd_bus_message_close_container(tm
);
786 r
= sd_bus_message_close_container(tm
);
792 r
= sd_bus_message_open_container(tm
, 'r', "sv");
796 r
= sd_bus_message_append(tm
, "s", "ExecStart");
800 r
= sd_bus_message_open_container(tm
, 'v', "a(sasb)");
804 r
= sd_bus_message_open_container(tm
, 'a', "(sasb)");
808 r
= sd_bus_message_open_container(tm
, 'r', "sasb");
812 r
= sd_bus_message_append(tm
, "s", path
);
816 r
= sd_bus_message_append_strv(tm
, args
);
820 r
= sd_bus_message_append(tm
, "b", true);
824 r
= sd_bus_message_close_container(tm
);
828 r
= sd_bus_message_close_container(tm
);
832 r
= sd_bus_message_close_container(tm
);
836 r
= sd_bus_message_close_container(tm
);
840 r
= sd_bus_message_close_container(tm
);
844 /* Auxiliary units */
845 r
= sd_bus_message_append(tm
, "a(sa(sv))", 0);
849 r
= sd_bus_call(container_bus
, tm
, 0, error
, NULL
);
853 slave
= safe_close(slave
);
855 r
= sd_bus_message_new_method_return(message
, &reply
);
859 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
863 return sd_bus_send(NULL
, reply
, NULL
);
866 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
867 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
868 char mount_slave
[] = "/tmp/propagate.XXXXXX", *mount_tmp
, *mount_outside
, *p
;
869 bool mount_slave_created
= false, mount_slave_mounted
= false,
870 mount_tmp_created
= false, mount_tmp_mounted
= false,
871 mount_outside_created
= false, mount_outside_mounted
= false;
872 const char *dest
, *src
;
873 Machine
*m
= userdata
;
874 int read_only
, make_directory
;
882 if (m
->class != MACHINE_CONTAINER
)
883 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
885 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_directory
);
889 if (!path_is_absolute(src
) || !path_is_safe(src
))
890 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and not contain ../.");
894 else if (!path_is_absolute(dest
) || !path_is_safe(dest
))
895 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and not contain ../.");
897 r
= bus_verify_polkit_async(
900 "org.freedesktop.machine1.manage-machines",
904 &m
->manager
->polkit_registry
,
909 return 1; /* Will call us back */
911 /* One day, when bind mounting /proc/self/fd/n works across
912 * namespace boundaries we should rework this logic to make
915 p
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/");
916 if (laccess(p
, F_OK
) < 0)
917 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Container does not allow propagation of mount points.");
919 /* Our goal is to install a new bind mount into the container,
920 possibly read-only. This is irritatingly complex
921 unfortunately, currently.
923 First, we start by creating a private playground in /tmp,
924 that we can mount MS_SLAVE. (Which is necessary, since
925 MS_MOVE cannot be applied to mounts with MS_SHARED parent
928 if (!mkdtemp(mount_slave
))
929 return sd_bus_error_set_errnof(error
, errno
, "Failed to create playground %s: %m", mount_slave
);
931 mount_slave_created
= true;
933 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
934 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to make bind mount %s: %m", mount_slave
);
938 mount_slave_mounted
= true;
940 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
941 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount slave %s: %m", mount_slave
);
945 /* Second, we mount the source directory to a directory inside
946 of our MS_SLAVE playground. */
947 mount_tmp
= strjoina(mount_slave
, "/mount");
948 if (mkdir(mount_tmp
, 0700) < 0) {
949 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount point %s: %m", mount_tmp
);
953 mount_tmp_created
= true;
955 if (mount(src
, mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
956 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to overmount %s: %m", mount_tmp
);
960 mount_tmp_mounted
= true;
962 /* Third, we remount the new bind mount read-only if requested. */
964 if (mount(NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
) < 0) {
965 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount read-only %s: %m", mount_tmp
);
969 /* Fourth, we move the new bind mount into the propagation
970 * directory. This way it will appear there read-only
973 mount_outside
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/XXXXXX");
974 if (!mkdtemp(mount_outside
)) {
975 r
= sd_bus_error_set_errnof(error
, errno
, "Cannot create propagation directory %s: %m", mount_outside
);
979 mount_outside_created
= true;
981 if (mount(mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
) < 0) {
982 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to move %s to %s: %m", mount_tmp
, mount_outside
);
986 mount_outside_mounted
= true;
987 mount_tmp_mounted
= false;
989 (void) rmdir(mount_tmp
);
990 mount_tmp_created
= false;
992 (void) umount(mount_slave
);
993 mount_slave_mounted
= false;
995 (void) rmdir(mount_slave
);
996 mount_slave_created
= false;
998 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0) {
999 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1005 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1010 const char *mount_inside
;
1014 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1016 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1017 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1019 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1023 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1024 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1029 (void) mkdir_p(dest
, 0755);
1031 /* Fifth, move the mount to the right place inside */
1032 mount_inside
= strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside
));
1033 if (mount(mount_inside
, dest
, NULL
, MS_MOVE
, NULL
) < 0) {
1034 r
= log_error_errno(errno
, "Failed to mount: %m");
1038 _exit(EXIT_SUCCESS
);
1041 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1042 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1044 _exit(EXIT_FAILURE
);
1047 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1049 r
= wait_for_terminate(child
, &si
);
1051 r
= sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1054 if (si
.si_code
!= CLD_EXITED
) {
1055 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1058 if (si
.si_status
!= EXIT_SUCCESS
) {
1060 if (read(errno_pipe_fd
[0], &r
, sizeof(r
)) == sizeof(r
))
1061 r
= sd_bus_error_set_errnof(error
, r
, "Failed to mount: %m");
1063 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1067 r
= sd_bus_reply_method_return(message
, NULL
);
1070 if (mount_outside_mounted
)
1071 umount(mount_outside
);
1072 if (mount_outside_created
)
1073 rmdir(mount_outside
);
1075 if (mount_tmp_mounted
)
1077 if (mount_tmp_created
)
1080 if (mount_slave_mounted
)
1081 umount(mount_slave
);
1082 if (mount_slave_created
)
1088 static int machine_operation_done(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
1089 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1090 MachineOperation
*o
= userdata
;
1098 if (si
->si_code
!= CLD_EXITED
) {
1099 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1103 if (si
->si_status
!= EXIT_SUCCESS
) {
1104 if (read(o
->errno_fd
, &r
, sizeof(r
)) == sizeof(r
))
1105 r
= sd_bus_error_set_errnof(&error
, r
, "%m");
1107 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1112 r
= sd_bus_reply_method_return(o
->message
, NULL
);
1114 log_error_errno(r
, "Failed to reply to message: %m");
1116 machine_operation_unref(o
);
1120 r
= sd_bus_reply_method_error(o
->message
, &error
);
1122 log_error_errno(r
, "Failed to reply to message: %m");
1124 machine_operation_unref(o
);
1128 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1129 const char *src
, *dest
, *host_path
, *container_path
, *host_basename
, *host_dirname
, *container_basename
, *container_dirname
;
1130 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
1131 _cleanup_close_
int hostfd
= -1;
1132 Machine
*m
= userdata
;
1133 MachineOperation
*o
;
1142 if (m
->n_operations
>= MACHINE_OPERATIONS_MAX
)
1143 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
1145 if (m
->class != MACHINE_CONTAINER
)
1146 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
1148 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
1152 if (!path_is_absolute(src
))
1153 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
1157 else if (!path_is_absolute(dest
))
1158 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
1160 r
= bus_verify_polkit_async(
1163 "org.freedesktop.machine1.manage-machines",
1167 &m
->manager
->polkit_registry
,
1172 return 1; /* Will call us back */
1174 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
1177 container_path
= src
;
1181 container_path
= dest
;
1184 host_basename
= basename(host_path
);
1185 t
= strdupa(host_path
);
1186 host_dirname
= dirname(t
);
1188 container_basename
= basename(container_path
);
1189 t
= strdupa(container_path
);
1190 container_dirname
= dirname(t
);
1192 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1194 return sd_bus_error_set_errnof(error
, errno
, "Failed to open host directory %s: %m", host_dirname
);
1196 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
1197 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1201 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1208 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1210 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1211 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1213 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1217 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1218 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1222 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1223 if (containerfd
< 0) {
1224 r
= log_error_errno(errno
, "Failed top open destination directory: %m");
1229 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, true);
1231 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, true);
1233 hostfd
= safe_close(hostfd
);
1234 containerfd
= safe_close(containerfd
);
1237 r
= log_error_errno(r
, "Failed to copy tree: %m");
1241 _exit(EXIT_SUCCESS
);
1244 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1245 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1247 _exit(EXIT_FAILURE
);
1250 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1252 /* Copying might take a while, hence install a watch the
1253 * child, and return */
1255 o
= new0(MachineOperation
, 1);
1260 o
->message
= sd_bus_message_ref(message
);
1261 o
->errno_fd
= errno_pipe_fd
[0];
1262 errno_pipe_fd
[0] = -1;
1264 r
= sd_event_add_child(m
->manager
->event
, &o
->event_source
, child
, WEXITED
, machine_operation_done
, o
);
1266 machine_operation_unref(o
);
1270 LIST_PREPEND(operations
, m
->operations
, o
);
1277 const sd_bus_vtable machine_vtable
[] = {
1278 SD_BUS_VTABLE_START(0),
1279 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1280 SD_BUS_PROPERTY("Id", "ay", property_get_id
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1281 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1282 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1283 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1284 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1285 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
1286 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1287 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1288 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1289 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1290 SD_BUS_METHOD("Terminate", NULL
, NULL
, bus_machine_method_terminate
, SD_BUS_VTABLE_UNPRIVILEGED
),
1291 SD_BUS_METHOD("Kill", "si", NULL
, bus_machine_method_kill
, SD_BUS_VTABLE_UNPRIVILEGED
),
1292 SD_BUS_METHOD("GetAddresses", NULL
, "a(iay)", bus_machine_method_get_addresses
, SD_BUS_VTABLE_UNPRIVILEGED
),
1293 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_machine_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
1294 SD_BUS_METHOD("OpenPTY", NULL
, "hs", bus_machine_method_open_pty
, SD_BUS_VTABLE_UNPRIVILEGED
),
1295 SD_BUS_METHOD("OpenLogin", NULL
, "hs", bus_machine_method_open_login
, SD_BUS_VTABLE_UNPRIVILEGED
),
1296 SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell
, SD_BUS_VTABLE_UNPRIVILEGED
),
1297 SD_BUS_METHOD("BindMount", "ssbb", NULL
, bus_machine_method_bind_mount
, SD_BUS_VTABLE_UNPRIVILEGED
),
1298 SD_BUS_METHOD("CopyFrom", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1299 SD_BUS_METHOD("CopyTo", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1303 int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1304 Manager
*m
= userdata
;
1314 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1315 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
1316 sd_bus_message
*message
;
1319 message
= sd_bus_get_current_message(bus
);
1323 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1327 r
= sd_bus_creds_get_pid(creds
, &pid
);
1331 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1335 _cleanup_free_
char *e
= NULL
;
1338 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1342 e
= bus_label_unescape(p
);
1346 machine
= hashmap_get(m
->machines
, e
);
1355 char *machine_bus_path(Machine
*m
) {
1356 _cleanup_free_
char *e
= NULL
;
1360 e
= bus_label_escape(m
->name
);
1364 return strappend("/org/freedesktop/machine1/machine/", e
);
1367 int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1368 _cleanup_strv_free_
char **l
= NULL
;
1369 Machine
*machine
= NULL
;
1370 Manager
*m
= userdata
;
1378 HASHMAP_FOREACH(machine
, m
->machines
, i
) {
1381 p
= machine_bus_path(machine
);
1385 r
= strv_consume(&l
, p
);
1396 int machine_send_signal(Machine
*m
, bool new_machine
) {
1397 _cleanup_free_
char *p
= NULL
;
1401 p
= machine_bus_path(m
);
1405 return sd_bus_emit_signal(
1407 "/org/freedesktop/machine1",
1408 "org.freedesktop.machine1.Manager",
1409 new_machine
? "MachineNew" : "MachineRemoved",
1413 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1414 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*c
= NULL
;
1415 _cleanup_free_
char *p
= NULL
;
1419 if (!m
->create_message
)
1422 c
= m
->create_message
;
1423 m
->create_message
= NULL
;
1426 return sd_bus_reply_method_error(c
, error
);
1428 /* Update the machine state file before we notify the client
1429 * about the result. */
1432 p
= machine_bus_path(m
);
1436 return sd_bus_reply_method_return(c
, "o", p
);