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"
39 typedef struct Transfer Transfer
;
40 typedef struct Manager Manager
;
42 typedef enum TransferType
{
51 _TRANSFER_TYPE_INVALID
= -1,
75 char log_message
[LINE_MAX
];
76 size_t log_message_size
;
78 sd_event_source
*pid_event_source
;
79 sd_event_source
*log_event_source
;
82 unsigned progress_percent
;
92 uint32_t current_transfer_id
;
95 Hashmap
*polkit_registry
;
99 sd_event_source
*notify_event_source
;
102 #define TRANSFERS_MAX 64
104 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
105 [TRANSFER_IMPORT_TAR
] = "import-tar",
106 [TRANSFER_IMPORT_RAW
] = "import-raw",
107 [TRANSFER_EXPORT_TAR
] = "export-tar",
108 [TRANSFER_EXPORT_RAW
] = "export-raw",
109 [TRANSFER_PULL_TAR
] = "pull-tar",
110 [TRANSFER_PULL_RAW
] = "pull-raw",
111 [TRANSFER_PULL_DKR
] = "pull-dkr",
114 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
116 static Transfer
*transfer_unref(Transfer
*t
) {
121 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
123 sd_event_source_unref(t
->pid_event_source
);
124 sd_event_source_unref(t
->log_event_source
);
128 free(t
->dkr_index_url
);
130 free(t
->object_path
);
133 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
134 (void) wait_for_terminate(t
->pid
, NULL
);
137 safe_close(t
->log_fd
);
138 safe_close(t
->stdin_fd
);
139 safe_close(t
->stdout_fd
);
145 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
147 static int transfer_new(Manager
*m
, Transfer
**ret
) {
148 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
155 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
158 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
162 t
= new0(Transfer
, 1);
166 t
->type
= _TRANSFER_TYPE_INVALID
;
169 t
->verify
= _IMPORT_VERIFY_INVALID
;
171 id
= m
->current_transfer_id
+ 1;
173 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
176 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
180 m
->current_transfer_id
= id
;
191 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
192 int r
, priority
= LOG_INFO
;
197 syslog_parse_priority(&line
, &priority
, true);
199 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
201 r
= sd_bus_emit_signal(
204 "org.freedesktop.import1.Transfer",
210 log_error_errno(r
, "Cannot emit message: %m");
213 static void transfer_send_logs(Transfer
*t
, bool flush
) {
216 /* Try to send out all log messages, if we can. But if we
217 * can't we remove the messages from the buffer, but don't
220 while (t
->log_message_size
> 0) {
221 _cleanup_free_
char *n
= NULL
;
224 if (t
->log_message_size
>= sizeof(t
->log_message
))
225 e
= t
->log_message
+ sizeof(t
->log_message
);
229 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
230 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
244 e
= t
->log_message
+ t
->log_message_size
;
247 n
= strndup(t
->log_message
, e
- t
->log_message
);
249 /* Skip over NUL and newlines */
250 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
253 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
254 t
->log_message_size
-= e
- t
->log_message
;
264 transfer_send_log_line(t
, n
);
268 static int transfer_finalize(Transfer
*t
, bool success
) {
273 transfer_send_logs(t
, true);
275 r
= sd_bus_emit_signal(
277 "/org/freedesktop/import1",
278 "org.freedesktop.import1.Manager",
284 t
->n_canceled
> 0 ? "canceled" : "failed");
287 log_error_errno(r
, "Cannot emit message: %m");
293 static int transfer_cancel(Transfer
*t
) {
298 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
306 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
307 Transfer
*t
= userdata
;
308 bool success
= false;
313 if (si
->si_code
== CLD_EXITED
) {
314 if (si
->si_status
!= 0)
315 log_error("Import process failed with exit code %i.", si
->si_status
);
317 log_debug("Import process succeeded.");
321 } else if (si
->si_code
== CLD_KILLED
||
322 si
->si_code
== CLD_DUMPED
)
324 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
326 log_error("Import process failed due to unknown reason.");
330 return transfer_finalize(t
, success
);
333 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
334 Transfer
*t
= userdata
;
340 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
342 /* EOF/read error. We just close the pipe here, and
343 * close the watch, waiting for the SIGCHLD to arrive,
344 * before we do anything else. */
347 log_error_errno(errno
, "Failed to read log message: %m");
349 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
353 t
->log_message_size
+= l
;
355 transfer_send_logs(t
, false);
360 static int transfer_start(Transfer
*t
) {
361 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
367 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
374 const char *cmd
[] = {
375 NULL
, /* systemd-import, systemd-export or systemd-pull */
376 NULL
, /* tar, raw, dkr */
377 NULL
, /* --verify= */
378 NULL
, /* verify argument */
379 NULL
, /* maybe --force */
380 NULL
, /* maybe --read-only */
381 NULL
, /* maybe --dkr-index-url */
382 NULL
, /* if so: the actual URL */
383 NULL
, /* maybe --format= */
384 NULL
, /* if so: the actual format */
393 (void) reset_all_signal_handlers();
394 (void) reset_signal_mask();
395 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
397 pipefd
[0] = safe_close(pipefd
[0]);
399 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
400 log_error_errno(errno
, "Failed to dup2() fd: %m");
404 if (t
->stdout_fd
>= 0) {
405 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
406 log_error_errno(errno
, "Failed to dup2() fd: %m");
410 if (t
->stdout_fd
!= STDOUT_FILENO
)
411 safe_close(t
->stdout_fd
);
413 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
414 log_error_errno(errno
, "Failed to dup2() fd: %m");
419 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
420 pipefd
[1] = safe_close(pipefd
[1]);
422 if (t
->stdin_fd
>= 0) {
423 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
424 log_error_errno(errno
, "Failed to dup2() fd: %m");
428 if (t
->stdin_fd
!= STDIN_FILENO
)
429 safe_close(t
->stdin_fd
);
433 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
435 log_error_errno(errno
, "Failed to open /dev/null: %m");
439 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
440 log_error_errno(errno
, "Failed to dup2() fd: %m");
444 if (null_fd
!= STDIN_FILENO
)
448 fd_cloexec(STDIN_FILENO
, false);
449 fd_cloexec(STDOUT_FILENO
, false);
450 fd_cloexec(STDERR_FILENO
, false);
452 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
453 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
455 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
456 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
457 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
458 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
460 cmd
[k
++] = SYSTEMD_PULL_PATH
;
462 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
464 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
469 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
470 cmd
[k
++] = "--verify";
471 cmd
[k
++] = import_verify_to_string(t
->verify
);
475 cmd
[k
++] = "--force";
477 cmd
[k
++] = "--read-only";
479 if (t
->dkr_index_url
) {
480 cmd
[k
++] = "--dkr-index-url";
481 cmd
[k
++] = t
->dkr_index_url
;
485 cmd
[k
++] = "--format";
486 cmd
[k
++] = t
->format
;
489 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
491 cmd
[k
++] = t
->remote
;
500 execv(cmd
[0], (char * const *) cmd
);
501 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
505 pipefd
[1] = safe_close(pipefd
[1]);
506 t
->log_fd
= pipefd
[0];
509 t
->stdin_fd
= safe_close(t
->stdin_fd
);
511 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
515 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
519 /* Make sure always process logging before SIGCHLD */
520 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
524 r
= sd_bus_emit_signal(
526 "/org/freedesktop/import1",
527 "org.freedesktop.import1.Manager",
538 static Manager
*manager_unref(Manager
*m
) {
544 sd_event_source_unref(m
->notify_event_source
);
545 safe_close(m
->notify_fd
);
547 while ((t
= hashmap_first(m
->transfers
)))
550 hashmap_free(m
->transfers
);
552 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
554 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
555 sd_event_unref(m
->event
);
561 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
563 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
565 char buf
[NOTIFY_BUFFER_MAX
+1];
566 struct iovec iovec
= {
568 .iov_len
= sizeof(buf
)-1,
571 struct cmsghdr cmsghdr
;
572 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
573 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
575 struct msghdr msghdr
= {
578 .msg_control
= &control
,
579 .msg_controllen
= sizeof(control
),
581 struct ucred
*ucred
= NULL
;
582 Manager
*m
= userdata
;
583 struct cmsghdr
*cmsg
;
591 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
593 if (errno
== EAGAIN
|| errno
== EINTR
)
599 cmsg_close_all(&msghdr
);
601 CMSG_FOREACH(cmsg
, &msghdr
) {
602 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
603 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
604 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
))) {
606 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
610 if (msghdr
.msg_flags
& MSG_TRUNC
) {
611 log_warning("Got overly long notification datagram, ignoring.");
615 if (!ucred
|| ucred
->pid
<= 0) {
616 log_warning("Got notification datagram lacking credential information, ignoring.");
620 HASHMAP_FOREACH(t
, m
->transfers
, i
)
621 if (ucred
->pid
== t
->pid
)
625 log_warning("Got notification datagram from unexpected peer, ignoring.");
631 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
633 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
640 e
= strchrnul(p
, '\n');
643 r
= safe_atou(p
, &percent
);
644 if (r
< 0 || percent
> 100) {
645 log_warning("Got invalid percent value, ignoring.");
649 t
->progress_percent
= percent
;
651 log_debug("Got percentage from client: %u%%", percent
);
655 static int manager_new(Manager
**ret
) {
656 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
657 static const union sockaddr_union sa
= {
658 .un
.sun_family
= AF_UNIX
,
659 .un
.sun_path
= "/run/systemd/import/notify",
661 static const int one
= 1;
666 m
= new0(Manager
, 1);
670 r
= sd_event_default(&m
->event
);
674 sd_event_set_watchdog(m
->event
, true);
676 r
= sd_bus_default_system(&m
->bus
);
680 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
681 if (m
->notify_fd
< 0)
684 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
685 (void) unlink(sa
.un
.sun_path
);
687 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
690 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
693 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
703 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *dkr_index_url
, const char *remote
) {
709 assert(type
< _TRANSFER_TYPE_MAX
);
711 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
713 if (t
->type
== type
&&
714 streq_ptr(t
->remote
, remote
) &&
715 streq_ptr(t
->dkr_index_url
, dkr_index_url
))
722 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
723 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
724 int fd
, force
, read_only
, r
;
725 const char *local
, *object
;
726 Manager
*m
= userdata
;
733 r
= bus_verify_polkit_async(
736 "org.freedesktop.import1.import",
744 return 1; /* Will call us back */
746 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
750 if (!machine_name_is_valid(local
))
751 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
753 r
= setup_machine_directory((uint64_t) -1, error
);
757 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
759 r
= transfer_new(m
, &t
);
764 t
->force_local
= force
;
765 t
->read_only
= read_only
;
767 t
->local
= strdup(local
);
771 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
775 r
= transfer_start(t
);
779 object
= t
->object_path
;
783 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
786 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
787 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
789 const char *local
, *object
, *format
;
790 Manager
*m
= userdata
;
797 r
= bus_verify_polkit_async(
800 "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",
873 return 1; /* Will call us back */
875 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
879 if (!http_url_is_valid(remote
))
880 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
884 else if (!machine_name_is_valid(local
))
885 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
888 v
= IMPORT_VERIFY_SIGNATURE
;
890 v
= import_verify_from_string(verify
);
892 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
894 r
= setup_machine_directory((uint64_t) -1, error
);
898 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
900 if (manager_find(m
, type
, NULL
, remote
))
901 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
903 r
= transfer_new(m
, &t
);
909 t
->force_local
= force
;
911 t
->remote
= strdup(remote
);
916 t
->local
= strdup(local
);
921 r
= transfer_start(t
);
925 object
= t
->object_path
;
929 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
932 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
933 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
934 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
935 Manager
*m
= userdata
;
943 r
= bus_verify_polkit_async(
946 "org.freedesktop.import1.pull",
954 return 1; /* Will call us back */
956 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
960 if (isempty(index_url
))
961 index_url
= DEFAULT_DKR_INDEX_URL
;
963 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
964 if (!http_url_is_valid(index_url
))
965 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
967 if (!dkr_name_is_valid(remote
))
968 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
972 else if (!dkr_tag_is_valid(tag
))
973 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
977 else if (!machine_name_is_valid(local
))
978 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
981 v
= IMPORT_VERIFY_SIGNATURE
;
983 v
= import_verify_from_string(verify
);
985 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
987 if (v
!= IMPORT_VERIFY_NO
)
988 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
990 r
= setup_machine_directory((uint64_t) -1, error
);
994 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
995 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
997 r
= transfer_new(m
, &t
);
1001 t
->type
= TRANSFER_PULL_DKR
;
1003 t
->force_local
= force
;
1005 t
->dkr_index_url
= strdup(index_url
);
1006 if (!t
->dkr_index_url
)
1009 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1014 t
->local
= strdup(local
);
1019 r
= transfer_start(t
);
1023 object
= t
->object_path
;
1027 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1030 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1031 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1032 Manager
*m
= userdata
;
1040 r
= sd_bus_message_new_method_return(msg
, &reply
);
1044 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1048 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1050 r
= sd_bus_message_append(
1054 transfer_type_to_string(t
->type
),
1057 (double) t
->progress_percent
/ 100.0,
1063 r
= sd_bus_message_close_container(reply
);
1067 return sd_bus_send(NULL
, reply
, NULL
);
1070 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1071 Transfer
*t
= userdata
;
1077 r
= bus_verify_polkit_async(
1080 "org.freedesktop.import1.pull",
1083 &t
->manager
->polkit_registry
,
1088 return 1; /* Will call us back */
1090 r
= transfer_cancel(t
);
1094 return sd_bus_reply_method_return(msg
, NULL
);
1097 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1098 Manager
*m
= userdata
;
1106 r
= bus_verify_polkit_async(
1109 "org.freedesktop.import1.pull",
1112 &m
->polkit_registry
,
1117 return 1; /* Will call us back */
1119 r
= sd_bus_message_read(msg
, "u", &id
);
1123 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1125 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1127 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1129 r
= transfer_cancel(t
);
1133 return sd_bus_reply_method_return(msg
, NULL
);
1136 static int property_get_progress(
1139 const char *interface
,
1140 const char *property
,
1141 sd_bus_message
*reply
,
1143 sd_bus_error
*error
) {
1145 Transfer
*t
= userdata
;
1151 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1154 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1155 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1157 static const sd_bus_vtable transfer_vtable
[] = {
1158 SD_BUS_VTABLE_START(0),
1159 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1160 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1161 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1162 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1163 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1164 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1165 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1166 SD_BUS_SIGNAL("LogMessage", "us", 0),
1170 static const sd_bus_vtable manager_vtable
[] = {
1171 SD_BUS_VTABLE_START(0),
1172 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1173 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1174 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1175 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1176 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1177 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1178 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1179 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1180 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1181 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1182 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1186 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1187 Manager
*m
= userdata
;
1199 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1203 r
= safe_atou32(p
, &id
);
1204 if (r
< 0 || id
== 0)
1207 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1215 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1216 _cleanup_strv_free_
char **l
= NULL
;
1217 Manager
*m
= userdata
;
1222 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1226 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1228 l
[k
] = strdup(t
->object_path
);
1241 static int manager_add_bus_objects(Manager
*m
) {
1246 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1248 return log_error_errno(r
, "Failed to register object: %m");
1250 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1252 return log_error_errno(r
, "Failed to register object: %m");
1254 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1256 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1258 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1260 return log_error_errno(r
, "Failed to register name: %m");
1262 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1264 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1269 static bool manager_check_idle(void *userdata
) {
1270 Manager
*m
= userdata
;
1272 return hashmap_isempty(m
->transfers
);
1275 static int manager_run(Manager
*m
) {
1278 return bus_event_loop_with_idle(
1281 "org.freedesktop.import1",
1287 int main(int argc
, char *argv
[]) {
1288 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1291 log_set_target(LOG_TARGET_AUTO
);
1292 log_parse_environment();
1298 log_error("This program takes no arguments.");
1303 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1305 r
= manager_new(&m
);
1307 log_error_errno(r
, "Failed to allocate manager object: %m");
1311 r
= manager_add_bus_objects(m
);
1317 log_error_errno(r
, "Failed to run event loop: %m");
1322 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;