1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
8 #include "bus-common-errors.h"
9 #include "bus-get-properties.h"
10 #include "bus-internal.h"
11 #include "bus-label.h"
12 #include "bus-locator.h"
13 #include "bus-polkit.h"
19 #include "format-util.h"
21 #include "in-addr-util.h"
22 #include "iovec-util.h"
23 #include "local-addresses.h"
24 #include "machine-dbus.h"
26 #include "missing_capability.h"
28 #include "mount-util.h"
29 #include "mountpoint-util.h"
30 #include "namespace-util.h"
32 #include "path-util.h"
33 #include "process-util.h"
34 #include "signal-util.h"
36 #include "terminal-util.h"
37 #include "tmpfile-util.h"
38 #include "user-util.h"
40 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
41 static BUS_DEFINE_PROPERTY_GET2(property_get_state
, "s", Machine
, machine_get_state
, machine_state_to_string
);
43 static int property_get_netif(
46 const char *interface
,
48 sd_bus_message
*reply
,
50 sd_bus_error
*error
) {
52 Machine
*m
= ASSERT_PTR(userdata
);
57 assert_cc(sizeof(int) == sizeof(int32_t));
59 return sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
62 int bus_machine_method_unregister(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
63 Machine
*m
= ASSERT_PTR(userdata
);
68 const char *details
[] = {
74 r
= bus_verify_polkit_async(
76 "org.freedesktop.machine1.manage-machines",
78 &m
->manager
->polkit_registry
,
83 return 1; /* Will call us back */
85 r
= machine_finalize(m
);
89 return sd_bus_reply_method_return(message
, NULL
);
92 int bus_machine_method_terminate(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
93 Machine
*m
= ASSERT_PTR(userdata
);
98 const char *details
[] = {
104 r
= bus_verify_polkit_async(
106 "org.freedesktop.machine1.manage-machines",
108 &m
->manager
->polkit_registry
,
113 return 1; /* Will call us back */
119 return sd_bus_reply_method_return(message
, NULL
);
122 int bus_machine_method_kill(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
123 Machine
*m
= ASSERT_PTR(userdata
);
131 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
138 who
= kill_who_from_string(swho
);
140 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
143 if (!SIGNAL_VALID(signo
))
144 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
146 const char *details
[] = {
152 r
= bus_verify_polkit_async(
154 "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
= ASSERT_PTR(userdata
);
177 r
= sd_bus_message_new_method_return(message
, &reply
);
181 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
188 _cleanup_free_
struct local_address
*addresses
= NULL
;
191 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
195 for (int i
= 0; i
< n
; i
++) {
196 r
= sd_bus_message_open_container(reply
, 'r', "iay");
200 r
= sd_bus_message_append(reply
, "i", addresses
[i
].family
);
204 r
= sd_bus_message_append_array(reply
, 'y', &addresses
[i
].address
, FAMILY_ADDRESS_SIZE(addresses
[i
].family
));
208 r
= sd_bus_message_close_container(reply
);
216 case MACHINE_CONTAINER
: {
217 _cleanup_close_pair_
int pair
[2] = EBADF_PAIR
;
218 _cleanup_free_
char *us
= NULL
, *them
= NULL
;
219 _cleanup_close_
int netns_fd
= -EBADF
;
223 r
= readlink_malloc("/proc/self/ns/net", &us
);
227 p
= procfs_file_alloca(m
->leader
.pid
, "ns/net");
228 r
= readlink_malloc(p
, &them
);
233 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
235 r
= namespace_open(m
->leader
.pid
,
236 /* ret_pidns_fd = */ NULL
,
237 /* ret_mntns_fd = */ NULL
,
239 /* ret_userns_fd = */ NULL
,
240 /* ret_root_fd = */ NULL
);
244 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
247 r
= namespace_fork("(sd-addrns)", "(sd-addr)", NULL
, 0, FORK_RESET_SIGNALS
|FORK_DEATHSIG_SIGKILL
,
248 -1, -1, netns_fd
, -1, -1, &child
);
250 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
252 _cleanup_free_
struct local_address
*addresses
= NULL
;
253 struct local_address
*a
;
256 pair
[0] = safe_close(pair
[0]);
258 n
= local_addresses(NULL
, 0, AF_UNSPEC
, &addresses
);
262 for (a
= addresses
, i
= 0; i
< n
; a
++, i
++) {
263 struct iovec iov
[2] = {
264 { .iov_base
= &a
->family
, .iov_len
= sizeof(a
->family
) },
265 { .iov_base
= &a
->address
, .iov_len
= FAMILY_ADDRESS_SIZE(a
->family
) },
268 r
= writev(pair
[1], iov
, 2);
273 pair
[1] = safe_close(pair
[1]);
278 pair
[1] = safe_close(pair
[1]);
283 union in_addr_union in_addr
;
290 iov
[0] = IOVEC_MAKE(&family
, sizeof(family
));
291 iov
[1] = IOVEC_MAKE(&in_addr
, sizeof(in_addr
));
293 n
= recvmsg(pair
[0], &mh
, 0);
296 if ((size_t) n
< sizeof(family
))
299 r
= sd_bus_message_open_container(reply
, 'r', "iay");
303 r
= sd_bus_message_append(reply
, "i", family
);
310 if (n
!= sizeof(struct in_addr
) + sizeof(family
))
313 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in
, sizeof(in_addr
.in
));
317 if (n
!= sizeof(struct in6_addr
) + sizeof(family
))
320 r
= sd_bus_message_append_array(reply
, 'y', &in_addr
.in6
, sizeof(in_addr
.in6
));
326 r
= sd_bus_message_close_container(reply
);
331 r
= wait_for_terminate_and_check("(sd-addrns)", child
, 0);
333 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
334 if (r
!= EXIT_SUCCESS
)
335 return sd_bus_error_set(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
340 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting IP address data is only supported on container machines.");
343 r
= sd_bus_message_close_container(reply
);
347 return sd_bus_send(NULL
, reply
, NULL
);
350 #define EXIT_NOT_FOUND 2
352 int bus_machine_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
353 _cleanup_strv_free_
char **l
= NULL
;
354 Machine
*m
= ASSERT_PTR(userdata
);
362 r
= load_os_release_pairs(NULL
, &l
);
368 case MACHINE_CONTAINER
: {
369 _cleanup_close_
int mntns_fd
= -EBADF
, root_fd
= -EBADF
, pidns_fd
= -EBADF
;
370 _cleanup_close_pair_
int pair
[2] = EBADF_PAIR
;
371 _cleanup_fclose_
FILE *f
= NULL
;
374 r
= namespace_open(m
->leader
.pid
,
377 /* ret_netns_fd = */ NULL
,
378 /* ret_userns_fd = */ NULL
,
383 if (socketpair(AF_UNIX
, SOCK_SEQPACKET
, 0, pair
) < 0)
386 r
= namespace_fork("(sd-osrelns)", "(sd-osrel)", NULL
, 0, FORK_RESET_SIGNALS
|FORK_DEATHSIG_SIGKILL
,
387 pidns_fd
, mntns_fd
, -1, -1, root_fd
,
390 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
394 pair
[0] = safe_close(pair
[0]);
396 r
= open_os_release(NULL
, NULL
, &fd
);
398 _exit(EXIT_NOT_FOUND
);
402 r
= copy_bytes(fd
, pair
[1], UINT64_MAX
, 0);
409 pair
[1] = safe_close(pair
[1]);
411 f
= take_fdopen(&pair
[0], "r");
415 r
= load_env_file_pairs(f
, "/etc/os-release", &l
);
419 r
= wait_for_terminate_and_check("(sd-osrelns)", child
, 0);
421 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
422 if (r
== EXIT_NOT_FOUND
)
423 return sd_bus_error_set(error
, SD_BUS_ERROR_FAILED
, "Machine does not contain OS release information");
424 if (r
!= EXIT_SUCCESS
)
425 return sd_bus_error_set(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
431 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting OS release data is only supported on container machines.");
434 return bus_reply_pair_array(message
, l
);
437 int bus_machine_method_open_pty(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
438 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
439 _cleanup_free_
char *pty_name
= NULL
;
440 _cleanup_close_
int master
= -EBADF
;
441 Machine
*m
= ASSERT_PTR(userdata
);
446 const char *details
[] = {
451 r
= bus_verify_polkit_async(
453 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
455 &m
->manager
->polkit_registry
,
460 return 1; /* Will call us back */
462 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
466 r
= sd_bus_message_new_method_return(message
, &reply
);
470 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
474 return sd_bus_send(NULL
, reply
, NULL
);
477 static int container_bus_new(Machine
*m
, sd_bus_error
*error
, sd_bus
**ret
) {
489 case MACHINE_CONTAINER
: {
490 _cleanup_(sd_bus_close_unrefp
) sd_bus
*bus
= NULL
;
493 r
= sd_bus_new(&bus
);
497 if (asprintf(&address
, "x-machine-unix:pid=%" PID_PRI
, m
->leader
.pid
) < 0)
500 bus
->address
= address
;
501 bus
->bus_client
= true;
502 bus
->trusted
= false;
503 bus
->runtime_scope
= RUNTIME_SCOPE_SYSTEM
;
505 r
= sd_bus_start(bus
);
507 return sd_bus_error_set_errnof(error
, r
, "There is no system bus in container %s.", m
->name
);
511 *ret
= TAKE_PTR(bus
);
522 int bus_machine_method_open_login(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
523 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
524 _cleanup_free_
char *pty_name
= NULL
;
525 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
526 _cleanup_close_
int master
= -EBADF
;
527 sd_bus
*container_bus
= NULL
;
528 Machine
*m
= ASSERT_PTR(userdata
);
529 const char *p
, *getty
;
534 const char *details
[] = {
540 r
= bus_verify_polkit_async(
542 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
544 &m
->manager
->polkit_registry
,
549 return 1; /* Will call us back */
551 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
555 p
= path_startswith(pty_name
, "/dev/pts/");
558 r
= container_bus_new(m
, error
, &allocated_bus
);
562 container_bus
= allocated_bus
?: m
->manager
->bus
;
564 getty
= strjoina("container-getty@", p
, ".service");
566 r
= bus_call_method(container_bus
, bus_systemd_mgr
, "StartUnit", error
, NULL
, "ss", getty
, "replace");
570 r
= sd_bus_message_new_method_return(message
, &reply
);
574 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
578 return sd_bus_send(NULL
, reply
, NULL
);
581 int bus_machine_method_open_shell(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
582 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
, *tm
= NULL
;
583 _cleanup_free_
char *pty_name
= NULL
;
584 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*allocated_bus
= NULL
;
585 sd_bus
*container_bus
= NULL
;
586 _cleanup_close_
int master
= -EBADF
, slave
= -EBADF
;
587 _cleanup_strv_free_
char **env
= NULL
, **args_wire
= NULL
, **args
= NULL
;
588 _cleanup_free_
char *command_line
= NULL
;
589 Machine
*m
= ASSERT_PTR(userdata
);
590 const char *p
, *unit
, *user
, *path
, *description
, *utmp_id
;
595 r
= sd_bus_message_read(message
, "ss", &user
, &path
);
598 user
= isempty(user
) ? "root" : user
;
599 r
= sd_bus_message_read_strv(message
, &args_wire
);
605 args
= new0(char*, 3 + 1);
608 args
[0] = strdup("sh");
611 args
[1] = strdup("-c");
614 r
= asprintf(&args
[2],
615 "shell=$(getent passwd %s 2>/dev/null | { IFS=: read _ _ _ _ _ _ x; echo \"$x\"; })\n"\
616 "exec \"${shell:-/bin/sh}\" -l", /* -l is means --login */
623 if (!path_is_absolute(path
))
624 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Specified path '%s' is not absolute", path
);
625 args
= TAKE_PTR(args_wire
);
626 if (strv_isempty(args
)) {
627 args
= strv_free(args
);
629 args
= strv_new(path
);
635 r
= sd_bus_message_read_strv(message
, &env
);
638 if (!strv_env_is_valid(env
))
639 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid environment assignments");
641 command_line
= strv_join(args
, " ");
644 const char *details
[] = {
648 "command_line", command_line
,
652 r
= bus_verify_polkit_async(
654 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
656 &m
->manager
->polkit_registry
,
661 return 1; /* Will call us back */
663 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
667 p
= path_startswith(pty_name
, "/dev/pts/");
670 slave
= machine_open_terminal(m
, pty_name
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
);
674 utmp_id
= path_startswith(pty_name
, "/dev/");
677 r
= container_bus_new(m
, error
, &allocated_bus
);
681 container_bus
= allocated_bus
?: m
->manager
->bus
;
683 r
= bus_message_new_method_call(container_bus
, &tm
, bus_systemd_mgr
, "StartTransientUnit");
688 unit
= strjoina("container-shell@", p
, ".service");
689 r
= sd_bus_message_append(tm
, "ss", unit
, "fail");
694 r
= sd_bus_message_open_container(tm
, 'a', "(sv)");
698 description
= strjoina("Shell for User ", user
);
699 r
= sd_bus_message_append(tm
,
700 "(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)(sv)",
701 "Description", "s", description
,
702 "StandardInputFileDescriptor", "h", slave
,
703 "StandardOutputFileDescriptor", "h", slave
,
704 "StandardErrorFileDescriptor", "h", slave
,
705 "SendSIGHUP", "b", true,
706 "IgnoreSIGPIPE", "b", false,
707 "KillMode", "s", "mixed",
708 "TTYPath", "s", pty_name
,
709 "TTYReset", "b", true,
710 "UtmpIdentifier", "s", utmp_id
,
711 "UtmpMode", "s", "user",
712 "PAMName", "s", "login",
713 "WorkingDirectory", "s", "-~");
717 r
= sd_bus_message_append(tm
, "(sv)", "User", "s", user
);
721 if (!strv_isempty(env
)) {
722 r
= sd_bus_message_open_container(tm
, 'r', "sv");
726 r
= sd_bus_message_append(tm
, "s", "Environment");
730 r
= sd_bus_message_open_container(tm
, 'v', "as");
734 r
= sd_bus_message_append_strv(tm
, env
);
738 r
= sd_bus_message_close_container(tm
);
742 r
= sd_bus_message_close_container(tm
);
748 r
= sd_bus_message_open_container(tm
, 'r', "sv");
752 r
= sd_bus_message_append(tm
, "s", "ExecStart");
756 r
= sd_bus_message_open_container(tm
, 'v', "a(sasb)");
760 r
= sd_bus_message_open_container(tm
, 'a', "(sasb)");
764 r
= sd_bus_message_open_container(tm
, 'r', "sasb");
768 r
= sd_bus_message_append(tm
, "s", path
);
772 r
= sd_bus_message_append_strv(tm
, args
);
776 r
= sd_bus_message_append(tm
, "b", true);
780 r
= sd_bus_message_close_container(tm
);
784 r
= sd_bus_message_close_container(tm
);
788 r
= sd_bus_message_close_container(tm
);
792 r
= sd_bus_message_close_container(tm
);
796 r
= sd_bus_message_close_container(tm
);
800 /* Auxiliary units */
801 r
= sd_bus_message_append(tm
, "a(sa(sv))", 0);
805 r
= sd_bus_call(container_bus
, tm
, 0, error
, NULL
);
809 slave
= safe_close(slave
);
811 r
= sd_bus_message_new_method_return(message
, &reply
);
815 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
819 return sd_bus_send(NULL
, reply
, NULL
);
822 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
823 int read_only
, make_file_or_directory
;
824 const char *dest
, *src
, *propagate_directory
;
825 Machine
*m
= ASSERT_PTR(userdata
);
831 if (m
->class != MACHINE_CONTAINER
)
832 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
834 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_file_or_directory
);
838 if (!path_is_absolute(src
) || !path_is_normalized(src
))
839 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and normalized.");
843 else if (!path_is_absolute(dest
) || !path_is_normalized(dest
))
844 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and normalized.");
846 const char *details
[] = {
854 r
= bus_verify_polkit_async(
856 "org.freedesktop.machine1.manage-machines",
858 &m
->manager
->polkit_registry
,
863 return 1; /* Will call us back */
865 r
= machine_get_uid_shift(m
, &uid
);
869 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Can't bind mount on container with user namespacing applied.");
871 propagate_directory
= strjoina("/run/systemd/nspawn/propagate/", m
->name
);
872 r
= bind_mount_in_namespace(
875 "/run/host/incoming/",
878 make_file_or_directory
);
880 return sd_bus_error_set_errnof(error
, r
, "Failed to mount %s on %s in machine's namespace: %m", src
, dest
);
882 return sd_bus_reply_method_return(message
, NULL
);
885 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
886 _cleanup_free_
char *host_basename
= NULL
, *container_basename
= NULL
;
887 const char *src
, *dest
, *host_path
, *container_path
;
888 _cleanup_close_pair_
int errno_pipe_fd
[2] = EBADF_PAIR
;
889 CopyFlags copy_flags
= COPY_REFLINK
|COPY_MERGE
|COPY_HARDLINKS
;
890 _cleanup_close_
int hostfd
= -EBADF
;
891 Machine
*m
= ASSERT_PTR(userdata
);
899 if (m
->manager
->n_operations
>= OPERATIONS_MAX
)
900 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
902 if (m
->class != MACHINE_CONTAINER
)
903 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
905 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
909 if (endswith(sd_bus_message_get_member(message
), "WithFlags")) {
912 r
= sd_bus_message_read(message
, "t", &raw_flags
);
916 if ((raw_flags
& ~_MACHINE_COPY_FLAGS_MASK_PUBLIC
) != 0)
919 if (raw_flags
& MACHINE_COPY_REPLACE
)
920 copy_flags
|= COPY_REPLACE
;
923 if (!path_is_absolute(src
))
924 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
928 else if (!path_is_absolute(dest
))
929 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
931 const char *details
[] = {
939 r
= bus_verify_polkit_async(
941 "org.freedesktop.machine1.manage-machines",
943 &m
->manager
->polkit_registry
,
948 return 1; /* Will call us back */
950 r
= machine_get_uid_shift(m
, &uid_shift
);
954 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
957 container_path
= src
;
961 container_path
= dest
;
964 r
= path_extract_filename(host_path
, &host_basename
);
966 return sd_bus_error_set_errnof(error
, r
, "Failed to extract file name of '%s' path: %m", host_path
);
968 r
= path_extract_filename(container_path
, &container_basename
);
970 return sd_bus_error_set_errnof(error
, r
, "Failed to extract file name of '%s' path: %m", container_path
);
972 hostfd
= open_parent(host_path
, O_CLOEXEC
, 0);
974 return sd_bus_error_set_errnof(error
, hostfd
, "Failed to open host directory %s: %m", host_path
);
976 if (pipe2(errno_pipe_fd
, O_CLOEXEC
|O_NONBLOCK
) < 0)
977 return sd_bus_error_set_errnof(error
, errno
, "Failed to create pipe: %m");
979 r
= safe_fork("(sd-copy)", FORK_RESET_SIGNALS
, &child
);
981 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
987 errno_pipe_fd
[0] = safe_close(errno_pipe_fd
[0]);
989 q
= procfs_file_alloca(m
->leader
.pid
, "ns/mnt");
990 mntfd
= open(q
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
992 r
= log_error_errno(errno
, "Failed to open mount namespace of leader: %m");
996 if (setns(mntfd
, CLONE_NEWNS
) < 0) {
997 r
= log_error_errno(errno
, "Failed to join namespace of leader: %m");
1001 containerfd
= open_parent(container_path
, O_CLOEXEC
, 0);
1002 if (containerfd
< 0) {
1003 r
= log_error_errno(containerfd
, "Failed to open destination directory: %m");
1007 /* Run the actual copy operation. Note that when a UID shift is set we'll either clamp the UID/GID to
1008 * 0 or to the actual UID shift depending on the direction we copy. If no UID shift is set we'll copy
1009 * the UID/GIDs as they are. */
1011 r
= copy_tree_at(containerfd
, container_basename
, hostfd
, host_basename
, uid_shift
== 0 ? UID_INVALID
: 0, uid_shift
== 0 ? GID_INVALID
: 0, copy_flags
, NULL
, NULL
);
1013 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
, NULL
, NULL
);
1015 hostfd
= safe_close(hostfd
);
1016 containerfd
= safe_close(containerfd
);
1019 r
= log_error_errno(r
, "Failed to copy tree: %m");
1023 _exit(EXIT_SUCCESS
);
1026 (void) write(errno_pipe_fd
[1], &r
, sizeof(r
));
1027 _exit(EXIT_FAILURE
);
1030 errno_pipe_fd
[1] = safe_close(errno_pipe_fd
[1]);
1032 /* Copying might take a while, hence install a watch on the child, and return */
1034 r
= operation_new(m
->manager
, m
, child
, message
, errno_pipe_fd
[0], NULL
);
1036 (void) sigkill_wait(child
);
1039 errno_pipe_fd
[0] = -EBADF
;
1044 int bus_machine_method_open_root_directory(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1045 _cleanup_close_
int fd
= -EBADF
;
1046 Machine
*m
= ASSERT_PTR(userdata
);
1051 const char *details
[] = {
1053 "verb", "open_root_directory",
1057 r
= bus_verify_polkit_async(
1059 "org.freedesktop.machine1.manage-machines",
1061 &m
->manager
->polkit_registry
,
1066 return 1; /* Will call us back */
1071 fd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1077 case MACHINE_CONTAINER
: {
1078 _cleanup_close_
int mntns_fd
= -EBADF
, root_fd
= -EBADF
;
1079 _cleanup_close_pair_
int pair
[2] = EBADF_PAIR
;
1082 r
= namespace_open(m
->leader
.pid
,
1083 /* ret_pidns_fd = */ NULL
,
1085 /* ret_netns_fd = */ NULL
,
1086 /* ret_userns_fd = */ NULL
,
1091 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, pair
) < 0)
1094 r
= namespace_fork("(sd-openrootns)", "(sd-openroot)", NULL
, 0, FORK_RESET_SIGNALS
|FORK_DEATHSIG_SIGKILL
,
1095 -1, mntns_fd
, -1, -1, root_fd
, &child
);
1097 return sd_bus_error_set_errnof(error
, r
, "Failed to fork(): %m");
1099 _cleanup_close_
int dfd
= -EBADF
;
1101 pair
[0] = safe_close(pair
[0]);
1103 dfd
= open("/", O_RDONLY
|O_CLOEXEC
|O_DIRECTORY
);
1105 _exit(EXIT_FAILURE
);
1107 r
= send_one_fd(pair
[1], dfd
, 0);
1108 dfd
= safe_close(dfd
);
1110 _exit(EXIT_FAILURE
);
1112 _exit(EXIT_SUCCESS
);
1115 pair
[1] = safe_close(pair
[1]);
1117 r
= wait_for_terminate_and_check("(sd-openrootns)", child
, 0);
1119 return sd_bus_error_set_errnof(error
, r
, "Failed to wait for child: %m");
1120 if (r
!= EXIT_SUCCESS
)
1121 return sd_bus_error_set(error
, SD_BUS_ERROR_FAILED
, "Child died abnormally.");
1123 fd
= receive_one_fd(pair
[0], MSG_DONTWAIT
);
1131 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Opening the root directory is only supported on container machines.");
1134 return sd_bus_reply_method_return(message
, "h", fd
);
1137 int bus_machine_method_get_uid_shift(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
1138 Machine
*m
= ASSERT_PTR(userdata
);
1144 /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
1145 * we kinda have to for this. */
1147 if (m
->class == MACHINE_HOST
)
1148 return sd_bus_reply_method_return(message
, "u", UINT32_C(0));
1150 if (m
->class != MACHINE_CONTAINER
)
1151 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "UID/GID shift may only be determined for container machines.");
1153 r
= machine_get_uid_shift(m
, &shift
);
1155 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m
->name
);
1159 return sd_bus_reply_method_return(message
, "u", (uint32_t) shift
);
1162 static int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1163 Manager
*m
= ASSERT_PTR(userdata
);
1172 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
1173 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
1174 sd_bus_message
*message
;
1177 message
= sd_bus_get_current_message(bus
);
1181 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
, &creds
);
1185 r
= sd_bus_creds_get_pid(creds
, &pid
);
1189 r
= manager_get_machine_by_pid(m
, pid
, &machine
);
1193 _cleanup_free_
char *e
= NULL
;
1196 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
1200 e
= bus_label_unescape(p
);
1204 machine
= hashmap_get(m
->machines
, e
);
1213 char *machine_bus_path(Machine
*m
) {
1214 _cleanup_free_
char *e
= NULL
;
1218 e
= bus_label_escape(m
->name
);
1222 return strjoin("/org/freedesktop/machine1/machine/", e
);
1225 static int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1226 _cleanup_strv_free_
char **l
= NULL
;
1227 Machine
*machine
= NULL
;
1228 Manager
*m
= userdata
;
1235 HASHMAP_FOREACH(machine
, m
->machines
) {
1238 p
= machine_bus_path(machine
);
1242 r
= strv_consume(&l
, p
);
1247 *nodes
= TAKE_PTR(l
);
1252 static const sd_bus_vtable machine_vtable
[] = {
1253 SD_BUS_VTABLE_START(0),
1254 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
1255 SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128
, offsetof(Machine
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1256 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
1257 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
1258 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
1259 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
1260 SD_BUS_PROPERTY("Leader", "u", NULL
, offsetof(Machine
, leader
.pid
), SD_BUS_VTABLE_PROPERTY_CONST
),
1261 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
1262 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
1263 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
1264 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
1266 SD_BUS_METHOD("Terminate",
1269 bus_machine_method_terminate
,
1270 SD_BUS_VTABLE_UNPRIVILEGED
),
1271 SD_BUS_METHOD_WITH_ARGS("Kill",
1272 SD_BUS_ARGS("s", who
, "i", signal
),
1274 bus_machine_method_kill
,
1275 SD_BUS_VTABLE_UNPRIVILEGED
),
1276 SD_BUS_METHOD_WITH_ARGS("GetAddresses",
1278 SD_BUS_RESULT("a(iay)", addresses
),
1279 bus_machine_method_get_addresses
,
1280 SD_BUS_VTABLE_UNPRIVILEGED
),
1281 SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
1283 SD_BUS_RESULT("a{ss}", fields
),
1284 bus_machine_method_get_os_release
,
1285 SD_BUS_VTABLE_UNPRIVILEGED
),
1286 SD_BUS_METHOD_WITH_ARGS("GetUIDShift",
1288 SD_BUS_RESULT("u", shift
),
1289 bus_machine_method_get_uid_shift
,
1290 SD_BUS_VTABLE_UNPRIVILEGED
),
1291 SD_BUS_METHOD_WITH_ARGS("OpenPTY",
1293 SD_BUS_RESULT("h", pty
, "s", pty_path
),
1294 bus_machine_method_open_pty
,
1295 SD_BUS_VTABLE_UNPRIVILEGED
),
1296 SD_BUS_METHOD_WITH_ARGS("OpenLogin",
1298 SD_BUS_RESULT("h", pty
, "s", pty_path
),
1299 bus_machine_method_open_login
,
1300 SD_BUS_VTABLE_UNPRIVILEGED
),
1301 SD_BUS_METHOD_WITH_ARGS("OpenShell",
1302 SD_BUS_ARGS("s", user
, "s", path
, "as", args
, "as", environment
),
1303 SD_BUS_RESULT("h", pty
, "s", pty_path
),
1304 bus_machine_method_open_shell
,
1305 SD_BUS_VTABLE_UNPRIVILEGED
),
1306 SD_BUS_METHOD_WITH_ARGS("BindMount",
1307 SD_BUS_ARGS("s", source
, "s", destination
, "b", read_only
, "b", mkdir
),
1309 bus_machine_method_bind_mount
,
1310 SD_BUS_VTABLE_UNPRIVILEGED
),
1311 SD_BUS_METHOD_WITH_ARGS("CopyFrom",
1312 SD_BUS_ARGS("s", source
, "s", destination
),
1314 bus_machine_method_copy
,
1315 SD_BUS_VTABLE_UNPRIVILEGED
),
1316 SD_BUS_METHOD_WITH_ARGS("CopyTo",
1317 SD_BUS_ARGS("s", source
, "s", destination
),
1319 bus_machine_method_copy
,
1320 SD_BUS_VTABLE_UNPRIVILEGED
),
1321 SD_BUS_METHOD_WITH_ARGS("CopyFromWithFlags",
1322 SD_BUS_ARGS("s", source
, "s", destination
, "t", flags
),
1324 bus_machine_method_copy
,
1325 SD_BUS_VTABLE_UNPRIVILEGED
),
1326 SD_BUS_METHOD_WITH_ARGS("CopyToWithFlags",
1327 SD_BUS_ARGS("s", source
, "s", destination
, "t", flags
),
1329 bus_machine_method_copy
,
1330 SD_BUS_VTABLE_UNPRIVILEGED
),
1331 SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory",
1333 SD_BUS_RESULT("h", fd
),
1334 bus_machine_method_open_root_directory
,
1335 SD_BUS_VTABLE_UNPRIVILEGED
),
1340 const BusObjectImplementation machine_object
= {
1341 "/org/freedesktop/machine1/machine",
1342 "org.freedesktop.machine1.Machine",
1343 .fallback_vtables
= BUS_FALLBACK_VTABLES({machine_vtable
, machine_object_find
}),
1344 .node_enumerator
= machine_node_enumerator
,
1347 int machine_send_signal(Machine
*m
, bool new_machine
) {
1348 _cleanup_free_
char *p
= NULL
;
1352 p
= machine_bus_path(m
);
1356 return sd_bus_emit_signal(
1358 "/org/freedesktop/machine1",
1359 "org.freedesktop.machine1.Manager",
1360 new_machine
? "MachineNew" : "MachineRemoved",
1364 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
1365 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*c
= NULL
;
1366 _cleanup_free_
char *p
= NULL
;
1370 if (!m
->create_message
)
1373 c
= TAKE_PTR(m
->create_message
);
1376 return sd_bus_reply_method_error(c
, error
);
1378 /* Update the machine state file before we notify the client
1379 * about the result. */
1382 p
= machine_bus_path(m
);
1386 return sd_bus_reply_method_return(c
, "o", p
);