1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
10 #include <sys/mount.h>
13 /* When we include libgen.h because we need dirname() we immediately
14 * undefine basename() since libgen.h defines it as a macro to the POSIX
15 * version which is really broken. We prefer GNU basename(). */
19 #include "alloc-util.h"
20 #include "bus-common-errors.h"
21 #include "bus-internal.h"
22 #include "bus-label.h"
28 #include "format-util.h"
30 #include "in-addr-util.h"
31 #include "local-addresses.h"
32 #include "machine-dbus.h"
35 #include "path-util.h"
36 #include "process-util.h"
37 #include "signal-util.h"
39 #include "terminal-util.h"
40 #include "user-util.h"
42 static int property_get_state(
45 const char *interface
,
47 sd_bus_message
*reply
,
49 sd_bus_error
*error
) {
51 Machine
*m
= userdata
;
59 state
= machine_state_to_string(machine_get_state(m
));
61 r
= sd_bus_message_append_basic(reply
, 's', state
);
68 static int property_get_netif(
71 const char *interface
,
73 sd_bus_message
*reply
,
75 sd_bus_error
*error
) {
77 Machine
*m
= userdata
;
83 assert_cc(sizeof(int) == sizeof(int32_t));
85 return sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
88 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
90 int bus_machine_method_terminate(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
91 Machine
*m
= userdata
;
97 r
= bus_verify_polkit_async(
100 "org.freedesktop.machine1.manage-machines",
104 &m
->manager
->polkit_registry
,
109 return 1; /* Will call us back */
115 return sd_bus_reply_method_return(message
, NULL
);
118 int bus_machine_method_kill(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
119 Machine
*m
= userdata
;
128 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
135 who
= kill_who_from_string(swho
);
137 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
140 if (!SIGNAL_VALID(signo
))
141 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
143 r
= bus_verify_polkit_async(
146 "org.freedesktop.machine1.manage-machines",
150 &m
->manager
->polkit_registry
,
155 return 1; /* Will call us back */
157 r
= machine_kill(m
, who
, signo
);
161 return sd_bus_reply_method_return(message
, NULL
);
164 int bus_machine_method_get_addresses(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
165 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
166 Machine
*m
= userdata
;
172 r
= sd_bus_message_new_method_return(message
, &reply
);
176 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
183 _cleanup_free_
struct local_address
*addresses
= NULL
;
184 struct local_address
*a
;
187 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
191 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
193 r
= sd_bus_message_open_container(reply
, 'r', "iay");
197 r
= sd_bus_message_append(reply
, "i", addresses
[i
].family
);
201 r
= sd_bus_message_append_array(reply
, 'y', &addresses
[i
].address
, FAMILY_ADDRESS_SIZE(addresses
[i
].family
));
205 r
= sd_bus_message_close_container(reply
);
213 case MACHINE_CONTAINER
: {
214 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
215 _cleanup_free_
char *us
= NULL
, *them
= NULL
;
216 _cleanup_close_
int netns_fd
= -1;
220 r
= readlink_malloc("/proc/self/ns/net", &us
);
224 p
= procfs_file_alloca(m
->leader
, "ns/net");
225 r
= readlink_malloc(p
, &them
);
230 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
232 r
= namespace_open(m
->leader
, NULL
, NULL
, &netns_fd
, NULL
, NULL
);
236 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
239 r
= safe_fork("(sd-addr)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &child
);
241 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
243 _cleanup_free_
struct local_address
*addresses
= NULL
;
244 struct local_address
*a
;
247 pair
[0] = safe_close(pair
[0]);
249 r
= namespace_enter(-1, -1, netns_fd
, -1, -1);
253 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
257 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
258 struct iovec iov
[2] = {
259 { .iov_base
= &a
->family
, .iov_len
= sizeof(a
->family
) },
260 { .iov_base
= &a
->address
, .iov_len
= FAMILY_ADDRESS_SIZE(a
->family
) },
263 r
= writev(pair
[1], iov
, 2);
268 pair
[1] = safe_close(pair
[1]);
273 pair
[1] = safe_close(pair
[1]);
278 union in_addr_union in_addr
;
285 iov
[0] = (struct iovec
) { .iov_base
= &family
, .iov_len
= sizeof(family
) };
286 iov
[1] = (struct iovec
) { .iov_base
= &in_addr
, .iov_len
= sizeof(in_addr
) };
288 n
= recvmsg(pair
[0], &mh
, 0);
291 if ((size_t) n
< sizeof(family
))
294 r
= sd_bus_message_open_container(reply
, 'r', "iay");
298 r
= sd_bus_message_append(reply
, "i", family
);
305 if (n
!= sizeof(struct in_addr
) + sizeof(family
))
308 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in
, sizeof(in_addr
.in
));
312 if (n
!= sizeof(struct in6_addr
) + sizeof(family
))
315 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in6
, sizeof(in_addr
.in6
));
321 r
= sd_bus_message_close_container(reply
);
326 r
= wait_for_terminate_and_check("(sd-addr)", child
, 0);
328 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
329 if (r
!= EXIT_SUCCESS
)
330 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
335 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting IP address data is only supported on container machines.");
338 r
= sd_bus_message_close_container(reply
);
342 return sd_bus_send(NULL
, reply
, NULL
);
345 #define EXIT_NOT_FOUND 2
347 int bus_machine_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
348 _cleanup_strv_free_
char **l
= NULL
;
349 Machine
*m
= userdata
;
358 r
= load_env_file_pairs(NULL
, "/etc/os-release", NULL
, &l
);
364 case MACHINE_CONTAINER
: {
365 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
366 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
367 _cleanup_fclose_
FILE *f
= NULL
;
370 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, NULL
, &root_fd
);
374 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
377 r
= safe_fork("(sd-osrel)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &child
);
379 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
383 pair
[0] = safe_close(pair
[0]);
385 r
= namespace_enter(-1, mntns_fd
, -1, -1, root_fd
);
389 fd
= open("/etc/os-release", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
390 if (fd
< 0 && errno
== ENOENT
) {
391 fd
= open("/usr/lib/os-release", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
392 if (fd
< 0 && errno
== ENOENT
)
393 _exit(EXIT_NOT_FOUND
);
398 r
= copy_bytes(fd
, pair
[1], (uint64_t) -1, 0);
405 pair
[1] = safe_close(pair
[1]);
407 f
= fdopen(pair
[0], "re");
413 r
= load_env_file_pairs(f
, "/etc/os-release", NULL
, &l
);
417 r
= wait_for_terminate_and_check("(sd-osrel)", child
, 0);
419 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
420 if (r
== EXIT_NOT_FOUND
)
421 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Machine does not contain OS release information");
422 if (r
!= EXIT_SUCCESS
)
423 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
429 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting OS release data is only supported on container machines.");
432 return bus_reply_pair_array(message
, l
);
435 int bus_machine_method_open_pty(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
436 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
437 _cleanup_free_
char *pty_name
= NULL
;
438 _cleanup_close_
int master
= -1;
439 Machine
*m
= userdata
;
445 r
= bus_verify_polkit_async(
448 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
452 &m
->manager
->polkit_registry
,
457 return 1; /* Will call us back */
459 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
463 r
= ptsname_namespace(master
, &pty_name
);
467 r
= sd_bus_message_new_method_return(message
, &reply
);
471 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
475 return sd_bus_send(NULL
, reply
, NULL
);
478 static int container_bus_new(Machine
*m
, sd_bus_error
*error
, sd_bus
**ret
) {
490 case MACHINE_CONTAINER
: {
491 _cleanup_(sd_bus_unrefp
) sd_bus
*bus
= NULL
;
494 r
= sd_bus_new(&bus
);
498 if (asprintf(&address
, "x-machine-kernel:pid=%1$" PID_PRI
";x-machine-unix:pid=%1$" PID_PRI
, m
->leader
) < 0)
501 bus
->address
= address
;
502 bus
->bus_client
= true;
503 bus
->trusted
= false;
504 bus
->is_system
= true;
506 r
= sd_bus_start(bus
);
508 return sd_bus_error_set_errnof(error
, r
, "There is no system bus in container %s.", m
->name
);
512 *ret
= TAKE_PTR(bus
);
523 int bus_machine_method_open_login(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
524 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
525 _cleanup_free_
char *pty_name
= NULL
;
526 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
527 _cleanup_close_
int master
= -1;
528 sd_bus
*container_bus
= NULL
;
529 Machine
*m
= userdata
;
530 const char *p
, *getty
;
536 r
= bus_verify_polkit_async(
539 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
543 &m
->manager
->polkit_registry
,
548 return 1; /* Will call us back */
550 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
554 r
= ptsname_namespace(master
, &pty_name
);
558 p
= path_startswith(pty_name
, "/dev/pts/");
560 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "PTS name %s is invalid", pty_name
);
562 r
= container_bus_new(m
, error
, &allocated_bus
);
566 container_bus
= allocated_bus
?: m
->manager
->bus
;
568 getty
= strjoina("container-getty@", p
, ".service");
570 r
= sd_bus_call_method(
572 "org.freedesktop.systemd1",
573 "/org/freedesktop/systemd1",
574 "org.freedesktop.systemd1.Manager",
577 "ss", getty
, "replace");
581 r
= sd_bus_message_new_method_return(message
, &reply
);
585 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
589 return sd_bus_send(NULL
, reply
, NULL
);
592 int bus_machine_method_open_shell(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
593 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *tm
= NULL
;
594 _cleanup_free_
char *pty_name
= NULL
;
595 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
596 sd_bus
*container_bus
= NULL
;
597 _cleanup_close_
int master
= -1, slave
= -1;
598 _cleanup_strv_free_
char **env
= NULL
, **args_wire
= NULL
, **args
= NULL
;
599 Machine
*m
= userdata
;
600 const char *p
, *unit
, *user
, *path
, *description
, *utmp_id
;
606 r
= sd_bus_message_read(message
, "ss", &user
, &path
);
609 user
= empty_to_null(user
);
610 r
= sd_bus_message_read_strv(message
, &args_wire
);
616 args
= new0(char*, 3 + 1);
619 args
[0] = strdup("sh");
622 args
[1] = strdup("-c");
625 r
= asprintf(&args
[2],
626 "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
627 "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
628 isempty(user
) ? "root" : user
);
634 if (!path_is_absolute(path
))
635 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Specified path '%s' is not absolute", path
);
636 args
= TAKE_PTR(args_wire
);
637 if (strv_isempty(args
)) {
638 args
= strv_free(args
);
640 args
= strv_new(path
, NULL
);
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
;
852 if (m
->class != MACHINE_CONTAINER
)
853 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
855 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_file_or_directory
);
859 if (!path_is_absolute(src
) || !path_is_normalized(src
))
860 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and not contain ../.");
864 else if (!path_is_absolute(dest
) || !path_is_normalized(dest
))
865 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and not contain ../.");
867 r
= bus_verify_polkit_async(
870 "org.freedesktop.machine1.manage-machines",
874 &m
->manager
->polkit_registry
,
879 return 1; /* Will call us back */
881 r
= machine_get_uid_shift(m
, &uid
);
885 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Can't bind mount on container with user namespacing applied.");
887 /* One day, when bind mounting /proc/self/fd/n works across
888 * namespace boundaries we should rework this logic to make
891 p
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/");
892 if (laccess(p
, F_OK
) < 0)
893 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Container does not allow propagation of mount points.");
895 r
= chase_symlinks(src
, NULL
, CHASE_TRAIL_SLASH
, &chased_src
);
897 return sd_bus_error_set_errnof(error
, r
, "Failed to resolve source path: %m");
899 if (lstat(chased_src
, &st
) < 0)
900 return sd_bus_error_set_errnof(error
, errno
, "Failed to stat() source path: %m");
901 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… */
902 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Source directory can't be a symbolic link");
904 /* Our goal is to install a new bind mount into the container,
905 possibly read-only. This is irritatingly complex
906 unfortunately, currently.
908 First, we start by creating a private playground in /tmp,
909 that we can mount MS_SLAVE. (Which is necessary, since
910 MS_MOVE cannot be applied to mounts with MS_SHARED parent
913 if (!mkdtemp(mount_slave
))
914 return sd_bus_error_set_errnof(error
, errno
, "Failed to create playground %s: %m", mount_slave
);
916 mount_slave_created
= true;
918 if (mount(mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
) < 0) {
919 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to make bind mount %s: %m", mount_slave
);
923 mount_slave_mounted
= true;
925 if (mount(NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
) < 0) {
926 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount slave %s: %m", mount_slave
);
930 /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
931 mount_tmp
= strjoina(mount_slave
, "/mount");
932 if (S_ISDIR(st
.st_mode
))
933 r
= mkdir_errno_wrapper(mount_tmp
, 0700);
935 r
= touch(mount_tmp
);
937 sd_bus_error_set_errnof(error
, errno
, "Failed to create temporary mount point %s: %m", mount_tmp
);
941 mount_tmp_created
= true;
943 if (mount(chased_src
, mount_tmp
, NULL
, MS_BIND
, NULL
) < 0) {
944 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to mount %s: %m", chased_src
);
948 mount_tmp_mounted
= true;
950 /* Third, we remount the new bind mount read-only if requested. */
952 if (mount(NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
) < 0) {
953 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to remount read-only %s: %m", mount_tmp
);
957 /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only
960 mount_outside
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/XXXXXX");
961 if (S_ISDIR(st
.st_mode
))
962 r
= mkdtemp(mount_outside
) ? 0 : -errno
;
964 r
= mkostemp_safe(mount_outside
);
968 sd_bus_error_set_errnof(error
, errno
, "Cannot create propagation file or 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 if (S_ISDIR(st
.st_mode
))
983 (void) rmdir(mount_tmp
);
985 (void) unlink(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");
999 r
= safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS
, &child
);
1001 sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
1005 const char *mount_inside
;
1009 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1011 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1012 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1014 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1018 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1019 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1023 if (make_file_or_directory
) {
1024 if (S_ISDIR(st
.st_mode
))
1025 (void) mkdir_p(dest
, 0755);
1027 (void) mkdir_parents(dest
, 0755);
1028 safe_close(open(dest
, O_CREAT
|O_EXCL
|O_WRONLY
|O_CLOEXEC
|O_NOCTTY
, 0600));
1032 /* Fifth, move the mount to the right place inside */
1033 mount_inside
= strjoina("/run/systemd/nspawn/incoming/", basename(mount_outside
));
1034 if (mount(mount_inside
, dest
, NULL
, MS_MOVE
, NULL
) < 0) {
1035 r
= log_error_errno(errno
, "Failed to mount: %m");
1039 _exit(EXIT_SUCCESS
);
1042 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1043 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1045 _exit(EXIT_FAILURE
);
1048 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1050 r
= wait_for_terminate_and_check("(sd-bindmnt)", child
, 0);
1052 r
= sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1055 if (r
!= 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 (void) umount(mount_outside
);
1068 if (mount_outside_created
) {
1069 if (S_ISDIR(st
.st_mode
))
1070 (void) rmdir(mount_outside
);
1072 (void) unlink(mount_outside
);
1075 if (mount_tmp_mounted
)
1076 (void) umount(mount_tmp
);
1077 if (mount_tmp_created
) {
1078 if (S_ISDIR(st
.st_mode
))
1079 (void) rmdir(mount_tmp
);
1081 (void) unlink(mount_tmp
);
1084 if (mount_slave_mounted
)
1085 (void) umount(mount_slave
);
1086 if (mount_slave_created
)
1087 (void) rmdir(mount_slave
);
1092 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1093 const char *src
, *dest
, *host_path
, *container_path
, *host_basename
, *host_dirname
, *container_basename
, *container_dirname
;
1094 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
1095 CopyFlags copy_flags
= COPY_REFLINK
|COPY_MERGE
;
1096 _cleanup_close_
int hostfd
= -1;
1097 Machine
*m
= userdata
;
1107 if (m
->manager
->n_operations
>= OPERATIONS_MAX
)
1108 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
1110 if (m
->class != MACHINE_CONTAINER
)
1111 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
1113 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
1117 if (!path_is_absolute(src
))
1118 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
1122 else if (!path_is_absolute(dest
))
1123 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
1125 r
= bus_verify_polkit_async(
1128 "org.freedesktop.machine1.manage-machines",
1132 &m
->manager
->polkit_registry
,
1137 return 1; /* Will call us back */
1139 r
= machine_get_uid_shift(m
, &uid_shift
);
1143 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
1146 container_path
= src
;
1150 container_path
= dest
;
1153 host_basename
= basename(host_path
);
1154 t
= strdupa(host_path
);
1155 host_dirname
= dirname(t
);
1157 container_basename
= basename(container_path
);
1158 t
= strdupa(container_path
);
1159 container_dirname
= dirname(t
);
1161 hostfd
= open(host_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1163 return sd_bus_error_set_errnof(error
, errno
, "Failed to open host directory %s: %m", host_dirname
);
1165 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
1166 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1168 r
= safe_fork("(sd-copy)", FORK_RESET_SIGNALS
, &child
);
1170 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
1176 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1178 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1179 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1181 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1185 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1186 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1190 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1191 if (containerfd
< 0) {
1192 r
= log_error_errno(errno
, "Failed to open destination directory: %m");
1196 /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
1197 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1198 * the UID/GIDs as they are. */
1200 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, uid_shift
== 0 ? UID_INVALID
: 0, uid_shift
== 0 ? GID_INVALID
: 0, copy_flags
);
1202 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
);
1204 hostfd
= safe_close(hostfd
);
1205 containerfd
= safe_close(containerfd
);
1208 r
= log_error_errno(r
, "Failed to copy tree: %m");
1212 _exit(EXIT_SUCCESS
);
1215 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1216 _exit(EXIT_FAILURE
);
1219 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1221 /* Copying might take a while, hence install a watch on the child, and return */
1223 r
= operation_new(m
->manager
, m
, child
, message
, errno_pipe_fd
[0], NULL
);
1225 (void) sigkill_wait(child
);
1228 errno_pipe_fd
[0] = -1;
1233 int bus_machine_method_open_root_directory(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1234 _cleanup_close_
int fd
= -1;
1235 Machine
*m
= userdata
;
1241 r
= bus_verify_polkit_async(
1244 "org.freedesktop.machine1.manage-machines",
1248 &m
->manager
->polkit_registry
,
1253 return 1; /* Will call us back */
1258 fd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1264 case MACHINE_CONTAINER
: {
1265 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
1266 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
1269 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, NULL
, &root_fd
);
1273 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1276 r
= safe_fork("(sd-openroot)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &child
);
1278 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
1280 _cleanup_close_
int dfd
= -1;
1282 pair
[0] = safe_close(pair
[0]);
1284 r
= namespace_enter(-1, mntns_fd
, -1, -1, root_fd
);
1286 _exit(EXIT_FAILURE
);
1288 dfd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1290 _exit(EXIT_FAILURE
);
1292 r
= send_one_fd(pair
[1], dfd
, 0);
1293 dfd
= safe_close(dfd
);
1295 _exit(EXIT_FAILURE
);
1297 _exit(EXIT_SUCCESS
);
1300 pair
[1] = safe_close(pair
[1]);
1302 r
= wait_for_terminate_and_check("(sd-openroot)", child
, 0);
1304 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1305 if (r
!= EXIT_SUCCESS
)
1306 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1308 fd
= receive_one_fd(pair
[0], MSG_DONTWAIT
);
1316 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Opening the root directory is only supported on container machines.");
1319 return sd_bus_reply_method_return(message
, "h", fd
);
1322 int bus_machine_method_get_uid_shift(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1323 Machine
*m
= userdata
;
1330 /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
1331 * we kinda have to for this. */
1333 if (m
->class == MACHINE_HOST
)
1334 return sd_bus_reply_method_return(message
, "u", UINT32_C(0));
1336 if (m
->class != MACHINE_CONTAINER
)
1337 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "UID/GID shift may only be determined for container machines.");
1339 r
= machine_get_uid_shift(m
, &shift
);
1341 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m
->name
);
1345 return sd_bus_reply_method_return(message
, "u", (uint32_t) shift
);
1348 const sd_bus_vtable machine_vtable
[] = {
1349 SD_BUS_VTABLE_START(0),
1350 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1351 SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128
, offsetof(Machine
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1352 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1353 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1354 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1355 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1356 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
1357 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1358 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1359 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1360 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1361 SD_BUS_METHOD("Terminate", NULL
, NULL
, bus_machine_method_terminate
, SD_BUS_VTABLE_UNPRIVILEGED
),
1362 SD_BUS_METHOD("Kill", "si", NULL
, bus_machine_method_kill
, SD_BUS_VTABLE_UNPRIVILEGED
),
1363 SD_BUS_METHOD("GetAddresses", NULL
, "a(iay)", bus_machine_method_get_addresses
, SD_BUS_VTABLE_UNPRIVILEGED
),
1364 SD_BUS_METHOD("GetOSRelease", NULL
, "a{ss}", bus_machine_method_get_os_release
, SD_BUS_VTABLE_UNPRIVILEGED
),
1365 SD_BUS_METHOD("GetUIDShift", NULL
, "u", bus_machine_method_get_uid_shift
, SD_BUS_VTABLE_UNPRIVILEGED
),
1366 SD_BUS_METHOD("OpenPTY", NULL
, "hs", bus_machine_method_open_pty
, SD_BUS_VTABLE_UNPRIVILEGED
),
1367 SD_BUS_METHOD("OpenLogin", NULL
, "hs", bus_machine_method_open_login
, SD_BUS_VTABLE_UNPRIVILEGED
),
1368 SD_BUS_METHOD("OpenShell", "ssasas", "hs", bus_machine_method_open_shell
, SD_BUS_VTABLE_UNPRIVILEGED
),
1369 SD_BUS_METHOD("BindMount", "ssbb", NULL
, bus_machine_method_bind_mount
, SD_BUS_VTABLE_UNPRIVILEGED
),
1370 SD_BUS_METHOD("CopyFrom", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1371 SD_BUS_METHOD("CopyTo", "ss", NULL
, bus_machine_method_copy
, SD_BUS_VTABLE_UNPRIVILEGED
),
1372 SD_BUS_METHOD("OpenRootDirectory", NULL
, "h", bus_machine_method_open_root_directory
, SD_BUS_VTABLE_UNPRIVILEGED
),
1376 int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1377 Manager
*m
= userdata
;
1387 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1388 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
1389 sd_bus_message
*message
;
1392 message
= sd_bus_get_current_message(bus
);
1396 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1400 r
= sd_bus_creds_get_pid(creds
, &pid
);
1404 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1408 _cleanup_free_
char *e
= NULL
;
1411 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1415 e
= bus_label_unescape(p
);
1419 machine
= hashmap_get(m
->machines
, e
);
1428 char *machine_bus_path(Machine
*m
) {
1429 _cleanup_free_
char *e
= NULL
;
1433 e
= bus_label_escape(m
->name
);
1437 return strappend("/org/freedesktop/machine1/machine/", e
);
1440 int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1441 _cleanup_strv_free_
char **l
= NULL
;
1442 Machine
*machine
= NULL
;
1443 Manager
*m
= userdata
;
1451 HASHMAP_FOREACH(machine
, m
->machines
, i
) {
1454 p
= machine_bus_path(machine
);
1458 r
= strv_consume(&l
, p
);
1463 *nodes
= TAKE_PTR(l
);
1468 int machine_send_signal(Machine
*m
, bool new_machine
) {
1469 _cleanup_free_
char *p
= NULL
;
1473 p
= machine_bus_path(m
);
1477 return sd_bus_emit_signal(
1479 "/org/freedesktop/machine1",
1480 "org.freedesktop.machine1.Manager",
1481 new_machine
? "MachineNew" : "MachineRemoved",
1485 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1486 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*c
= NULL
;
1487 _cleanup_free_
char *p
= NULL
;
1491 if (!m
->create_message
)
1494 c
= TAKE_PTR(m
->create_message
);
1497 return sd_bus_reply_method_error(c
, error
);
1499 /* Update the machine state file before we notify the client
1500 * about the result. */
1503 p
= machine_bus_path(m
);
1507 return sd_bus_reply_method_return(c
, "o", p
);