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
&& cmsg
->cmsg_type
== SCM_CREDENTIALS
&& cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
605 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
608 if (msghdr
.msg_flags
& MSG_TRUNC
) {
609 log_warning("Got overly long notification datagram, ignoring.");
613 if (!ucred
|| ucred
->pid
<= 0) {
614 log_warning("Got notification datagram lacking credential information, ignoring.");
618 HASHMAP_FOREACH(t
, m
->transfers
, i
)
619 if (ucred
->pid
== t
->pid
)
623 log_warning("Got notification datagram from unexpected peer, ignoring.");
629 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
631 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
638 e
= strchrnul(p
, '\n');
641 r
= safe_atou(p
, &percent
);
642 if (r
< 0 || percent
> 100) {
643 log_warning("Got invalid percent value, ignoring.");
647 t
->progress_percent
= percent
;
649 log_debug("Got percentage from client: %u%%", percent
);
653 static int manager_new(Manager
**ret
) {
654 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
655 static const union sockaddr_union sa
= {
656 .un
.sun_family
= AF_UNIX
,
657 .un
.sun_path
= "/run/systemd/import/notify",
659 static const int one
= 1;
664 m
= new0(Manager
, 1);
668 r
= sd_event_default(&m
->event
);
672 sd_event_set_watchdog(m
->event
, true);
674 r
= sd_bus_default_system(&m
->bus
);
678 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
679 if (m
->notify_fd
< 0)
682 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
683 (void) unlink(sa
.un
.sun_path
);
685 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
688 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
691 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
701 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *dkr_index_url
, const char *remote
) {
707 assert(type
< _TRANSFER_TYPE_MAX
);
709 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
711 if (t
->type
== type
&&
712 streq_ptr(t
->remote
, remote
) &&
713 streq_ptr(t
->dkr_index_url
, dkr_index_url
))
720 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
721 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
722 int fd
, force
, read_only
, r
;
723 const char *local
, *object
;
724 Manager
*m
= userdata
;
731 r
= bus_verify_polkit_async(
734 "org.freedesktop.import1.import",
743 return 1; /* Will call us back */
745 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
749 if (!machine_name_is_valid(local
))
750 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
752 r
= setup_machine_directory((uint64_t) -1, error
);
756 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
758 r
= transfer_new(m
, &t
);
763 t
->force_local
= force
;
764 t
->read_only
= read_only
;
766 t
->local
= strdup(local
);
770 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
774 r
= transfer_start(t
);
778 object
= t
->object_path
;
782 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
785 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
786 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
788 const char *local
, *object
, *format
;
789 Manager
*m
= userdata
;
796 r
= bus_verify_polkit_async(
799 "org.freedesktop.import1.export",
808 return 1; /* Will call us back */
810 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
814 if (!machine_name_is_valid(local
))
815 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
817 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
819 r
= transfer_new(m
, &t
);
825 if (!isempty(format
)) {
826 t
->format
= strdup(format
);
831 t
->local
= strdup(local
);
835 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
836 if (t
->stdout_fd
< 0)
839 r
= transfer_start(t
);
843 object
= t
->object_path
;
847 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
850 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
851 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
852 const char *remote
, *local
, *verify
, *object
;
853 Manager
*m
= userdata
;
862 r
= bus_verify_polkit_async(
865 "org.freedesktop.import1.pull",
874 return 1; /* Will call us back */
876 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
880 if (!http_url_is_valid(remote
))
881 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
885 else if (!machine_name_is_valid(local
))
886 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
889 v
= IMPORT_VERIFY_SIGNATURE
;
891 v
= import_verify_from_string(verify
);
893 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
895 r
= setup_machine_directory((uint64_t) -1, error
);
899 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
901 if (manager_find(m
, type
, NULL
, remote
))
902 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
904 r
= transfer_new(m
, &t
);
910 t
->force_local
= force
;
912 t
->remote
= strdup(remote
);
917 t
->local
= strdup(local
);
922 r
= transfer_start(t
);
926 object
= t
->object_path
;
930 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
933 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
934 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
935 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
936 Manager
*m
= userdata
;
944 r
= bus_verify_polkit_async(
947 "org.freedesktop.import1.pull",
956 return 1; /* Will call us back */
958 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
962 if (isempty(index_url
))
963 index_url
= DEFAULT_DKR_INDEX_URL
;
965 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
966 if (!http_url_is_valid(index_url
))
967 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
969 if (!dkr_name_is_valid(remote
))
970 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
974 else if (!dkr_tag_is_valid(tag
))
975 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
979 else if (!machine_name_is_valid(local
))
980 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
983 v
= IMPORT_VERIFY_SIGNATURE
;
985 v
= import_verify_from_string(verify
);
987 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
989 if (v
!= IMPORT_VERIFY_NO
)
990 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
992 r
= setup_machine_directory((uint64_t) -1, error
);
996 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
997 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
999 r
= transfer_new(m
, &t
);
1003 t
->type
= TRANSFER_PULL_DKR
;
1005 t
->force_local
= force
;
1007 t
->dkr_index_url
= strdup(index_url
);
1008 if (!t
->dkr_index_url
)
1011 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1016 t
->local
= strdup(local
);
1021 r
= transfer_start(t
);
1025 object
= t
->object_path
;
1029 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1032 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1033 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1034 Manager
*m
= userdata
;
1042 r
= sd_bus_message_new_method_return(msg
, &reply
);
1046 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1050 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1052 r
= sd_bus_message_append(
1056 transfer_type_to_string(t
->type
),
1059 (double) t
->progress_percent
/ 100.0,
1065 r
= sd_bus_message_close_container(reply
);
1069 return sd_bus_send(NULL
, reply
, NULL
);
1072 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1073 Transfer
*t
= userdata
;
1079 r
= bus_verify_polkit_async(
1082 "org.freedesktop.import1.pull",
1086 &t
->manager
->polkit_registry
,
1091 return 1; /* Will call us back */
1093 r
= transfer_cancel(t
);
1097 return sd_bus_reply_method_return(msg
, NULL
);
1100 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1101 Manager
*m
= userdata
;
1109 r
= bus_verify_polkit_async(
1112 "org.freedesktop.import1.pull",
1116 &m
->polkit_registry
,
1121 return 1; /* Will call us back */
1123 r
= sd_bus_message_read(msg
, "u", &id
);
1127 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1129 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1131 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1133 r
= transfer_cancel(t
);
1137 return sd_bus_reply_method_return(msg
, NULL
);
1140 static int property_get_progress(
1143 const char *interface
,
1144 const char *property
,
1145 sd_bus_message
*reply
,
1147 sd_bus_error
*error
) {
1149 Transfer
*t
= userdata
;
1155 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1158 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1159 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1161 static const sd_bus_vtable transfer_vtable
[] = {
1162 SD_BUS_VTABLE_START(0),
1163 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1164 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1165 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1166 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1167 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1168 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1169 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1170 SD_BUS_SIGNAL("LogMessage", "us", 0),
1174 static const sd_bus_vtable manager_vtable
[] = {
1175 SD_BUS_VTABLE_START(0),
1176 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1177 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1178 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1179 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1180 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1181 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1182 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1183 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1184 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1185 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1186 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1190 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1191 Manager
*m
= userdata
;
1203 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1207 r
= safe_atou32(p
, &id
);
1208 if (r
< 0 || id
== 0)
1211 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1219 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1220 _cleanup_strv_free_
char **l
= NULL
;
1221 Manager
*m
= userdata
;
1226 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1230 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1232 l
[k
] = strdup(t
->object_path
);
1245 static int manager_add_bus_objects(Manager
*m
) {
1250 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1252 return log_error_errno(r
, "Failed to register object: %m");
1254 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1256 return log_error_errno(r
, "Failed to register object: %m");
1258 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1260 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1262 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1264 return log_error_errno(r
, "Failed to register name: %m");
1266 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1268 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1273 static bool manager_check_idle(void *userdata
) {
1274 Manager
*m
= userdata
;
1276 return hashmap_isempty(m
->transfers
);
1279 static int manager_run(Manager
*m
) {
1282 return bus_event_loop_with_idle(
1285 "org.freedesktop.import1",
1291 int main(int argc
, char *argv
[]) {
1292 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1295 log_set_target(LOG_TARGET_AUTO
);
1296 log_parse_environment();
1302 log_error("This program takes no arguments.");
1307 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1309 r
= manager_new(&m
);
1311 log_error_errno(r
, "Failed to allocate manager object: %m");
1315 r
= manager_add_bus_objects(m
);
1321 log_error_errno(r
, "Failed to run event loop: %m");
1326 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;