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, slave
= -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/");
705 slave
= machine_open_terminal(m
, pty_name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
709 utmp_id
= path_startswith(pty_name
, "/dev/");
712 r
= container_bus_new(m
, error
, &allocated_bus
);
716 container_bus
= allocated_bus
?: m
->manager
->bus
;
718 r
= sd_bus_message_new_method_call(
721 "org.freedesktop.systemd1",
722 "/org/freedesktop/systemd1",
723 "org.freedesktop.systemd1.Manager",
724 "StartTransientUnit");
729 unit
= strjoina("container-shell@", p
, ".service", NULL
);
730 r
= sd_bus_message_append(tm
, "ss", unit
, "fail");
735 r
= sd_bus_message_open_container(tm
, 'a', "(sv)");
739 description
= strjoina("Shell for User ", isempty(user
) ? "root" : user
);
740 r
= sd_bus_message_append(tm
,
741 "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
742 "Description", "s", description
,
743 "StandardInputFileDescriptor", "h", slave
,
744 "StandardOutputFileDescriptor", "h", slave
,
745 "StandardErrorFileDescriptor", "h", slave
,
746 "SendSIGHUP", "b", true,
747 "IgnoreSIGPIPE", "b", false,
748 "KillMode", "s", "mixed",
749 "TTYReset", "b", true,
750 "UtmpIdentifier", "s", utmp_id
,
751 "UtmpMode", "s", "user",
752 "PAMName", "s", "login",
753 "WorkingDirectory", "s", "-~");
757 r
= sd_bus_message_append(tm
, "(sv)", "User", "s", isempty(user
) ? "root" : user
);
761 if (!strv_isempty(env
)) {
762 r
= sd_bus_message_open_container(tm
, 'r', "sv");
766 r
= sd_bus_message_append(tm
, "s", "Environment");
770 r
= sd_bus_message_open_container(tm
, 'v', "as");
774 r
= sd_bus_message_append_strv(tm
, env
);
778 r
= sd_bus_message_close_container(tm
);
782 r
= sd_bus_message_close_container(tm
);
788 r
= sd_bus_message_open_container(tm
, 'r', "sv");
792 r
= sd_bus_message_append(tm
, "s", "ExecStart");
796 r
= sd_bus_message_open_container(tm
, 'v', "a(sasb)");
800 r
= sd_bus_message_open_container(tm
, 'a', "(sasb)");
804 r
= sd_bus_message_open_container(tm
, 'r', "sasb");
808 r
= sd_bus_message_append(tm
, "s", path
);
812 r
= sd_bus_message_append_strv(tm
, args
);
816 r
= sd_bus_message_append(tm
, "b", true);
820 r
= sd_bus_message_close_container(tm
);
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 /* Auxiliary units */
841 r
= sd_bus_message_append(tm
, "a(sa(sv))", 0);
845 r
= sd_bus_call(container_bus
, tm
, 0, error
, NULL
);
849 slave
= safe_close(slave
);
851 r
= sd_bus_message_new_method_return(message
, &reply
);
855 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
859 return sd_bus_send(NULL
, reply
, NULL
);
862 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
863 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
864 char mount_slave
[] = "/tmp/propagate.XXXXXX", *mount_tmp
, *mount_outside
, *p
;
865 bool mount_slave_created
= false, mount_slave_mounted
= false,
866 mount_tmp_created
= false, mount_tmp_mounted
= false,
867 mount_outside_created
= false, mount_outside_mounted
= false;
868 const char *dest
, *src
;
869 Machine
*m
= userdata
;
870 int read_only
, make_directory
;
878 if (m
->class != MACHINE_CONTAINER
)
879 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
881 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_directory
);
885 if (!path_is_absolute(src
) || !path_is_safe(src
))
886 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and not contain ../.");
890 else if (!path_is_absolute(dest
) || !path_is_safe(dest
))
891 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and not contain ../.");
893 r
= bus_verify_polkit_async(
896 "org.freedesktop.machine1.manage-machines",
900 &m
->manager
->polkit_registry
,
905 return 1; /* Will call us back */
907 /* One day, when bind mounting /proc/self/fd/n works across
908 * namespace boundaries we should rework this logic to make
911 p
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/");
912 if (laccess(p
, F_OK
) < 0)
913 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Container does not allow propagation of mount points.");
915 /* Our goal is to install a new bind mount into the container,
916 possibly read-only. This is irritatingly complex
917 unfortunately, currently.
919 First, we start by creating a private playground in /tmp,
920 that we can mount MS_SLAVE. (Which is necessary, since
921 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
924 if (!mkdtemp(mount_slave
))
925 return sd_bus_error_set_errnof(error
, errno
, "Failed to create playground %s: %m", mount_slave
);
927 mount_slave_created
= true;
929 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
930 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to make bind mount %s: %m", mount_slave
);
934 mount_slave_mounted
= true;
936 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
937 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount slave %s: %m", mount_slave
);
941 /* Second, we mount the source directory to a directory inside
942 of our MS_SLAVE playground. */
943 mount_tmp
= strjoina(mount_slave
, "/mount");
944 if (mkdir(mount_tmp
, 0700) < 0) {
945 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount point %s: %m", mount_tmp
);
949 mount_tmp_created
= true;
951 if (mount(src
, mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
952 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to overmount %s: %m", mount_tmp
);
956 mount_tmp_mounted
= true;
958 /* Third, we remount the new bind mount read-only if requested. */
960 if (mount(NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
) < 0) {
961 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount read-only %s: %m", mount_tmp
);
965 /* Fourth, we move the new bind mount into the propagation
966 * directory. This way it will appear there read-only
969 mount_outside
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/XXXXXX");
970 if (!mkdtemp(mount_outside
)) {
971 r
= sd_bus_error_set_errnof(error
, errno
, "Cannot create propagation directory %s: %m", mount_outside
);
975 mount_outside_created
= true;
977 if (mount(mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
) < 0) {
978 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to move %s to %s: %m", mount_tmp
, mount_outside
);
982 mount_outside_mounted
= true;
983 mount_tmp_mounted
= false;
985 (void) rmdir(mount_tmp
);
986 mount_tmp_created
= false;
988 (void) umount(mount_slave
);
989 mount_slave_mounted
= false;
991 (void) rmdir(mount_slave
);
992 mount_slave_created
= false;
994 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0) {
995 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1001 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1006 const char *mount_inside
;
1010 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1012 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1013 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1015 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1019 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1020 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1025 (void) mkdir_p(dest
, 0755);
1027 /* Fifth, move the mount to the right place inside */
1028 mount_inside
= strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside
));
1029 if (mount(mount_inside
, dest
, NULL
, MS_MOVE
, NULL
) < 0) {
1030 r
= log_error_errno(errno
, "Failed to mount: %m");
1034 _exit(EXIT_SUCCESS
);
1037 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1038 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1040 _exit(EXIT_FAILURE
);
1043 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1045 r
= wait_for_terminate(child
, &si
);
1047 r
= sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1050 if (si
.si_code
!= CLD_EXITED
) {
1051 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1054 if (si
.si_status
!= EXIT_SUCCESS
) {
1056 if (read(errno_pipe_fd
[0], &r
, sizeof(r
)) == sizeof(r
))
1057 r
= sd_bus_error_set_errnof(error
, r
, "Failed to mount: %m");
1059 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1063 r
= sd_bus_reply_method_return(message
, NULL
);
1066 if (mount_outside_mounted
)
1067 umount(mount_outside
);
1068 if (mount_outside_created
)
1069 rmdir(mount_outside
);
1071 if (mount_tmp_mounted
)
1073 if (mount_tmp_created
)
1076 if (mount_slave_mounted
)
1077 umount(mount_slave
);
1078 if (mount_slave_created
)
1084 static int machine_operation_done(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
1085 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1086 MachineOperation
*o
= userdata
;
1094 if (si
->si_code
!= CLD_EXITED
) {
1095 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1099 if (si
->si_status
!= EXIT_SUCCESS
) {
1100 if (read(o
->errno_fd
, &r
, sizeof(r
)) == sizeof(r
))
1101 r
= sd_bus_error_set_errnof(&error
, r
, "%m");
1103 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1108 r
= sd_bus_reply_method_return(o
->message
, NULL
);
1110 log_error_errno(r
, "Failed to reply to message: %m");
1112 machine_operation_unref(o
);
1116 r
= sd_bus_reply_method_error(o
->message
, &error
);
1118 log_error_errno(r
, "Failed to reply to message: %m");
1120 machine_operation_unref(o
);
1124 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1125 const char *src
, *dest
, *host_path
, *container_path
, *host_basename
, *host_dirname
, *container_basename
, *container_dirname
;
1126 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
1127 _cleanup_close_
int hostfd
= -1;
1128 Machine
*m
= userdata
;
1129 MachineOperation
*o
;
1138 if (m
->n_operations
>= MACHINE_OPERATIONS_MAX
)
1139 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
1141 if (m
->class != MACHINE_CONTAINER
)
1142 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
1144 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
1148 if (!path_is_absolute(src
))
1149 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
1153 else if (!path_is_absolute(dest
))
1154 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
1156 r
= bus_verify_polkit_async(
1159 "org.freedesktop.machine1.manage-machines",
1163 &m
->manager
->polkit_registry
,
1168 return 1; /* Will call us back */
1170 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
1173 container_path
= src
;
1177 container_path
= dest
;
1180 host_basename
= basename(host_path
);
1181 t
= strdupa(host_path
);
1182 host_dirname
= dirname(t
);
1184 container_basename
= basename(container_path
);
1185 t
= strdupa(container_path
);
1186 container_dirname
= dirname(t
);
1188 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1190 return sd_bus_error_set_errnof(error
, errno
, "Failed to open host directory %s: %m", host_dirname
);
1192 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
1193 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1197 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1204 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1206 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1207 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1209 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1213 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1214 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1218 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1219 if (containerfd
< 0) {
1220 r
= log_error_errno(errno
, "Failed top open destination directory: %m");
1225 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, true);
1227 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, true);
1229 hostfd
= safe_close(hostfd
);
1230 containerfd
= safe_close(containerfd
);
1233 r
= log_error_errno(r
, "Failed to copy tree: %m");
1237 _exit(EXIT_SUCCESS
);
1240 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1241 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1243 _exit(EXIT_FAILURE
);
1246 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1248 /* Copying might take a while, hence install a watch the
1249 * child, and return */
1251 o
= new0(MachineOperation
, 1);
1256 o
->message
= sd_bus_message_ref(message
);
1257 o
->errno_fd
= errno_pipe_fd
[0];
1258 errno_pipe_fd
[0] = -1;
1260 r
= sd_event_add_child(m
->manager
->event
, &o
->event_source
, child
, WEXITED
, machine_operation_done
, o
);
1262 machine_operation_unref(o
);
1266 LIST_PREPEND(operations
, m
->operations
, o
);
1273 const sd_bus_vtable machine_vtable
[] = {
1274 SD_BUS_VTABLE_START(0),
1275 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1276 SD_BUS_PROPERTY("Id", "ay", property_get_id
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1277 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1278 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1279 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1280 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1281 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
1282 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1283 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1284 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1285 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1286 SD_BUS_METHOD("Terminate", NULL
, NULL
, bus_machine_method_terminate
, SD_BUS_VTABLE_UNPRIVILEGED
),
1287 SD_BUS_METHOD("Kill", "si", NULL
, bus_machine_method_kill
, SD_BUS_VTABLE_UNPRIVILEGED
),
1288 SD_BUS_METHOD("GetAddresses", NULL
, "a(iay)", bus_machine_method_get_addresses
, SD_BUS_VTABLE_UNPRIVILEGED
),
1289 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_machine_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
1290 SD_BUS_METHOD("OpenPTY", NULL
, "hs", bus_machine_method_open_pty
, SD_BUS_VTABLE_UNPRIVILEGED
),
1291 SD_BUS_METHOD("OpenLogin", NULL
, "hs", bus_machine_method_open_login
, SD_BUS_VTABLE_UNPRIVILEGED
),
1292 SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell
, SD_BUS_VTABLE_UNPRIVILEGED
),
1293 SD_BUS_METHOD("BindMount", "ssbb", NULL
, bus_machine_method_bind_mount
, SD_BUS_VTABLE_UNPRIVILEGED
),
1294 SD_BUS_METHOD("CopyFrom", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1295 SD_BUS_METHOD("CopyTo", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1299 int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1300 Manager
*m
= userdata
;
1310 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1311 _cleanup_bus_creds_unref_ sd_bus_creds
*creds
= NULL
;
1312 sd_bus_message
*message
;
1315 message
= sd_bus_get_current_message(bus
);
1319 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1323 r
= sd_bus_creds_get_pid(creds
, &pid
);
1327 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1331 _cleanup_free_
char *e
= NULL
;
1334 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1338 e
= bus_label_unescape(p
);
1342 machine
= hashmap_get(m
->machines
, e
);
1351 char *machine_bus_path(Machine
*m
) {
1352 _cleanup_free_
char *e
= NULL
;
1356 e
= bus_label_escape(m
->name
);
1360 return strappend("/org/freedesktop/machine1/machine/", e
);
1363 int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1364 _cleanup_strv_free_
char **l
= NULL
;
1365 Machine
*machine
= NULL
;
1366 Manager
*m
= userdata
;
1374 HASHMAP_FOREACH(machine
, m
->machines
, i
) {
1377 p
= machine_bus_path(machine
);
1381 r
= strv_consume(&l
, p
);
1392 int machine_send_signal(Machine
*m
, bool new_machine
) {
1393 _cleanup_free_
char *p
= NULL
;
1397 p
= machine_bus_path(m
);
1401 return sd_bus_emit_signal(
1403 "/org/freedesktop/machine1",
1404 "org.freedesktop.machine1.Manager",
1405 new_machine
? "MachineNew" : "MachineRemoved",
1409 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1410 _cleanup_bus_message_unref_ sd_bus_message
*c
= NULL
;
1411 _cleanup_free_
char *p
= NULL
;
1415 if (!m
->create_message
)
1418 c
= m
->create_message
;
1419 m
->create_message
= NULL
;
1422 return sd_bus_reply_method_error(c
, error
);
1424 /* Update the machine state file before we notify the client
1425 * about the result. */
1428 p
= machine_bus_path(m
);
1432 return sd_bus_reply_method_return(c
, "o", p
);