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"
38 #include "hostname-util.h"
40 typedef struct Transfer Transfer
;
41 typedef struct Manager Manager
;
43 typedef enum TransferType
{
52 _TRANSFER_TYPE_INVALID
= -1,
76 char log_message
[LINE_MAX
];
77 size_t log_message_size
;
79 sd_event_source
*pid_event_source
;
80 sd_event_source
*log_event_source
;
83 unsigned progress_percent
;
93 uint32_t current_transfer_id
;
96 Hashmap
*polkit_registry
;
100 sd_event_source
*notify_event_source
;
103 #define TRANSFERS_MAX 64
105 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
106 [TRANSFER_IMPORT_TAR
] = "import-tar",
107 [TRANSFER_IMPORT_RAW
] = "import-raw",
108 [TRANSFER_EXPORT_TAR
] = "export-tar",
109 [TRANSFER_EXPORT_RAW
] = "export-raw",
110 [TRANSFER_PULL_TAR
] = "pull-tar",
111 [TRANSFER_PULL_RAW
] = "pull-raw",
112 [TRANSFER_PULL_DKR
] = "pull-dkr",
115 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
117 static Transfer
*transfer_unref(Transfer
*t
) {
122 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
124 sd_event_source_unref(t
->pid_event_source
);
125 sd_event_source_unref(t
->log_event_source
);
129 free(t
->dkr_index_url
);
131 free(t
->object_path
);
134 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
135 (void) wait_for_terminate(t
->pid
, NULL
);
138 safe_close(t
->log_fd
);
139 safe_close(t
->stdin_fd
);
140 safe_close(t
->stdout_fd
);
146 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
148 static int transfer_new(Manager
*m
, Transfer
**ret
) {
149 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
156 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
159 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
163 t
= new0(Transfer
, 1);
167 t
->type
= _TRANSFER_TYPE_INVALID
;
170 t
->verify
= _IMPORT_VERIFY_INVALID
;
172 id
= m
->current_transfer_id
+ 1;
174 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
177 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
181 m
->current_transfer_id
= id
;
192 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
193 int r
, priority
= LOG_INFO
;
198 syslog_parse_priority(&line
, &priority
, true);
200 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
202 r
= sd_bus_emit_signal(
205 "org.freedesktop.import1.Transfer",
211 log_error_errno(r
, "Cannot emit message: %m");
214 static void transfer_send_logs(Transfer
*t
, bool flush
) {
217 /* Try to send out all log messages, if we can. But if we
218 * can't we remove the messages from the buffer, but don't
221 while (t
->log_message_size
> 0) {
222 _cleanup_free_
char *n
= NULL
;
225 if (t
->log_message_size
>= sizeof(t
->log_message
))
226 e
= t
->log_message
+ sizeof(t
->log_message
);
230 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
231 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
245 e
= t
->log_message
+ t
->log_message_size
;
248 n
= strndup(t
->log_message
, e
- t
->log_message
);
250 /* Skip over NUL and newlines */
251 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
254 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
255 t
->log_message_size
-= e
- t
->log_message
;
265 transfer_send_log_line(t
, n
);
269 static int transfer_finalize(Transfer
*t
, bool success
) {
274 transfer_send_logs(t
, true);
276 r
= sd_bus_emit_signal(
278 "/org/freedesktop/import1",
279 "org.freedesktop.import1.Manager",
285 t
->n_canceled
> 0 ? "canceled" : "failed");
288 log_error_errno(r
, "Cannot emit message: %m");
294 static int transfer_cancel(Transfer
*t
) {
299 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
307 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
308 Transfer
*t
= userdata
;
309 bool success
= false;
314 if (si
->si_code
== CLD_EXITED
) {
315 if (si
->si_status
!= 0)
316 log_error("Import process failed with exit code %i.", si
->si_status
);
318 log_debug("Import process succeeded.");
322 } else if (si
->si_code
== CLD_KILLED
||
323 si
->si_code
== CLD_DUMPED
)
325 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
327 log_error("Import 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 /* EOF/read error. We just close the pipe here, and
344 * close the watch, waiting for the SIGCHLD to arrive,
345 * before we do anything else. */
348 log_error_errno(errno
, "Failed to read log message: %m");
350 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
354 t
->log_message_size
+= l
;
356 transfer_send_logs(t
, false);
361 static int transfer_start(Transfer
*t
) {
362 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
368 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
375 const char *cmd
[] = {
376 NULL
, /* systemd-import, systemd-export or systemd-pull */
377 NULL
, /* tar, raw, dkr */
378 NULL
, /* --verify= */
379 NULL
, /* verify argument */
380 NULL
, /* maybe --force */
381 NULL
, /* maybe --read-only */
382 NULL
, /* maybe --dkr-index-url */
383 NULL
, /* if so: the actual URL */
384 NULL
, /* maybe --format= */
385 NULL
, /* if so: the actual format */
394 (void) reset_all_signal_handlers();
395 (void) reset_signal_mask();
396 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
398 pipefd
[0] = safe_close(pipefd
[0]);
400 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
401 log_error_errno(errno
, "Failed to dup2() fd: %m");
405 if (t
->stdout_fd
>= 0) {
406 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
407 log_error_errno(errno
, "Failed to dup2() fd: %m");
411 if (t
->stdout_fd
!= STDOUT_FILENO
)
412 safe_close(t
->stdout_fd
);
414 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
415 log_error_errno(errno
, "Failed to dup2() fd: %m");
420 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
421 pipefd
[1] = safe_close(pipefd
[1]);
423 if (t
->stdin_fd
>= 0) {
424 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
425 log_error_errno(errno
, "Failed to dup2() fd: %m");
429 if (t
->stdin_fd
!= STDIN_FILENO
)
430 safe_close(t
->stdin_fd
);
434 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
436 log_error_errno(errno
, "Failed to open /dev/null: %m");
440 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
441 log_error_errno(errno
, "Failed to dup2() fd: %m");
445 if (null_fd
!= STDIN_FILENO
)
449 fd_cloexec(STDIN_FILENO
, false);
450 fd_cloexec(STDOUT_FILENO
, false);
451 fd_cloexec(STDERR_FILENO
, false);
453 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
454 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
456 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
457 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
458 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
459 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
461 cmd
[k
++] = SYSTEMD_PULL_PATH
;
463 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
465 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
470 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
471 cmd
[k
++] = "--verify";
472 cmd
[k
++] = import_verify_to_string(t
->verify
);
476 cmd
[k
++] = "--force";
478 cmd
[k
++] = "--read-only";
480 if (t
->dkr_index_url
) {
481 cmd
[k
++] = "--dkr-index-url";
482 cmd
[k
++] = t
->dkr_index_url
;
486 cmd
[k
++] = "--format";
487 cmd
[k
++] = t
->format
;
490 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
492 cmd
[k
++] = t
->remote
;
501 execv(cmd
[0], (char * const *) cmd
);
502 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
506 pipefd
[1] = safe_close(pipefd
[1]);
507 t
->log_fd
= pipefd
[0];
510 t
->stdin_fd
= safe_close(t
->stdin_fd
);
512 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
516 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
520 /* Make sure always process logging before SIGCHLD */
521 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
525 r
= sd_bus_emit_signal(
527 "/org/freedesktop/import1",
528 "org.freedesktop.import1.Manager",
539 static Manager
*manager_unref(Manager
*m
) {
545 sd_event_source_unref(m
->notify_event_source
);
546 safe_close(m
->notify_fd
);
548 while ((t
= hashmap_first(m
->transfers
)))
551 hashmap_free(m
->transfers
);
553 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
555 m
->bus
= sd_bus_flush_close_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 CMSG_FOREACH(cmsg
, &msghdr
) {
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
, NULL
, 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
;