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 "parse-util.h"
36 #include "path-util.h"
37 #include "process-util.h"
38 #include "signal-util.h"
39 #include "socket-util.h"
40 #include "string-table.h"
44 typedef struct Transfer Transfer
;
45 typedef struct Manager Manager
;
47 typedef enum TransferType
{
56 _TRANSFER_TYPE_INVALID
= -1,
80 char log_message
[LINE_MAX
];
81 size_t log_message_size
;
83 sd_event_source
*pid_event_source
;
84 sd_event_source
*log_event_source
;
87 unsigned progress_percent
;
97 uint32_t current_transfer_id
;
100 Hashmap
*polkit_registry
;
104 sd_event_source
*notify_event_source
;
107 #define TRANSFERS_MAX 64
109 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
110 [TRANSFER_IMPORT_TAR
] = "import-tar",
111 [TRANSFER_IMPORT_RAW
] = "import-raw",
112 [TRANSFER_EXPORT_TAR
] = "export-tar",
113 [TRANSFER_EXPORT_RAW
] = "export-raw",
114 [TRANSFER_PULL_TAR
] = "pull-tar",
115 [TRANSFER_PULL_RAW
] = "pull-raw",
116 [TRANSFER_PULL_DKR
] = "pull-dkr",
119 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
121 static Transfer
*transfer_unref(Transfer
*t
) {
126 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
128 sd_event_source_unref(t
->pid_event_source
);
129 sd_event_source_unref(t
->log_event_source
);
133 free(t
->dkr_index_url
);
135 free(t
->object_path
);
138 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
139 (void) wait_for_terminate(t
->pid
, NULL
);
142 safe_close(t
->log_fd
);
143 safe_close(t
->stdin_fd
);
144 safe_close(t
->stdout_fd
);
150 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
152 static int transfer_new(Manager
*m
, Transfer
**ret
) {
153 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
160 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
163 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
167 t
= new0(Transfer
, 1);
171 t
->type
= _TRANSFER_TYPE_INVALID
;
175 t
->verify
= _IMPORT_VERIFY_INVALID
;
177 id
= m
->current_transfer_id
+ 1;
179 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
182 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
186 m
->current_transfer_id
= id
;
197 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
198 int r
, priority
= LOG_INFO
;
203 syslog_parse_priority(&line
, &priority
, true);
205 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
207 r
= sd_bus_emit_signal(
210 "org.freedesktop.import1.Transfer",
216 log_error_errno(r
, "Cannot emit message: %m");
219 static void transfer_send_logs(Transfer
*t
, bool flush
) {
222 /* Try to send out all log messages, if we can. But if we
223 * can't we remove the messages from the buffer, but don't
226 while (t
->log_message_size
> 0) {
227 _cleanup_free_
char *n
= NULL
;
230 if (t
->log_message_size
>= sizeof(t
->log_message
))
231 e
= t
->log_message
+ sizeof(t
->log_message
);
235 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
236 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
250 e
= t
->log_message
+ t
->log_message_size
;
253 n
= strndup(t
->log_message
, e
- t
->log_message
);
255 /* Skip over NUL and newlines */
256 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
259 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
260 t
->log_message_size
-= e
- t
->log_message
;
270 transfer_send_log_line(t
, n
);
274 static int transfer_finalize(Transfer
*t
, bool success
) {
279 transfer_send_logs(t
, true);
281 r
= sd_bus_emit_signal(
283 "/org/freedesktop/import1",
284 "org.freedesktop.import1.Manager",
290 t
->n_canceled
> 0 ? "canceled" : "failed");
293 log_error_errno(r
, "Cannot emit message: %m");
299 static int transfer_cancel(Transfer
*t
) {
304 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
312 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
313 Transfer
*t
= userdata
;
314 bool success
= false;
319 if (si
->si_code
== CLD_EXITED
) {
320 if (si
->si_status
!= 0)
321 log_error("Import process failed with exit code %i.", si
->si_status
);
323 log_debug("Import process succeeded.");
327 } else if (si
->si_code
== CLD_KILLED
||
328 si
->si_code
== CLD_DUMPED
)
330 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
332 log_error("Import process failed due to unknown reason.");
336 return transfer_finalize(t
, success
);
339 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
340 Transfer
*t
= userdata
;
346 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
348 /* EOF/read error. We just close the pipe here, and
349 * close the watch, waiting for the SIGCHLD to arrive,
350 * before we do anything else. */
353 log_error_errno(errno
, "Failed to read log message: %m");
355 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
359 t
->log_message_size
+= l
;
361 transfer_send_logs(t
, false);
366 static int transfer_start(Transfer
*t
) {
367 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
373 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
380 const char *cmd
[] = {
381 NULL
, /* systemd-import, systemd-export or systemd-pull */
382 NULL
, /* tar, raw, dkr */
383 NULL
, /* --verify= */
384 NULL
, /* verify argument */
385 NULL
, /* maybe --force */
386 NULL
, /* maybe --read-only */
387 NULL
, /* maybe --dkr-index-url */
388 NULL
, /* if so: the actual URL */
389 NULL
, /* maybe --format= */
390 NULL
, /* if so: the actual format */
399 (void) reset_all_signal_handlers();
400 (void) reset_signal_mask();
401 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
403 pipefd
[0] = safe_close(pipefd
[0]);
405 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
406 log_error_errno(errno
, "Failed to dup2() fd: %m");
410 if (t
->stdout_fd
>= 0) {
411 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
412 log_error_errno(errno
, "Failed to dup2() fd: %m");
416 if (t
->stdout_fd
!= STDOUT_FILENO
)
417 safe_close(t
->stdout_fd
);
419 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
420 log_error_errno(errno
, "Failed to dup2() fd: %m");
425 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
426 pipefd
[1] = safe_close(pipefd
[1]);
428 if (t
->stdin_fd
>= 0) {
429 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
430 log_error_errno(errno
, "Failed to dup2() fd: %m");
434 if (t
->stdin_fd
!= STDIN_FILENO
)
435 safe_close(t
->stdin_fd
);
439 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
441 log_error_errno(errno
, "Failed to open /dev/null: %m");
445 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
446 log_error_errno(errno
, "Failed to dup2() fd: %m");
450 if (null_fd
!= STDIN_FILENO
)
454 fd_cloexec(STDIN_FILENO
, false);
455 fd_cloexec(STDOUT_FILENO
, false);
456 fd_cloexec(STDERR_FILENO
, false);
458 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
459 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
461 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
462 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
463 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
464 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
466 cmd
[k
++] = SYSTEMD_PULL_PATH
;
468 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
470 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
475 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
476 cmd
[k
++] = "--verify";
477 cmd
[k
++] = import_verify_to_string(t
->verify
);
481 cmd
[k
++] = "--force";
483 cmd
[k
++] = "--read-only";
485 if (t
->dkr_index_url
) {
486 cmd
[k
++] = "--dkr-index-url";
487 cmd
[k
++] = t
->dkr_index_url
;
491 cmd
[k
++] = "--format";
492 cmd
[k
++] = t
->format
;
495 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
497 cmd
[k
++] = t
->remote
;
506 execv(cmd
[0], (char * const *) cmd
);
507 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
511 pipefd
[1] = safe_close(pipefd
[1]);
512 t
->log_fd
= pipefd
[0];
515 t
->stdin_fd
= safe_close(t
->stdin_fd
);
517 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
521 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
525 /* Make sure always process logging before SIGCHLD */
526 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
530 r
= sd_bus_emit_signal(
532 "/org/freedesktop/import1",
533 "org.freedesktop.import1.Manager",
544 static Manager
*manager_unref(Manager
*m
) {
550 sd_event_source_unref(m
->notify_event_source
);
551 safe_close(m
->notify_fd
);
553 while ((t
= hashmap_first(m
->transfers
)))
556 hashmap_free(m
->transfers
);
558 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
560 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
561 sd_event_unref(m
->event
);
567 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
569 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
571 char buf
[NOTIFY_BUFFER_MAX
+1];
572 struct iovec iovec
= {
574 .iov_len
= sizeof(buf
)-1,
577 struct cmsghdr cmsghdr
;
578 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
579 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
581 struct msghdr msghdr
= {
584 .msg_control
= &control
,
585 .msg_controllen
= sizeof(control
),
587 struct ucred
*ucred
= NULL
;
588 Manager
*m
= userdata
;
589 struct cmsghdr
*cmsg
;
597 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
599 if (errno
== EAGAIN
|| errno
== EINTR
)
605 cmsg_close_all(&msghdr
);
607 CMSG_FOREACH(cmsg
, &msghdr
)
608 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
609 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
610 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
611 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
613 if (msghdr
.msg_flags
& MSG_TRUNC
) {
614 log_warning("Got overly long notification datagram, ignoring.");
618 if (!ucred
|| ucred
->pid
<= 0) {
619 log_warning("Got notification datagram lacking credential information, ignoring.");
623 HASHMAP_FOREACH(t
, m
->transfers
, i
)
624 if (ucred
->pid
== t
->pid
)
628 log_warning("Got notification datagram from unexpected peer, ignoring.");
634 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
636 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
643 e
= strchrnul(p
, '\n');
646 r
= safe_atou(p
, &percent
);
647 if (r
< 0 || percent
> 100) {
648 log_warning("Got invalid percent value, ignoring.");
652 t
->progress_percent
= percent
;
654 log_debug("Got percentage from client: %u%%", percent
);
658 static int manager_new(Manager
**ret
) {
659 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
660 static const union sockaddr_union sa
= {
661 .un
.sun_family
= AF_UNIX
,
662 .un
.sun_path
= "/run/systemd/import/notify",
664 static const int one
= 1;
669 m
= new0(Manager
, 1);
673 r
= sd_event_default(&m
->event
);
677 sd_event_set_watchdog(m
->event
, true);
679 r
= sd_bus_default_system(&m
->bus
);
683 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
684 if (m
->notify_fd
< 0)
687 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
688 (void) unlink(sa
.un
.sun_path
);
690 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
693 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
696 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
706 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *dkr_index_url
, const char *remote
) {
712 assert(type
< _TRANSFER_TYPE_MAX
);
714 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
716 if (t
->type
== type
&&
717 streq_ptr(t
->remote
, remote
) &&
718 streq_ptr(t
->dkr_index_url
, dkr_index_url
))
725 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
726 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
727 int fd
, force
, read_only
, r
;
728 const char *local
, *object
;
729 Manager
*m
= userdata
;
736 r
= bus_verify_polkit_async(
739 "org.freedesktop.import1.import",
748 return 1; /* Will call us back */
750 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
754 if (!machine_name_is_valid(local
))
755 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
757 r
= setup_machine_directory((uint64_t) -1, error
);
761 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
763 r
= transfer_new(m
, &t
);
768 t
->force_local
= force
;
769 t
->read_only
= read_only
;
771 t
->local
= strdup(local
);
775 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
779 r
= transfer_start(t
);
783 object
= t
->object_path
;
787 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
790 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
791 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
793 const char *local
, *object
, *format
;
794 Manager
*m
= userdata
;
801 r
= bus_verify_polkit_async(
804 "org.freedesktop.import1.export",
813 return 1; /* Will call us back */
815 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
819 if (!machine_name_is_valid(local
))
820 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
822 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
824 r
= transfer_new(m
, &t
);
830 if (!isempty(format
)) {
831 t
->format
= strdup(format
);
836 t
->local
= strdup(local
);
840 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
841 if (t
->stdout_fd
< 0)
844 r
= transfer_start(t
);
848 object
= t
->object_path
;
852 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
855 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
856 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
857 const char *remote
, *local
, *verify
, *object
;
858 Manager
*m
= userdata
;
867 r
= bus_verify_polkit_async(
870 "org.freedesktop.import1.pull",
879 return 1; /* Will call us back */
881 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
885 if (!http_url_is_valid(remote
))
886 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
890 else if (!machine_name_is_valid(local
))
891 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
894 v
= IMPORT_VERIFY_SIGNATURE
;
896 v
= import_verify_from_string(verify
);
898 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
900 r
= setup_machine_directory((uint64_t) -1, error
);
904 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
906 if (manager_find(m
, type
, NULL
, remote
))
907 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
909 r
= transfer_new(m
, &t
);
915 t
->force_local
= force
;
917 t
->remote
= strdup(remote
);
922 t
->local
= strdup(local
);
927 r
= transfer_start(t
);
931 object
= t
->object_path
;
935 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
938 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
939 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
940 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
941 Manager
*m
= userdata
;
949 r
= bus_verify_polkit_async(
952 "org.freedesktop.import1.pull",
961 return 1; /* Will call us back */
963 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
967 if (isempty(index_url
))
968 index_url
= DEFAULT_DKR_INDEX_URL
;
970 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
971 if (!http_url_is_valid(index_url
))
972 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
974 if (!dkr_name_is_valid(remote
))
975 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
979 else if (!dkr_tag_is_valid(tag
))
980 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
984 else if (!machine_name_is_valid(local
))
985 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
988 v
= IMPORT_VERIFY_SIGNATURE
;
990 v
= import_verify_from_string(verify
);
992 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
994 if (v
!= IMPORT_VERIFY_NO
)
995 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
997 r
= setup_machine_directory((uint64_t) -1, error
);
1001 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
1002 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
1004 r
= transfer_new(m
, &t
);
1008 t
->type
= TRANSFER_PULL_DKR
;
1010 t
->force_local
= force
;
1012 t
->dkr_index_url
= strdup(index_url
);
1013 if (!t
->dkr_index_url
)
1016 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1021 t
->local
= strdup(local
);
1026 r
= transfer_start(t
);
1030 object
= t
->object_path
;
1034 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1037 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1038 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1039 Manager
*m
= userdata
;
1047 r
= sd_bus_message_new_method_return(msg
, &reply
);
1051 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1055 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1057 r
= sd_bus_message_append(
1061 transfer_type_to_string(t
->type
),
1064 (double) t
->progress_percent
/ 100.0,
1070 r
= sd_bus_message_close_container(reply
);
1074 return sd_bus_send(NULL
, reply
, NULL
);
1077 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1078 Transfer
*t
= userdata
;
1084 r
= bus_verify_polkit_async(
1087 "org.freedesktop.import1.pull",
1091 &t
->manager
->polkit_registry
,
1096 return 1; /* Will call us back */
1098 r
= transfer_cancel(t
);
1102 return sd_bus_reply_method_return(msg
, NULL
);
1105 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1106 Manager
*m
= userdata
;
1114 r
= bus_verify_polkit_async(
1117 "org.freedesktop.import1.pull",
1121 &m
->polkit_registry
,
1126 return 1; /* Will call us back */
1128 r
= sd_bus_message_read(msg
, "u", &id
);
1132 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1134 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1136 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1138 r
= transfer_cancel(t
);
1142 return sd_bus_reply_method_return(msg
, NULL
);
1145 static int property_get_progress(
1148 const char *interface
,
1149 const char *property
,
1150 sd_bus_message
*reply
,
1152 sd_bus_error
*error
) {
1154 Transfer
*t
= userdata
;
1160 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1163 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1164 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1166 static const sd_bus_vtable transfer_vtable
[] = {
1167 SD_BUS_VTABLE_START(0),
1168 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1169 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1170 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1171 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1172 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1173 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1174 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1175 SD_BUS_SIGNAL("LogMessage", "us", 0),
1179 static const sd_bus_vtable manager_vtable
[] = {
1180 SD_BUS_VTABLE_START(0),
1181 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1182 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1183 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1184 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1185 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1186 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1187 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1188 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1189 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1190 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1191 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1195 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1196 Manager
*m
= userdata
;
1208 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1212 r
= safe_atou32(p
, &id
);
1213 if (r
< 0 || id
== 0)
1216 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1224 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1225 _cleanup_strv_free_
char **l
= NULL
;
1226 Manager
*m
= userdata
;
1231 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1235 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1237 l
[k
] = strdup(t
->object_path
);
1250 static int manager_add_bus_objects(Manager
*m
) {
1255 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1257 return log_error_errno(r
, "Failed to register object: %m");
1259 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1261 return log_error_errno(r
, "Failed to register object: %m");
1263 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1265 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1267 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1269 return log_error_errno(r
, "Failed to register name: %m");
1271 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1273 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1278 static bool manager_check_idle(void *userdata
) {
1279 Manager
*m
= userdata
;
1281 return hashmap_isempty(m
->transfers
);
1284 static int manager_run(Manager
*m
) {
1287 return bus_event_loop_with_idle(
1290 "org.freedesktop.import1",
1296 int main(int argc
, char *argv
[]) {
1297 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1300 log_set_target(LOG_TARGET_AUTO
);
1301 log_parse_environment();
1307 log_error("This program takes no arguments.");
1312 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1314 r
= manager_new(&m
);
1316 log_error_errno(r
, "Failed to allocate manager object: %m");
1320 r
= manager_add_bus_objects(m
);
1326 log_error_errno(r
, "Failed to run event loop: %m");
1331 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;