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)(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",
752 "WorkingDirectory", "s", "-~");
756 r
= sd_bus_message_append(tm
, "(sv)", "User", "s", isempty(user
) ? "root" : user
);
760 if (!strv_isempty(env
)) {
761 r
= sd_bus_message_open_container(tm
, 'r', "sv");
765 r
= sd_bus_message_append(tm
, "s", "Environment");
769 r
= sd_bus_message_open_container(tm
, 'v', "as");
773 r
= sd_bus_message_append_strv(tm
, env
);
777 r
= sd_bus_message_close_container(tm
);
781 r
= sd_bus_message_close_container(tm
);
787 r
= sd_bus_message_open_container(tm
, 'r', "sv");
791 r
= sd_bus_message_append(tm
, "s", "ExecStart");
795 r
= sd_bus_message_open_container(tm
, 'v', "a(sasb)");
799 r
= sd_bus_message_open_container(tm
, 'a', "(sasb)");
803 r
= sd_bus_message_open_container(tm
, 'r', "sasb");
807 r
= sd_bus_message_append(tm
, "s", path
);
811 r
= sd_bus_message_append_strv(tm
, args
);
815 r
= sd_bus_message_append(tm
, "b", true);
819 r
= sd_bus_message_close_container(tm
);
823 r
= sd_bus_message_close_container(tm
);
827 r
= sd_bus_message_close_container(tm
);
831 r
= sd_bus_message_close_container(tm
);
835 r
= sd_bus_message_close_container(tm
);
839 /* Auxiliary units */
840 r
= sd_bus_message_append(tm
, "a(sa(sv))", 0);
844 r
= sd_bus_call(container_bus
, tm
, 0, error
, NULL
);
848 r
= sd_bus_message_new_method_return(message
, &reply
);
852 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
856 return sd_bus_send(NULL
, reply
, NULL
);
859 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
860 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
861 char mount_slave
[] = "/tmp/propagate.XXXXXX", *mount_tmp
, *mount_outside
, *p
;
862 bool mount_slave_created
= false, mount_slave_mounted
= false,
863 mount_tmp_created
= false, mount_tmp_mounted
= false,
864 mount_outside_created
= false, mount_outside_mounted
= false;
865 const char *dest
, *src
;
866 Machine
*m
= userdata
;
867 int read_only
, make_directory
;
875 if (m
->class != MACHINE_CONTAINER
)
876 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
878 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_directory
);
882 if (!path_is_absolute(src
) || !path_is_safe(src
))
883 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and not contain ../.");
887 else if (!path_is_absolute(dest
) || !path_is_safe(dest
))
888 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and not contain ../.");
890 r
= bus_verify_polkit_async(
893 "org.freedesktop.machine1.manage-machines",
897 &m
->manager
->polkit_registry
,
902 return 1; /* Will call us back */
904 /* One day, when bind mounting /proc/self/fd/n works across
905 * namespace boundaries we should rework this logic to make
908 p
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/");
909 if (laccess(p
, F_OK
) < 0)
910 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Container does not allow propagation of mount points.");
912 /* Our goal is to install a new bind mount into the container,
913 possibly read-only. This is irritatingly complex
914 unfortunately, currently.
916 First, we start by creating a private playground in /tmp,
917 that we can mount MS_SLAVE. (Which is necessary, since
918 MS_MOUNT cannot be applied to mounts with MS_SHARED parent
921 if (!mkdtemp(mount_slave
))
922 return sd_bus_error_set_errnof(error
, errno
, "Failed to create playground %s: %m", mount_slave
);
924 mount_slave_created
= true;
926 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
927 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to make bind mount %s: %m", mount_slave
);
931 mount_slave_mounted
= true;
933 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
934 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount slave %s: %m", mount_slave
);
938 /* Second, we mount the source directory to a directory inside
939 of our MS_SLAVE playground. */
940 mount_tmp
= strjoina(mount_slave
, "/mount");
941 if (mkdir(mount_tmp
, 0700) < 0) {
942 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount point %s: %m", mount_tmp
);
946 mount_tmp_created
= true;
948 if (mount(src
, mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
949 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to overmount %s: %m", mount_tmp
);
953 mount_tmp_mounted
= true;
955 /* Third, we remount the new bind mount read-only if requested. */
957 if (mount(NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
) < 0) {
958 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount read-only %s: %m", mount_tmp
);
962 /* Fourth, we move the new bind mount into the propagation
963 * directory. This way it will appear there read-only
966 mount_outside
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/XXXXXX");
967 if (!mkdtemp(mount_outside
)) {
968 r
= sd_bus_error_set_errnof(error
, errno
, "Cannot create propagation directory %s: %m", mount_outside
);
972 mount_outside_created
= true;
974 if (mount(mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
) < 0) {
975 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to move %s to %s: %m", mount_tmp
, mount_outside
);
979 mount_outside_mounted
= true;
980 mount_tmp_mounted
= false;
982 (void) rmdir(mount_tmp
);
983 mount_tmp_created
= false;
985 (void) umount(mount_slave
);
986 mount_slave_mounted
= false;
988 (void) rmdir(mount_slave
);
989 mount_slave_created
= false;
991 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0) {
992 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
998 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1003 const char *mount_inside
;
1007 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1009 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1010 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1012 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1016 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1017 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1022 (void) mkdir_p(dest
, 0755);
1024 /* Fifth, move the mount to the right place inside */
1025 mount_inside
= strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside
));
1026 if (mount(mount_inside
, dest
, NULL
, MS_MOVE
, NULL
) < 0) {
1027 r
= log_error_errno(errno
, "Failed to mount: %m");
1031 _exit(EXIT_SUCCESS
);
1034 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1035 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1037 _exit(EXIT_FAILURE
);
1040 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1042 r
= wait_for_terminate(child
, &si
);
1044 r
= sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1047 if (si
.si_code
!= CLD_EXITED
) {
1048 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1051 if (si
.si_status
!= EXIT_SUCCESS
) {
1053 if (read(errno_pipe_fd
[0], &r
, sizeof(r
)) == sizeof(r
))
1054 r
= sd_bus_error_set_errnof(error
, r
, "Failed to mount: %m");
1056 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1060 r
= sd_bus_reply_method_return(message
, NULL
);
1063 if (mount_outside_mounted
)
1064 umount(mount_outside
);
1065 if (mount_outside_created
)
1066 rmdir(mount_outside
);
1068 if (mount_tmp_mounted
)
1070 if (mount_tmp_created
)
1073 if (mount_slave_mounted
)
1074 umount(mount_slave
);
1075 if (mount_slave_created
)
1081 static int machine_operation_done(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
1082 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1083 MachineOperation
*o
= userdata
;
1091 if (si
->si_code
!= CLD_EXITED
) {
1092 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1096 if (si
->si_status
!= EXIT_SUCCESS
) {
1097 if (read(o
->errno_fd
, &r
, sizeof(r
)) == sizeof(r
))
1098 r
= sd_bus_error_set_errnof(&error
, r
, "%m");
1100 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1105 r
= sd_bus_reply_method_return(o
->message
, NULL
);
1107 log_error_errno(r
, "Failed to reply to message: %m");
1109 machine_operation_unref(o
);
1113 r
= sd_bus_reply_method_error(o
->message
, &error
);
1115 log_error_errno(r
, "Failed to reply to message: %m");
1117 machine_operation_unref(o
);
1121 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1122 const char *src
, *dest
, *host_path
, *container_path
, *host_basename
, *host_dirname
, *container_basename
, *container_dirname
;
1123 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
1124 _cleanup_close_
int hostfd
= -1;
1125 Machine
*m
= userdata
;
1126 MachineOperation
*o
;
1135 if (m
->n_operations
>= MACHINE_OPERATIONS_MAX
)
1136 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
1138 if (m
->class != MACHINE_CONTAINER
)
1139 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
1141 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
1145 if (!path_is_absolute(src
))
1146 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
1150 else if (!path_is_absolute(dest
))
1151 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
1153 r
= bus_verify_polkit_async(
1156 "org.freedesktop.machine1.manage-machines",
1160 &m
->manager
->polkit_registry
,
1165 return 1; /* Will call us back */
1167 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
1170 container_path
= src
;
1174 container_path
= dest
;
1177 host_basename
= basename(host_path
);
1178 t
= strdupa(host_path
);
1179 host_dirname
= dirname(t
);
1181 container_basename
= basename(container_path
);
1182 t
= strdupa(container_path
);
1183 container_dirname
= dirname(t
);
1185 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1187 return sd_bus_error_set_errnof(error
, errno
, "Failed to open host directory %s: %m", host_dirname
);
1189 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
1190 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1194 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1201 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1203 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1204 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1206 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1210 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1211 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1215 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1216 if (containerfd
< 0) {
1217 r
= log_error_errno(errno
, "Failed top open destination directory: %m");
1222 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, true);
1224 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, true);
1226 hostfd
= safe_close(hostfd
);
1227 containerfd
= safe_close(containerfd
);
1230 r
= log_error_errno(r
, "Failed to copy tree: %m");
1234 _exit(EXIT_SUCCESS
);
1237 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1238 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1240 _exit(EXIT_FAILURE
);
1243 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1245 /* Copying might take a while, hence install a watch the
1246 * child, and return */
1248 o
= new0(MachineOperation
, 1);
1253 o
->message
= sd_bus_message_ref(message
);
1254 o
->errno_fd
= errno_pipe_fd
[0];
1255 errno_pipe_fd
[0] = -1;
1257 r
= sd_event_add_child(m
->manager
->event
, &o
->event_source
, child
, WEXITED
, machine_operation_done
, o
);
1259 machine_operation_unref(o
);
1263 LIST_PREPEND(operations
, m
->operations
, o
);
1270 const sd_bus_vtable machine_vtable
[] = {
1271 SD_BUS_VTABLE_START(0),
1272 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1273 SD_BUS_PROPERTY("Id", "ay", property_get_id
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1274 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1275 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1276 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1277 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1278 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
1279 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1280 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1281 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1282 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1283 SD_BUS_METHOD("Terminate", NULL
, NULL
, bus_machine_method_terminate
, SD_BUS_VTABLE_UNPRIVILEGED
),
1284 SD_BUS_METHOD("Kill", "si", NULL
, bus_machine_method_kill
, SD_BUS_VTABLE_UNPRIVILEGED
),
1285 SD_BUS_METHOD("GetAddresses", NULL
, "a(iay)", bus_machine_method_get_addresses
, SD_BUS_VTABLE_UNPRIVILEGED
),
1286 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_machine_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
1287 SD_BUS_METHOD("OpenPTY", NULL
, "hs", bus_machine_method_open_pty
, SD_BUS_VTABLE_UNPRIVILEGED
),
1288 SD_BUS_METHOD("OpenLogin", NULL
, "hs", bus_machine_method_open_login
, SD_BUS_VTABLE_UNPRIVILEGED
),
1289 SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell
, SD_BUS_VTABLE_UNPRIVILEGED
),
1290 SD_BUS_METHOD("BindMount", "ssbb", NULL
, bus_machine_method_bind_mount
, SD_BUS_VTABLE_UNPRIVILEGED
),
1291 SD_BUS_METHOD("CopyFrom", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1292 SD_BUS_METHOD("CopyTo", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1296 int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1297 Manager
*m
= userdata
;
1307 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1308 _cleanup_bus_creds_unref_ sd_bus_creds
*creds
= NULL
;
1309 sd_bus_message
*message
;
1312 message
= sd_bus_get_current_message(bus
);
1316 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1320 r
= sd_bus_creds_get_pid(creds
, &pid
);
1324 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1328 _cleanup_free_
char *e
= NULL
;
1331 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1335 e
= bus_label_unescape(p
);
1339 machine
= hashmap_get(m
->machines
, e
);
1348 char *machine_bus_path(Machine
*m
) {
1349 _cleanup_free_
char *e
= NULL
;
1353 e
= bus_label_escape(m
->name
);
1357 return strappend("/org/freedesktop/machine1/machine/", e
);
1360 int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1361 _cleanup_strv_free_
char **l
= NULL
;
1362 Machine
*machine
= NULL
;
1363 Manager
*m
= userdata
;
1371 HASHMAP_FOREACH(machine
, m
->machines
, i
) {
1374 p
= machine_bus_path(machine
);
1378 r
= strv_consume(&l
, p
);
1389 int machine_send_signal(Machine
*m
, bool new_machine
) {
1390 _cleanup_free_
char *p
= NULL
;
1394 p
= machine_bus_path(m
);
1398 return sd_bus_emit_signal(
1400 "/org/freedesktop/machine1",
1401 "org.freedesktop.machine1.Manager",
1402 new_machine
? "MachineNew" : "MachineRemoved",
1406 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1407 _cleanup_bus_message_unref_ sd_bus_message
*c
= NULL
;
1408 _cleanup_free_
char *p
= NULL
;
1412 if (!m
->create_message
)
1415 c
= m
->create_message
;
1416 m
->create_message
= NULL
;
1419 return sd_bus_reply_method_error(c
, error
);
1421 /* Update the machine state file before we notify the client
1422 * about the result. */
1425 p
= machine_bus_path(m
);
1429 return sd_bus_reply_method_return(c
, "o", p
);