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"
43 typedef struct Transfer Transfer
;
44 typedef struct Manager Manager
;
46 typedef enum TransferType
{
55 _TRANSFER_TYPE_INVALID
= -1,
79 char log_message
[LINE_MAX
];
80 size_t log_message_size
;
82 sd_event_source
*pid_event_source
;
83 sd_event_source
*log_event_source
;
86 unsigned progress_percent
;
96 uint32_t current_transfer_id
;
99 Hashmap
*polkit_registry
;
103 sd_event_source
*notify_event_source
;
106 #define TRANSFERS_MAX 64
108 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
109 [TRANSFER_IMPORT_TAR
] = "import-tar",
110 [TRANSFER_IMPORT_RAW
] = "import-raw",
111 [TRANSFER_EXPORT_TAR
] = "export-tar",
112 [TRANSFER_EXPORT_RAW
] = "export-raw",
113 [TRANSFER_PULL_TAR
] = "pull-tar",
114 [TRANSFER_PULL_RAW
] = "pull-raw",
115 [TRANSFER_PULL_DKR
] = "pull-dkr",
118 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
120 static Transfer
*transfer_unref(Transfer
*t
) {
125 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
127 sd_event_source_unref(t
->pid_event_source
);
128 sd_event_source_unref(t
->log_event_source
);
132 free(t
->dkr_index_url
);
134 free(t
->object_path
);
137 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
138 (void) wait_for_terminate(t
->pid
, NULL
);
141 safe_close(t
->log_fd
);
142 safe_close(t
->stdin_fd
);
143 safe_close(t
->stdout_fd
);
149 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
151 static int transfer_new(Manager
*m
, Transfer
**ret
) {
152 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
159 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
162 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
166 t
= new0(Transfer
, 1);
170 t
->type
= _TRANSFER_TYPE_INVALID
;
174 t
->verify
= _IMPORT_VERIFY_INVALID
;
176 id
= m
->current_transfer_id
+ 1;
178 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
181 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
185 m
->current_transfer_id
= id
;
196 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
197 int r
, priority
= LOG_INFO
;
202 syslog_parse_priority(&line
, &priority
, true);
204 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
206 r
= sd_bus_emit_signal(
209 "org.freedesktop.import1.Transfer",
215 log_error_errno(r
, "Cannot emit message: %m");
218 static void transfer_send_logs(Transfer
*t
, bool flush
) {
221 /* Try to send out all log messages, if we can. But if we
222 * can't we remove the messages from the buffer, but don't
225 while (t
->log_message_size
> 0) {
226 _cleanup_free_
char *n
= NULL
;
229 if (t
->log_message_size
>= sizeof(t
->log_message
))
230 e
= t
->log_message
+ sizeof(t
->log_message
);
234 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
235 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
249 e
= t
->log_message
+ t
->log_message_size
;
252 n
= strndup(t
->log_message
, e
- t
->log_message
);
254 /* Skip over NUL and newlines */
255 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
258 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
259 t
->log_message_size
-= e
- t
->log_message
;
269 transfer_send_log_line(t
, n
);
273 static int transfer_finalize(Transfer
*t
, bool success
) {
278 transfer_send_logs(t
, true);
280 r
= sd_bus_emit_signal(
282 "/org/freedesktop/import1",
283 "org.freedesktop.import1.Manager",
289 t
->n_canceled
> 0 ? "canceled" : "failed");
292 log_error_errno(r
, "Cannot emit message: %m");
298 static int transfer_cancel(Transfer
*t
) {
303 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
311 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
312 Transfer
*t
= userdata
;
313 bool success
= false;
318 if (si
->si_code
== CLD_EXITED
) {
319 if (si
->si_status
!= 0)
320 log_error("Import process failed with exit code %i.", si
->si_status
);
322 log_debug("Import process succeeded.");
326 } else if (si
->si_code
== CLD_KILLED
||
327 si
->si_code
== CLD_DUMPED
)
329 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
331 log_error("Import process failed due to unknown reason.");
335 return transfer_finalize(t
, success
);
338 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
339 Transfer
*t
= userdata
;
345 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
347 /* EOF/read error. We just close the pipe here, and
348 * close the watch, waiting for the SIGCHLD to arrive,
349 * before we do anything else. */
352 log_error_errno(errno
, "Failed to read log message: %m");
354 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
358 t
->log_message_size
+= l
;
360 transfer_send_logs(t
, false);
365 static int transfer_start(Transfer
*t
) {
366 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
372 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
379 const char *cmd
[] = {
380 NULL
, /* systemd-import, systemd-export or systemd-pull */
381 NULL
, /* tar, raw, dkr */
382 NULL
, /* --verify= */
383 NULL
, /* verify argument */
384 NULL
, /* maybe --force */
385 NULL
, /* maybe --read-only */
386 NULL
, /* maybe --dkr-index-url */
387 NULL
, /* if so: the actual URL */
388 NULL
, /* maybe --format= */
389 NULL
, /* if so: the actual format */
398 (void) reset_all_signal_handlers();
399 (void) reset_signal_mask();
400 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
402 pipefd
[0] = safe_close(pipefd
[0]);
404 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
405 log_error_errno(errno
, "Failed to dup2() fd: %m");
409 if (t
->stdout_fd
>= 0) {
410 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
411 log_error_errno(errno
, "Failed to dup2() fd: %m");
415 if (t
->stdout_fd
!= STDOUT_FILENO
)
416 safe_close(t
->stdout_fd
);
418 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
419 log_error_errno(errno
, "Failed to dup2() fd: %m");
424 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
425 pipefd
[1] = safe_close(pipefd
[1]);
427 if (t
->stdin_fd
>= 0) {
428 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
429 log_error_errno(errno
, "Failed to dup2() fd: %m");
433 if (t
->stdin_fd
!= STDIN_FILENO
)
434 safe_close(t
->stdin_fd
);
438 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
440 log_error_errno(errno
, "Failed to open /dev/null: %m");
444 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
445 log_error_errno(errno
, "Failed to dup2() fd: %m");
449 if (null_fd
!= STDIN_FILENO
)
453 fd_cloexec(STDIN_FILENO
, false);
454 fd_cloexec(STDOUT_FILENO
, false);
455 fd_cloexec(STDERR_FILENO
, false);
457 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
458 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
460 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
461 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
462 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
463 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
465 cmd
[k
++] = SYSTEMD_PULL_PATH
;
467 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
469 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
474 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
475 cmd
[k
++] = "--verify";
476 cmd
[k
++] = import_verify_to_string(t
->verify
);
480 cmd
[k
++] = "--force";
482 cmd
[k
++] = "--read-only";
484 if (t
->dkr_index_url
) {
485 cmd
[k
++] = "--dkr-index-url";
486 cmd
[k
++] = t
->dkr_index_url
;
490 cmd
[k
++] = "--format";
491 cmd
[k
++] = t
->format
;
494 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
496 cmd
[k
++] = t
->remote
;
505 execv(cmd
[0], (char * const *) cmd
);
506 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
510 pipefd
[1] = safe_close(pipefd
[1]);
511 t
->log_fd
= pipefd
[0];
514 t
->stdin_fd
= safe_close(t
->stdin_fd
);
516 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
520 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
524 /* Make sure always process logging before SIGCHLD */
525 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
529 r
= sd_bus_emit_signal(
531 "/org/freedesktop/import1",
532 "org.freedesktop.import1.Manager",
543 static Manager
*manager_unref(Manager
*m
) {
549 sd_event_source_unref(m
->notify_event_source
);
550 safe_close(m
->notify_fd
);
552 while ((t
= hashmap_first(m
->transfers
)))
555 hashmap_free(m
->transfers
);
557 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
559 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
560 sd_event_unref(m
->event
);
566 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
568 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
570 char buf
[NOTIFY_BUFFER_MAX
+1];
571 struct iovec iovec
= {
573 .iov_len
= sizeof(buf
)-1,
576 struct cmsghdr cmsghdr
;
577 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
578 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
580 struct msghdr msghdr
= {
583 .msg_control
= &control
,
584 .msg_controllen
= sizeof(control
),
586 struct ucred
*ucred
= NULL
;
587 Manager
*m
= userdata
;
588 struct cmsghdr
*cmsg
;
596 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
598 if (errno
== EAGAIN
|| errno
== EINTR
)
604 cmsg_close_all(&msghdr
);
606 CMSG_FOREACH(cmsg
, &msghdr
)
607 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
608 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
609 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
610 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
612 if (msghdr
.msg_flags
& MSG_TRUNC
) {
613 log_warning("Got overly long notification datagram, ignoring.");
617 if (!ucred
|| ucred
->pid
<= 0) {
618 log_warning("Got notification datagram lacking credential information, ignoring.");
622 HASHMAP_FOREACH(t
, m
->transfers
, i
)
623 if (ucred
->pid
== t
->pid
)
627 log_warning("Got notification datagram from unexpected peer, ignoring.");
633 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
635 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
642 e
= strchrnul(p
, '\n');
645 r
= safe_atou(p
, &percent
);
646 if (r
< 0 || percent
> 100) {
647 log_warning("Got invalid percent value, ignoring.");
651 t
->progress_percent
= percent
;
653 log_debug("Got percentage from client: %u%%", percent
);
657 static int manager_new(Manager
**ret
) {
658 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
659 static const union sockaddr_union sa
= {
660 .un
.sun_family
= AF_UNIX
,
661 .un
.sun_path
= "/run/systemd/import/notify",
663 static const int one
= 1;
668 m
= new0(Manager
, 1);
672 r
= sd_event_default(&m
->event
);
676 sd_event_set_watchdog(m
->event
, true);
678 r
= sd_bus_default_system(&m
->bus
);
682 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
683 if (m
->notify_fd
< 0)
686 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
687 (void) unlink(sa
.un
.sun_path
);
689 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
692 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
695 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
705 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *dkr_index_url
, const char *remote
) {
711 assert(type
< _TRANSFER_TYPE_MAX
);
713 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
715 if (t
->type
== type
&&
716 streq_ptr(t
->remote
, remote
) &&
717 streq_ptr(t
->dkr_index_url
, dkr_index_url
))
724 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
725 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
726 int fd
, force
, read_only
, r
;
727 const char *local
, *object
;
728 Manager
*m
= userdata
;
735 r
= bus_verify_polkit_async(
738 "org.freedesktop.import1.import",
747 return 1; /* Will call us back */
749 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
753 if (!machine_name_is_valid(local
))
754 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
756 r
= setup_machine_directory((uint64_t) -1, error
);
760 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
762 r
= transfer_new(m
, &t
);
767 t
->force_local
= force
;
768 t
->read_only
= read_only
;
770 t
->local
= strdup(local
);
774 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
778 r
= transfer_start(t
);
782 object
= t
->object_path
;
786 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
789 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
790 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
792 const char *local
, *object
, *format
;
793 Manager
*m
= userdata
;
800 r
= bus_verify_polkit_async(
803 "org.freedesktop.import1.export",
812 return 1; /* Will call us back */
814 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
818 if (!machine_name_is_valid(local
))
819 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
821 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
823 r
= transfer_new(m
, &t
);
829 if (!isempty(format
)) {
830 t
->format
= strdup(format
);
835 t
->local
= strdup(local
);
839 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
840 if (t
->stdout_fd
< 0)
843 r
= transfer_start(t
);
847 object
= t
->object_path
;
851 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
854 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
855 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
856 const char *remote
, *local
, *verify
, *object
;
857 Manager
*m
= userdata
;
866 r
= bus_verify_polkit_async(
869 "org.freedesktop.import1.pull",
878 return 1; /* Will call us back */
880 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
884 if (!http_url_is_valid(remote
))
885 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
889 else if (!machine_name_is_valid(local
))
890 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
893 v
= IMPORT_VERIFY_SIGNATURE
;
895 v
= import_verify_from_string(verify
);
897 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
899 r
= setup_machine_directory((uint64_t) -1, error
);
903 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
905 if (manager_find(m
, type
, NULL
, remote
))
906 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
908 r
= transfer_new(m
, &t
);
914 t
->force_local
= force
;
916 t
->remote
= strdup(remote
);
921 t
->local
= strdup(local
);
926 r
= transfer_start(t
);
930 object
= t
->object_path
;
934 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
937 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
938 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
939 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
940 Manager
*m
= userdata
;
948 r
= bus_verify_polkit_async(
951 "org.freedesktop.import1.pull",
960 return 1; /* Will call us back */
962 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
966 if (isempty(index_url
))
967 index_url
= DEFAULT_DKR_INDEX_URL
;
969 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
970 if (!http_url_is_valid(index_url
))
971 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
973 if (!dkr_name_is_valid(remote
))
974 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
978 else if (!dkr_tag_is_valid(tag
))
979 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
983 else if (!machine_name_is_valid(local
))
984 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
987 v
= IMPORT_VERIFY_SIGNATURE
;
989 v
= import_verify_from_string(verify
);
991 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
993 if (v
!= IMPORT_VERIFY_NO
)
994 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
996 r
= setup_machine_directory((uint64_t) -1, error
);
1000 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
1001 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
1003 r
= transfer_new(m
, &t
);
1007 t
->type
= TRANSFER_PULL_DKR
;
1009 t
->force_local
= force
;
1011 t
->dkr_index_url
= strdup(index_url
);
1012 if (!t
->dkr_index_url
)
1015 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1020 t
->local
= strdup(local
);
1025 r
= transfer_start(t
);
1029 object
= t
->object_path
;
1033 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1036 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1037 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1038 Manager
*m
= userdata
;
1046 r
= sd_bus_message_new_method_return(msg
, &reply
);
1050 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1054 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1056 r
= sd_bus_message_append(
1060 transfer_type_to_string(t
->type
),
1063 (double) t
->progress_percent
/ 100.0,
1069 r
= sd_bus_message_close_container(reply
);
1073 return sd_bus_send(NULL
, reply
, NULL
);
1076 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1077 Transfer
*t
= userdata
;
1083 r
= bus_verify_polkit_async(
1086 "org.freedesktop.import1.pull",
1090 &t
->manager
->polkit_registry
,
1095 return 1; /* Will call us back */
1097 r
= transfer_cancel(t
);
1101 return sd_bus_reply_method_return(msg
, NULL
);
1104 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1105 Manager
*m
= userdata
;
1113 r
= bus_verify_polkit_async(
1116 "org.freedesktop.import1.pull",
1120 &m
->polkit_registry
,
1125 return 1; /* Will call us back */
1127 r
= sd_bus_message_read(msg
, "u", &id
);
1131 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1133 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1135 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1137 r
= transfer_cancel(t
);
1141 return sd_bus_reply_method_return(msg
, NULL
);
1144 static int property_get_progress(
1147 const char *interface
,
1148 const char *property
,
1149 sd_bus_message
*reply
,
1151 sd_bus_error
*error
) {
1153 Transfer
*t
= userdata
;
1159 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1162 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1163 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1165 static const sd_bus_vtable transfer_vtable
[] = {
1166 SD_BUS_VTABLE_START(0),
1167 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1168 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1169 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1170 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1171 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1172 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1173 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1174 SD_BUS_SIGNAL("LogMessage", "us", 0),
1178 static const sd_bus_vtable manager_vtable
[] = {
1179 SD_BUS_VTABLE_START(0),
1180 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1181 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1182 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1183 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1184 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1185 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1186 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1187 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1188 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1189 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1190 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1194 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1195 Manager
*m
= userdata
;
1207 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1211 r
= safe_atou32(p
, &id
);
1212 if (r
< 0 || id
== 0)
1215 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1223 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1224 _cleanup_strv_free_
char **l
= NULL
;
1225 Manager
*m
= userdata
;
1230 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1234 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1236 l
[k
] = strdup(t
->object_path
);
1249 static int manager_add_bus_objects(Manager
*m
) {
1254 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1256 return log_error_errno(r
, "Failed to register object: %m");
1258 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1260 return log_error_errno(r
, "Failed to register object: %m");
1262 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1264 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1266 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1268 return log_error_errno(r
, "Failed to register name: %m");
1270 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1272 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1277 static bool manager_check_idle(void *userdata
) {
1278 Manager
*m
= userdata
;
1280 return hashmap_isempty(m
->transfers
);
1283 static int manager_run(Manager
*m
) {
1286 return bus_event_loop_with_idle(
1289 "org.freedesktop.import1",
1295 int main(int argc
, char *argv
[]) {
1296 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1299 log_set_target(LOG_TARGET_AUTO
);
1300 log_parse_environment();
1306 log_error("This program takes no arguments.");
1311 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1313 r
= manager_new(&m
);
1315 log_error_errno(r
, "Failed to allocate manager object: %m");
1319 r
= manager_add_bus_objects(m
);
1325 log_error_errno(r
, "Failed to run event loop: %m");
1330 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;