1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/prctl.h>
26 #include "bus-common-errors.h"
30 #include "hostname-util.h"
31 #include "import-util.h"
32 #include "machine-pool.h"
35 #include "path-util.h"
36 #include "process-util.h"
37 #include "signal-util.h"
38 #include "socket-util.h"
42 typedef struct Transfer Transfer
;
43 typedef struct Manager Manager
;
45 typedef enum TransferType
{
54 _TRANSFER_TYPE_INVALID
= -1,
78 char log_message
[LINE_MAX
];
79 size_t log_message_size
;
81 sd_event_source
*pid_event_source
;
82 sd_event_source
*log_event_source
;
85 unsigned progress_percent
;
95 uint32_t current_transfer_id
;
98 Hashmap
*polkit_registry
;
102 sd_event_source
*notify_event_source
;
105 #define TRANSFERS_MAX 64
107 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
108 [TRANSFER_IMPORT_TAR
] = "import-tar",
109 [TRANSFER_IMPORT_RAW
] = "import-raw",
110 [TRANSFER_EXPORT_TAR
] = "export-tar",
111 [TRANSFER_EXPORT_RAW
] = "export-raw",
112 [TRANSFER_PULL_TAR
] = "pull-tar",
113 [TRANSFER_PULL_RAW
] = "pull-raw",
114 [TRANSFER_PULL_DKR
] = "pull-dkr",
117 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
119 static Transfer
*transfer_unref(Transfer
*t
) {
124 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
126 sd_event_source_unref(t
->pid_event_source
);
127 sd_event_source_unref(t
->log_event_source
);
131 free(t
->dkr_index_url
);
133 free(t
->object_path
);
136 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
137 (void) wait_for_terminate(t
->pid
, NULL
);
140 safe_close(t
->log_fd
);
141 safe_close(t
->stdin_fd
);
142 safe_close(t
->stdout_fd
);
148 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
150 static int transfer_new(Manager
*m
, Transfer
**ret
) {
151 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
158 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
161 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
165 t
= new0(Transfer
, 1);
169 t
->type
= _TRANSFER_TYPE_INVALID
;
173 t
->verify
= _IMPORT_VERIFY_INVALID
;
175 id
= m
->current_transfer_id
+ 1;
177 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
180 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
184 m
->current_transfer_id
= id
;
195 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
196 int r
, priority
= LOG_INFO
;
201 syslog_parse_priority(&line
, &priority
, true);
203 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
205 r
= sd_bus_emit_signal(
208 "org.freedesktop.import1.Transfer",
214 log_error_errno(r
, "Cannot emit message: %m");
217 static void transfer_send_logs(Transfer
*t
, bool flush
) {
220 /* Try to send out all log messages, if we can. But if we
221 * can't we remove the messages from the buffer, but don't
224 while (t
->log_message_size
> 0) {
225 _cleanup_free_
char *n
= NULL
;
228 if (t
->log_message_size
>= sizeof(t
->log_message
))
229 e
= t
->log_message
+ sizeof(t
->log_message
);
233 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
234 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
248 e
= t
->log_message
+ t
->log_message_size
;
251 n
= strndup(t
->log_message
, e
- t
->log_message
);
253 /* Skip over NUL and newlines */
254 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
257 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
258 t
->log_message_size
-= e
- t
->log_message
;
268 transfer_send_log_line(t
, n
);
272 static int transfer_finalize(Transfer
*t
, bool success
) {
277 transfer_send_logs(t
, true);
279 r
= sd_bus_emit_signal(
281 "/org/freedesktop/import1",
282 "org.freedesktop.import1.Manager",
288 t
->n_canceled
> 0 ? "canceled" : "failed");
291 log_error_errno(r
, "Cannot emit message: %m");
297 static int transfer_cancel(Transfer
*t
) {
302 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
310 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
311 Transfer
*t
= userdata
;
312 bool success
= false;
317 if (si
->si_code
== CLD_EXITED
) {
318 if (si
->si_status
!= 0)
319 log_error("Import process failed with exit code %i.", si
->si_status
);
321 log_debug("Import process succeeded.");
325 } else if (si
->si_code
== CLD_KILLED
||
326 si
->si_code
== CLD_DUMPED
)
328 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
330 log_error("Import process failed due to unknown reason.");
334 return transfer_finalize(t
, success
);
337 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
338 Transfer
*t
= userdata
;
344 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
346 /* EOF/read error. We just close the pipe here, and
347 * close the watch, waiting for the SIGCHLD to arrive,
348 * before we do anything else. */
351 log_error_errno(errno
, "Failed to read log message: %m");
353 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
357 t
->log_message_size
+= l
;
359 transfer_send_logs(t
, false);
364 static int transfer_start(Transfer
*t
) {
365 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
371 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
378 const char *cmd
[] = {
379 NULL
, /* systemd-import, systemd-export or systemd-pull */
380 NULL
, /* tar, raw, dkr */
381 NULL
, /* --verify= */
382 NULL
, /* verify argument */
383 NULL
, /* maybe --force */
384 NULL
, /* maybe --read-only */
385 NULL
, /* maybe --dkr-index-url */
386 NULL
, /* if so: the actual URL */
387 NULL
, /* maybe --format= */
388 NULL
, /* if so: the actual format */
397 (void) reset_all_signal_handlers();
398 (void) reset_signal_mask();
399 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
401 pipefd
[0] = safe_close(pipefd
[0]);
403 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
404 log_error_errno(errno
, "Failed to dup2() fd: %m");
408 if (t
->stdout_fd
>= 0) {
409 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
410 log_error_errno(errno
, "Failed to dup2() fd: %m");
414 if (t
->stdout_fd
!= STDOUT_FILENO
)
415 safe_close(t
->stdout_fd
);
417 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
418 log_error_errno(errno
, "Failed to dup2() fd: %m");
423 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
424 pipefd
[1] = safe_close(pipefd
[1]);
426 if (t
->stdin_fd
>= 0) {
427 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
428 log_error_errno(errno
, "Failed to dup2() fd: %m");
432 if (t
->stdin_fd
!= STDIN_FILENO
)
433 safe_close(t
->stdin_fd
);
437 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
439 log_error_errno(errno
, "Failed to open /dev/null: %m");
443 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
444 log_error_errno(errno
, "Failed to dup2() fd: %m");
448 if (null_fd
!= STDIN_FILENO
)
452 fd_cloexec(STDIN_FILENO
, false);
453 fd_cloexec(STDOUT_FILENO
, false);
454 fd_cloexec(STDERR_FILENO
, false);
456 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
457 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
459 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
460 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
461 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
462 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
464 cmd
[k
++] = SYSTEMD_PULL_PATH
;
466 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
468 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
473 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
474 cmd
[k
++] = "--verify";
475 cmd
[k
++] = import_verify_to_string(t
->verify
);
479 cmd
[k
++] = "--force";
481 cmd
[k
++] = "--read-only";
483 if (t
->dkr_index_url
) {
484 cmd
[k
++] = "--dkr-index-url";
485 cmd
[k
++] = t
->dkr_index_url
;
489 cmd
[k
++] = "--format";
490 cmd
[k
++] = t
->format
;
493 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
495 cmd
[k
++] = t
->remote
;
504 execv(cmd
[0], (char * const *) cmd
);
505 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
509 pipefd
[1] = safe_close(pipefd
[1]);
510 t
->log_fd
= pipefd
[0];
513 t
->stdin_fd
= safe_close(t
->stdin_fd
);
515 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
519 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
523 /* Make sure always process logging before SIGCHLD */
524 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
528 r
= sd_bus_emit_signal(
530 "/org/freedesktop/import1",
531 "org.freedesktop.import1.Manager",
542 static Manager
*manager_unref(Manager
*m
) {
548 sd_event_source_unref(m
->notify_event_source
);
549 safe_close(m
->notify_fd
);
551 while ((t
= hashmap_first(m
->transfers
)))
554 hashmap_free(m
->transfers
);
556 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
558 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
559 sd_event_unref(m
->event
);
565 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
567 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
569 char buf
[NOTIFY_BUFFER_MAX
+1];
570 struct iovec iovec
= {
572 .iov_len
= sizeof(buf
)-1,
575 struct cmsghdr cmsghdr
;
576 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
577 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
579 struct msghdr msghdr
= {
582 .msg_control
= &control
,
583 .msg_controllen
= sizeof(control
),
585 struct ucred
*ucred
= NULL
;
586 Manager
*m
= userdata
;
587 struct cmsghdr
*cmsg
;
595 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
597 if (errno
== EAGAIN
|| errno
== EINTR
)
603 cmsg_close_all(&msghdr
);
605 CMSG_FOREACH(cmsg
, &msghdr
)
606 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
607 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
608 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
609 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
611 if (msghdr
.msg_flags
& MSG_TRUNC
) {
612 log_warning("Got overly long notification datagram, ignoring.");
616 if (!ucred
|| ucred
->pid
<= 0) {
617 log_warning("Got notification datagram lacking credential information, ignoring.");
621 HASHMAP_FOREACH(t
, m
->transfers
, i
)
622 if (ucred
->pid
== t
->pid
)
626 log_warning("Got notification datagram from unexpected peer, ignoring.");
632 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
634 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
641 e
= strchrnul(p
, '\n');
644 r
= safe_atou(p
, &percent
);
645 if (r
< 0 || percent
> 100) {
646 log_warning("Got invalid percent value, ignoring.");
650 t
->progress_percent
= percent
;
652 log_debug("Got percentage from client: %u%%", percent
);
656 static int manager_new(Manager
**ret
) {
657 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
658 static const union sockaddr_union sa
= {
659 .un
.sun_family
= AF_UNIX
,
660 .un
.sun_path
= "/run/systemd/import/notify",
662 static const int one
= 1;
667 m
= new0(Manager
, 1);
671 r
= sd_event_default(&m
->event
);
675 sd_event_set_watchdog(m
->event
, true);
677 r
= sd_bus_default_system(&m
->bus
);
681 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
682 if (m
->notify_fd
< 0)
685 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
686 (void) unlink(sa
.un
.sun_path
);
688 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
691 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
694 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
704 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *dkr_index_url
, const char *remote
) {
710 assert(type
< _TRANSFER_TYPE_MAX
);
712 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
714 if (t
->type
== type
&&
715 streq_ptr(t
->remote
, remote
) &&
716 streq_ptr(t
->dkr_index_url
, dkr_index_url
))
723 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
724 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
725 int fd
, force
, read_only
, r
;
726 const char *local
, *object
;
727 Manager
*m
= userdata
;
734 r
= bus_verify_polkit_async(
737 "org.freedesktop.import1.import",
746 return 1; /* Will call us back */
748 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
752 if (!machine_name_is_valid(local
))
753 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
755 r
= setup_machine_directory((uint64_t) -1, error
);
759 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
761 r
= transfer_new(m
, &t
);
766 t
->force_local
= force
;
767 t
->read_only
= read_only
;
769 t
->local
= strdup(local
);
773 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
777 r
= transfer_start(t
);
781 object
= t
->object_path
;
785 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
788 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
789 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
791 const char *local
, *object
, *format
;
792 Manager
*m
= userdata
;
799 r
= bus_verify_polkit_async(
802 "org.freedesktop.import1.export",
811 return 1; /* Will call us back */
813 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
817 if (!machine_name_is_valid(local
))
818 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
820 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
822 r
= transfer_new(m
, &t
);
828 if (!isempty(format
)) {
829 t
->format
= strdup(format
);
834 t
->local
= strdup(local
);
838 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
839 if (t
->stdout_fd
< 0)
842 r
= transfer_start(t
);
846 object
= t
->object_path
;
850 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
853 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
854 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
855 const char *remote
, *local
, *verify
, *object
;
856 Manager
*m
= userdata
;
865 r
= bus_verify_polkit_async(
868 "org.freedesktop.import1.pull",
877 return 1; /* Will call us back */
879 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
883 if (!http_url_is_valid(remote
))
884 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
888 else if (!machine_name_is_valid(local
))
889 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
892 v
= IMPORT_VERIFY_SIGNATURE
;
894 v
= import_verify_from_string(verify
);
896 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
898 r
= setup_machine_directory((uint64_t) -1, error
);
902 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
904 if (manager_find(m
, type
, NULL
, remote
))
905 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
907 r
= transfer_new(m
, &t
);
913 t
->force_local
= force
;
915 t
->remote
= strdup(remote
);
920 t
->local
= strdup(local
);
925 r
= transfer_start(t
);
929 object
= t
->object_path
;
933 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
936 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
937 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
938 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
939 Manager
*m
= userdata
;
947 r
= bus_verify_polkit_async(
950 "org.freedesktop.import1.pull",
959 return 1; /* Will call us back */
961 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
965 if (isempty(index_url
))
966 index_url
= DEFAULT_DKR_INDEX_URL
;
968 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
969 if (!http_url_is_valid(index_url
))
970 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
972 if (!dkr_name_is_valid(remote
))
973 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
977 else if (!dkr_tag_is_valid(tag
))
978 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
982 else if (!machine_name_is_valid(local
))
983 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
986 v
= IMPORT_VERIFY_SIGNATURE
;
988 v
= import_verify_from_string(verify
);
990 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
992 if (v
!= IMPORT_VERIFY_NO
)
993 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
995 r
= setup_machine_directory((uint64_t) -1, error
);
999 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
1000 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
1002 r
= transfer_new(m
, &t
);
1006 t
->type
= TRANSFER_PULL_DKR
;
1008 t
->force_local
= force
;
1010 t
->dkr_index_url
= strdup(index_url
);
1011 if (!t
->dkr_index_url
)
1014 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1019 t
->local
= strdup(local
);
1024 r
= transfer_start(t
);
1028 object
= t
->object_path
;
1032 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1035 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1036 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1037 Manager
*m
= userdata
;
1045 r
= sd_bus_message_new_method_return(msg
, &reply
);
1049 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1053 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1055 r
= sd_bus_message_append(
1059 transfer_type_to_string(t
->type
),
1062 (double) t
->progress_percent
/ 100.0,
1068 r
= sd_bus_message_close_container(reply
);
1072 return sd_bus_send(NULL
, reply
, NULL
);
1075 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1076 Transfer
*t
= userdata
;
1082 r
= bus_verify_polkit_async(
1085 "org.freedesktop.import1.pull",
1089 &t
->manager
->polkit_registry
,
1094 return 1; /* Will call us back */
1096 r
= transfer_cancel(t
);
1100 return sd_bus_reply_method_return(msg
, NULL
);
1103 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1104 Manager
*m
= userdata
;
1112 r
= bus_verify_polkit_async(
1115 "org.freedesktop.import1.pull",
1119 &m
->polkit_registry
,
1124 return 1; /* Will call us back */
1126 r
= sd_bus_message_read(msg
, "u", &id
);
1130 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1132 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1134 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1136 r
= transfer_cancel(t
);
1140 return sd_bus_reply_method_return(msg
, NULL
);
1143 static int property_get_progress(
1146 const char *interface
,
1147 const char *property
,
1148 sd_bus_message
*reply
,
1150 sd_bus_error
*error
) {
1152 Transfer
*t
= userdata
;
1158 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1161 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1162 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1164 static const sd_bus_vtable transfer_vtable
[] = {
1165 SD_BUS_VTABLE_START(0),
1166 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1167 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1168 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1169 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1170 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1171 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1172 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1173 SD_BUS_SIGNAL("LogMessage", "us", 0),
1177 static const sd_bus_vtable manager_vtable
[] = {
1178 SD_BUS_VTABLE_START(0),
1179 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1180 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1181 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1182 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1183 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1184 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1185 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1186 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1187 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1188 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1189 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1193 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1194 Manager
*m
= userdata
;
1206 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1210 r
= safe_atou32(p
, &id
);
1211 if (r
< 0 || id
== 0)
1214 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1222 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1223 _cleanup_strv_free_
char **l
= NULL
;
1224 Manager
*m
= userdata
;
1229 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1233 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1235 l
[k
] = strdup(t
->object_path
);
1248 static int manager_add_bus_objects(Manager
*m
) {
1253 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1255 return log_error_errno(r
, "Failed to register object: %m");
1257 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1259 return log_error_errno(r
, "Failed to register object: %m");
1261 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1263 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1265 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1267 return log_error_errno(r
, "Failed to register name: %m");
1269 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1271 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1276 static bool manager_check_idle(void *userdata
) {
1277 Manager
*m
= userdata
;
1279 return hashmap_isempty(m
->transfers
);
1282 static int manager_run(Manager
*m
) {
1285 return bus_event_loop_with_idle(
1288 "org.freedesktop.import1",
1294 int main(int argc
, char *argv
[]) {
1295 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1298 log_set_target(LOG_TARGET_AUTO
);
1299 log_parse_environment();
1305 log_error("This program takes no arguments.");
1310 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1312 r
= manager_new(&m
);
1314 log_error_errno(r
, "Failed to allocate manager object: %m");
1318 r
= manager_add_bus_objects(m
);
1324 log_error_errno(r
, "Failed to run event loop: %m");
1329 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;