1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "alloc-util.h"
9 #include "bus-common-errors.h"
10 #include "bus-polkit.h"
14 #include "hostname-util.h"
15 #include "import-util.h"
16 #include "machine-pool.h"
17 #include "main-func.h"
18 #include "missing_capability.h"
20 #include "parse-util.h"
21 #include "path-util.h"
22 #include "process-util.h"
23 #include "signal-util.h"
24 #include "socket-util.h"
25 #include "stat-util.h"
26 #include "string-table.h"
28 #include "syslog-util.h"
29 #include "user-util.h"
33 typedef struct Transfer Transfer
;
34 typedef struct Manager Manager
;
36 typedef enum TransferType
{
45 _TRANSFER_TYPE_INVALID
= -1,
68 char log_message
[LINE_MAX
];
69 size_t log_message_size
;
71 sd_event_source
*pid_event_source
;
72 sd_event_source
*log_event_source
;
75 unsigned progress_percent
;
85 uint32_t current_transfer_id
;
88 Hashmap
*polkit_registry
;
92 sd_event_source
*notify_event_source
;
95 #define TRANSFERS_MAX 64
97 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
98 [TRANSFER_IMPORT_TAR
] = "import-tar",
99 [TRANSFER_IMPORT_RAW
] = "import-raw",
100 [TRANSFER_IMPORT_FS
] = "import-fs",
101 [TRANSFER_EXPORT_TAR
] = "export-tar",
102 [TRANSFER_EXPORT_RAW
] = "export-raw",
103 [TRANSFER_PULL_TAR
] = "pull-tar",
104 [TRANSFER_PULL_RAW
] = "pull-raw",
107 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
109 static Transfer
*transfer_unref(Transfer
*t
) {
114 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
116 sd_event_source_unref(t
->pid_event_source
);
117 sd_event_source_unref(t
->log_event_source
);
122 free(t
->object_path
);
125 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
126 (void) wait_for_terminate(t
->pid
, NULL
);
129 safe_close(t
->log_fd
);
130 safe_close(t
->stdin_fd
);
131 safe_close(t
->stdout_fd
);
136 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
138 static int transfer_new(Manager
*m
, Transfer
**ret
) {
139 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
146 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
149 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
153 t
= new(Transfer
, 1);
158 .type
= _TRANSFER_TYPE_INVALID
,
162 .verify
= _IMPORT_VERIFY_INVALID
,
163 .progress_percent
= (unsigned) -1,
166 id
= m
->current_transfer_id
+ 1;
168 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
171 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
175 m
->current_transfer_id
= id
;
185 static double transfer_percent_as_double(Transfer
*t
) {
188 if (t
->progress_percent
== (unsigned) -1)
191 return (double) t
->progress_percent
/ 100.0;
194 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
195 int r
, priority
= LOG_INFO
;
200 syslog_parse_priority(&line
, &priority
, true);
202 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
204 r
= sd_bus_emit_signal(
207 "org.freedesktop.import1.Transfer",
213 log_warning_errno(r
, "Cannot emit log message signal, ignoring: %m");
216 static void transfer_send_logs(Transfer
*t
, bool flush
) {
219 /* Try to send out all log messages, if we can. But if we
220 * can't we remove the messages from the buffer, but don't
223 while (t
->log_message_size
> 0) {
224 _cleanup_free_
char *n
= NULL
;
227 if (t
->log_message_size
>= sizeof(t
->log_message
))
228 e
= t
->log_message
+ sizeof(t
->log_message
);
232 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
233 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
247 e
= t
->log_message
+ t
->log_message_size
;
250 n
= strndup(t
->log_message
, e
- t
->log_message
);
252 /* Skip over NUL and newlines */
253 while (e
< t
->log_message
+ t
->log_message_size
&& IN_SET(*e
, 0, '\n'))
256 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
257 t
->log_message_size
-= e
- t
->log_message
;
267 transfer_send_log_line(t
, n
);
271 static int transfer_finalize(Transfer
*t
, bool success
) {
276 transfer_send_logs(t
, true);
278 r
= sd_bus_emit_signal(
280 "/org/freedesktop/import1",
281 "org.freedesktop.import1.Manager",
287 t
->n_canceled
> 0 ? "canceled" : "failed");
290 log_error_errno(r
, "Cannot emit message: %m");
296 static int transfer_cancel(Transfer
*t
) {
301 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
309 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
310 Transfer
*t
= userdata
;
311 bool success
= false;
316 if (si
->si_code
== CLD_EXITED
) {
317 if (si
->si_status
!= 0)
318 log_error("Transfer process failed with exit code %i.", si
->si_status
);
320 log_debug("Transfer process succeeded.");
324 } else if (IN_SET(si
->si_code
, CLD_KILLED
, CLD_DUMPED
))
325 log_error("Transfer process terminated by signal %s.", signal_to_string(si
->si_status
));
327 log_error("Transfer process failed due to unknown reason.");
331 return transfer_finalize(t
, success
);
334 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
335 Transfer
*t
= userdata
;
341 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
343 log_error_errno(errno
, "Failed to read log message: %m");
345 /* EOF/read error. We just close the pipe here, and
346 * close the watch, waiting for the SIGCHLD to arrive,
347 * before we do anything else. */
348 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
352 t
->log_message_size
+= l
;
354 transfer_send_logs(t
, false);
359 static int transfer_start(Transfer
*t
) {
360 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
366 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
369 r
= safe_fork("(sd-transfer)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &t
->pid
);
373 const char *cmd
[] = {
374 NULL
, /* systemd-import, systemd-import-fs, systemd-export or systemd-pull */
376 NULL
, /* --verify= */
377 NULL
, /* verify argument */
378 NULL
, /* maybe --force */
379 NULL
, /* maybe --read-only */
380 NULL
, /* if so: the actual URL */
381 NULL
, /* maybe --format= */
382 NULL
, /* if so: the actual format */
391 pipefd
[0] = safe_close(pipefd
[0]);
393 r
= rearrange_stdio(t
->stdin_fd
,
394 t
->stdout_fd
< 0 ? pipefd
[1] : t
->stdout_fd
,
397 log_error_errno(r
, "Failed to set stdin/stdout/stderr: %m");
401 if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 ||
402 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) {
403 log_error_errno(errno
, "setenv() failed: %m");
409 case TRANSFER_IMPORT_TAR
:
410 case TRANSFER_IMPORT_RAW
:
411 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
414 case TRANSFER_IMPORT_FS
:
415 cmd
[k
++] = SYSTEMD_IMPORT_FS_PATH
;
418 case TRANSFER_EXPORT_TAR
:
419 case TRANSFER_EXPORT_RAW
:
420 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
423 case TRANSFER_PULL_TAR
:
424 case TRANSFER_PULL_RAW
:
425 cmd
[k
++] = SYSTEMD_PULL_PATH
;
429 assert_not_reached("Unexpected transfer type");
434 case TRANSFER_IMPORT_TAR
:
435 case TRANSFER_EXPORT_TAR
:
436 case TRANSFER_PULL_TAR
:
440 case TRANSFER_IMPORT_RAW
:
441 case TRANSFER_EXPORT_RAW
:
442 case TRANSFER_PULL_RAW
:
446 case TRANSFER_IMPORT_FS
:
454 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
455 cmd
[k
++] = "--verify";
456 cmd
[k
++] = import_verify_to_string(t
->verify
);
460 cmd
[k
++] = "--force";
462 cmd
[k
++] = "--read-only";
465 cmd
[k
++] = "--format";
466 cmd
[k
++] = t
->format
;
469 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
471 cmd
[k
++] = t
->remote
;
480 execv(cmd
[0], (char * const *) cmd
);
481 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
485 pipefd
[1] = safe_close(pipefd
[1]);
486 t
->log_fd
= TAKE_FD(pipefd
[0]);
488 t
->stdin_fd
= safe_close(t
->stdin_fd
);
490 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
494 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
498 /* Make sure always process logging before SIGCHLD */
499 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
503 r
= sd_bus_emit_signal(
505 "/org/freedesktop/import1",
506 "org.freedesktop.import1.Manager",
517 static Manager
*manager_unref(Manager
*m
) {
523 sd_event_source_unref(m
->notify_event_source
);
524 safe_close(m
->notify_fd
);
526 while ((t
= hashmap_first(m
->transfers
)))
529 hashmap_free(m
->transfers
);
531 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
533 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
534 sd_event_unref(m
->event
);
539 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
541 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
543 char buf
[NOTIFY_BUFFER_MAX
+1];
544 struct iovec iovec
= {
546 .iov_len
= sizeof(buf
)-1,
549 struct cmsghdr cmsghdr
;
550 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
551 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
553 struct msghdr msghdr
= {
556 .msg_control
= &control
,
557 .msg_controllen
= sizeof(control
),
559 struct ucred
*ucred
= NULL
;
560 Manager
*m
= userdata
;
561 struct cmsghdr
*cmsg
;
568 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
570 if (IN_SET(errno
, EAGAIN
, EINTR
))
576 cmsg_close_all(&msghdr
);
578 CMSG_FOREACH(cmsg
, &msghdr
)
579 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
580 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
581 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
582 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
584 if (msghdr
.msg_flags
& MSG_TRUNC
) {
585 log_warning("Got overly long notification datagram, ignoring.");
589 if (!ucred
|| ucred
->pid
<= 0) {
590 log_warning("Got notification datagram lacking credential information, ignoring.");
594 HASHMAP_FOREACH(t
, m
->transfers
, i
)
595 if (ucred
->pid
== t
->pid
)
599 log_warning("Got notification datagram from unexpected peer, ignoring.");
605 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
607 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
614 e
= strchrnul(p
, '\n');
617 r
= parse_percent(p
);
619 log_warning("Got invalid percent value, ignoring.");
623 t
->progress_percent
= (unsigned) r
;
625 log_debug("Got percentage from client: %u%%", t
->progress_percent
);
629 static int manager_new(Manager
**ret
) {
630 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
631 static const union sockaddr_union sa
= {
632 .un
.sun_family
= AF_UNIX
,
633 .un
.sun_path
= "/run/systemd/import/notify",
639 m
= new0(Manager
, 1);
643 r
= sd_event_default(&m
->event
);
647 sd_event_set_watchdog(m
->event
, true);
649 r
= sd_bus_default_system(&m
->bus
);
653 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
654 if (m
->notify_fd
< 0)
657 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
658 (void) sockaddr_un_unlink(&sa
.un
);
660 if (bind(m
->notify_fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
)) < 0)
663 r
= setsockopt_int(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, true);
667 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
676 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *remote
) {
682 assert(type
< _TRANSFER_TYPE_MAX
);
684 HASHMAP_FOREACH(t
, m
->transfers
, i
)
685 if (t
->type
== type
&& streq_ptr(t
->remote
, remote
))
691 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
692 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
693 int fd
, force
, read_only
, r
;
694 const char *local
, *object
;
695 Manager
*m
= userdata
;
703 r
= bus_verify_polkit_async(
706 "org.freedesktop.import1.import",
715 return 1; /* Will call us back */
717 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
721 if (fstat(fd
, &st
) < 0)
724 if (!S_ISREG(st
.st_mode
) && !S_ISFIFO(st
.st_mode
))
727 if (!machine_name_is_valid(local
))
728 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
730 r
= setup_machine_directory(error
);
734 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
736 r
= transfer_new(m
, &t
);
741 t
->force_local
= force
;
742 t
->read_only
= read_only
;
744 t
->local
= strdup(local
);
748 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
752 r
= transfer_start(t
);
756 object
= t
->object_path
;
760 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
763 static int method_import_fs(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
764 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
765 int fd
, force
, read_only
, r
;
766 const char *local
, *object
;
767 Manager
*m
= userdata
;
773 r
= bus_verify_polkit_async(
776 "org.freedesktop.import1.import",
785 return 1; /* Will call us back */
787 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
791 r
= fd_verify_directory(fd
);
795 if (!machine_name_is_valid(local
))
796 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
798 r
= setup_machine_directory(error
);
802 r
= transfer_new(m
, &t
);
806 t
->type
= TRANSFER_IMPORT_FS
;
807 t
->force_local
= force
;
808 t
->read_only
= read_only
;
810 t
->local
= strdup(local
);
814 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
818 r
= transfer_start(t
);
822 object
= t
->object_path
;
826 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
829 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
830 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
832 const char *local
, *object
, *format
;
833 Manager
*m
= userdata
;
841 r
= bus_verify_polkit_async(
844 "org.freedesktop.import1.export",
853 return 1; /* Will call us back */
855 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
859 if (!machine_name_is_valid(local
))
860 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
862 if (fstat(fd
, &st
) < 0)
865 if (!S_ISREG(st
.st_mode
) && !S_ISFIFO(st
.st_mode
))
868 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
870 r
= transfer_new(m
, &t
);
876 if (!isempty(format
)) {
877 t
->format
= strdup(format
);
882 t
->local
= strdup(local
);
886 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
887 if (t
->stdout_fd
< 0)
890 r
= transfer_start(t
);
894 object
= t
->object_path
;
898 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
901 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
902 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
903 const char *remote
, *local
, *verify
, *object
;
904 Manager
*m
= userdata
;
913 r
= bus_verify_polkit_async(
916 "org.freedesktop.import1.pull",
925 return 1; /* Will call us back */
927 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
931 if (!http_url_is_valid(remote
))
932 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
936 else if (!machine_name_is_valid(local
))
937 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
940 v
= IMPORT_VERIFY_SIGNATURE
;
942 v
= import_verify_from_string(verify
);
944 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
946 r
= setup_machine_directory(error
);
950 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
952 if (manager_find(m
, type
, remote
))
953 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
955 r
= transfer_new(m
, &t
);
961 t
->force_local
= force
;
963 t
->remote
= strdup(remote
);
968 t
->local
= strdup(local
);
973 r
= transfer_start(t
);
977 object
= t
->object_path
;
981 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
984 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
985 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
986 Manager
*m
= userdata
;
994 r
= sd_bus_message_new_method_return(msg
, &reply
);
998 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1002 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1004 r
= sd_bus_message_append(
1008 transfer_type_to_string(t
->type
),
1011 transfer_percent_as_double(t
),
1017 r
= sd_bus_message_close_container(reply
);
1021 return sd_bus_send(NULL
, reply
, NULL
);
1024 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1025 Transfer
*t
= userdata
;
1031 r
= bus_verify_polkit_async(
1034 "org.freedesktop.import1.pull",
1038 &t
->manager
->polkit_registry
,
1043 return 1; /* Will call us back */
1045 r
= transfer_cancel(t
);
1049 return sd_bus_reply_method_return(msg
, NULL
);
1052 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1053 Manager
*m
= userdata
;
1061 r
= bus_verify_polkit_async(
1064 "org.freedesktop.import1.pull",
1068 &m
->polkit_registry
,
1073 return 1; /* Will call us back */
1075 r
= sd_bus_message_read(msg
, "u", &id
);
1079 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1081 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1083 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1085 r
= transfer_cancel(t
);
1089 return sd_bus_reply_method_return(msg
, NULL
);
1092 static int property_get_progress(
1095 const char *interface
,
1096 const char *property
,
1097 sd_bus_message
*reply
,
1099 sd_bus_error
*error
) {
1101 Transfer
*t
= userdata
;
1107 return sd_bus_message_append(reply
, "d", transfer_percent_as_double(t
));
1110 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1111 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1113 static const sd_bus_vtable transfer_vtable
[] = {
1114 SD_BUS_VTABLE_START(0),
1115 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1116 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1117 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1118 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1119 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1120 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1121 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1122 SD_BUS_SIGNAL("LogMessage", "us", 0),
1126 static const sd_bus_vtable manager_vtable
[] = {
1127 SD_BUS_VTABLE_START(0),
1128 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1129 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1130 SD_BUS_METHOD("ImportFileSystem", "hsbb", "uo", method_import_fs
, SD_BUS_VTABLE_UNPRIVILEGED
),
1131 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1132 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1133 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1134 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1135 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1136 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1137 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1138 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1142 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1143 Manager
*m
= userdata
;
1155 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1159 r
= safe_atou32(p
, &id
);
1160 if (r
< 0 || id
== 0)
1163 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1171 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1172 _cleanup_strv_free_
char **l
= NULL
;
1173 Manager
*m
= userdata
;
1178 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1182 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1184 l
[k
] = strdup(t
->object_path
);
1191 *nodes
= TAKE_PTR(l
);
1196 static int manager_add_bus_objects(Manager
*m
) {
1201 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1203 return log_error_errno(r
, "Failed to register object: %m");
1205 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1207 return log_error_errno(r
, "Failed to register object: %m");
1209 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1211 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1213 r
= sd_bus_request_name_async(m
->bus
, NULL
, "org.freedesktop.import1", 0, NULL
, NULL
);
1215 return log_error_errno(r
, "Failed to request name: %m");
1217 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1219 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1224 static bool manager_check_idle(void *userdata
) {
1225 Manager
*m
= userdata
;
1227 return hashmap_isempty(m
->transfers
);
1230 static int manager_run(Manager
*m
) {
1233 return bus_event_loop_with_idle(
1236 "org.freedesktop.import1",
1242 static int run(int argc
, char *argv
[]) {
1243 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1246 log_setup_service();
1251 log_error("This program takes no arguments.");
1255 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1257 r
= manager_new(&m
);
1259 return log_error_errno(r
, "Failed to allocate manager object: %m");
1261 r
= manager_add_bus_objects(m
);
1267 return log_error_errno(r
, "Failed to run event loop: %m");
1272 DEFINE_MAIN_FUNCTION(run
);