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",
746 return 1; /* Will call us back */
748 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
752 if (!machine_name_is_valid(local
))
753 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
755 r
= setup_machine_directory((uint64_t) -1, error
);
759 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
761 r
= transfer_new(m
, &t
);
766 t
->force_local
= force
;
767 t
->read_only
= read_only
;
769 t
->local
= strdup(local
);
773 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
777 r
= transfer_start(t
);
781 object
= t
->object_path
;
785 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
788 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
789 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
791 const char *local
, *object
, *format
;
792 Manager
*m
= userdata
;
799 r
= bus_verify_polkit_async(
802 "org.freedesktop.import1.export",
811 return 1; /* Will call us back */
813 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
817 if (!machine_name_is_valid(local
))
818 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
820 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
822 r
= transfer_new(m
, &t
);
828 if (!isempty(format
)) {
829 t
->format
= strdup(format
);
834 t
->local
= strdup(local
);
838 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
839 if (t
->stdout_fd
< 0)
842 r
= transfer_start(t
);
846 object
= t
->object_path
;
850 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
853 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
854 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
855 const char *remote
, *local
, *verify
, *object
;
856 Manager
*m
= userdata
;
865 r
= bus_verify_polkit_async(
868 "org.freedesktop.import1.pull",
877 return 1; /* Will call us back */
879 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
883 if (!http_url_is_valid(remote
))
884 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
888 else if (!machine_name_is_valid(local
))
889 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
892 v
= IMPORT_VERIFY_SIGNATURE
;
894 v
= import_verify_from_string(verify
);
896 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
898 r
= setup_machine_directory((uint64_t) -1, error
);
902 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
904 if (manager_find(m
, type
, NULL
, remote
))
905 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
907 r
= transfer_new(m
, &t
);
913 t
->force_local
= force
;
915 t
->remote
= strdup(remote
);
920 t
->local
= strdup(local
);
925 r
= transfer_start(t
);
929 object
= t
->object_path
;
933 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
936 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
937 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
938 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
939 Manager
*m
= userdata
;
947 r
= bus_verify_polkit_async(
950 "org.freedesktop.import1.pull",
959 return 1; /* Will call us back */
961 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
965 if (isempty(index_url
))
966 index_url
= DEFAULT_DKR_INDEX_URL
;
968 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
969 if (!http_url_is_valid(index_url
))
970 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
972 if (!dkr_name_is_valid(remote
))
973 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
977 else if (!dkr_tag_is_valid(tag
))
978 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
982 else if (!machine_name_is_valid(local
))
983 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
986 v
= IMPORT_VERIFY_SIGNATURE
;
988 v
= import_verify_from_string(verify
);
990 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
992 if (v
!= IMPORT_VERIFY_NO
)
993 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
995 r
= setup_machine_directory((uint64_t) -1, error
);
999 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
1000 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
1002 r
= transfer_new(m
, &t
);
1006 t
->type
= TRANSFER_PULL_DKR
;
1008 t
->force_local
= force
;
1010 t
->dkr_index_url
= strdup(index_url
);
1011 if (!t
->dkr_index_url
)
1014 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1019 t
->local
= strdup(local
);
1024 r
= transfer_start(t
);
1028 object
= t
->object_path
;
1032 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1035 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1036 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1037 Manager
*m
= userdata
;
1045 r
= sd_bus_message_new_method_return(msg
, &reply
);
1049 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1053 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1055 r
= sd_bus_message_append(
1059 transfer_type_to_string(t
->type
),
1062 (double) t
->progress_percent
/ 100.0,
1068 r
= sd_bus_message_close_container(reply
);
1072 return sd_bus_send(NULL
, reply
, NULL
);
1075 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1076 Transfer
*t
= userdata
;
1082 r
= bus_verify_polkit_async(
1085 "org.freedesktop.import1.pull",
1089 &t
->manager
->polkit_registry
,
1094 return 1; /* Will call us back */
1096 r
= transfer_cancel(t
);
1100 return sd_bus_reply_method_return(msg
, NULL
);
1103 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1104 Manager
*m
= userdata
;
1112 r
= bus_verify_polkit_async(
1115 "org.freedesktop.import1.pull",
1119 &m
->polkit_registry
,
1124 return 1; /* Will call us back */
1126 r
= sd_bus_message_read(msg
, "u", &id
);
1130 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1132 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1134 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1136 r
= transfer_cancel(t
);
1140 return sd_bus_reply_method_return(msg
, NULL
);
1143 static int property_get_progress(
1146 const char *interface
,
1147 const char *property
,
1148 sd_bus_message
*reply
,
1150 sd_bus_error
*error
) {
1152 Transfer
*t
= userdata
;
1158 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1161 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1162 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1164 static const sd_bus_vtable transfer_vtable
[] = {
1165 SD_BUS_VTABLE_START(0),
1166 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1167 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1168 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1169 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1170 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1171 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1172 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1173 SD_BUS_SIGNAL("LogMessage", "us", 0),
1177 static const sd_bus_vtable manager_vtable
[] = {
1178 SD_BUS_VTABLE_START(0),
1179 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1180 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1181 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1182 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1183 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1184 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1185 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1186 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1187 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1188 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1189 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1193 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1194 Manager
*m
= userdata
;
1206 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1210 r
= safe_atou32(p
, &id
);
1211 if (r
< 0 || id
== 0)
1214 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1222 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1223 _cleanup_strv_free_
char **l
= NULL
;
1224 Manager
*m
= userdata
;
1229 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1233 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1235 l
[k
] = strdup(t
->object_path
);
1248 static int manager_add_bus_objects(Manager
*m
) {
1253 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1255 return log_error_errno(r
, "Failed to register object: %m");
1257 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1259 return log_error_errno(r
, "Failed to register object: %m");
1261 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1263 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1265 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1267 return log_error_errno(r
, "Failed to register name: %m");
1269 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1271 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1276 static bool manager_check_idle(void *userdata
) {
1277 Manager
*m
= userdata
;
1279 return hashmap_isempty(m
->transfers
);
1282 static int manager_run(Manager
*m
) {
1285 return bus_event_loop_with_idle(
1288 "org.freedesktop.import1",
1294 int main(int argc
, char *argv
[]) {
1295 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1298 log_set_target(LOG_TARGET_AUTO
);
1299 log_parse_environment();
1305 log_error("This program takes no arguments.");
1310 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1312 r
= manager_new(&m
);
1314 log_error_errno(r
, "Failed to allocate manager object: %m");
1318 r
= manager_add_bus_objects(m
);
1324 log_error_errno(r
, "Failed to run event loop: %m");
1329 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;