1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <sys/prctl.h>
26 #include "alloc-util.h"
27 #include "bus-common-errors.h"
31 #include "hostname-util.h"
32 #include "import-util.h"
33 #include "machine-pool.h"
36 #include "parse-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "signal-util.h"
40 #include "socket-util.h"
41 #include "string-table.h"
43 #include "syslog-util.h"
44 #include "user-util.h"
48 typedef struct Transfer Transfer
;
49 typedef struct Manager Manager
;
51 typedef enum TransferType
{
59 _TRANSFER_TYPE_INVALID
= -1,
82 char log_message
[LINE_MAX
];
83 size_t log_message_size
;
85 sd_event_source
*pid_event_source
;
86 sd_event_source
*log_event_source
;
89 unsigned progress_percent
;
99 uint32_t current_transfer_id
;
102 Hashmap
*polkit_registry
;
106 sd_event_source
*notify_event_source
;
109 #define TRANSFERS_MAX 64
111 static const char* const transfer_type_table
[_TRANSFER_TYPE_MAX
] = {
112 [TRANSFER_IMPORT_TAR
] = "import-tar",
113 [TRANSFER_IMPORT_RAW
] = "import-raw",
114 [TRANSFER_EXPORT_TAR
] = "export-tar",
115 [TRANSFER_EXPORT_RAW
] = "export-raw",
116 [TRANSFER_PULL_TAR
] = "pull-tar",
117 [TRANSFER_PULL_RAW
] = "pull-raw",
120 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type
, TransferType
);
122 static Transfer
*transfer_unref(Transfer
*t
) {
127 hashmap_remove(t
->manager
->transfers
, UINT32_TO_PTR(t
->id
));
129 sd_event_source_unref(t
->pid_event_source
);
130 sd_event_source_unref(t
->log_event_source
);
135 free(t
->object_path
);
138 (void) kill_and_sigcont(t
->pid
, SIGKILL
);
139 (void) wait_for_terminate(t
->pid
, NULL
);
142 safe_close(t
->log_fd
);
143 safe_close(t
->stdin_fd
);
144 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
;
195 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
196 int r
, priority
= LOG_INFO
;
201 syslog_parse_priority(&line
, &priority
, true);
203 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
205 r
= sd_bus_emit_signal(
208 "org.freedesktop.import1.Transfer",
214 log_error_errno(r
, "Cannot emit message: %m");
217 static void transfer_send_logs(Transfer
*t
, bool flush
) {
220 /* Try to send out all log messages, if we can. But if we
221 * can't we remove the messages from the buffer, but don't
224 while (t
->log_message_size
> 0) {
225 _cleanup_free_
char *n
= NULL
;
228 if (t
->log_message_size
>= sizeof(t
->log_message
))
229 e
= t
->log_message
+ sizeof(t
->log_message
);
233 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
234 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
248 e
= t
->log_message
+ t
->log_message_size
;
251 n
= strndup(t
->log_message
, e
- t
->log_message
);
253 /* Skip over NUL and newlines */
254 while (e
< t
->log_message
+ t
->log_message_size
&& (*e
== 0 || *e
== '\n'))
257 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
258 t
->log_message_size
-= e
- t
->log_message
;
268 transfer_send_log_line(t
, n
);
272 static int transfer_finalize(Transfer
*t
, bool success
) {
277 transfer_send_logs(t
, true);
279 r
= sd_bus_emit_signal(
281 "/org/freedesktop/import1",
282 "org.freedesktop.import1.Manager",
288 t
->n_canceled
> 0 ? "canceled" : "failed");
291 log_error_errno(r
, "Cannot emit message: %m");
297 static int transfer_cancel(Transfer
*t
) {
302 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
310 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
311 Transfer
*t
= userdata
;
312 bool success
= false;
317 if (si
->si_code
== CLD_EXITED
) {
318 if (si
->si_status
!= 0)
319 log_error("Import process failed with exit code %i.", si
->si_status
);
321 log_debug("Import process succeeded.");
325 } else if (IN_SET(si
->si_code
, CLD_KILLED
, CLD_DUMPED
))
327 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
329 log_error("Import process failed due to unknown reason.");
333 return transfer_finalize(t
, success
);
336 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
337 Transfer
*t
= userdata
;
343 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
345 /* EOF/read error. We just close the pipe here, and
346 * close the watch, waiting for the SIGCHLD to arrive,
347 * before we do anything else. */
350 log_error_errno(errno
, "Failed to read log message: %m");
352 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
356 t
->log_message_size
+= l
;
358 transfer_send_logs(t
, false);
363 static int transfer_start(Transfer
*t
) {
364 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
370 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
373 r
= safe_fork("(sd-transfer)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
, &t
->pid
);
377 const char *cmd
[] = {
378 NULL
, /* systemd-import, systemd-export or systemd-pull */
380 NULL
, /* --verify= */
381 NULL
, /* verify argument */
382 NULL
, /* maybe --force */
383 NULL
, /* maybe --read-only */
384 NULL
, /* if so: the actual URL */
385 NULL
, /* maybe --format= */
386 NULL
, /* if so: the actual format */
395 pipefd
[0] = safe_close(pipefd
[0]);
397 r
= rearrange_stdio(t
->stdin_fd
,
398 t
->stdout_fd
< 0 ? pipefd
[1] : t
->stdout_fd
,
401 log_error_errno(r
, "Failed to set stdin/stdout/stderr: %m");
405 if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 ||
406 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) {
407 log_error_errno(errno
, "setenv() failed: %m");
411 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
412 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
413 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
414 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
416 cmd
[k
++] = SYSTEMD_PULL_PATH
;
418 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
423 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
424 cmd
[k
++] = "--verify";
425 cmd
[k
++] = import_verify_to_string(t
->verify
);
429 cmd
[k
++] = "--force";
431 cmd
[k
++] = "--read-only";
434 cmd
[k
++] = "--format";
435 cmd
[k
++] = t
->format
;
438 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
440 cmd
[k
++] = t
->remote
;
449 execv(cmd
[0], (char * const *) cmd
);
450 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
454 pipefd
[1] = safe_close(pipefd
[1]);
455 t
->log_fd
= TAKE_FD(pipefd
[0]);
457 t
->stdin_fd
= safe_close(t
->stdin_fd
);
459 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
463 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
467 /* Make sure always process logging before SIGCHLD */
468 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
472 r
= sd_bus_emit_signal(
474 "/org/freedesktop/import1",
475 "org.freedesktop.import1.Manager",
486 static Manager
*manager_unref(Manager
*m
) {
492 sd_event_source_unref(m
->notify_event_source
);
493 safe_close(m
->notify_fd
);
495 while ((t
= hashmap_first(m
->transfers
)))
498 hashmap_free(m
->transfers
);
500 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
502 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
503 sd_event_unref(m
->event
);
508 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
510 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
512 char buf
[NOTIFY_BUFFER_MAX
+1];
513 struct iovec iovec
= {
515 .iov_len
= sizeof(buf
)-1,
518 struct cmsghdr cmsghdr
;
519 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
520 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
522 struct msghdr msghdr
= {
525 .msg_control
= &control
,
526 .msg_controllen
= sizeof(control
),
528 struct ucred
*ucred
= NULL
;
529 Manager
*m
= userdata
;
530 struct cmsghdr
*cmsg
;
538 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
540 if (IN_SET(errno
, EAGAIN
, EINTR
))
546 cmsg_close_all(&msghdr
);
548 CMSG_FOREACH(cmsg
, &msghdr
)
549 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
550 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
551 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
552 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
554 if (msghdr
.msg_flags
& MSG_TRUNC
) {
555 log_warning("Got overly long notification datagram, ignoring.");
559 if (!ucred
|| ucred
->pid
<= 0) {
560 log_warning("Got notification datagram lacking credential information, ignoring.");
564 HASHMAP_FOREACH(t
, m
->transfers
, i
)
565 if (ucred
->pid
== t
->pid
)
569 log_warning("Got notification datagram from unexpected peer, ignoring.");
575 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
577 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
584 e
= strchrnul(p
, '\n');
587 r
= safe_atou(p
, &percent
);
588 if (r
< 0 || percent
> 100) {
589 log_warning("Got invalid percent value, ignoring.");
593 t
->progress_percent
= percent
;
595 log_debug("Got percentage from client: %u%%", percent
);
599 static int manager_new(Manager
**ret
) {
600 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
601 static const union sockaddr_union sa
= {
602 .un
.sun_family
= AF_UNIX
,
603 .un
.sun_path
= "/run/systemd/import/notify",
605 static const int one
= 1;
610 m
= new0(Manager
, 1);
614 r
= sd_event_default(&m
->event
);
618 sd_event_set_watchdog(m
->event
, true);
620 r
= sd_bus_default_system(&m
->bus
);
624 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
625 if (m
->notify_fd
< 0)
628 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
629 (void) unlink(sa
.un
.sun_path
);
631 if (bind(m
->notify_fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
)) < 0)
634 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
637 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
646 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *remote
) {
652 assert(type
< _TRANSFER_TYPE_MAX
);
654 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
656 if (t
->type
== type
&&
657 streq_ptr(t
->remote
, remote
))
664 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
665 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
666 int fd
, force
, read_only
, r
;
667 const char *local
, *object
;
668 Manager
*m
= userdata
;
675 r
= bus_verify_polkit_async(
678 "org.freedesktop.import1.import",
687 return 1; /* Will call us back */
689 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
693 if (!machine_name_is_valid(local
))
694 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
696 r
= setup_machine_directory((uint64_t) -1, error
);
700 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
702 r
= transfer_new(m
, &t
);
707 t
->force_local
= force
;
708 t
->read_only
= read_only
;
710 t
->local
= strdup(local
);
714 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
718 r
= transfer_start(t
);
722 object
= t
->object_path
;
726 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
729 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
730 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
732 const char *local
, *object
, *format
;
733 Manager
*m
= userdata
;
740 r
= bus_verify_polkit_async(
743 "org.freedesktop.import1.export",
752 return 1; /* Will call us back */
754 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
758 if (!machine_name_is_valid(local
))
759 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
761 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
763 r
= transfer_new(m
, &t
);
769 if (!isempty(format
)) {
770 t
->format
= strdup(format
);
775 t
->local
= strdup(local
);
779 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
780 if (t
->stdout_fd
< 0)
783 r
= transfer_start(t
);
787 object
= t
->object_path
;
791 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
794 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
795 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
796 const char *remote
, *local
, *verify
, *object
;
797 Manager
*m
= userdata
;
806 r
= bus_verify_polkit_async(
809 "org.freedesktop.import1.pull",
818 return 1; /* Will call us back */
820 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
824 if (!http_url_is_valid(remote
))
825 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
829 else if (!machine_name_is_valid(local
))
830 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
833 v
= IMPORT_VERIFY_SIGNATURE
;
835 v
= import_verify_from_string(verify
);
837 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
839 r
= setup_machine_directory((uint64_t) -1, error
);
843 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
845 if (manager_find(m
, type
, remote
))
846 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
848 r
= transfer_new(m
, &t
);
854 t
->force_local
= force
;
856 t
->remote
= strdup(remote
);
861 t
->local
= strdup(local
);
866 r
= transfer_start(t
);
870 object
= t
->object_path
;
874 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
877 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
878 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
879 Manager
*m
= userdata
;
887 r
= sd_bus_message_new_method_return(msg
, &reply
);
891 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
895 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
897 r
= sd_bus_message_append(
901 transfer_type_to_string(t
->type
),
904 (double) t
->progress_percent
/ 100.0,
910 r
= sd_bus_message_close_container(reply
);
914 return sd_bus_send(NULL
, reply
, NULL
);
917 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
918 Transfer
*t
= userdata
;
924 r
= bus_verify_polkit_async(
927 "org.freedesktop.import1.pull",
931 &t
->manager
->polkit_registry
,
936 return 1; /* Will call us back */
938 r
= transfer_cancel(t
);
942 return sd_bus_reply_method_return(msg
, NULL
);
945 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
946 Manager
*m
= userdata
;
954 r
= bus_verify_polkit_async(
957 "org.freedesktop.import1.pull",
966 return 1; /* Will call us back */
968 r
= sd_bus_message_read(msg
, "u", &id
);
972 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
974 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
976 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
978 r
= transfer_cancel(t
);
982 return sd_bus_reply_method_return(msg
, NULL
);
985 static int property_get_progress(
988 const char *interface
,
989 const char *property
,
990 sd_bus_message
*reply
,
992 sd_bus_error
*error
) {
994 Transfer
*t
= userdata
;
1000 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1003 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1004 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1006 static const sd_bus_vtable transfer_vtable
[] = {
1007 SD_BUS_VTABLE_START(0),
1008 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1009 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1010 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1011 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1012 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1013 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1014 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1015 SD_BUS_SIGNAL("LogMessage", "us", 0),
1019 static const sd_bus_vtable manager_vtable
[] = {
1020 SD_BUS_VTABLE_START(0),
1021 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1022 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1023 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1024 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1025 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1026 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1027 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1028 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1029 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1030 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1034 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1035 Manager
*m
= userdata
;
1047 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1051 r
= safe_atou32(p
, &id
);
1052 if (r
< 0 || id
== 0)
1055 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1063 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1064 _cleanup_strv_free_
char **l
= NULL
;
1065 Manager
*m
= userdata
;
1070 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1074 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1076 l
[k
] = strdup(t
->object_path
);
1083 *nodes
= TAKE_PTR(l
);
1088 static int manager_add_bus_objects(Manager
*m
) {
1093 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1095 return log_error_errno(r
, "Failed to register object: %m");
1097 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1099 return log_error_errno(r
, "Failed to register object: %m");
1101 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1103 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1105 r
= sd_bus_request_name_async(m
->bus
, NULL
, "org.freedesktop.import1", 0, NULL
, NULL
);
1107 return log_error_errno(r
, "Failed to request name: %m");
1109 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1111 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1116 static bool manager_check_idle(void *userdata
) {
1117 Manager
*m
= userdata
;
1119 return hashmap_isempty(m
->transfers
);
1122 static int manager_run(Manager
*m
) {
1125 return bus_event_loop_with_idle(
1128 "org.freedesktop.import1",
1134 int main(int argc
, char *argv
[]) {
1135 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1138 log_set_target(LOG_TARGET_AUTO
);
1139 log_parse_environment();
1145 log_error("This program takes no arguments.");
1150 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1152 r
= manager_new(&m
);
1154 log_error_errno(r
, "Failed to allocate manager object: %m");
1158 r
= manager_add_bus_objects(m
);
1164 log_error_errno(r
, "Failed to run event loop: %m");
1169 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;