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>
28 #include "bus-common-errors.h"
29 #include "socket-util.h"
33 #include "machine-pool.h"
34 #include "path-util.h"
35 #include "import-util.h"
36 #include "process-util.h"
37 #include "signal-util.h"
39 typedef struct Transfer Transfer
;
40 typedef struct Manager Manager
;
42 typedef enum TransferType
{
51 _TRANSFER_TYPE_INVALID
= -1,
75 char log_message
[LINE_MAX
];
76 size_t log_message_size
;
78 sd_event_source
*pid_event_source
;
79 sd_event_source
*log_event_source
;
82 unsigned progress_percent
;
92 uint32_t current_transfer_id
;
95 Hashmap
*polkit_registry
;
99 sd_event_source
*notify_event_source
;
102 #define TRANSFERS_MAX 64
104 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
105 [TRANSFER_IMPORT_TAR
] = "import-tar",
106 [TRANSFER_IMPORT_RAW
] = "import-raw",
107 [TRANSFER_EXPORT_TAR
] = "export-tar",
108 [TRANSFER_EXPORT_RAW
] = "export-raw",
109 [TRANSFER_PULL_TAR
] = "pull-tar",
110 [TRANSFER_PULL_RAW
] = "pull-raw",
111 [TRANSFER_PULL_DKR
] = "pull-dkr",
114 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
116 static Transfer
*transfer_unref(Transfer
*t
) {
121 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
123 sd_event_source_unref(t
->pid_event_source
);
124 sd_event_source_unref(t
->log_event_source
);
128 free(t
->dkr_index_url
);
130 free(t
->object_path
);
133 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
134 (void) wait_for_terminate(t
->pid
, NULL
);
137 safe_close(t
->log_fd
);
138 safe_close(t
->stdin_fd
);
139 safe_close(t
->stdout_fd
);
145 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
147 static int transfer_new(Manager
*m
, Transfer
**ret
) {
148 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
155 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
158 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
162 t
= new0(Transfer
, 1);
166 t
->type
= _TRANSFER_TYPE_INVALID
;
169 t
->verify
= _IMPORT_VERIFY_INVALID
;
171 id
= m
->current_transfer_id
+ 1;
173 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
176 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
180 m
->current_transfer_id
= id
;
191 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
192 int r
, priority
= LOG_INFO
;
197 syslog_parse_priority(&line
, &priority
, true);
199 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
201 r
= sd_bus_emit_signal(
204 "org.freedesktop.import1.Transfer",
210 log_error_errno(r
, "Cannot emit message: %m");
213 static void transfer_send_logs(Transfer
*t
, bool flush
) {
216 /* Try to send out all log messages, if we can. But if we
217 * can't we remove the messages from the buffer, but don't
220 while (t
->log_message_size
> 0) {
221 _cleanup_free_
char *n
= NULL
;
224 if (t
->log_message_size
>= sizeof(t
->log_message
))
225 e
= t
->log_message
+ sizeof(t
->log_message
);
229 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
230 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
244 e
= t
->log_message
+ t
->log_message_size
;
247 n
= strndup(t
->log_message
, e
- t
->log_message
);
249 /* Skip over NUL and newlines */
250 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
253 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
254 t
->log_message_size
-= e
- t
->log_message
;
264 transfer_send_log_line(t
, n
);
268 static int transfer_finalize(Transfer
*t
, bool success
) {
273 transfer_send_logs(t
, true);
275 r
= sd_bus_emit_signal(
277 "/org/freedesktop/import1",
278 "org.freedesktop.import1.Manager",
284 t
->n_canceled
> 0 ? "canceled" : "failed");
287 log_error_errno(r
, "Cannot emit message: %m");
293 static int transfer_cancel(Transfer
*t
) {
298 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
306 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
307 Transfer
*t
= userdata
;
308 bool success
= false;
313 if (si
->si_code
== CLD_EXITED
) {
314 if (si
->si_status
!= 0)
315 log_error("Import process failed with exit code %i.", si
->si_status
);
317 log_debug("Import process succeeded.");
321 } else if (si
->si_code
== CLD_KILLED
||
322 si
->si_code
== CLD_DUMPED
)
324 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
326 log_error("Import process failed due to unknown reason.");
330 return transfer_finalize(t
, success
);
333 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
334 Transfer
*t
= userdata
;
340 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
342 /* EOF/read error. We just close the pipe here, and
343 * close the watch, waiting for the SIGCHLD to arrive,
344 * before we do anything else. */
347 log_error_errno(errno
, "Failed to read log message: %m");
349 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
353 t
->log_message_size
+= l
;
355 transfer_send_logs(t
, false);
360 static int transfer_start(Transfer
*t
) {
361 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
367 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
374 const char *cmd
[] = {
375 NULL
, /* systemd-import, systemd-export or systemd-pull */
376 NULL
, /* tar, raw, dkr */
377 NULL
, /* --verify= */
378 NULL
, /* verify argument */
379 NULL
, /* maybe --force */
380 NULL
, /* maybe --read-only */
381 NULL
, /* maybe --dkr-index-url */
382 NULL
, /* if so: the actual URL */
383 NULL
, /* maybe --format= */
384 NULL
, /* if so: the actual format */
393 reset_all_signal_handlers();
395 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
397 pipefd
[0] = safe_close(pipefd
[0]);
399 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
400 log_error_errno(errno
, "Failed to dup2() fd: %m");
404 if (t
->stdout_fd
>= 0) {
405 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
406 log_error_errno(errno
, "Failed to dup2() fd: %m");
410 if (t
->stdout_fd
!= STDOUT_FILENO
)
411 safe_close(t
->stdout_fd
);
413 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
414 log_error_errno(errno
, "Failed to dup2() fd: %m");
419 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
420 pipefd
[1] = safe_close(pipefd
[1]);
422 if (t
->stdin_fd
>= 0) {
423 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
424 log_error_errno(errno
, "Failed to dup2() fd: %m");
428 if (t
->stdin_fd
!= STDIN_FILENO
)
429 safe_close(t
->stdin_fd
);
433 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
435 log_error_errno(errno
, "Failed to open /dev/null: %m");
439 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
440 log_error_errno(errno
, "Failed to dup2() fd: %m");
444 if (null_fd
!= STDIN_FILENO
)
448 fd_cloexec(STDIN_FILENO
, false);
449 fd_cloexec(STDOUT_FILENO
, false);
450 fd_cloexec(STDERR_FILENO
, false);
452 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
453 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
455 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
456 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
457 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
458 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
460 cmd
[k
++] = SYSTEMD_PULL_PATH
;
462 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
464 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
469 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
470 cmd
[k
++] = "--verify";
471 cmd
[k
++] = import_verify_to_string(t
->verify
);
475 cmd
[k
++] = "--force";
477 cmd
[k
++] = "--read-only";
479 if (t
->dkr_index_url
) {
480 cmd
[k
++] = "--dkr-index-url";
481 cmd
[k
++] = t
->dkr_index_url
;
485 cmd
[k
++] = "--format";
486 cmd
[k
++] = t
->format
;
489 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
491 cmd
[k
++] = t
->remote
;
500 execv(cmd
[0], (char * const *) cmd
);
501 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
505 pipefd
[1] = safe_close(pipefd
[1]);
506 t
->log_fd
= pipefd
[0];
509 t
->stdin_fd
= safe_close(t
->stdin_fd
);
511 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
515 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
519 /* Make sure always process logging before SIGCHLD */
520 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
524 r
= sd_bus_emit_signal(
526 "/org/freedesktop/import1",
527 "org.freedesktop.import1.Manager",
538 static Manager
*manager_unref(Manager
*m
) {
544 sd_event_source_unref(m
->notify_event_source
);
545 safe_close(m
->notify_fd
);
547 while ((t
= hashmap_first(m
->transfers
)))
550 hashmap_free(m
->transfers
);
552 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
554 sd_bus_close(m
->bus
);
555 sd_bus_unref(m
->bus
);
556 sd_event_unref(m
->event
);
562 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
564 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
566 char buf
[NOTIFY_BUFFER_MAX
+1];
567 struct iovec iovec
= {
569 .iov_len
= sizeof(buf
)-1,
572 struct cmsghdr cmsghdr
;
573 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
574 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
576 struct msghdr msghdr
= {
579 .msg_control
= &control
,
580 .msg_controllen
= sizeof(control
),
582 struct ucred
*ucred
= NULL
;
583 Manager
*m
= userdata
;
584 struct cmsghdr
*cmsg
;
592 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
594 if (errno
== EAGAIN
|| errno
== EINTR
)
600 cmsg_close_all(&msghdr
);
602 for (cmsg
= CMSG_FIRSTHDR(&msghdr
); cmsg
; cmsg
= CMSG_NXTHDR(&msghdr
, cmsg
)) {
603 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
604 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
605 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
))) {
607 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",
745 return 1; /* Will call us back */
747 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
751 if (!machine_name_is_valid(local
))
752 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
754 r
= setup_machine_directory((uint64_t) -1, error
);
758 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
760 r
= transfer_new(m
, &t
);
765 t
->force_local
= force
;
766 t
->read_only
= read_only
;
768 t
->local
= strdup(local
);
772 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
776 r
= transfer_start(t
);
780 object
= t
->object_path
;
784 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
787 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
788 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
790 const char *local
, *object
, *format
;
791 Manager
*m
= userdata
;
798 r
= bus_verify_polkit_async(
801 "org.freedesktop.import1.export",
809 return 1; /* Will call us back */
811 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
815 if (!machine_name_is_valid(local
))
816 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
818 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
820 r
= transfer_new(m
, &t
);
826 if (!isempty(format
)) {
827 t
->format
= strdup(format
);
832 t
->local
= strdup(local
);
836 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
837 if (t
->stdout_fd
< 0)
840 r
= transfer_start(t
);
844 object
= t
->object_path
;
848 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
851 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
852 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
853 const char *remote
, *local
, *verify
, *object
;
854 Manager
*m
= userdata
;
863 r
= bus_verify_polkit_async(
866 "org.freedesktop.import1.pull",
874 return 1; /* Will call us back */
876 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
880 if (!http_url_is_valid(remote
))
881 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
885 else if (!machine_name_is_valid(local
))
886 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
889 v
= IMPORT_VERIFY_SIGNATURE
;
891 v
= import_verify_from_string(verify
);
893 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
895 r
= setup_machine_directory((uint64_t) -1, error
);
899 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
901 if (manager_find(m
, type
, NULL
, remote
))
902 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
904 r
= transfer_new(m
, &t
);
910 t
->force_local
= force
;
912 t
->remote
= strdup(remote
);
917 t
->local
= strdup(local
);
922 r
= transfer_start(t
);
926 object
= t
->object_path
;
930 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
933 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
934 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
935 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
936 Manager
*m
= userdata
;
944 r
= bus_verify_polkit_async(
947 "org.freedesktop.import1.pull",
955 return 1; /* Will call us back */
957 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
961 if (isempty(index_url
))
962 index_url
= DEFAULT_DKR_INDEX_URL
;
964 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
965 if (!http_url_is_valid(index_url
))
966 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
968 if (!dkr_name_is_valid(remote
))
969 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
973 else if (!dkr_tag_is_valid(tag
))
974 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
978 else if (!machine_name_is_valid(local
))
979 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
982 v
= IMPORT_VERIFY_SIGNATURE
;
984 v
= import_verify_from_string(verify
);
986 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
988 if (v
!= IMPORT_VERIFY_NO
)
989 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
991 r
= setup_machine_directory((uint64_t) -1, error
);
995 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
996 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
998 r
= transfer_new(m
, &t
);
1002 t
->type
= TRANSFER_PULL_DKR
;
1004 t
->force_local
= force
;
1006 t
->dkr_index_url
= strdup(index_url
);
1007 if (!t
->dkr_index_url
)
1010 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1015 t
->local
= strdup(local
);
1020 r
= transfer_start(t
);
1024 object
= t
->object_path
;
1028 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1031 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1032 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1033 Manager
*m
= userdata
;
1041 r
= sd_bus_message_new_method_return(msg
, &reply
);
1045 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1049 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1051 r
= sd_bus_message_append(
1055 transfer_type_to_string(t
->type
),
1058 (double) t
->progress_percent
/ 100.0,
1064 r
= sd_bus_message_close_container(reply
);
1068 return sd_bus_send(NULL
, reply
, NULL
);
1071 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1072 Transfer
*t
= userdata
;
1078 r
= bus_verify_polkit_async(
1081 "org.freedesktop.import1.pull",
1084 &t
->manager
->polkit_registry
,
1089 return 1; /* Will call us back */
1091 r
= transfer_cancel(t
);
1095 return sd_bus_reply_method_return(msg
, NULL
);
1098 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1099 Manager
*m
= userdata
;
1107 r
= bus_verify_polkit_async(
1110 "org.freedesktop.import1.pull",
1113 &m
->polkit_registry
,
1118 return 1; /* Will call us back */
1120 r
= sd_bus_message_read(msg
, "u", &id
);
1124 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1126 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1128 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1130 r
= transfer_cancel(t
);
1134 return sd_bus_reply_method_return(msg
, NULL
);
1137 static int property_get_progress(
1140 const char *interface
,
1141 const char *property
,
1142 sd_bus_message
*reply
,
1144 sd_bus_error
*error
) {
1146 Transfer
*t
= userdata
;
1152 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1155 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1156 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1158 static const sd_bus_vtable transfer_vtable
[] = {
1159 SD_BUS_VTABLE_START(0),
1160 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1161 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1162 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1163 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1164 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1165 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1166 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1167 SD_BUS_SIGNAL("LogMessage", "us", 0),
1171 static const sd_bus_vtable manager_vtable
[] = {
1172 SD_BUS_VTABLE_START(0),
1173 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1174 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1175 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1176 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1177 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1178 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1179 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1180 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1181 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1182 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1183 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1187 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1188 Manager
*m
= userdata
;
1200 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1204 r
= safe_atou32(p
, &id
);
1205 if (r
< 0 || id
== 0)
1208 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1216 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1217 _cleanup_strv_free_
char **l
= NULL
;
1218 Manager
*m
= userdata
;
1223 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1227 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1229 l
[k
] = strdup(t
->object_path
);
1242 static int manager_add_bus_objects(Manager
*m
) {
1247 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1249 return log_error_errno(r
, "Failed to register object: %m");
1251 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1253 return log_error_errno(r
, "Failed to register object: %m");
1255 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1257 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1259 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1261 return log_error_errno(r
, "Failed to register name: %m");
1263 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1265 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1270 static bool manager_check_idle(void *userdata
) {
1271 Manager
*m
= userdata
;
1273 return hashmap_isempty(m
->transfers
);
1276 static int manager_run(Manager
*m
) {
1279 return bus_event_loop_with_idle(
1282 "org.freedesktop.import1",
1288 int main(int argc
, char *argv
[]) {
1289 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1292 log_set_target(LOG_TARGET_AUTO
);
1293 log_parse_environment();
1299 log_error("This program takes no arguments.");
1304 assert_se(sigprocmask_many(SIG_BLOCK
, SIGCHLD
, -1) >= 0);
1306 r
= manager_new(&m
);
1308 log_error_errno(r
, "Failed to allocate manager object: %m");
1312 r
= manager_add_bus_objects(m
);
1318 log_error_errno(r
, "Failed to run event loop: %m");
1323 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;