1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #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 "format-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"
50 #include "signal-util.h"
52 #include "terminal-util.h"
53 #include "user-util.h"
55 static int property_get_state(
58 const char *interface
,
60 sd_bus_message
*reply
,
62 sd_bus_error
*error
) {
64 Machine
*m
= userdata
;
72 state
= machine_state_to_string(machine_get_state(m
));
74 r
= sd_bus_message_append_basic(reply
, 's', state
);
81 static int property_get_netif(
84 const char *interface
,
86 sd_bus_message
*reply
,
88 sd_bus_error
*error
) {
90 Machine
*m
= userdata
;
96 assert_cc(sizeof(int) == sizeof(int32_t));
98 return sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
101 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
103 int bus_machine_method_terminate(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
104 Machine
*m
= userdata
;
110 r
= bus_verify_polkit_async(
113 "org.freedesktop.machine1.manage-machines",
117 &m
->manager
->polkit_registry
,
122 return 1; /* Will call us back */
128 return sd_bus_reply_method_return(message
, NULL
);
131 int bus_machine_method_kill(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
132 Machine
*m
= userdata
;
141 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
148 who
= kill_who_from_string(swho
);
150 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
153 if (!SIGNAL_VALID(signo
))
154 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
156 r
= bus_verify_polkit_async(
159 "org.freedesktop.machine1.manage-machines",
163 &m
->manager
->polkit_registry
,
168 return 1; /* Will call us back */
170 r
= machine_kill(m
, who
, signo
);
174 return sd_bus_reply_method_return(message
, NULL
);
177 int bus_machine_method_get_addresses(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
178 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
179 Machine
*m
= userdata
;
185 r
= sd_bus_message_new_method_return(message
, &reply
);
189 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
196 _cleanup_free_
struct local_address
*addresses
= NULL
;
197 struct local_address
*a
;
200 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
204 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
206 r
= sd_bus_message_open_container(reply
, 'r', "iay");
210 r
= sd_bus_message_append(reply
, "i", addresses
[i
].family
);
214 r
= sd_bus_message_append_array(reply
, 'y', &addresses
[i
].address
, FAMILY_ADDRESS_SIZE(addresses
[i
].family
));
218 r
= sd_bus_message_close_container(reply
);
226 case MACHINE_CONTAINER
: {
227 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
228 _cleanup_free_
char *us
= NULL
, *them
= NULL
;
229 _cleanup_close_
int netns_fd
= -1;
234 r
= readlink_malloc("/proc/self/ns/net", &us
);
238 p
= procfs_file_alloca(m
->leader
, "ns/net");
239 r
= readlink_malloc(p
, &them
);
244 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
246 r
= namespace_open(m
->leader
, NULL
, NULL
, &netns_fd
, NULL
, NULL
);
250 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
255 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
258 _cleanup_free_
struct local_address
*addresses
= NULL
;
259 struct local_address
*a
;
262 pair
[0] = safe_close(pair
[0]);
264 r
= namespace_enter(-1, -1, netns_fd
, -1, -1);
268 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
272 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
273 struct iovec iov
[2] = {
274 { .iov_base
= &a
->family
, .iov_len
= sizeof(a
->family
) },
275 { .iov_base
= &a
->address
, .iov_len
= FAMILY_ADDRESS_SIZE(a
->family
) },
278 r
= writev(pair
[1], iov
, 2);
283 pair
[1] = safe_close(pair
[1]);
288 pair
[1] = safe_close(pair
[1]);
293 union in_addr_union in_addr
;
300 iov
[0] = (struct iovec
) { .iov_base
= &family
, .iov_len
= sizeof(family
) };
301 iov
[1] = (struct iovec
) { .iov_base
= &in_addr
, .iov_len
= sizeof(in_addr
) };
303 n
= recvmsg(pair
[0], &mh
, 0);
306 if ((size_t) n
< sizeof(family
))
309 r
= sd_bus_message_open_container(reply
, 'r', "iay");
313 r
= sd_bus_message_append(reply
, "i", family
);
320 if (n
!= sizeof(struct in_addr
) + sizeof(family
))
323 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in
, sizeof(in_addr
.in
));
327 if (n
!= sizeof(struct in6_addr
) + sizeof(family
))
330 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in6
, sizeof(in_addr
.in6
));
336 r
= sd_bus_message_close_container(reply
);
341 r
= wait_for_terminate(child
, &si
);
343 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
344 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
345 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
350 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting IP address data is only supported on container machines.");
353 r
= sd_bus_message_close_container(reply
);
357 return sd_bus_send(NULL
, reply
, NULL
);
360 #define EXIT_NOT_FOUND 2
362 int bus_machine_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
363 _cleanup_strv_free_
char **l
= NULL
;
364 Machine
*m
= userdata
;
373 r
= load_env_file_pairs(NULL
, "/etc/os-release", NULL
, &l
);
379 case MACHINE_CONTAINER
: {
380 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
381 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
382 _cleanup_fclose_
FILE *f
= NULL
;
386 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, NULL
, &root_fd
);
390 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
395 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
400 pair
[0] = safe_close(pair
[0]);
402 r
= namespace_enter(-1, mntns_fd
, -1, -1, root_fd
);
406 fd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
407 if (fd
< 0 && errno
== ENOENT
) {
408 fd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
409 if (fd
< 0 && errno
== ENOENT
)
410 _exit(EXIT_NOT_FOUND
);
415 r
= copy_bytes(fd
, pair
[1], (uint64_t) -1, 0);
422 pair
[1] = safe_close(pair
[1]);
424 f
= fdopen(pair
[0], "re");
430 r
= load_env_file_pairs(f
, "/etc/os-release", NULL
, &l
);
434 r
= wait_for_terminate(child
, &si
);
436 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
437 if (si
.si_code
== CLD_EXITED
&& si
.si_status
== EXIT_NOT_FOUND
)
438 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Machine does not contain OS release information");
439 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
440 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
446 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting OS release data is only supported on container machines.");
449 return bus_reply_pair_array(message
, l
);
452 int bus_machine_method_open_pty(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
453 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
454 _cleanup_free_
char *pty_name
= NULL
;
455 _cleanup_close_
int master
= -1;
456 Machine
*m
= userdata
;
462 r
= bus_verify_polkit_async(
465 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
469 &m
->manager
->polkit_registry
,
474 return 1; /* Will call us back */
476 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
480 r
= ptsname_namespace(master
, &pty_name
);
484 r
= sd_bus_message_new_method_return(message
, &reply
);
488 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
492 return sd_bus_send(NULL
, reply
, NULL
);
495 static int container_bus_new(Machine
*m
, sd_bus_error
*error
, sd_bus
**ret
) {
507 case MACHINE_CONTAINER
: {
508 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
511 r
= sd_bus_new(&bus
);
515 if (asprintf(&address
, "x-machine-kernel:pid=%1$" PID_PRI
";x-machine-unix:pid=%1$" PID_PRI
, m
->leader
) < 0)
518 bus
->address
= address
;
519 bus
->bus_client
= true;
520 bus
->trusted
= false;
521 bus
->is_system
= true;
523 r
= sd_bus_start(bus
);
525 return sd_bus_error_set_errnof(error
, r
, "There is no system bus in container %s.", m
->name
);
541 int bus_machine_method_open_login(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
542 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
543 _cleanup_free_
char *pty_name
= NULL
;
544 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
545 _cleanup_close_
int master
= -1;
546 sd_bus
*container_bus
= NULL
;
547 Machine
*m
= userdata
;
548 const char *p
, *getty
;
554 r
= bus_verify_polkit_async(
557 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
561 &m
->manager
->polkit_registry
,
566 return 1; /* Will call us back */
568 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
572 r
= ptsname_namespace(master
, &pty_name
);
576 p
= path_startswith(pty_name
, "/dev/pts/");
578 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "PTS name %s is invalid", pty_name
);
580 r
= container_bus_new(m
, error
, &allocated_bus
);
584 container_bus
= allocated_bus
?: m
->manager
->bus
;
586 getty
= strjoina("container-getty@", p
, ".service");
588 r
= sd_bus_call_method(
590 "org.freedesktop.systemd1",
591 "/org/freedesktop/systemd1",
592 "org.freedesktop.systemd1.Manager",
595 "ss", getty
, "replace");
599 r
= sd_bus_message_new_method_return(message
, &reply
);
603 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
607 return sd_bus_send(NULL
, reply
, NULL
);
610 int bus_machine_method_open_shell(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
611 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *tm
= NULL
;
612 _cleanup_free_
char *pty_name
= NULL
;
613 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
614 sd_bus
*container_bus
= NULL
;
615 _cleanup_close_
int master
= -1, slave
= -1;
616 _cleanup_strv_free_
char **env
= NULL
, **args
= NULL
;
617 Machine
*m
= userdata
;
618 const char *p
, *unit
, *user
, *path
, *description
, *utmp_id
;
624 r
= sd_bus_message_read(message
, "ss", &user
, &path
);
627 user
= empty_to_null(user
);
630 if (!path_is_absolute(path
))
631 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Specified path '%s' is not absolute", path
);
633 r
= sd_bus_message_read_strv(message
, &args
);
636 if (strv_isempty(args
)) {
637 args
= strv_free(args
);
639 args
= strv_new(path
, NULL
);
643 args
[0][0] = '-'; /* Tell /bin/sh that this shall be a login shell */
646 r
= sd_bus_message_read_strv(message
, &env
);
649 if (!strv_env_is_valid(env
))
650 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid environment assignments");
652 r
= bus_verify_polkit_async(
655 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
659 &m
->manager
->polkit_registry
,
664 return 1; /* Will call us back */
666 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
670 r
= ptsname_namespace(master
, &pty_name
);
674 p
= path_startswith(pty_name
, "/dev/pts/");
677 slave
= machine_open_terminal(m
, pty_name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
681 utmp_id
= path_startswith(pty_name
, "/dev/");
684 r
= container_bus_new(m
, error
, &allocated_bus
);
688 container_bus
= allocated_bus
?: m
->manager
->bus
;
690 r
= sd_bus_message_new_method_call(
693 "org.freedesktop.systemd1",
694 "/org/freedesktop/systemd1",
695 "org.freedesktop.systemd1.Manager",
696 "StartTransientUnit");
701 unit
= strjoina("container-shell@", p
, ".service");
702 r
= sd_bus_message_append(tm
, "ss", unit
, "fail");
707 r
= sd_bus_message_open_container(tm
, 'a', "(sv)");
711 description
= strjoina("Shell for User ", isempty(user
) ? "root" : user
);
712 r
= sd_bus_message_append(tm
,
713 "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
714 "Description", "s", description
,
715 "StandardInputFileDescriptor", "h", slave
,
716 "StandardOutputFileDescriptor", "h", slave
,
717 "StandardErrorFileDescriptor", "h", slave
,
718 "SendSIGHUP", "b", true,
719 "IgnoreSIGPIPE", "b", false,
720 "KillMode", "s", "mixed",
721 "TTYReset", "b", true,
722 "UtmpIdentifier", "s", utmp_id
,
723 "UtmpMode", "s", "user",
724 "PAMName", "s", "login",
725 "WorkingDirectory", "s", "-~");
729 r
= sd_bus_message_append(tm
, "(sv)", "User", "s", isempty(user
) ? "root" : user
);
733 if (!strv_isempty(env
)) {
734 r
= sd_bus_message_open_container(tm
, 'r', "sv");
738 r
= sd_bus_message_append(tm
, "s", "Environment");
742 r
= sd_bus_message_open_container(tm
, 'v', "as");
746 r
= sd_bus_message_append_strv(tm
, env
);
750 r
= sd_bus_message_close_container(tm
);
754 r
= sd_bus_message_close_container(tm
);
760 r
= sd_bus_message_open_container(tm
, 'r', "sv");
764 r
= sd_bus_message_append(tm
, "s", "ExecStart");
768 r
= sd_bus_message_open_container(tm
, 'v', "a(sasb)");
772 r
= sd_bus_message_open_container(tm
, 'a', "(sasb)");
776 r
= sd_bus_message_open_container(tm
, 'r', "sasb");
780 r
= sd_bus_message_append(tm
, "s", path
);
784 r
= sd_bus_message_append_strv(tm
, args
);
788 r
= sd_bus_message_append(tm
, "b", true);
792 r
= sd_bus_message_close_container(tm
);
796 r
= sd_bus_message_close_container(tm
);
800 r
= sd_bus_message_close_container(tm
);
804 r
= sd_bus_message_close_container(tm
);
808 r
= sd_bus_message_close_container(tm
);
812 /* Auxiliary units */
813 r
= sd_bus_message_append(tm
, "a(sa(sv))", 0);
817 r
= sd_bus_call(container_bus
, tm
, 0, error
, NULL
);
821 slave
= safe_close(slave
);
823 r
= sd_bus_message_new_method_return(message
, &reply
);
827 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
831 return sd_bus_send(NULL
, reply
, NULL
);
834 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
835 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
836 char mount_slave
[] = "/tmp/propagate.XXXXXX", *mount_tmp
, *mount_outside
, *p
;
837 bool mount_slave_created
= false, mount_slave_mounted
= false,
838 mount_tmp_created
= false, mount_tmp_mounted
= false,
839 mount_outside_created
= false, mount_outside_mounted
= false;
840 _cleanup_free_
char *chased_src
= NULL
;
841 int read_only
, make_file_or_directory
;
842 const char *dest
, *src
;
843 Machine
*m
= userdata
;
853 if (m
->class != MACHINE_CONTAINER
)
854 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
856 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_file_or_directory
);
860 if (!path_is_absolute(src
) || !path_is_normalized(src
))
861 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and not contain ../.");
865 else if (!path_is_absolute(dest
) || !path_is_normalized(dest
))
866 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and not contain ../.");
868 r
= bus_verify_polkit_async(
871 "org.freedesktop.machine1.manage-machines",
875 &m
->manager
->polkit_registry
,
880 return 1; /* Will call us back */
882 r
= machine_get_uid_shift(m
, &uid
);
886 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Can't bind mount on container with user namespacing applied.");
888 /* One day, when bind mounting /proc/self/fd/n works across
889 * namespace boundaries we should rework this logic to make
892 p
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/");
893 if (laccess(p
, F_OK
) < 0)
894 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Container does not allow propagation of mount points.");
896 r
= chase_symlinks(src
, NULL
, 0, &chased_src
);
898 return sd_bus_error_set_errnof(error
, r
, "Failed to resolve source path: %m");
900 if (lstat(chased_src
, &st
) < 0)
901 return sd_bus_error_set_errnof(error
, errno
, "Failed to stat() source path: %m");
902 if (S_ISLNK(st
.st_mode
)) /* This shouldn't really happen, given that we just chased the symlinks above, but let's better be safe… */
903 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Source directory can't be a symbolic link");
905 /* Our goal is to install a new bind mount into the container,
906 possibly read-only. This is irritatingly complex
907 unfortunately, currently.
909 First, we start by creating a private playground in /tmp,
910 that we can mount MS_SLAVE. (Which is necessary, since
911 MS_MOVE cannot be applied to mounts with MS_SHARED parent
914 if (!mkdtemp(mount_slave
))
915 return sd_bus_error_set_errnof(error
, errno
, "Failed to create playground %s: %m", mount_slave
);
917 mount_slave_created
= true;
919 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
920 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to make bind mount %s: %m", mount_slave
);
924 mount_slave_mounted
= true;
926 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
927 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount slave %s: %m", mount_slave
);
931 /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
932 mount_tmp
= strjoina(mount_slave
, "/mount");
933 if (S_ISDIR(st
.st_mode
))
934 r
= mkdir(mount_tmp
, 0700) < 0 ? -errno
: 0;
936 r
= touch(mount_tmp
);
938 sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount point %s: %m", mount_tmp
);
942 mount_tmp_created
= true;
944 if (mount(chased_src
, mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
945 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount %s: %m", chased_src
);
949 mount_tmp_mounted
= true;
951 /* Third, we remount the new bind mount read-only if requested. */
953 if (mount(NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
) < 0) {
954 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount read-only %s: %m", mount_tmp
);
958 /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only
961 mount_outside
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/XXXXXX");
962 if (S_ISDIR(st
.st_mode
))
963 r
= mkdtemp(mount_outside
) ? 0 : -errno
;
965 r
= mkostemp_safe(mount_outside
);
969 sd_bus_error_set_errnof(error
, errno
, "Cannot create propagation file or directory %s: %m", mount_outside
);
973 mount_outside_created
= true;
975 if (mount(mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
) < 0) {
976 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to move %s to %s: %m", mount_tmp
, mount_outside
);
980 mount_outside_mounted
= true;
981 mount_tmp_mounted
= false;
983 if (S_ISDIR(st
.st_mode
))
984 (void) rmdir(mount_tmp
);
986 (void) unlink(mount_tmp
);
987 mount_tmp_created
= false;
989 (void) umount(mount_slave
);
990 mount_slave_mounted
= false;
992 (void) rmdir(mount_slave
);
993 mount_slave_created
= false;
995 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0) {
996 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1002 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1007 const char *mount_inside
;
1011 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1013 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1014 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1016 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1020 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1021 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1025 if (make_file_or_directory
) {
1026 if (S_ISDIR(st
.st_mode
))
1027 (void) mkdir_p(dest
, 0755);
1029 (void) mkdir_parents(dest
, 0755);
1030 safe_close(open(dest
, O_CREAT
|O_EXCL
|O_WRONLY
|O_CLOEXEC
|O_NOCTTY
, 0600));
1034 /* Fifth, move the mount to the right place inside */
1035 mount_inside
= strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside
));
1036 if (mount(mount_inside
, dest
, NULL
, MS_MOVE
, NULL
) < 0) {
1037 r
= log_error_errno(errno
, "Failed to mount: %m");
1041 _exit(EXIT_SUCCESS
);
1044 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1045 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1047 _exit(EXIT_FAILURE
);
1050 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1052 r
= wait_for_terminate(child
, &si
);
1054 r
= sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1057 if (si
.si_code
!= CLD_EXITED
) {
1058 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1061 if (si
.si_status
!= EXIT_SUCCESS
) {
1063 if (read(errno_pipe_fd
[0], &r
, sizeof(r
)) == sizeof(r
))
1064 r
= sd_bus_error_set_errnof(error
, r
, "Failed to mount: %m");
1066 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1070 r
= sd_bus_reply_method_return(message
, NULL
);
1073 if (mount_outside_mounted
)
1074 (void) umount(mount_outside
);
1075 if (mount_outside_created
) {
1076 if (S_ISDIR(st
.st_mode
))
1077 (void) rmdir(mount_outside
);
1079 (void) unlink(mount_outside
);
1082 if (mount_tmp_mounted
)
1083 (void) umount(mount_tmp
);
1084 if (mount_tmp_created
) {
1085 if (S_ISDIR(st
.st_mode
))
1086 (void) rmdir(mount_tmp
);
1088 (void) unlink(mount_tmp
);
1091 if (mount_slave_mounted
)
1092 (void) umount(mount_slave
);
1093 if (mount_slave_created
)
1094 (void) rmdir(mount_slave
);
1099 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1100 const char *src
, *dest
, *host_path
, *container_path
, *host_basename
, *host_dirname
, *container_basename
, *container_dirname
;
1101 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
1102 CopyFlags copy_flags
= COPY_REFLINK
|COPY_MERGE
;
1103 _cleanup_close_
int hostfd
= -1;
1104 Machine
*m
= userdata
;
1114 if (m
->manager
->n_operations
>= OPERATIONS_MAX
)
1115 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
1117 if (m
->class != MACHINE_CONTAINER
)
1118 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
1120 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
1124 if (!path_is_absolute(src
))
1125 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
1129 else if (!path_is_absolute(dest
))
1130 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
1132 r
= bus_verify_polkit_async(
1135 "org.freedesktop.machine1.manage-machines",
1139 &m
->manager
->polkit_registry
,
1144 return 1; /* Will call us back */
1146 r
= machine_get_uid_shift(m
, &uid_shift
);
1150 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
1153 container_path
= src
;
1157 container_path
= dest
;
1160 host_basename
= basename(host_path
);
1161 t
= strdupa(host_path
);
1162 host_dirname
= dirname(t
);
1164 container_basename
= basename(container_path
);
1165 t
= strdupa(container_path
);
1166 container_dirname
= dirname(t
);
1168 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1170 return sd_bus_error_set_errnof(error
, errno
, "Failed to open host directory %s: %m", host_dirname
);
1172 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
1173 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1177 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1184 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1186 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1187 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1189 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1193 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1194 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1198 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1199 if (containerfd
< 0) {
1200 r
= log_error_errno(errno
, "Failed top open destination directory: %m");
1204 /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
1205 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1206 * the UID/GIDs as they are. */
1208 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, uid_shift
== 0 ? UID_INVALID
: 0, uid_shift
== 0 ? GID_INVALID
: 0, copy_flags
);
1210 r
= copy_tree_at(hostfd
, host_basename
, containerfd
, container_basename
, uid_shift
== 0 ? UID_INVALID
: uid_shift
, uid_shift
== 0 ? GID_INVALID
: uid_shift
, copy_flags
);
1212 hostfd
= safe_close(hostfd
);
1213 containerfd
= safe_close(containerfd
);
1216 r
= log_error_errno(r
, "Failed to copy tree: %m");
1220 _exit(EXIT_SUCCESS
);
1223 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1224 _exit(EXIT_FAILURE
);
1227 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1229 /* Copying might take a while, hence install a watch on the child, and return */
1231 r
= operation_new(m
->manager
, m
, child
, message
, errno_pipe_fd
[0], NULL
);
1233 (void) sigkill_wait(child
);
1236 errno_pipe_fd
[0] = -1;
1241 int bus_machine_method_open_root_directory(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1242 _cleanup_close_
int fd
= -1;
1243 Machine
*m
= userdata
;
1249 r
= bus_verify_polkit_async(
1252 "org.freedesktop.machine1.manage-machines",
1256 &m
->manager
->polkit_registry
,
1261 return 1; /* Will call us back */
1266 fd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1272 case MACHINE_CONTAINER
: {
1273 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
1274 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
1278 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, NULL
, &root_fd
);
1282 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1287 return sd_bus_error_set_errnof(error
, errno
, "Failed to fork(): %m");
1290 _cleanup_close_
int dfd
= -1;
1292 pair
[0] = safe_close(pair
[0]);
1294 r
= namespace_enter(-1, mntns_fd
, -1, -1, root_fd
);
1296 _exit(EXIT_FAILURE
);
1298 dfd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1300 _exit(EXIT_FAILURE
);
1302 r
= send_one_fd(pair
[1], dfd
, 0);
1303 dfd
= safe_close(dfd
);
1305 _exit(EXIT_FAILURE
);
1307 _exit(EXIT_SUCCESS
);
1310 pair
[1] = safe_close(pair
[1]);
1312 r
= wait_for_terminate(child
, &si
);
1314 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1315 if (si
.si_code
!= CLD_EXITED
|| si
.si_status
!= EXIT_SUCCESS
)
1316 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1318 fd
= receive_one_fd(pair
[0], MSG_DONTWAIT
);
1326 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Opening the root directory is only supported on container machines.");
1329 return sd_bus_reply_method_return(message
, "h", fd
);
1332 int bus_machine_method_get_uid_shift(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1333 Machine
*m
= userdata
;
1340 /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
1341 * we kinda have to for this. */
1343 if (m
->class == MACHINE_HOST
)
1344 return sd_bus_reply_method_return(message
, "u", UINT32_C(0));
1346 if (m
->class != MACHINE_CONTAINER
)
1347 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "UID/GID shift may only be determined for container machines.");
1349 r
= machine_get_uid_shift(m
, &shift
);
1351 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m
->name
);
1355 return sd_bus_reply_method_return(message
, "u", (uint32_t) shift
);
1358 const sd_bus_vtable machine_vtable
[] = {
1359 SD_BUS_VTABLE_START(0),
1360 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1361 SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128
, offsetof(Machine
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1362 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1363 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1364 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1365 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1366 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
1367 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1368 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1369 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1370 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1371 SD_BUS_METHOD("Terminate", NULL
, NULL
, bus_machine_method_terminate
, SD_BUS_VTABLE_UNPRIVILEGED
),
1372 SD_BUS_METHOD("Kill", "si", NULL
, bus_machine_method_kill
, SD_BUS_VTABLE_UNPRIVILEGED
),
1373 SD_BUS_METHOD("GetAddresses", NULL
, "a(iay)", bus_machine_method_get_addresses
, SD_BUS_VTABLE_UNPRIVILEGED
),
1374 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_machine_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
1375 SD_BUS_METHOD("GetUIDShift", NULL
, "u", bus_machine_method_get_uid_shift
, SD_BUS_VTABLE_UNPRIVILEGED
),
1376 SD_BUS_METHOD("OpenPTY", NULL
, "hs", bus_machine_method_open_pty
, SD_BUS_VTABLE_UNPRIVILEGED
),
1377 SD_BUS_METHOD("OpenLogin", NULL
, "hs", bus_machine_method_open_login
, SD_BUS_VTABLE_UNPRIVILEGED
),
1378 SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell
, SD_BUS_VTABLE_UNPRIVILEGED
),
1379 SD_BUS_METHOD("BindMount", "ssbb", NULL
, bus_machine_method_bind_mount
, SD_BUS_VTABLE_UNPRIVILEGED
),
1380 SD_BUS_METHOD("CopyFrom", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1381 SD_BUS_METHOD("CopyTo", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1382 SD_BUS_METHOD("OpenRootDirectory", NULL
, "h", bus_machine_method_open_root_directory
, SD_BUS_VTABLE_UNPRIVILEGED
),
1386 int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1387 Manager
*m
= userdata
;
1397 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1398 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
1399 sd_bus_message
*message
;
1402 message
= sd_bus_get_current_message(bus
);
1406 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1410 r
= sd_bus_creds_get_pid(creds
, &pid
);
1414 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1418 _cleanup_free_
char *e
= NULL
;
1421 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1425 e
= bus_label_unescape(p
);
1429 machine
= hashmap_get(m
->machines
, e
);
1438 char *machine_bus_path(Machine
*m
) {
1439 _cleanup_free_
char *e
= NULL
;
1443 e
= bus_label_escape(m
->name
);
1447 return strappend("/org/freedesktop/machine1/machine/", e
);
1450 int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1451 _cleanup_strv_free_
char **l
= NULL
;
1452 Machine
*machine
= NULL
;
1453 Manager
*m
= userdata
;
1461 HASHMAP_FOREACH(machine
, m
->machines
, i
) {
1464 p
= machine_bus_path(machine
);
1468 r
= strv_consume(&l
, p
);
1479 int machine_send_signal(Machine
*m
, bool new_machine
) {
1480 _cleanup_free_
char *p
= NULL
;
1484 p
= machine_bus_path(m
);
1488 return sd_bus_emit_signal(
1490 "/org/freedesktop/machine1",
1491 "org.freedesktop.machine1.Manager",
1492 new_machine
? "MachineNew" : "MachineRemoved",
1496 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1497 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*c
= NULL
;
1498 _cleanup_free_
char *p
= NULL
;
1502 if (!m
->create_message
)
1505 c
= m
->create_message
;
1506 m
->create_message
= NULL
;
1509 return sd_bus_reply_method_error(c
, error
);
1511 /* Update the machine state file before we notify the client
1512 * about the result. */
1515 p
= machine_bus_path(m
);
1519 return sd_bus_reply_method_return(c
, "o", p
);