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"
37 typedef struct Transfer Transfer
;
38 typedef struct Manager Manager
;
40 typedef enum TransferType
{
49 _TRANSFER_TYPE_INVALID
= -1,
73 char log_message
[LINE_MAX
];
74 size_t log_message_size
;
76 sd_event_source
*pid_event_source
;
77 sd_event_source
*log_event_source
;
80 unsigned progress_percent
;
90 uint32_t current_transfer_id
;
93 Hashmap
*polkit_registry
;
97 sd_event_source
*notify_event_source
;
100 #define TRANSFERS_MAX 64
102 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
103 [TRANSFER_IMPORT_TAR
] = "import-tar",
104 [TRANSFER_IMPORT_RAW
] = "import-raw",
105 [TRANSFER_EXPORT_TAR
] = "export-tar",
106 [TRANSFER_EXPORT_RAW
] = "export-raw",
107 [TRANSFER_PULL_TAR
] = "pull-tar",
108 [TRANSFER_PULL_RAW
] = "pull-raw",
109 [TRANSFER_PULL_DKR
] = "pull-dkr",
112 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
114 static Transfer
*transfer_unref(Transfer
*t
) {
119 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
121 sd_event_source_unref(t
->pid_event_source
);
122 sd_event_source_unref(t
->log_event_source
);
126 free(t
->dkr_index_url
);
128 free(t
->object_path
);
131 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
132 (void) wait_for_terminate(t
->pid
, NULL
);
135 safe_close(t
->log_fd
);
136 safe_close(t
->stdin_fd
);
137 safe_close(t
->stdout_fd
);
143 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
145 static int transfer_new(Manager
*m
, Transfer
**ret
) {
146 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
153 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
156 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
160 t
= new0(Transfer
, 1);
164 t
->type
= _TRANSFER_TYPE_INVALID
;
167 t
->verify
= _IMPORT_VERIFY_INVALID
;
169 id
= m
->current_transfer_id
+ 1;
171 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
174 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
178 m
->current_transfer_id
= id
;
189 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
190 int r
, priority
= LOG_INFO
;
195 syslog_parse_priority(&line
, &priority
, true);
197 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
199 r
= sd_bus_emit_signal(
202 "org.freedesktop.import1.Transfer",
208 log_error_errno(r
, "Cannot emit message: %m");
211 static void transfer_send_logs(Transfer
*t
, bool flush
) {
214 /* Try to send out all log messages, if we can. But if we
215 * can't we remove the messages from the buffer, but don't
218 while (t
->log_message_size
> 0) {
219 _cleanup_free_
char *n
= NULL
;
222 if (t
->log_message_size
>= sizeof(t
->log_message
))
223 e
= t
->log_message
+ sizeof(t
->log_message
);
227 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
228 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
242 e
= t
->log_message
+ t
->log_message_size
;
245 n
= strndup(t
->log_message
, e
- t
->log_message
);
247 /* Skip over NUL and newlines */
248 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
251 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
252 t
->log_message_size
-= e
- t
->log_message
;
262 transfer_send_log_line(t
, n
);
266 static int transfer_finalize(Transfer
*t
, bool success
) {
271 transfer_send_logs(t
, true);
273 r
= sd_bus_emit_signal(
275 "/org/freedesktop/import1",
276 "org.freedesktop.import1.Manager",
282 t
->n_canceled
> 0 ? "canceled" : "failed");
285 log_error_errno(r
, "Cannot emit message: %m");
291 static int transfer_cancel(Transfer
*t
) {
296 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
304 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
305 Transfer
*t
= userdata
;
306 bool success
= false;
311 if (si
->si_code
== CLD_EXITED
) {
312 if (si
->si_status
!= 0)
313 log_error("Import process failed with exit code %i.", si
->si_status
);
315 log_debug("Import process succeeded.");
319 } else if (si
->si_code
== CLD_KILLED
||
320 si
->si_code
== CLD_DUMPED
)
322 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
324 log_error("Import process failed due to unknown reason.");
328 return transfer_finalize(t
, success
);
331 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
332 Transfer
*t
= userdata
;
338 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
340 /* EOF/read error. We just close the pipe here, and
341 * close the watch, waiting for the SIGCHLD to arrive,
342 * before we do anything else. */
345 log_error_errno(errno
, "Failed to read log message: %m");
347 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
351 t
->log_message_size
+= l
;
353 transfer_send_logs(t
, false);
358 static int transfer_start(Transfer
*t
) {
359 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
365 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
372 const char *cmd
[] = {
373 NULL
, /* systemd-import, systemd-export or systemd-pull */
374 NULL
, /* tar, raw, dkr */
375 NULL
, /* --verify= */
376 NULL
, /* verify argument */
377 NULL
, /* maybe --force */
378 NULL
, /* maybe --read-only */
379 NULL
, /* maybe --dkr-index-url */
380 NULL
, /* if so: the actual URL */
381 NULL
, /* maybe --format= */
382 NULL
, /* if so: the actual format */
391 reset_all_signal_handlers();
393 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
395 pipefd
[0] = safe_close(pipefd
[0]);
397 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
398 log_error_errno(errno
, "Failed to dup2() fd: %m");
402 if (t
->stdout_fd
>= 0) {
403 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
404 log_error_errno(errno
, "Failed to dup2() fd: %m");
408 if (t
->stdout_fd
!= STDOUT_FILENO
)
409 safe_close(t
->stdout_fd
);
411 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
412 log_error_errno(errno
, "Failed to dup2() fd: %m");
417 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
418 pipefd
[1] = safe_close(pipefd
[1]);
420 if (t
->stdin_fd
>= 0) {
421 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
422 log_error_errno(errno
, "Failed to dup2() fd: %m");
426 if (t
->stdin_fd
!= STDIN_FILENO
)
427 safe_close(t
->stdin_fd
);
431 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
433 log_error_errno(errno
, "Failed to open /dev/null: %m");
437 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
438 log_error_errno(errno
, "Failed to dup2() fd: %m");
442 if (null_fd
!= STDIN_FILENO
)
446 fd_cloexec(STDIN_FILENO
, false);
447 fd_cloexec(STDOUT_FILENO
, false);
448 fd_cloexec(STDERR_FILENO
, false);
450 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
451 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
453 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
454 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
455 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
456 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
458 cmd
[k
++] = SYSTEMD_PULL_PATH
;
460 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
462 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
467 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
468 cmd
[k
++] = "--verify";
469 cmd
[k
++] = import_verify_to_string(t
->verify
);
473 cmd
[k
++] = "--force";
475 cmd
[k
++] = "--read-only";
477 if (t
->dkr_index_url
) {
478 cmd
[k
++] = "--dkr-index-url";
479 cmd
[k
++] = t
->dkr_index_url
;
483 cmd
[k
++] = "--format";
484 cmd
[k
++] = t
->format
;
487 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
489 cmd
[k
++] = t
->remote
;
498 execv(cmd
[0], (char * const *) cmd
);
499 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
503 pipefd
[1] = safe_close(pipefd
[1]);
504 t
->log_fd
= pipefd
[0];
507 t
->stdin_fd
= safe_close(t
->stdin_fd
);
509 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
513 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
517 /* Make sure always process logging before SIGCHLD */
518 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
522 r
= sd_bus_emit_signal(
524 "/org/freedesktop/import1",
525 "org.freedesktop.import1.Manager",
536 static Manager
*manager_unref(Manager
*m
) {
542 sd_event_source_unref(m
->notify_event_source
);
543 safe_close(m
->notify_fd
);
545 while ((t
= hashmap_first(m
->transfers
)))
548 hashmap_free(m
->transfers
);
550 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
552 sd_bus_close(m
->bus
);
553 sd_bus_unref(m
->bus
);
554 sd_event_unref(m
->event
);
560 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
562 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
564 char buf
[NOTIFY_BUFFER_MAX
+1];
565 struct iovec iovec
= {
567 .iov_len
= sizeof(buf
)-1,
570 struct cmsghdr cmsghdr
;
571 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
572 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
574 struct msghdr msghdr
= {
577 .msg_control
= &control
,
578 .msg_controllen
= sizeof(control
),
580 struct ucred
*ucred
= NULL
;
581 Manager
*m
= userdata
;
582 struct cmsghdr
*cmsg
;
590 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
592 if (errno
== EAGAIN
|| errno
== EINTR
)
598 cmsg_close_all(&msghdr
);
600 for (cmsg
= CMSG_FIRSTHDR(&msghdr
); cmsg
; cmsg
= CMSG_NXTHDR(&msghdr
, cmsg
)) {
601 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
602 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
603 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
))) {
605 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
609 if (msghdr
.msg_flags
& MSG_TRUNC
) {
610 log_warning("Got overly long notification datagram, ignoring.");
614 if (!ucred
|| ucred
->pid
<= 0) {
615 log_warning("Got notification datagram lacking credential information, ignoring.");
619 HASHMAP_FOREACH(t
, m
->transfers
, i
)
620 if (ucred
->pid
== t
->pid
)
624 log_warning("Got notification datagram from unexpected peer, ignoring.");
630 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
632 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
639 e
= strchrnul(p
, '\n');
642 r
= safe_atou(p
, &percent
);
643 if (r
< 0 || percent
> 100) {
644 log_warning("Got invalid percent value, ignoring.");
648 t
->progress_percent
= percent
;
650 log_debug("Got percentage from client: %u%%", percent
);
654 static int manager_new(Manager
**ret
) {
655 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
656 static const union sockaddr_union sa
= {
657 .un
.sun_family
= AF_UNIX
,
658 .un
.sun_path
= "/run/systemd/import/notify",
660 static const int one
= 1;
665 m
= new0(Manager
, 1);
669 r
= sd_event_default(&m
->event
);
673 sd_event_set_watchdog(m
->event
, true);
675 r
= sd_bus_default_system(&m
->bus
);
679 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
680 if (m
->notify_fd
< 0)
683 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
684 (void) unlink(sa
.un
.sun_path
);
686 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
689 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
692 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
702 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *dkr_index_url
, const char *remote
) {
708 assert(type
< _TRANSFER_TYPE_MAX
);
710 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
712 if (t
->type
== type
&&
713 streq_ptr(t
->remote
, remote
) &&
714 streq_ptr(t
->dkr_index_url
, dkr_index_url
))
721 static int method_import_tar_or_raw(sd_bus
*bus
, sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
722 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
723 int fd
, force
, read_only
, r
;
724 const char *local
, *object
;
725 Manager
*m
= userdata
;
729 r
= bus_verify_polkit_async(
732 "org.freedesktop.import1.import",
740 return 1; /* Will call us back */
742 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
746 if (!machine_name_is_valid(local
))
747 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
749 r
= setup_machine_directory((uint64_t) -1, error
);
753 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
755 r
= transfer_new(m
, &t
);
760 t
->force_local
= force
;
761 t
->read_only
= read_only
;
763 t
->local
= strdup(local
);
767 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
771 r
= transfer_start(t
);
775 object
= t
->object_path
;
779 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
782 static int method_export_tar_or_raw(sd_bus
*bus
, sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
783 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
785 const char *local
, *object
, *format
;
786 Manager
*m
= userdata
;
790 r
= bus_verify_polkit_async(
793 "org.freedesktop.import1.export",
801 return 1; /* Will call us back */
803 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
807 if (!machine_name_is_valid(local
))
808 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
810 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
812 r
= transfer_new(m
, &t
);
818 if (!isempty(format
)) {
819 t
->format
= strdup(format
);
824 t
->local
= strdup(local
);
828 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
829 if (t
->stdout_fd
< 0)
832 r
= transfer_start(t
);
836 object
= t
->object_path
;
840 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
843 static int method_pull_tar_or_raw(sd_bus
*bus
, sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
844 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
845 const char *remote
, *local
, *verify
, *object
;
846 Manager
*m
= userdata
;
856 r
= bus_verify_polkit_async(
859 "org.freedesktop.import1.pull",
867 return 1; /* Will call us back */
869 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
873 if (!http_url_is_valid(remote
))
874 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
878 else if (!machine_name_is_valid(local
))
879 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
882 v
= IMPORT_VERIFY_SIGNATURE
;
884 v
= import_verify_from_string(verify
);
886 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
888 r
= setup_machine_directory((uint64_t) -1, error
);
892 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
894 if (manager_find(m
, type
, NULL
, remote
))
895 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
897 r
= transfer_new(m
, &t
);
903 t
->force_local
= force
;
905 t
->remote
= strdup(remote
);
910 t
->local
= strdup(local
);
915 r
= transfer_start(t
);
919 object
= t
->object_path
;
923 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
926 static int method_pull_dkr(sd_bus
*bus
, sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
927 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
928 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
929 Manager
*m
= userdata
;
938 r
= bus_verify_polkit_async(
941 "org.freedesktop.import1.pull",
949 return 1; /* Will call us back */
951 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
955 if (isempty(index_url
))
956 index_url
= DEFAULT_DKR_INDEX_URL
;
958 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
959 if (!http_url_is_valid(index_url
))
960 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
962 if (!dkr_name_is_valid(remote
))
963 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
967 else if (!dkr_tag_is_valid(tag
))
968 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
972 else if (!machine_name_is_valid(local
))
973 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
976 v
= IMPORT_VERIFY_SIGNATURE
;
978 v
= import_verify_from_string(verify
);
980 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
982 if (v
!= IMPORT_VERIFY_NO
)
983 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
985 r
= setup_machine_directory((uint64_t) -1, error
);
989 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
990 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
992 r
= transfer_new(m
, &t
);
996 t
->type
= TRANSFER_PULL_DKR
;
998 t
->force_local
= force
;
1000 t
->dkr_index_url
= strdup(index_url
);
1001 if (!t
->dkr_index_url
)
1004 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1009 t
->local
= strdup(local
);
1014 r
= transfer_start(t
);
1018 object
= t
->object_path
;
1022 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1025 static int method_list_transfers(sd_bus
*bus
, sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1026 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1027 Manager
*m
= userdata
;
1036 r
= sd_bus_message_new_method_return(msg
, &reply
);
1040 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1044 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1046 r
= sd_bus_message_append(
1050 transfer_type_to_string(t
->type
),
1053 (double) t
->progress_percent
/ 100.0,
1059 r
= sd_bus_message_close_container(reply
);
1063 return sd_bus_send(bus
, reply
, NULL
);
1066 static int method_cancel(sd_bus
*bus
, sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1067 Transfer
*t
= userdata
;
1074 r
= bus_verify_polkit_async(
1077 "org.freedesktop.import1.pull",
1080 &t
->manager
->polkit_registry
,
1085 return 1; /* Will call us back */
1087 r
= transfer_cancel(t
);
1091 return sd_bus_reply_method_return(msg
, NULL
);
1094 static int method_cancel_transfer(sd_bus
*bus
, sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1095 Manager
*m
= userdata
;
1104 r
= bus_verify_polkit_async(
1107 "org.freedesktop.import1.pull",
1110 &m
->polkit_registry
,
1115 return 1; /* Will call us back */
1117 r
= sd_bus_message_read(msg
, "u", &id
);
1121 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1123 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1125 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1127 r
= transfer_cancel(t
);
1131 return sd_bus_reply_method_return(msg
, NULL
);
1134 static int property_get_progress(
1137 const char *interface
,
1138 const char *property
,
1139 sd_bus_message
*reply
,
1141 sd_bus_error
*error
) {
1143 Transfer
*t
= userdata
;
1149 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1152 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1153 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1155 static const sd_bus_vtable transfer_vtable
[] = {
1156 SD_BUS_VTABLE_START(0),
1157 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1158 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1159 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1160 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1161 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1162 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1163 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1164 SD_BUS_SIGNAL("LogMessage", "us", 0),
1168 static const sd_bus_vtable manager_vtable
[] = {
1169 SD_BUS_VTABLE_START(0),
1170 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1171 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1172 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1173 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1174 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1175 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1176 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1177 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1178 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1179 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1180 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1184 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1185 Manager
*m
= userdata
;
1197 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1201 r
= safe_atou32(p
, &id
);
1202 if (r
< 0 || id
== 0)
1205 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1213 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1214 _cleanup_strv_free_
char **l
= NULL
;
1215 Manager
*m
= userdata
;
1220 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1224 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1226 l
[k
] = strdup(t
->object_path
);
1239 static int manager_add_bus_objects(Manager
*m
) {
1244 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1246 return log_error_errno(r
, "Failed to register object: %m");
1248 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1250 return log_error_errno(r
, "Failed to register object: %m");
1252 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1254 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1256 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1258 return log_error_errno(r
, "Failed to register name: %m");
1260 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1262 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1267 static bool manager_check_idle(void *userdata
) {
1268 Manager
*m
= userdata
;
1270 return hashmap_isempty(m
->transfers
);
1273 static int manager_run(Manager
*m
) {
1276 return bus_event_loop_with_idle(
1279 "org.freedesktop.import1",
1285 int main(int argc
, char *argv
[]) {
1286 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1289 log_set_target(LOG_TARGET_AUTO
);
1290 log_parse_environment();
1296 log_error("This program takes no arguments.");
1301 assert_se(sigprocmask_many(SIG_BLOCK
, SIGCHLD
, -1) >= 0);
1303 r
= manager_new(&m
);
1305 log_error_errno(r
, "Failed to allocate manager object: %m");
1309 r
= manager_add_bus_objects(m
);
1315 log_error_errno(r
, "Failed to run event loop: %m");
1320 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;