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
;
196 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
197 int r
, priority
= LOG_INFO
;
202 syslog_parse_priority(&line
, &priority
, true);
204 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
206 r
= sd_bus_emit_signal(
209 "org.freedesktop.import1.Transfer",
215 log_error_errno(r
, "Cannot emit message: %m");
218 static void transfer_send_logs(Transfer
*t
, bool flush
) {
221 /* Try to send out all log messages, if we can. But if we
222 * can't we remove the messages from the buffer, but don't
225 while (t
->log_message_size
> 0) {
226 _cleanup_free_
char *n
= NULL
;
229 if (t
->log_message_size
>= sizeof(t
->log_message
))
230 e
= t
->log_message
+ sizeof(t
->log_message
);
234 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
235 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
249 e
= t
->log_message
+ t
->log_message_size
;
252 n
= strndup(t
->log_message
, e
- t
->log_message
);
254 /* Skip over NUL and newlines */
255 while (e
< t
->log_message
+ t
->log_message_size
&& (*e
== 0 || *e
== '\n'))
258 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
259 t
->log_message_size
-= e
- t
->log_message
;
269 transfer_send_log_line(t
, n
);
273 static int transfer_finalize(Transfer
*t
, bool success
) {
278 transfer_send_logs(t
, true);
280 r
= sd_bus_emit_signal(
282 "/org/freedesktop/import1",
283 "org.freedesktop.import1.Manager",
289 t
->n_canceled
> 0 ? "canceled" : "failed");
292 log_error_errno(r
, "Cannot emit message: %m");
298 static int transfer_cancel(Transfer
*t
) {
303 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
311 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
312 Transfer
*t
= userdata
;
313 bool success
= false;
318 if (si
->si_code
== CLD_EXITED
) {
319 if (si
->si_status
!= 0)
320 log_error("Import process failed with exit code %i.", si
->si_status
);
322 log_debug("Import process succeeded.");
326 } else if (IN_SET(si
->si_code
, CLD_KILLED
, CLD_DUMPED
))
328 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
330 log_error("Import process failed due to unknown reason.");
334 return transfer_finalize(t
, success
);
337 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
338 Transfer
*t
= userdata
;
344 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
346 /* EOF/read error. We just close the pipe here, and
347 * close the watch, waiting for the SIGCHLD to arrive,
348 * before we do anything else. */
351 log_error_errno(errno
, "Failed to read log message: %m");
353 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
357 t
->log_message_size
+= l
;
359 transfer_send_logs(t
, false);
364 static int transfer_start(Transfer
*t
) {
365 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
371 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
378 const char *cmd
[] = {
379 NULL
, /* systemd-import, systemd-export or systemd-pull */
381 NULL
, /* --verify= */
382 NULL
, /* verify argument */
383 NULL
, /* maybe --force */
384 NULL
, /* maybe --read-only */
385 NULL
, /* if so: the actual URL */
386 NULL
, /* maybe --format= */
387 NULL
, /* if so: the actual format */
396 (void) reset_all_signal_handlers();
397 (void) reset_signal_mask();
398 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
400 pipefd
[0] = safe_close(pipefd
[0]);
402 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
403 log_error_errno(errno
, "Failed to dup2() fd: %m");
407 if (t
->stdout_fd
>= 0) {
408 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
409 log_error_errno(errno
, "Failed to dup2() fd: %m");
413 if (t
->stdout_fd
!= STDOUT_FILENO
)
414 safe_close(t
->stdout_fd
);
416 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
417 log_error_errno(errno
, "Failed to dup2() fd: %m");
422 if (!IN_SET(pipefd
[1], STDOUT_FILENO
, STDERR_FILENO
))
423 pipefd
[1] = safe_close(pipefd
[1]);
425 if (t
->stdin_fd
>= 0) {
426 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
427 log_error_errno(errno
, "Failed to dup2() fd: %m");
431 if (t
->stdin_fd
!= STDIN_FILENO
)
432 safe_close(t
->stdin_fd
);
436 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
438 log_error_errno(errno
, "Failed to open /dev/null: %m");
442 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
443 log_error_errno(errno
, "Failed to dup2() fd: %m");
447 if (null_fd
!= STDIN_FILENO
)
451 stdio_unset_cloexec();
453 if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 ||
454 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) {
455 log_error_errno(errno
, "setenv() failed: %m");
459 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
460 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
461 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
462 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
464 cmd
[k
++] = SYSTEMD_PULL_PATH
;
466 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
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";
482 cmd
[k
++] = "--format";
483 cmd
[k
++] = t
->format
;
486 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
488 cmd
[k
++] = t
->remote
;
497 execv(cmd
[0], (char * const *) cmd
);
498 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
502 pipefd
[1] = safe_close(pipefd
[1]);
503 t
->log_fd
= pipefd
[0];
506 t
->stdin_fd
= safe_close(t
->stdin_fd
);
508 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
512 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
516 /* Make sure always process logging before SIGCHLD */
517 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
521 r
= sd_bus_emit_signal(
523 "/org/freedesktop/import1",
524 "org.freedesktop.import1.Manager",
535 static Manager
*manager_unref(Manager
*m
) {
541 sd_event_source_unref(m
->notify_event_source
);
542 safe_close(m
->notify_fd
);
544 while ((t
= hashmap_first(m
->transfers
)))
547 hashmap_free(m
->transfers
);
549 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
551 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
552 sd_event_unref(m
->event
);
557 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
559 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
561 char buf
[NOTIFY_BUFFER_MAX
+1];
562 struct iovec iovec
= {
564 .iov_len
= sizeof(buf
)-1,
567 struct cmsghdr cmsghdr
;
568 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
569 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
571 struct msghdr msghdr
= {
574 .msg_control
= &control
,
575 .msg_controllen
= sizeof(control
),
577 struct ucred
*ucred
= NULL
;
578 Manager
*m
= userdata
;
579 struct cmsghdr
*cmsg
;
587 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
589 if (IN_SET(errno
, EAGAIN
, EINTR
))
595 cmsg_close_all(&msghdr
);
597 CMSG_FOREACH(cmsg
, &msghdr
)
598 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
599 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
600 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
601 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
603 if (msghdr
.msg_flags
& MSG_TRUNC
) {
604 log_warning("Got overly long notification datagram, ignoring.");
608 if (!ucred
|| ucred
->pid
<= 0) {
609 log_warning("Got notification datagram lacking credential information, ignoring.");
613 HASHMAP_FOREACH(t
, m
->transfers
, i
)
614 if (ucred
->pid
== t
->pid
)
618 log_warning("Got notification datagram from unexpected peer, ignoring.");
624 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
626 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
633 e
= strchrnul(p
, '\n');
636 r
= safe_atou(p
, &percent
);
637 if (r
< 0 || percent
> 100) {
638 log_warning("Got invalid percent value, ignoring.");
642 t
->progress_percent
= percent
;
644 log_debug("Got percentage from client: %u%%", percent
);
648 static int manager_new(Manager
**ret
) {
649 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
650 static const union sockaddr_union sa
= {
651 .un
.sun_family
= AF_UNIX
,
652 .un
.sun_path
= "/run/systemd/import/notify",
654 static const int one
= 1;
659 m
= new0(Manager
, 1);
663 r
= sd_event_default(&m
->event
);
667 sd_event_set_watchdog(m
->event
, true);
669 r
= sd_bus_default_system(&m
->bus
);
673 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
674 if (m
->notify_fd
< 0)
677 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
678 (void) unlink(sa
.un
.sun_path
);
680 if (bind(m
->notify_fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
)) < 0)
683 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
686 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
696 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *remote
) {
702 assert(type
< _TRANSFER_TYPE_MAX
);
704 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
706 if (t
->type
== type
&&
707 streq_ptr(t
->remote
, remote
))
714 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
715 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
716 int fd
, force
, read_only
, r
;
717 const char *local
, *object
;
718 Manager
*m
= userdata
;
725 r
= bus_verify_polkit_async(
728 "org.freedesktop.import1.import",
737 return 1; /* Will call us back */
739 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
743 if (!machine_name_is_valid(local
))
744 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
746 r
= setup_machine_directory((uint64_t) -1, error
);
750 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
752 r
= transfer_new(m
, &t
);
757 t
->force_local
= force
;
758 t
->read_only
= read_only
;
760 t
->local
= strdup(local
);
764 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
768 r
= transfer_start(t
);
772 object
= t
->object_path
;
776 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
779 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
780 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
782 const char *local
, *object
, *format
;
783 Manager
*m
= userdata
;
790 r
= bus_verify_polkit_async(
793 "org.freedesktop.import1.export",
802 return 1; /* Will call us back */
804 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
808 if (!machine_name_is_valid(local
))
809 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
811 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
813 r
= transfer_new(m
, &t
);
819 if (!isempty(format
)) {
820 t
->format
= strdup(format
);
825 t
->local
= strdup(local
);
829 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
830 if (t
->stdout_fd
< 0)
833 r
= transfer_start(t
);
837 object
= t
->object_path
;
841 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
844 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
845 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
846 const char *remote
, *local
, *verify
, *object
;
847 Manager
*m
= userdata
;
856 r
= bus_verify_polkit_async(
859 "org.freedesktop.import1.pull",
868 return 1; /* Will call us back */
870 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
874 if (!http_url_is_valid(remote
))
875 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
879 else if (!machine_name_is_valid(local
))
880 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
883 v
= IMPORT_VERIFY_SIGNATURE
;
885 v
= import_verify_from_string(verify
);
887 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
889 r
= setup_machine_directory((uint64_t) -1, error
);
893 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
895 if (manager_find(m
, type
, remote
))
896 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
898 r
= transfer_new(m
, &t
);
904 t
->force_local
= force
;
906 t
->remote
= strdup(remote
);
911 t
->local
= strdup(local
);
916 r
= transfer_start(t
);
920 object
= t
->object_path
;
924 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
927 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
928 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
929 Manager
*m
= userdata
;
937 r
= sd_bus_message_new_method_return(msg
, &reply
);
941 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
945 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
947 r
= sd_bus_message_append(
951 transfer_type_to_string(t
->type
),
954 (double) t
->progress_percent
/ 100.0,
960 r
= sd_bus_message_close_container(reply
);
964 return sd_bus_send(NULL
, reply
, NULL
);
967 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
968 Transfer
*t
= userdata
;
974 r
= bus_verify_polkit_async(
977 "org.freedesktop.import1.pull",
981 &t
->manager
->polkit_registry
,
986 return 1; /* Will call us back */
988 r
= transfer_cancel(t
);
992 return sd_bus_reply_method_return(msg
, NULL
);
995 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
996 Manager
*m
= userdata
;
1004 r
= bus_verify_polkit_async(
1007 "org.freedesktop.import1.pull",
1011 &m
->polkit_registry
,
1016 return 1; /* Will call us back */
1018 r
= sd_bus_message_read(msg
, "u", &id
);
1022 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1024 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1026 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1028 r
= transfer_cancel(t
);
1032 return sd_bus_reply_method_return(msg
, NULL
);
1035 static int property_get_progress(
1038 const char *interface
,
1039 const char *property
,
1040 sd_bus_message
*reply
,
1042 sd_bus_error
*error
) {
1044 Transfer
*t
= userdata
;
1050 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1053 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1054 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1056 static const sd_bus_vtable transfer_vtable
[] = {
1057 SD_BUS_VTABLE_START(0),
1058 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1059 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1060 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1061 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1062 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1063 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1064 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1065 SD_BUS_SIGNAL("LogMessage", "us", 0),
1069 static const sd_bus_vtable manager_vtable
[] = {
1070 SD_BUS_VTABLE_START(0),
1071 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1072 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1073 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1074 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1075 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1076 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1077 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1078 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1079 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1080 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1084 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1085 Manager
*m
= userdata
;
1097 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1101 r
= safe_atou32(p
, &id
);
1102 if (r
< 0 || id
== 0)
1105 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1113 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1114 _cleanup_strv_free_
char **l
= NULL
;
1115 Manager
*m
= userdata
;
1120 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1124 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1126 l
[k
] = strdup(t
->object_path
);
1139 static int manager_add_bus_objects(Manager
*m
) {
1144 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1146 return log_error_errno(r
, "Failed to register object: %m");
1148 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1150 return log_error_errno(r
, "Failed to register object: %m");
1152 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1154 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1156 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1158 return log_error_errno(r
, "Failed to register name: %m");
1160 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1162 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1167 static bool manager_check_idle(void *userdata
) {
1168 Manager
*m
= userdata
;
1170 return hashmap_isempty(m
->transfers
);
1173 static int manager_run(Manager
*m
) {
1176 return bus_event_loop_with_idle(
1179 "org.freedesktop.import1",
1185 int main(int argc
, char *argv
[]) {
1186 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1189 log_set_target(LOG_TARGET_AUTO
);
1190 log_parse_environment();
1196 log_error("This program takes no arguments.");
1201 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1203 r
= manager_new(&m
);
1205 log_error_errno(r
, "Failed to allocate manager object: %m");
1209 r
= manager_add_bus_objects(m
);
1215 log_error_errno(r
, "Failed to run event loop: %m");
1220 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;