1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "bus-common-errors.h"
7 #include "bus-get-properties.h"
9 #include "bus-object.h"
10 #include "bus-polkit.h"
14 #include "errno-util.h"
17 #include "in-addr-util.h"
18 #include "local-addresses.h"
20 #include "machine-dbus.h"
22 #include "mount-util.h"
23 #include "namespace-util.h"
24 #include "operation.h"
25 #include "path-util.h"
26 #include "signal-util.h"
27 #include "string-util.h"
30 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class
, machine_class
, MachineClass
);
31 static BUS_DEFINE_PROPERTY_GET2(property_get_state
, "s", Machine
, machine_get_state
, machine_state_to_string
);
33 static int property_get_netif(
36 const char *interface
,
38 sd_bus_message
*reply
,
40 sd_bus_error
*error
) {
42 Machine
*m
= ASSERT_PTR(userdata
);
47 assert_cc(sizeof(int) == sizeof(int32_t));
49 return sd_bus_message_append_array(reply
, 'i', m
->netif
, m
->n_netif
* sizeof(int));
52 int bus_machine_method_unregister(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
53 Machine
*m
= ASSERT_PTR(userdata
);
58 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
59 const char *details
[] = {
65 r
= bus_verify_polkit_async_full(
67 "org.freedesktop.machine1.manage-machines",
71 &m
->manager
->polkit_registry
,
76 return 1; /* Will call us back */
79 r
= machine_finalize(m
);
83 return sd_bus_reply_method_return(message
, NULL
);
86 int bus_machine_method_terminate(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
87 Machine
*m
= ASSERT_PTR(userdata
);
92 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
93 const char *details
[] = {
99 r
= bus_verify_polkit_async_full(
101 "org.freedesktop.machine1.manage-machines",
105 &m
->manager
->polkit_registry
,
110 return 1; /* Will call us back */
117 return sd_bus_reply_method_return(message
, NULL
);
120 int bus_machine_method_kill(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
121 Machine
*m
= ASSERT_PTR(userdata
);
129 r
= sd_bus_message_read(message
, "si", &swho
, &signo
);
136 whom
= kill_whom_from_string(swho
);
138 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid kill parameter '%s'", swho
);
141 if (!SIGNAL_VALID(signo
))
142 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid signal %i", signo
);
144 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
145 const char *details
[] = {
151 r
= bus_verify_polkit_async_full(
153 "org.freedesktop.machine1.manage-machines",
157 &m
->manager
->polkit_registry
,
162 return 1; /* Will call us back */
165 r
= machine_kill(m
, whom
, signo
);
169 return sd_bus_reply_method_return(message
, NULL
);
172 int bus_machine_method_get_addresses(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
173 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
174 _cleanup_free_
struct local_address
*addresses
= NULL
;
175 Machine
*m
= ASSERT_PTR(userdata
);
180 r
= sd_bus_message_new_method_return(message
, &reply
);
184 r
= sd_bus_message_open_container(reply
, 'a', "(iay)");
188 int n
= machine_get_addresses(m
, &addresses
);
190 return sd_bus_error_setf(error
, BUS_ERROR_NO_PRIVATE_NETWORKING
, "Machine %s does not use private networking", m
->name
);
191 if (ERRNO_IS_NEG_NOT_SUPPORTED(n
))
192 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting IP address data is only supported on container machines.");
194 return sd_bus_error_set_errnof(error
, n
, "Failed to get addresses: %m");
196 for (int i
= 0; i
< n
; i
++) {
197 r
= sd_bus_message_open_container(reply
, 'r', "iay");
201 r
= sd_bus_message_append(reply
, "i", addresses
[i
].family
);
205 r
= sd_bus_message_append_array(reply
, 'y', &addresses
[i
].address
, FAMILY_ADDRESS_SIZE(addresses
[i
].family
));
209 r
= sd_bus_message_close_container(reply
);
214 r
= sd_bus_message_close_container(reply
);
218 return sd_bus_message_send(reply
);
221 int bus_machine_method_get_ssh_info(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
222 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
223 Machine
*m
= ASSERT_PTR(userdata
);
228 r
= sd_bus_message_new_method_return(message
, &reply
);
232 if (!m
->ssh_address
|| !m
->ssh_private_key_path
)
235 r
= sd_bus_message_append(reply
, "ss", m
->ssh_address
, m
->ssh_private_key_path
);
239 return sd_bus_message_send(reply
);
242 int bus_machine_method_get_os_release(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
243 _cleanup_strv_free_
char **l
= NULL
;
244 Machine
*m
= ASSERT_PTR(userdata
);
249 r
= machine_get_os_release(m
, &l
);
251 return sd_bus_error_set(error
, SD_BUS_ERROR_FAILED
, "Machine does not contain OS release information.");
252 if (ERRNO_IS_NEG_NOT_SUPPORTED(r
))
253 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Requesting OS release data is only supported on container machines.");
255 return sd_bus_error_set_errnof(error
, r
, "Failed to get OS release: %m");
257 return bus_reply_pair_array(message
, l
);
260 int bus_machine_method_open_pty(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
261 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
262 _cleanup_free_
char *pty_name
= NULL
;
263 _cleanup_close_
int master
= -EBADF
;
264 Machine
*m
= ASSERT_PTR(userdata
);
269 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
270 const char *details
[] = {
275 r
= bus_verify_polkit_async_full(
277 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty",
281 &m
->manager
->polkit_registry
,
286 return 1; /* Will call us back */
289 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
293 r
= sd_bus_message_new_method_return(message
, &reply
);
297 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
301 return sd_bus_message_send(reply
);
304 int bus_machine_method_open_login(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
305 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
306 _cleanup_free_
char *pty_name
= NULL
;
307 _cleanup_close_
int master
= -EBADF
;
308 Machine
*m
= ASSERT_PTR(userdata
);
313 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
314 const char *details
[] = {
320 r
= bus_verify_polkit_async_full(
322 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login",
326 &m
->manager
->polkit_registry
,
331 return 1; /* Will call us back */
334 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
338 r
= machine_start_getty(m
, pty_name
, error
);
342 r
= sd_bus_message_new_method_return(message
, &reply
);
346 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
350 return sd_bus_message_send(reply
);
353 int bus_machine_method_open_shell(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
354 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
355 _cleanup_free_
char *pty_name
= NULL
;
356 _cleanup_close_
int master
= -EBADF
;
357 _cleanup_strv_free_
char **env
= NULL
, **args_wire
= NULL
, **args
= NULL
;
358 Machine
*m
= ASSERT_PTR(userdata
);
359 const char *user
, *path
;
364 r
= sd_bus_message_read(message
, "ss", &user
, &path
);
367 user
= isempty(user
) ? "root" : user
;
369 /* Ensure only root can shell into the root namespace, unless it's specifically the host machine,
370 * which is owned by uid 0 anyway and cannot be self-registered. This is to avoid unprivileged
371 * users registering a process they own in the root user namespace, and then shelling in as root
372 * or another user. Note that the shell operation is privileged and requires 'auth_admin', so we
373 * do not need to check the caller's uid, as that will be checked by polkit, and if they machine's
374 * and the caller's do not match, authorization will be required. It's only the case where the
375 * caller owns the machine that will be shortcut and needs to be checked here. */
376 if (m
->uid
!= 0 && m
->class != MACHINE_HOST
) {
377 r
= pidref_in_same_namespace(&PIDREF_MAKE_FROM_PID(1), &m
->leader
, NAMESPACE_USER
);
381 return sd_bus_error_set(
383 SD_BUS_ERROR_ACCESS_DENIED
,
384 "Only root may shell into the root user namespace");
387 r
= sd_bus_message_read_strv(message
, &args_wire
);
391 path
= machine_default_shell_path();
392 args
= machine_default_shell_args(user
);
396 if (!path_is_absolute(path
))
397 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Specified path '%s' is not absolute", path
);
398 args
= TAKE_PTR(args_wire
);
399 if (strv_isempty(args
)) {
400 args
= strv_free(args
);
402 args
= strv_new(path
);
408 r
= sd_bus_message_read_strv(message
, &env
);
411 if (!strv_env_is_valid(env
))
412 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid environment assignments");
414 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
415 _cleanup_free_
char *command_line
= strv_join(args
, " ");
419 const char *details
[] = {
423 "command_line", command_line
,
427 r
= bus_verify_polkit_async_full(
429 m
->class == MACHINE_HOST
? "org.freedesktop.machine1.host-shell" : "org.freedesktop.machine1.shell",
433 &m
->manager
->polkit_registry
,
438 return 1; /* Will call us back */
441 master
= machine_openpt(m
, O_RDWR
|O_NOCTTY
|O_CLOEXEC
, &pty_name
);
445 r
= machine_start_shell(m
, master
, pty_name
, user
, path
, args
, env
, error
);
449 r
= sd_bus_message_new_method_return(message
, &reply
);
453 r
= sd_bus_message_append(reply
, "hs", master
, pty_name
);
457 return sd_bus_message_send(reply
);
460 int bus_machine_method_bind_mount(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
461 int read_only
, make_file_or_directory
;
462 const char *dest
, *src
, *propagate_directory
;
463 Machine
*m
= ASSERT_PTR(userdata
);
464 MountInNamespaceFlags flags
= 0;
470 if (m
->class != MACHINE_CONTAINER
)
471 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Bind mounting is only supported on container machines.");
473 r
= sd_bus_message_read(message
, "ssbb", &src
, &dest
, &read_only
, &make_file_or_directory
);
477 if (!path_is_absolute(src
) || !path_is_normalized(src
))
478 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute and normalized.");
482 else if (!path_is_absolute(dest
) || !path_is_normalized(dest
))
483 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute and normalized.");
485 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
486 const char *details
[] = {
494 /* NB: For now not opened up to owner of machine without auth */
495 r
= bus_verify_polkit_async(
497 "org.freedesktop.machine1.manage-machines",
499 &m
->manager
->polkit_registry
,
504 return 1; /* Will call us back */
507 r
= machine_get_uid_shift(m
, &uid
);
511 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Can't bind mount on container with user namespacing applied.");
514 flags
|= MOUNT_IN_NAMESPACE_READ_ONLY
;
515 if (make_file_or_directory
)
516 flags
|= MOUNT_IN_NAMESPACE_MAKE_FILE_OR_DIRECTORY
;
518 propagate_directory
= strjoina("/run/systemd/nspawn/propagate/", m
->name
);
519 r
= bind_mount_in_namespace(
522 "/run/host/incoming/",
526 return sd_bus_error_set_errnof(error
, r
, "Failed to mount %s on %s in machine's namespace: %m", src
, dest
);
528 return sd_bus_reply_method_return(message
, NULL
);
531 int bus_machine_method_copy(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
532 const char *src
, *dest
, *host_path
, *container_path
;
533 CopyFlags copy_flags
= COPY_REFLINK
|COPY_MERGE
|COPY_HARDLINKS
;
534 Machine
*m
= ASSERT_PTR(userdata
);
535 Manager
*manager
= m
->manager
;
541 if (m
->manager
->n_operations
>= OPERATIONS_MAX
)
542 return sd_bus_error_set(error
, SD_BUS_ERROR_LIMITS_EXCEEDED
, "Too many ongoing copies.");
544 if (m
->class != MACHINE_CONTAINER
)
545 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Copying files is only supported on container machines.");
547 r
= sd_bus_message_read(message
, "ss", &src
, &dest
);
551 if (endswith(sd_bus_message_get_member(message
), "WithFlags")) {
554 r
= sd_bus_message_read(message
, "t", &raw_flags
);
558 if ((raw_flags
& ~_MACHINE_COPY_FLAGS_MASK_PUBLIC
) != 0)
561 if (raw_flags
& MACHINE_COPY_REPLACE
)
562 copy_flags
|= COPY_REPLACE
;
565 if (!path_is_absolute(src
))
566 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Source path must be absolute.");
570 else if (!path_is_absolute(dest
))
571 return sd_bus_error_set(error
, SD_BUS_ERROR_INVALID_ARGS
, "Destination path must be absolute.");
573 if (manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
574 const char *details
[] = {
582 /* NB: For now not opened up to owner of machine without auth */
583 r
= bus_verify_polkit_async(
585 "org.freedesktop.machine1.manage-machines",
587 &manager
->polkit_registry
,
592 return 1; /* Will call us back */
595 copy_from
= strstr(sd_bus_message_get_member(message
), "CopyFrom");
598 container_path
= src
;
602 container_path
= dest
;
606 r
= machine_copy_from_to_operation(manager
, m
, host_path
, container_path
, copy_from
, copy_flags
, &op
);
608 return sd_bus_error_set_errnof(error
, r
, "Failed to copy from/to machine '%s': %m", m
->name
);
610 operation_attach_bus_reply(op
, message
);
614 int bus_machine_method_open_root_directory(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
615 _cleanup_close_
int fd
= -EBADF
;
616 Machine
*m
= ASSERT_PTR(userdata
);
621 if (m
->manager
->runtime_scope
!= RUNTIME_SCOPE_USER
) {
622 const char *details
[] = {
624 "verb", "open_root_directory",
628 /* NB: For now not opened up to owner of machine without auth */
629 r
= bus_verify_polkit_async(
631 "org.freedesktop.machine1.manage-machines",
633 &m
->manager
->polkit_registry
,
638 return 1; /* Will call us back */
641 fd
= machine_open_root_directory(m
);
642 if (ERRNO_IS_NEG_NOT_SUPPORTED(fd
))
643 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Opening the root directory is only supported on container machines.");
645 return sd_bus_error_set_errnof(error
, fd
, "Failed to open root directory of machine '%s': %m", m
->name
);
647 return sd_bus_reply_method_return(message
, "h", fd
);
650 int bus_machine_method_get_uid_shift(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
651 Machine
*m
= ASSERT_PTR(userdata
);
657 /* You wonder why this is a method and not a property? Well, properties are not supposed to return errors, but
658 * we kinda have to for this. */
660 if (m
->class == MACHINE_HOST
)
661 return sd_bus_reply_method_return(message
, "u", UINT32_C(0));
663 if (m
->class != MACHINE_CONTAINER
)
664 return sd_bus_error_set(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "UID/GID shift may only be determined for container machines.");
666 r
= machine_get_uid_shift(m
, &shift
);
668 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "Machine %s uses a complex UID/GID mapping, cannot determine shift", m
->name
);
672 return sd_bus_reply_method_return(message
, "u", (uint32_t) shift
);
675 static int machine_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
676 Manager
*m
= ASSERT_PTR(userdata
);
685 if (streq(path
, "/org/freedesktop/machine1/machine/self")) {
686 _cleanup_(pidref_done
) PidRef pidref
= PIDREF_NULL
;
687 _cleanup_(sd_bus_creds_unrefp
) sd_bus_creds
*creds
= NULL
;
688 sd_bus_message
*message
;
690 message
= sd_bus_get_current_message(bus
);
694 r
= sd_bus_query_sender_creds(message
, SD_BUS_CREDS_PID
|SD_BUS_CREDS_PIDFD
, &creds
);
698 r
= bus_creds_get_pidref(creds
, &pidref
);
702 r
= manager_get_machine_by_pidref(m
, &pidref
, &machine
);
706 _cleanup_free_
char *e
= NULL
;
709 p
= startswith(path
, "/org/freedesktop/machine1/machine/");
713 e
= bus_label_unescape(p
);
717 machine
= hashmap_get(m
->machines
, e
);
726 char* machine_bus_path(Machine
*m
) {
727 _cleanup_free_
char *e
= NULL
;
731 e
= bus_label_escape(m
->name
);
735 return strjoin("/org/freedesktop/machine1/machine/", e
);
738 static int machine_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
739 _cleanup_strv_free_
char **l
= NULL
;
740 Machine
*machine
= NULL
;
741 Manager
*m
= userdata
;
748 HASHMAP_FOREACH(machine
, m
->machines
) {
751 p
= machine_bus_path(machine
);
755 r
= strv_consume(&l
, p
);
760 *nodes
= TAKE_PTR(l
);
765 static const sd_bus_vtable machine_vtable
[] = {
766 SD_BUS_VTABLE_START(0),
767 SD_BUS_PROPERTY("Name", "s", NULL
, offsetof(Machine
, name
), SD_BUS_VTABLE_PROPERTY_CONST
),
768 SD_BUS_PROPERTY("Id", "ay", bus_property_get_id128
, offsetof(Machine
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
769 BUS_PROPERTY_DUAL_TIMESTAMP("Timestamp", offsetof(Machine
, timestamp
), SD_BUS_VTABLE_PROPERTY_CONST
),
770 SD_BUS_PROPERTY("Service", "s", NULL
, offsetof(Machine
, service
), SD_BUS_VTABLE_PROPERTY_CONST
),
771 SD_BUS_PROPERTY("Unit", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
),
772 SD_BUS_PROPERTY("Scope", "s", NULL
, offsetof(Machine
, unit
), SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_HIDDEN
),
773 SD_BUS_PROPERTY("Subgroup", "s", NULL
, offsetof(Machine
, subgroup
), SD_BUS_VTABLE_PROPERTY_CONST
),
774 SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid
, offsetof(Machine
, leader
.pid
), SD_BUS_VTABLE_PROPERTY_CONST
),
775 SD_BUS_PROPERTY("LeaderPIDFDId", "t", bus_property_get_pidfdid
, offsetof(Machine
, leader
), SD_BUS_VTABLE_PROPERTY_CONST
),
776 SD_BUS_PROPERTY("Supervisor", "u", bus_property_get_pid
, offsetof(Machine
, supervisor
.pid
), SD_BUS_VTABLE_PROPERTY_CONST
),
777 SD_BUS_PROPERTY("SupervisorPIDFDId", "t", bus_property_get_pidfdid
, offsetof(Machine
, supervisor
), SD_BUS_VTABLE_PROPERTY_CONST
),
778 SD_BUS_PROPERTY("Class", "s", property_get_class
, offsetof(Machine
, class), SD_BUS_VTABLE_PROPERTY_CONST
),
779 SD_BUS_PROPERTY("RootDirectory", "s", NULL
, offsetof(Machine
, root_directory
), SD_BUS_VTABLE_PROPERTY_CONST
),
780 SD_BUS_PROPERTY("NetworkInterfaces", "ai", property_get_netif
, 0, SD_BUS_VTABLE_PROPERTY_CONST
),
781 SD_BUS_PROPERTY("VSockCID", "u", NULL
, offsetof(Machine
, vsock_cid
), SD_BUS_VTABLE_PROPERTY_CONST
),
782 SD_BUS_PROPERTY("SSHAddress", "s", NULL
, offsetof(Machine
, ssh_address
), SD_BUS_VTABLE_PROPERTY_CONST
),
783 SD_BUS_PROPERTY("SSHPrivateKeyPath", "s", NULL
, offsetof(Machine
, ssh_private_key_path
), SD_BUS_VTABLE_PROPERTY_CONST
),
784 SD_BUS_PROPERTY("State", "s", property_get_state
, 0, 0),
785 SD_BUS_PROPERTY("UID", "u", bus_property_get_uid
, offsetof(Machine
, uid
), SD_BUS_VTABLE_PROPERTY_CONST
),
787 SD_BUS_METHOD("Terminate",
790 bus_machine_method_terminate
,
791 SD_BUS_VTABLE_UNPRIVILEGED
),
792 SD_BUS_METHOD_WITH_ARGS("Kill",
793 SD_BUS_ARGS("s", whom
, "i", signal
),
795 bus_machine_method_kill
,
796 SD_BUS_VTABLE_UNPRIVILEGED
),
797 SD_BUS_METHOD_WITH_ARGS("GetAddresses",
799 SD_BUS_RESULT("a(iay)", addresses
),
800 bus_machine_method_get_addresses
,
801 SD_BUS_VTABLE_UNPRIVILEGED
),
802 SD_BUS_METHOD_WITH_ARGS("GetSSHInfo",
804 SD_BUS_RESULT("s", ssh_address
, "s", ssh_private_key_path
),
805 bus_machine_method_get_ssh_info
,
806 SD_BUS_VTABLE_UNPRIVILEGED
),
807 SD_BUS_METHOD_WITH_ARGS("GetOSRelease",
809 SD_BUS_RESULT("a{ss}", fields
),
810 bus_machine_method_get_os_release
,
811 SD_BUS_VTABLE_UNPRIVILEGED
),
812 SD_BUS_METHOD_WITH_ARGS("GetUIDShift",
814 SD_BUS_RESULT("u", shift
),
815 bus_machine_method_get_uid_shift
,
816 SD_BUS_VTABLE_UNPRIVILEGED
),
817 SD_BUS_METHOD_WITH_ARGS("OpenPTY",
819 SD_BUS_RESULT("h", pty
, "s", pty_path
),
820 bus_machine_method_open_pty
,
821 SD_BUS_VTABLE_UNPRIVILEGED
),
822 SD_BUS_METHOD_WITH_ARGS("OpenLogin",
824 SD_BUS_RESULT("h", pty
, "s", pty_path
),
825 bus_machine_method_open_login
,
826 SD_BUS_VTABLE_UNPRIVILEGED
),
827 SD_BUS_METHOD_WITH_ARGS("OpenShell",
828 SD_BUS_ARGS("s", user
, "s", path
, "as", args
, "as", environment
),
829 SD_BUS_RESULT("h", pty
, "s", pty_path
),
830 bus_machine_method_open_shell
,
831 SD_BUS_VTABLE_UNPRIVILEGED
),
832 SD_BUS_METHOD_WITH_ARGS("BindMount",
833 SD_BUS_ARGS("s", source
, "s", destination
, "b", read_only
, "b", mkdir
),
835 bus_machine_method_bind_mount
,
836 SD_BUS_VTABLE_UNPRIVILEGED
),
837 SD_BUS_METHOD_WITH_ARGS("CopyFrom",
838 SD_BUS_ARGS("s", source
, "s", destination
),
840 bus_machine_method_copy
,
841 SD_BUS_VTABLE_UNPRIVILEGED
),
842 SD_BUS_METHOD_WITH_ARGS("CopyTo",
843 SD_BUS_ARGS("s", source
, "s", destination
),
845 bus_machine_method_copy
,
846 SD_BUS_VTABLE_UNPRIVILEGED
),
847 SD_BUS_METHOD_WITH_ARGS("CopyFromWithFlags",
848 SD_BUS_ARGS("s", source
, "s", destination
, "t", flags
),
850 bus_machine_method_copy
,
851 SD_BUS_VTABLE_UNPRIVILEGED
),
852 SD_BUS_METHOD_WITH_ARGS("CopyToWithFlags",
853 SD_BUS_ARGS("s", source
, "s", destination
, "t", flags
),
855 bus_machine_method_copy
,
856 SD_BUS_VTABLE_UNPRIVILEGED
),
857 SD_BUS_METHOD_WITH_ARGS("OpenRootDirectory",
859 SD_BUS_RESULT("h", fd
),
860 bus_machine_method_open_root_directory
,
861 SD_BUS_VTABLE_UNPRIVILEGED
),
866 const BusObjectImplementation machine_object
= {
867 "/org/freedesktop/machine1/machine",
868 "org.freedesktop.machine1.Machine",
869 .fallback_vtables
= BUS_FALLBACK_VTABLES({machine_vtable
, machine_object_find
}),
870 .node_enumerator
= machine_node_enumerator
,
873 int machine_send_signal(Machine
*m
, bool new_machine
) {
874 _cleanup_free_
char *p
= NULL
;
878 p
= machine_bus_path(m
);
882 return sd_bus_emit_signal(
884 "/org/freedesktop/machine1",
885 "org.freedesktop.machine1.Manager",
886 new_machine
? "MachineNew" : "MachineRemoved",
890 int machine_send_create_reply(Machine
*m
, sd_bus_error
*error
) {
891 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*c
= NULL
;
892 _cleanup_free_
char *p
= NULL
;
896 if (!m
->create_message
)
899 c
= TAKE_PTR(m
->create_message
);
902 return sd_bus_reply_method_error(c
, error
);
904 /* Update the machine state file before we notify the client
905 * about the result. */
908 p
= machine_bus_path(m
);
912 return sd_bus_reply_method_return(c
, "o", p
);