1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 /* When we include libgen.h because we need dirname() we immediately
8 * undefine basename() since libgen.h defines it as a macro to the POSIX
9 * version which is really broken. We prefer GNU basename(). */
13 #include "alloc-util.h"
14 #include "bus-common-errors.h"
15 #include "bus-get-properties.h"
16 #include "bus-internal.h"
17 #include "bus-label.h"
18 #include "bus-locator.h"
19 #include "bus-polkit.h"
25 #include "format-util.h"
27 #include "in-addr-util.h"
29 #include "local-addresses.h"
30 #include "machine-dbus.h"
32 #include "missing_capability.h"
34 #include "mount-util.h"
35 #include "namespace-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "signal-util.h"
41 #include "terminal-util.h"
42 #include "tmpfile-util.h"
43 #include "user-util.h"
45 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
46 static BUS_DEFINE_PROPERTY_GET2(property_get_state
, "s", Machine
, machine_get_state
, machine_state_to_string
);
48 static int property_get_netif(
51 const char *interface
,
53 sd_bus_message
*reply
,
55 sd_bus_error
*error
) {
57 Machine
*m
= userdata
;
63 assert_cc(sizeof(int) == sizeof(int32_t));
65 return sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
68 int bus_machine_method_unregister(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
69 Machine
*m
= userdata
;
75 r
= bus_verify_polkit_async(
78 "org.freedesktop.machine1.manage-machines",
82 &m
->manager
->polkit_registry
,
87 return 1; /* Will call us back */
89 r
= machine_finalize(m
);
93 return sd_bus_reply_method_return(message
, NULL
);
96 int bus_machine_method_terminate(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
97 Machine
*m
= userdata
;
103 r
= bus_verify_polkit_async(
106 "org.freedesktop.machine1.manage-machines",
110 &m
->manager
->polkit_registry
,
115 return 1; /* Will call us back */
121 return sd_bus_reply_method_return(message
, NULL
);
124 int bus_machine_method_kill(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
125 Machine
*m
= userdata
;
134 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
141 who
= kill_who_from_string(swho
);
143 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
146 if (!SIGNAL_VALID(signo
))
147 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
149 r
= bus_verify_polkit_async(
152 "org.freedesktop.machine1.manage-machines",
156 &m
->manager
->polkit_registry
,
161 return 1; /* Will call us back */
163 r
= machine_kill(m
, who
, signo
);
167 return sd_bus_reply_method_return(message
, NULL
);
170 int bus_machine_method_get_addresses(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
171 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
172 Machine
*m
= userdata
;
178 r
= sd_bus_message_new_method_return(message
, &reply
);
182 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
189 _cleanup_free_
struct local_address
*addresses
= NULL
;
190 struct local_address
*a
;
193 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
197 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
199 r
= sd_bus_message_open_container(reply
, 'r', "iay");
203 r
= sd_bus_message_append(reply
, "i", addresses
[i
].family
);
207 r
= sd_bus_message_append_array(reply
, 'y', &addresses
[i
].address
, FAMILY_ADDRESS_SIZE(addresses
[i
].family
));
211 r
= sd_bus_message_close_container(reply
);
219 case MACHINE_CONTAINER
: {
220 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
221 _cleanup_free_
char *us
= NULL
, *them
= NULL
;
222 _cleanup_close_
int netns_fd
= -1;
226 r
= readlink_malloc("/proc/self/ns/net", &us
);
230 p
= procfs_file_alloca(m
->leader
, "ns/net");
231 r
= readlink_malloc(p
, &them
);
236 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
238 r
= namespace_open(m
->leader
, NULL
, NULL
, &netns_fd
, NULL
, NULL
);
242 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
245 r
= namespace_fork("(sd-addrns)", "(sd-addr)", NULL
, 0, FORK_RESET_SIGNALS
|FORK_DEATHSIG
,
246 -1, -1, netns_fd
, -1, -1, &child
);
248 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
250 _cleanup_free_
struct local_address
*addresses
= NULL
;
251 struct local_address
*a
;
254 pair
[0] = safe_close(pair
[0]);
256 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
260 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
261 struct iovec iov
[2] = {
262 { .iov_base
= &a
->family
, .iov_len
= sizeof(a
->family
) },
263 { .iov_base
= &a
->address
, .iov_len
= FAMILY_ADDRESS_SIZE(a
->family
) },
266 r
= writev(pair
[1], iov
, 2);
271 pair
[1] = safe_close(pair
[1]);
276 pair
[1] = safe_close(pair
[1]);
281 union in_addr_union in_addr
;
288 iov
[0] = IOVEC_MAKE(&family
, sizeof(family
));
289 iov
[1] = IOVEC_MAKE(&in_addr
, sizeof(in_addr
));
291 n
= recvmsg(pair
[0], &mh
, 0);
294 if ((size_t) n
< sizeof(family
))
297 r
= sd_bus_message_open_container(reply
, 'r', "iay");
301 r
= sd_bus_message_append(reply
, "i", family
);
308 if (n
!= sizeof(struct in_addr
) + sizeof(family
))
311 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in
, sizeof(in_addr
.in
));
315 if (n
!= sizeof(struct in6_addr
) + sizeof(family
))
318 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in6
, sizeof(in_addr
.in6
));
324 r
= sd_bus_message_close_container(reply
);
329 r
= wait_for_terminate_and_check("(sd-addrns)", child
, 0);
331 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
332 if (r
!= EXIT_SUCCESS
)
333 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
338 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting IP address data is only supported on container machines.");
341 r
= sd_bus_message_close_container(reply
);
345 return sd_bus_send(NULL
, reply
, NULL
);
348 #define EXIT_NOT_FOUND 2
350 int bus_machine_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
351 _cleanup_strv_free_
char **l
= NULL
;
352 Machine
*m
= userdata
;
361 r
= load_os_release_pairs(NULL
, &l
);
367 case MACHINE_CONTAINER
: {
368 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1, pidns_fd
= -1;
369 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
370 _cleanup_fclose_
FILE *f
= NULL
;
373 r
= namespace_open(m
->leader
, &pidns_fd
, &mntns_fd
, NULL
, NULL
, &root_fd
);
377 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
380 r
= namespace_fork("(sd-osrelns)", "(sd-osrel)", NULL
, 0, FORK_RESET_SIGNALS
|FORK_DEATHSIG
,
381 pidns_fd
, mntns_fd
, -1, -1, root_fd
,
384 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
388 pair
[0] = safe_close(pair
[0]);
390 r
= open_os_release(NULL
, NULL
, &fd
);
392 _exit(EXIT_NOT_FOUND
);
396 r
= copy_bytes(fd
, pair
[1], (uint64_t) -1, 0);
403 pair
[1] = safe_close(pair
[1]);
405 f
= take_fdopen(&pair
[0], "r");
409 r
= load_env_file_pairs(f
, "/etc/os-release", &l
);
413 r
= wait_for_terminate_and_check("(sd-osrelns)", child
, 0);
415 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
416 if (r
== EXIT_NOT_FOUND
)
417 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Machine does not contain OS release information");
418 if (r
!= EXIT_SUCCESS
)
419 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
425 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting OS release data is only supported on container machines.");
428 return bus_reply_pair_array(message
, l
);
431 int bus_machine_method_open_pty(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
432 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
433 _cleanup_free_
char *pty_name
= NULL
;
434 _cleanup_close_
int master
= -1;
435 Machine
*m
= userdata
;
441 r
= bus_verify_polkit_async(
444 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
448 &m
->manager
->polkit_registry
,
453 return 1; /* Will call us back */
455 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
459 r
= sd_bus_message_new_method_return(message
, &reply
);
463 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
467 return sd_bus_send(NULL
, reply
, NULL
);
470 static int container_bus_new(Machine
*m
, sd_bus_error
*error
, sd_bus
**ret
) {
482 case MACHINE_CONTAINER
: {
483 _cleanup_(sd_bus_close_unrefp
) sd_bus
*bus
= NULL
;
486 r
= sd_bus_new(&bus
);
490 if (asprintf(&address
, "x-machine-kernel:pid=%1$" PID_PRI
";x-machine-unix:pid=%1$" PID_PRI
, m
->leader
) < 0)
493 bus
->address
= address
;
494 bus
->bus_client
= true;
495 bus
->trusted
= false;
496 bus
->is_system
= true;
498 r
= sd_bus_start(bus
);
500 return sd_bus_error_set_errnof(error
, r
, "There is no system bus in container %s.", m
->name
);
504 *ret
= TAKE_PTR(bus
);
515 int bus_machine_method_open_login(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
516 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
517 _cleanup_free_
char *pty_name
= NULL
;
518 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
519 _cleanup_close_
int master
= -1;
520 sd_bus
*container_bus
= NULL
;
521 Machine
*m
= userdata
;
522 const char *p
, *getty
;
528 r
= bus_verify_polkit_async(
531 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
535 &m
->manager
->polkit_registry
,
540 return 1; /* Will call us back */
542 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
546 p
= path_startswith(pty_name
, "/dev/pts/");
549 r
= container_bus_new(m
, error
, &allocated_bus
);
553 container_bus
= allocated_bus
?: m
->manager
->bus
;
555 getty
= strjoina("container-getty@", p
, ".service");
557 r
= bus_call_method(container_bus
, bus_systemd_mgr
, "StartUnit", error
, NULL
, "ss", getty
, "replace");
561 r
= sd_bus_message_new_method_return(message
, &reply
);
565 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
569 return sd_bus_send(NULL
, reply
, NULL
);
572 int bus_machine_method_open_shell(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
573 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *tm
= NULL
;
574 _cleanup_free_
char *pty_name
= NULL
;
575 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
576 sd_bus
*container_bus
= NULL
;
577 _cleanup_close_
int master
= -1, slave
= -1;
578 _cleanup_strv_free_
char **env
= NULL
, **args_wire
= NULL
, **args
= NULL
;
579 Machine
*m
= userdata
;
580 const char *p
, *unit
, *user
, *path
, *description
, *utmp_id
;
586 r
= sd_bus_message_read(message
, "ss", &user
, &path
);
589 user
= isempty(user
) ? "root" : user
;
590 r
= sd_bus_message_read_strv(message
, &args_wire
);
596 args
= new0(char*, 3 + 1);
599 args
[0] = strdup("sh");
602 args
[1] = strdup("-c");
605 r
= asprintf(&args
[2],
606 "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
607 "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
614 if (!path_is_absolute(path
))
615 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Specified path '%s' is not absolute", path
);
616 args
= TAKE_PTR(args_wire
);
617 if (strv_isempty(args
)) {
618 args
= strv_free(args
);
620 args
= strv_new(path
);
626 r
= sd_bus_message_read_strv(message
, &env
);
629 if (!strv_env_is_valid(env
))
630 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid environment assignments");
632 const char *details
[] = {
639 r
= bus_verify_polkit_async(
642 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
646 &m
->manager
->polkit_registry
,
651 return 1; /* Will call us back */
653 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
657 p
= path_startswith(pty_name
, "/dev/pts/");
660 slave
= machine_open_terminal(m
, pty_name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
664 utmp_id
= path_startswith(pty_name
, "/dev/");
667 r
= container_bus_new(m
, error
, &allocated_bus
);
671 container_bus
= allocated_bus
?: m
->manager
->bus
;
673 r
= bus_message_new_method_call(container_bus
, &tm
, bus_systemd_mgr
, "StartTransientUnit");
678 unit
= strjoina("container-shell@", p
, ".service");
679 r
= sd_bus_message_append(tm
, "ss", unit
, "fail");
684 r
= sd_bus_message_open_container(tm
, 'a', "(sv)");
688 description
= strjoina("Shell for User ", user
);
689 r
= sd_bus_message_append(tm
,
690 "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
691 "Description", "s", description
,
692 "StandardInputFileDescriptor", "h", slave
,
693 "StandardOutputFileDescriptor", "h", slave
,
694 "StandardErrorFileDescriptor", "h", slave
,
695 "SendSIGHUP", "b", true,
696 "IgnoreSIGPIPE", "b", false,
697 "KillMode", "s", "mixed",
698 "TTYReset", "b", true,
699 "UtmpIdentifier", "s", utmp_id
,
700 "UtmpMode", "s", "user",
701 "PAMName", "s", "login",
702 "WorkingDirectory", "s", "-~");
706 r
= sd_bus_message_append(tm
, "(sv)", "User", "s", user
);
710 if (!strv_isempty(env
)) {
711 r
= sd_bus_message_open_container(tm
, 'r', "sv");
715 r
= sd_bus_message_append(tm
, "s", "Environment");
719 r
= sd_bus_message_open_container(tm
, 'v', "as");
723 r
= sd_bus_message_append_strv(tm
, env
);
727 r
= sd_bus_message_close_container(tm
);
731 r
= sd_bus_message_close_container(tm
);
737 r
= sd_bus_message_open_container(tm
, 'r', "sv");
741 r
= sd_bus_message_append(tm
, "s", "ExecStart");
745 r
= sd_bus_message_open_container(tm
, 'v', "a(sasb)");
749 r
= sd_bus_message_open_container(tm
, 'a', "(sasb)");
753 r
= sd_bus_message_open_container(tm
, 'r', "sasb");
757 r
= sd_bus_message_append(tm
, "s", path
);
761 r
= sd_bus_message_append_strv(tm
, args
);
765 r
= sd_bus_message_append(tm
, "b", true);
769 r
= sd_bus_message_close_container(tm
);
773 r
= sd_bus_message_close_container(tm
);
777 r
= sd_bus_message_close_container(tm
);
781 r
= sd_bus_message_close_container(tm
);
785 r
= sd_bus_message_close_container(tm
);
789 /* Auxiliary units */
790 r
= sd_bus_message_append(tm
, "a(sa(sv))", 0);
794 r
= sd_bus_call(container_bus
, tm
, 0, error
, NULL
);
798 slave
= safe_close(slave
);
800 r
= sd_bus_message_new_method_return(message
, &reply
);
804 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
808 return sd_bus_send(NULL
, reply
, NULL
);
811 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
812 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
813 char mount_slave
[] = "/tmp/propagate.XXXXXX", *mount_tmp
, *mount_outside
, *p
;
814 bool mount_slave_created
= false, mount_slave_mounted
= false,
815 mount_tmp_created
= false, mount_tmp_mounted
= false,
816 mount_outside_created
= false, mount_outside_mounted
= false;
817 _cleanup_free_
char *chased_src
= NULL
;
818 int read_only
, make_file_or_directory
;
819 const char *dest
, *src
;
820 Machine
*m
= userdata
;
829 if (m
->class != MACHINE_CONTAINER
)
830 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
832 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_file_or_directory
);
836 if (!path_is_absolute(src
) || !path_is_normalized(src
))
837 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and not contain ../.");
841 else if (!path_is_absolute(dest
) || !path_is_normalized(dest
))
842 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and not contain ../.");
844 r
= bus_verify_polkit_async(
847 "org.freedesktop.machine1.manage-machines",
851 &m
->manager
->polkit_registry
,
856 return 1; /* Will call us back */
858 r
= machine_get_uid_shift(m
, &uid
);
862 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Can't bind mount on container with user namespacing applied.");
864 /* One day, when bind mounting /proc/self/fd/n works across
865 * namespace boundaries we should rework this logic to make
868 p
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/");
869 if (laccess(p
, F_OK
) < 0)
870 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Container does not allow propagation of mount points.");
872 r
= chase_symlinks(src
, NULL
, CHASE_TRAIL_SLASH
, &chased_src
, NULL
);
874 return sd_bus_error_set_errnof(error
, r
, "Failed to resolve source path: %m");
876 if (lstat(chased_src
, &st
) < 0)
877 return sd_bus_error_set_errnof(error
, errno
, "Failed to stat() source path: %m");
878 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… */
879 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Source directory can't be a symbolic link");
881 /* Our goal is to install a new bind mount into the container,
882 possibly read-only. This is irritatingly complex
883 unfortunately, currently.
885 First, we start by creating a private playground in /tmp,
886 that we can mount MS_SLAVE. (Which is necessary, since
887 MS_MOVE cannot be applied to mounts with MS_SHARED parent
890 if (!mkdtemp(mount_slave
))
891 return sd_bus_error_set_errnof(error
, errno
, "Failed to create playground %s: %m", mount_slave
);
893 mount_slave_created
= true;
895 r
= mount_nofollow_verbose(LOG_DEBUG
, mount_slave
, mount_slave
, NULL
, MS_BIND
, NULL
);
897 sd_bus_error_set_errnof(error
, r
, "Failed to make bind mount %s: %m", mount_slave
);
901 mount_slave_mounted
= true;
903 r
= mount_nofollow_verbose(LOG_DEBUG
, NULL
, mount_slave
, NULL
, MS_SLAVE
, NULL
);
905 sd_bus_error_set_errnof(error
, r
, "Failed to remount slave %s: %m", mount_slave
);
909 /* Second, we mount the source file or directory to a directory inside of our MS_SLAVE playground. */
910 mount_tmp
= strjoina(mount_slave
, "/mount");
911 if (S_ISDIR(st
.st_mode
))
912 r
= mkdir_errno_wrapper(mount_tmp
, 0700);
914 r
= touch(mount_tmp
);
916 sd_bus_error_set_errnof(error
, r
, "Failed to create temporary mount point %s: %m", mount_tmp
);
920 mount_tmp_created
= true;
922 r
= mount_nofollow_verbose(LOG_DEBUG
, chased_src
, mount_tmp
, NULL
, MS_BIND
, NULL
);
924 sd_bus_error_set_errnof(error
, r
, "Failed to mount %s: %m", chased_src
);
928 mount_tmp_mounted
= true;
930 /* Third, we remount the new bind mount read-only if requested. */
932 r
= mount_nofollow_verbose(LOG_DEBUG
, NULL
, mount_tmp
, NULL
, MS_BIND
|MS_REMOUNT
|MS_RDONLY
, NULL
);
934 sd_bus_error_set_errnof(error
, r
, "Failed to remount read-only %s: %m", mount_tmp
);
939 /* Fourth, we move the new bind mount into the propagation directory. This way it will appear there read-only
942 mount_outside
= strjoina("/run/systemd/nspawn/propagate/", m
->name
, "/XXXXXX");
943 if (S_ISDIR(st
.st_mode
))
944 r
= mkdtemp(mount_outside
) ? 0 : -errno
;
946 r
= mkostemp_safe(mount_outside
);
950 sd_bus_error_set_errnof(error
, r
, "Cannot create propagation file or directory %s: %m", mount_outside
);
954 mount_outside_created
= true;
956 r
= mount_nofollow_verbose(LOG_DEBUG
, mount_tmp
, mount_outside
, NULL
, MS_MOVE
, NULL
);
958 sd_bus_error_set_errnof(error
, r
, "Failed to move %s to %s: %m", mount_tmp
, mount_outside
);
962 mount_outside_mounted
= true;
963 mount_tmp_mounted
= false;
965 if (S_ISDIR(st
.st_mode
))
966 (void) rmdir(mount_tmp
);
968 (void) unlink(mount_tmp
);
969 mount_tmp_created
= false;
971 (void) umount_verbose(LOG_DEBUG
, mount_slave
, UMOUNT_NOFOLLOW
);
972 mount_slave_mounted
= false;
974 (void) rmdir(mount_slave
);
975 mount_slave_created
= false;
977 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0) {
978 r
= sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
982 r
= safe_fork("(sd-bindmnt)", FORK_RESET_SIGNALS
, &child
);
984 sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
988 const char *mount_inside
, *q
;
991 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
993 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
994 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
996 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1000 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1001 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1005 if (make_file_or_directory
) {
1006 if (S_ISDIR(st
.st_mode
))
1007 (void) mkdir_p(dest
, 0755);
1009 (void) mkdir_parents(dest
, 0755);
1010 (void) mknod(dest
, S_IFREG
|0600, 0);
1014 mount_inside
= strjoina("/run/host/incoming/", basename(mount_outside
));
1015 r
= mount_nofollow_verbose(LOG_ERR
, mount_inside
, dest
, NULL
, MS_MOVE
, NULL
);
1019 _exit(EXIT_SUCCESS
);
1022 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1023 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1025 _exit(EXIT_FAILURE
);
1028 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1030 r
= wait_for_terminate_and_check("(sd-bindmnt)", child
, 0);
1032 r
= sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1035 if (r
!= EXIT_SUCCESS
) {
1036 if (read(errno_pipe_fd
[0], &r
, sizeof(r
)) == sizeof(r
))
1037 r
= sd_bus_error_set_errnof(error
, r
, "Failed to mount: %m");
1039 r
= sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child failed.");
1043 r
= sd_bus_reply_method_return(message
, NULL
);
1046 if (mount_outside_mounted
)
1047 (void) umount_verbose(LOG_DEBUG
, mount_outside
, UMOUNT_NOFOLLOW
);
1048 if (mount_outside_created
) {
1049 if (S_ISDIR(st
.st_mode
))
1050 (void) rmdir(mount_outside
);
1052 (void) unlink(mount_outside
);
1055 if (mount_tmp_mounted
)
1056 (void) umount_verbose(LOG_DEBUG
, mount_tmp
, UMOUNT_NOFOLLOW
);
1057 if (mount_tmp_created
) {
1058 if (S_ISDIR(st
.st_mode
))
1059 (void) rmdir(mount_tmp
);
1061 (void) unlink(mount_tmp
);
1064 if (mount_slave_mounted
)
1065 (void) umount_verbose(LOG_DEBUG
, mount_slave
, UMOUNT_NOFOLLOW
);
1066 if (mount_slave_created
)
1067 (void) rmdir(mount_slave
);
1072 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1073 const char *src
, *dest
, *host_path
, *container_path
, *host_basename
, *container_basename
, *container_dirname
;
1074 _cleanup_close_pair_
int errno_pipe_fd
[2] = { -1, -1 };
1075 CopyFlags copy_flags
= COPY_REFLINK
|COPY_MERGE
|COPY_HARDLINKS
;
1076 _cleanup_close_
int hostfd
= -1;
1077 Machine
*m
= userdata
;
1087 if (m
->manager
->n_operations
>= OPERATIONS_MAX
)
1088 return sd_bus_error_setf(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
1090 if (m
->class != MACHINE_CONTAINER
)
1091 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
1093 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
1097 if (!path_is_absolute(src
))
1098 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
1102 else if (!path_is_absolute(dest
))
1103 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
1105 r
= bus_verify_polkit_async(
1108 "org.freedesktop.machine1.manage-machines",
1112 &m
->manager
->polkit_registry
,
1117 return 1; /* Will call us back */
1119 r
= machine_get_uid_shift(m
, &uid_shift
);
1123 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
1126 container_path
= src
;
1130 container_path
= dest
;
1133 host_basename
= basename(host_path
);
1135 container_basename
= basename(container_path
);
1136 t
= strdupa(container_path
);
1137 container_dirname
= dirname(t
);
1139 hostfd
= open_parent(host_path
, O_CLOEXEC
, 0);
1141 return sd_bus_error_set_errnof(error
, hostfd
, "Failed to open host directory %s: %m", host_path
);
1143 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
1144 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
1146 r
= safe_fork("(sd-copy)", FORK_RESET_SIGNALS
, &child
);
1148 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
1154 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
1156 q
= procfs_file_alloca(m
->leader
, "ns/mnt");
1157 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
1159 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
1163 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
1164 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1168 containerfd
= open(container_dirname
, O_CLOEXEC
|O_RDONLY
|O_NOCTTY
|O_DIRECTORY
);
1169 if (containerfd
< 0) {
1170 r
= log_error_errno(errno
, "Failed to open destination directory: %m");
1174 /* Run the actual copy operation. Note that when an UID shift is set we'll either clamp the UID/GID to
1175 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1176 * the UID/GIDs as they are. */
1178 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, uid_shift
== 0 ? UID_INVALID
: 0, uid_shift
== 0 ? GID_INVALID
: 0, copy_flags
);
1180 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
);
1182 hostfd
= safe_close(hostfd
);
1183 containerfd
= safe_close(containerfd
);
1186 r
= log_error_errno(r
, "Failed to copy tree: %m");
1190 _exit(EXIT_SUCCESS
);
1193 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1194 _exit(EXIT_FAILURE
);
1197 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1199 /* Copying might take a while, hence install a watch on the child, and return */
1201 r
= operation_new(m
->manager
, m
, child
, message
, errno_pipe_fd
[0], NULL
);
1203 (void) sigkill_wait(child
);
1206 errno_pipe_fd
[0] = -1;
1211 int bus_machine_method_open_root_directory(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1212 _cleanup_close_
int fd
= -1;
1213 Machine
*m
= userdata
;
1219 r
= bus_verify_polkit_async(
1222 "org.freedesktop.machine1.manage-machines",
1226 &m
->manager
->polkit_registry
,
1231 return 1; /* Will call us back */
1236 fd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1242 case MACHINE_CONTAINER
: {
1243 _cleanup_close_
int mntns_fd
= -1, root_fd
= -1;
1244 _cleanup_close_pair_
int pair
[2] = { -1, -1 };
1247 r
= namespace_open(m
->leader
, NULL
, &mntns_fd
, NULL
, NULL
, &root_fd
);
1251 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1254 r
= namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL
, 0, FORK_RESET_SIGNALS
|FORK_DEATHSIG
,
1255 -1, mntns_fd
, -1, -1, root_fd
, &child
);
1257 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
1259 _cleanup_close_
int dfd
= -1;
1261 pair
[0] = safe_close(pair
[0]);
1263 dfd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1265 _exit(EXIT_FAILURE
);
1267 r
= send_one_fd(pair
[1], dfd
, 0);
1268 dfd
= safe_close(dfd
);
1270 _exit(EXIT_FAILURE
);
1272 _exit(EXIT_SUCCESS
);
1275 pair
[1] = safe_close(pair
[1]);
1277 r
= wait_for_terminate_and_check("(sd-openrootns)", child
, 0);
1279 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1280 if (r
!= EXIT_SUCCESS
)
1281 return sd_bus_error_setf(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1283 fd
= receive_one_fd(pair
[0], MSG_DONTWAIT
);
1291 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Opening the root directory is only supported on container machines.");
1294 return sd_bus_reply_method_return(message
, "h", fd
);
1297 int bus_machine_method_get_uid_shift(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1298 Machine
*m
= userdata
;
1305 /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
1306 * we kinda have to for this. */
1308 if (m
->class == MACHINE_HOST
)
1309 return sd_bus_reply_method_return(message
, "u", UINT32_C(0));
1311 if (m
->class != MACHINE_CONTAINER
)
1312 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "UID/GID shift may only be determined for container machines.");
1314 r
= machine_get_uid_shift(m
, &shift
);
1316 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m
->name
);
1320 return sd_bus_reply_method_return(message
, "u", (uint32_t) shift
);
1323 static int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1324 Manager
*m
= userdata
;
1334 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1335 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
1336 sd_bus_message
*message
;
1339 message
= sd_bus_get_current_message(bus
);
1343 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1347 r
= sd_bus_creds_get_pid(creds
, &pid
);
1351 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1355 _cleanup_free_
char *e
= NULL
;
1358 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1362 e
= bus_label_unescape(p
);
1366 machine
= hashmap_get(m
->machines
, e
);
1375 char *machine_bus_path(Machine
*m
) {
1376 _cleanup_free_
char *e
= NULL
;
1380 e
= bus_label_escape(m
->name
);
1384 return strjoin("/org/freedesktop/machine1/machine/", e
);
1387 static int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1388 _cleanup_strv_free_
char **l
= NULL
;
1389 Machine
*machine
= NULL
;
1390 Manager
*m
= userdata
;
1397 HASHMAP_FOREACH(machine
, m
->machines
) {
1400 p
= machine_bus_path(machine
);
1404 r
= strv_consume(&l
, p
);
1409 *nodes
= TAKE_PTR(l
);
1414 static const sd_bus_vtable machine_vtable
[] = {
1415 SD_BUS_VTABLE_START(0),
1416 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1417 SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128
, offsetof(Machine
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1418 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1419 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1420 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1421 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1422 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
1423 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1424 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1425 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1426 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1428 SD_BUS_METHOD("Terminate",
1431 bus_machine_method_terminate
,
1432 SD_BUS_VTABLE_UNPRIVILEGED
),
1433 SD_BUS_METHOD_WITH_NAMES("Kill",
1436 SD_BUS_PARAM(signal
),
1438 bus_machine_method_kill
,
1439 SD_BUS_VTABLE_UNPRIVILEGED
),
1440 SD_BUS_METHOD_WITH_NAMES("GetAddresses",
1443 SD_BUS_PARAM(addresses
),
1444 bus_machine_method_get_addresses
,
1445 SD_BUS_VTABLE_UNPRIVILEGED
),
1446 SD_BUS_METHOD_WITH_NAMES("GetOSRelease",
1449 SD_BUS_PARAM(fields
),
1450 bus_machine_method_get_os_release
,
1451 SD_BUS_VTABLE_UNPRIVILEGED
),
1452 SD_BUS_METHOD_WITH_NAMES("GetUIDShift",
1455 SD_BUS_PARAM(shift
),
1456 bus_machine_method_get_uid_shift
,
1457 SD_BUS_VTABLE_UNPRIVILEGED
),
1458 SD_BUS_METHOD_WITH_NAMES("OpenPTY",
1462 SD_BUS_PARAM(pty_path
),
1463 bus_machine_method_open_pty
,
1464 SD_BUS_VTABLE_UNPRIVILEGED
),
1465 SD_BUS_METHOD_WITH_NAMES("OpenLogin",
1469 SD_BUS_PARAM(pty_path
),
1470 bus_machine_method_open_login
,
1471 SD_BUS_VTABLE_UNPRIVILEGED
),
1472 SD_BUS_METHOD_WITH_NAMES("OpenShell",
1477 SD_BUS_PARAM(environment
),
1480 SD_BUS_PARAM(pty_path
),
1481 bus_machine_method_open_shell
,
1482 SD_BUS_VTABLE_UNPRIVILEGED
),
1483 SD_BUS_METHOD_WITH_NAMES("BindMount",
1485 SD_BUS_PARAM(source
)
1486 SD_BUS_PARAM(destination
)
1487 SD_BUS_PARAM(read_only
)
1488 SD_BUS_PARAM(mkdir
),
1490 bus_machine_method_bind_mount
,
1491 SD_BUS_VTABLE_UNPRIVILEGED
),
1492 SD_BUS_METHOD_WITH_NAMES("CopyFrom",
1494 SD_BUS_PARAM(source
)
1495 SD_BUS_PARAM(destination
),
1497 bus_machine_method_copy
,
1498 SD_BUS_VTABLE_UNPRIVILEGED
),
1499 SD_BUS_METHOD_WITH_NAMES("CopyTo",
1501 SD_BUS_PARAM(source
)
1502 SD_BUS_PARAM(destination
),
1504 bus_machine_method_copy
,
1505 SD_BUS_VTABLE_UNPRIVILEGED
),
1506 SD_BUS_METHOD_WITH_NAMES("OpenRootDirectory",
1510 bus_machine_method_open_root_directory
,
1511 SD_BUS_VTABLE_UNPRIVILEGED
),
1516 const BusObjectImplementation machine_object
= {
1517 "/org/freedesktop/machine1/machine",
1518 "org.freedesktop.machine1.Machine",
1519 .fallback_vtables
= BUS_FALLBACK_VTABLES({machine_vtable
, machine_object_find
}),
1520 .node_enumerator
= machine_node_enumerator
,
1523 int machine_send_signal(Machine
*m
, bool new_machine
) {
1524 _cleanup_free_
char *p
= NULL
;
1528 p
= machine_bus_path(m
);
1532 return sd_bus_emit_signal(
1534 "/org/freedesktop/machine1",
1535 "org.freedesktop.machine1.Manager",
1536 new_machine
? "MachineNew" : "MachineRemoved",
1540 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1541 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*c
= NULL
;
1542 _cleanup_free_
char *p
= NULL
;
1546 if (!m
->create_message
)
1549 c
= TAKE_PTR(m
->create_message
);
1552 return sd_bus_reply_method_error(c
, error
);
1554 /* Update the machine state file before we notify the client
1555 * about the result. */
1558 p
= machine_bus_path(m
);
1562 return sd_bus_reply_method_return(c
, "o", p
);