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
;
171 t
->verify
= _IMPORT_VERIFY_INVALID
;
173 id
= m
->current_transfer_id
+ 1;
175 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
178 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
182 m
->current_transfer_id
= id
;
193 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
194 int r
, priority
= LOG_INFO
;
199 syslog_parse_priority(&line
, &priority
, true);
201 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
203 r
= sd_bus_emit_signal(
206 "org.freedesktop.import1.Transfer",
212 log_error_errno(r
, "Cannot emit message: %m");
215 static void transfer_send_logs(Transfer
*t
, bool flush
) {
218 /* Try to send out all log messages, if we can. But if we
219 * can't we remove the messages from the buffer, but don't
222 while (t
->log_message_size
> 0) {
223 _cleanup_free_
char *n
= NULL
;
226 if (t
->log_message_size
>= sizeof(t
->log_message
))
227 e
= t
->log_message
+ sizeof(t
->log_message
);
231 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
232 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
246 e
= t
->log_message
+ t
->log_message_size
;
249 n
= strndup(t
->log_message
, e
- t
->log_message
);
251 /* Skip over NUL and newlines */
252 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
255 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
256 t
->log_message_size
-= e
- t
->log_message
;
266 transfer_send_log_line(t
, n
);
270 static int transfer_finalize(Transfer
*t
, bool success
) {
275 transfer_send_logs(t
, true);
277 r
= sd_bus_emit_signal(
279 "/org/freedesktop/import1",
280 "org.freedesktop.import1.Manager",
286 t
->n_canceled
> 0 ? "canceled" : "failed");
289 log_error_errno(r
, "Cannot emit message: %m");
295 static int transfer_cancel(Transfer
*t
) {
300 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
308 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
309 Transfer
*t
= userdata
;
310 bool success
= false;
315 if (si
->si_code
== CLD_EXITED
) {
316 if (si
->si_status
!= 0)
317 log_error("Import process failed with exit code %i.", si
->si_status
);
319 log_debug("Import process succeeded.");
323 } else if (si
->si_code
== CLD_KILLED
||
324 si
->si_code
== CLD_DUMPED
)
326 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
328 log_error("Import process failed due to unknown reason.");
332 return transfer_finalize(t
, success
);
335 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
336 Transfer
*t
= userdata
;
342 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
344 /* EOF/read error. We just close the pipe here, and
345 * close the watch, waiting for the SIGCHLD to arrive,
346 * before we do anything else. */
349 log_error_errno(errno
, "Failed to read log message: %m");
351 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
355 t
->log_message_size
+= l
;
357 transfer_send_logs(t
, false);
362 static int transfer_start(Transfer
*t
) {
363 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
369 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
376 const char *cmd
[] = {
377 NULL
, /* systemd-import, systemd-export or systemd-pull */
378 NULL
, /* tar, raw, dkr */
379 NULL
, /* --verify= */
380 NULL
, /* verify argument */
381 NULL
, /* maybe --force */
382 NULL
, /* maybe --read-only */
383 NULL
, /* maybe --dkr-index-url */
384 NULL
, /* if so: the actual URL */
385 NULL
, /* maybe --format= */
386 NULL
, /* if so: the actual format */
395 (void) reset_all_signal_handlers();
396 (void) reset_signal_mask();
397 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
399 pipefd
[0] = safe_close(pipefd
[0]);
401 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
402 log_error_errno(errno
, "Failed to dup2() fd: %m");
406 if (t
->stdout_fd
>= 0) {
407 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
408 log_error_errno(errno
, "Failed to dup2() fd: %m");
412 if (t
->stdout_fd
!= STDOUT_FILENO
)
413 safe_close(t
->stdout_fd
);
415 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
416 log_error_errno(errno
, "Failed to dup2() fd: %m");
421 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
422 pipefd
[1] = safe_close(pipefd
[1]);
424 if (t
->stdin_fd
>= 0) {
425 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
426 log_error_errno(errno
, "Failed to dup2() fd: %m");
430 if (t
->stdin_fd
!= STDIN_FILENO
)
431 safe_close(t
->stdin_fd
);
435 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
437 log_error_errno(errno
, "Failed to open /dev/null: %m");
441 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
442 log_error_errno(errno
, "Failed to dup2() fd: %m");
446 if (null_fd
!= STDIN_FILENO
)
450 fd_cloexec(STDIN_FILENO
, false);
451 fd_cloexec(STDOUT_FILENO
, false);
452 fd_cloexec(STDERR_FILENO
, false);
454 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
455 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
457 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
458 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
459 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
460 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
462 cmd
[k
++] = SYSTEMD_PULL_PATH
;
464 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
466 else if (IN_SET(t
->type
, TRANSFER_IMPORT_RAW
, TRANSFER_EXPORT_RAW
, TRANSFER_PULL_RAW
))
471 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
472 cmd
[k
++] = "--verify";
473 cmd
[k
++] = import_verify_to_string(t
->verify
);
477 cmd
[k
++] = "--force";
479 cmd
[k
++] = "--read-only";
481 if (t
->dkr_index_url
) {
482 cmd
[k
++] = "--dkr-index-url";
483 cmd
[k
++] = t
->dkr_index_url
;
487 cmd
[k
++] = "--format";
488 cmd
[k
++] = t
->format
;
491 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
493 cmd
[k
++] = t
->remote
;
502 execv(cmd
[0], (char * const *) cmd
);
503 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
507 pipefd
[1] = safe_close(pipefd
[1]);
508 t
->log_fd
= pipefd
[0];
511 t
->stdin_fd
= safe_close(t
->stdin_fd
);
513 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
517 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
521 /* Make sure always process logging before SIGCHLD */
522 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
526 r
= sd_bus_emit_signal(
528 "/org/freedesktop/import1",
529 "org.freedesktop.import1.Manager",
540 static Manager
*manager_unref(Manager
*m
) {
546 sd_event_source_unref(m
->notify_event_source
);
547 safe_close(m
->notify_fd
);
549 while ((t
= hashmap_first(m
->transfers
)))
552 hashmap_free(m
->transfers
);
554 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
556 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
557 sd_event_unref(m
->event
);
563 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
565 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
567 char buf
[NOTIFY_BUFFER_MAX
+1];
568 struct iovec iovec
= {
570 .iov_len
= sizeof(buf
)-1,
573 struct cmsghdr cmsghdr
;
574 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
575 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
577 struct msghdr msghdr
= {
580 .msg_control
= &control
,
581 .msg_controllen
= sizeof(control
),
583 struct ucred
*ucred
= NULL
;
584 Manager
*m
= userdata
;
585 struct cmsghdr
*cmsg
;
593 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
595 if (errno
== EAGAIN
|| errno
== EINTR
)
601 cmsg_close_all(&msghdr
);
603 CMSG_FOREACH(cmsg
, &msghdr
) {
604 if (cmsg
->cmsg_level
== SOL_SOCKET
&& cmsg
->cmsg_type
== SCM_CREDENTIALS
&& cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
606 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
609 if (msghdr
.msg_flags
& MSG_TRUNC
) {
610 log_warning("Got overly long notification datagram, ignoring.");
614 if (!ucred
|| ucred
->pid
<= 0) {
615 log_warning("Got notification datagram lacking credential information, ignoring.");
619 HASHMAP_FOREACH(t
, m
->transfers
, i
)
620 if (ucred
->pid
== t
->pid
)
624 log_warning("Got notification datagram from unexpected peer, ignoring.");
630 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
632 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
639 e
= strchrnul(p
, '\n');
642 r
= safe_atou(p
, &percent
);
643 if (r
< 0 || percent
> 100) {
644 log_warning("Got invalid percent value, ignoring.");
648 t
->progress_percent
= percent
;
650 log_debug("Got percentage from client: %u%%", percent
);
654 static int manager_new(Manager
**ret
) {
655 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
656 static const union sockaddr_union sa
= {
657 .un
.sun_family
= AF_UNIX
,
658 .un
.sun_path
= "/run/systemd/import/notify",
660 static const int one
= 1;
665 m
= new0(Manager
, 1);
669 r
= sd_event_default(&m
->event
);
673 sd_event_set_watchdog(m
->event
, true);
675 r
= sd_bus_default_system(&m
->bus
);
679 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
680 if (m
->notify_fd
< 0)
683 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
684 (void) unlink(sa
.un
.sun_path
);
686 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
689 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
692 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
702 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *dkr_index_url
, const char *remote
) {
708 assert(type
< _TRANSFER_TYPE_MAX
);
710 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
712 if (t
->type
== type
&&
713 streq_ptr(t
->remote
, remote
) &&
714 streq_ptr(t
->dkr_index_url
, dkr_index_url
))
721 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
722 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
723 int fd
, force
, read_only
, r
;
724 const char *local
, *object
;
725 Manager
*m
= userdata
;
732 r
= bus_verify_polkit_async(
735 "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",
809 return 1; /* Will call us back */
811 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
815 if (!machine_name_is_valid(local
))
816 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
818 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
820 r
= transfer_new(m
, &t
);
826 if (!isempty(format
)) {
827 t
->format
= strdup(format
);
832 t
->local
= strdup(local
);
836 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
837 if (t
->stdout_fd
< 0)
840 r
= transfer_start(t
);
844 object
= t
->object_path
;
848 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
851 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
852 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
853 const char *remote
, *local
, *verify
, *object
;
854 Manager
*m
= userdata
;
863 r
= bus_verify_polkit_async(
866 "org.freedesktop.import1.pull",
875 return 1; /* Will call us back */
877 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
881 if (!http_url_is_valid(remote
))
882 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
886 else if (!machine_name_is_valid(local
))
887 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
890 v
= IMPORT_VERIFY_SIGNATURE
;
892 v
= import_verify_from_string(verify
);
894 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
896 r
= setup_machine_directory((uint64_t) -1, error
);
900 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
902 if (manager_find(m
, type
, NULL
, remote
))
903 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
905 r
= transfer_new(m
, &t
);
911 t
->force_local
= force
;
913 t
->remote
= strdup(remote
);
918 t
->local
= strdup(local
);
923 r
= transfer_start(t
);
927 object
= t
->object_path
;
931 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
934 static int method_pull_dkr(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
935 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
936 const char *index_url
, *remote
, *tag
, *local
, *verify
, *object
;
937 Manager
*m
= userdata
;
945 r
= bus_verify_polkit_async(
948 "org.freedesktop.import1.pull",
957 return 1; /* Will call us back */
959 r
= sd_bus_message_read(msg
, "sssssb", &index_url
, &remote
, &tag
, &local
, &verify
, &force
);
963 if (isempty(index_url
))
964 index_url
= DEFAULT_DKR_INDEX_URL
;
966 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL must be specified.");
967 if (!http_url_is_valid(index_url
))
968 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Index URL %s is invalid", index_url
);
970 if (!dkr_name_is_valid(remote
))
971 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Remote name %s is not valid", remote
);
975 else if (!dkr_tag_is_valid(tag
))
976 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Tag %s is not valid", tag
);
980 else if (!machine_name_is_valid(local
))
981 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
984 v
= IMPORT_VERIFY_SIGNATURE
;
986 v
= import_verify_from_string(verify
);
988 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
990 if (v
!= IMPORT_VERIFY_NO
)
991 return sd_bus_error_setf(error
, SD_BUS_ERROR_NOT_SUPPORTED
, "DKR does not support verification.");
993 r
= setup_machine_directory((uint64_t) -1, error
);
997 if (manager_find(m
, TRANSFER_PULL_DKR
, index_url
, remote
))
998 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
1000 r
= transfer_new(m
, &t
);
1004 t
->type
= TRANSFER_PULL_DKR
;
1006 t
->force_local
= force
;
1008 t
->dkr_index_url
= strdup(index_url
);
1009 if (!t
->dkr_index_url
)
1012 t
->remote
= strjoin(remote
, ":", tag
, NULL
);
1017 t
->local
= strdup(local
);
1022 r
= transfer_start(t
);
1026 object
= t
->object_path
;
1030 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
1033 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1034 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1035 Manager
*m
= userdata
;
1043 r
= sd_bus_message_new_method_return(msg
, &reply
);
1047 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
1051 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1053 r
= sd_bus_message_append(
1057 transfer_type_to_string(t
->type
),
1060 (double) t
->progress_percent
/ 100.0,
1066 r
= sd_bus_message_close_container(reply
);
1070 return sd_bus_send(NULL
, reply
, NULL
);
1073 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1074 Transfer
*t
= userdata
;
1080 r
= bus_verify_polkit_async(
1083 "org.freedesktop.import1.pull",
1087 &t
->manager
->polkit_registry
,
1092 return 1; /* Will call us back */
1094 r
= transfer_cancel(t
);
1098 return sd_bus_reply_method_return(msg
, NULL
);
1101 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
1102 Manager
*m
= userdata
;
1110 r
= bus_verify_polkit_async(
1113 "org.freedesktop.import1.pull",
1117 &m
->polkit_registry
,
1122 return 1; /* Will call us back */
1124 r
= sd_bus_message_read(msg
, "u", &id
);
1128 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1130 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1132 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1134 r
= transfer_cancel(t
);
1138 return sd_bus_reply_method_return(msg
, NULL
);
1141 static int property_get_progress(
1144 const char *interface
,
1145 const char *property
,
1146 sd_bus_message
*reply
,
1148 sd_bus_error
*error
) {
1150 Transfer
*t
= userdata
;
1156 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1159 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1160 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1162 static const sd_bus_vtable transfer_vtable
[] = {
1163 SD_BUS_VTABLE_START(0),
1164 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1165 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1166 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1167 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1168 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1169 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1170 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1171 SD_BUS_SIGNAL("LogMessage", "us", 0),
1175 static const sd_bus_vtable manager_vtable
[] = {
1176 SD_BUS_VTABLE_START(0),
1177 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1178 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1179 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1180 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1181 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1182 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1183 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr
, SD_BUS_VTABLE_UNPRIVILEGED
),
1184 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1185 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1186 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1187 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1191 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1192 Manager
*m
= userdata
;
1204 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1208 r
= safe_atou32(p
, &id
);
1209 if (r
< 0 || id
== 0)
1212 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1220 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1221 _cleanup_strv_free_
char **l
= NULL
;
1222 Manager
*m
= userdata
;
1227 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1231 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1233 l
[k
] = strdup(t
->object_path
);
1246 static int manager_add_bus_objects(Manager
*m
) {
1251 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1253 return log_error_errno(r
, "Failed to register object: %m");
1255 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1257 return log_error_errno(r
, "Failed to register object: %m");
1259 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1261 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1263 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1265 return log_error_errno(r
, "Failed to register name: %m");
1267 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1269 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1274 static bool manager_check_idle(void *userdata
) {
1275 Manager
*m
= userdata
;
1277 return hashmap_isempty(m
->transfers
);
1280 static int manager_run(Manager
*m
) {
1283 return bus_event_loop_with_idle(
1286 "org.freedesktop.import1",
1292 int main(int argc
, char *argv
[]) {
1293 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1296 log_set_target(LOG_TARGET_AUTO
);
1297 log_parse_environment();
1303 log_error("This program takes no arguments.");
1308 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1310 r
= manager_new(&m
);
1312 log_error_errno(r
, "Failed to allocate manager object: %m");
1316 r
= manager_add_bus_objects(m
);
1322 log_error_errno(r
, "Failed to run event loop: %m");
1327 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;