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>
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
);
150 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer
*, transfer_unref
);
152 static int transfer_new(Manager
*m
, Transfer
**ret
) {
153 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
160 if (hashmap_size(m
->transfers
) >= TRANSFERS_MAX
)
163 r
= hashmap_ensure_allocated(&m
->transfers
, &trivial_hash_ops
);
167 t
= new0(Transfer
, 1);
171 t
->type
= _TRANSFER_TYPE_INVALID
;
175 t
->verify
= _IMPORT_VERIFY_INVALID
;
177 id
= m
->current_transfer_id
+ 1;
179 if (asprintf(&t
->object_path
, "/org/freedesktop/import1/transfer/_%" PRIu32
, id
) < 0)
182 r
= hashmap_put(m
->transfers
, UINT32_TO_PTR(id
), t
);
186 m
->current_transfer_id
= id
;
197 static void transfer_send_log_line(Transfer
*t
, const char *line
) {
198 int r
, priority
= LOG_INFO
;
203 syslog_parse_priority(&line
, &priority
, true);
205 log_full(priority
, "(transfer%" PRIu32
") %s", t
->id
, line
);
207 r
= sd_bus_emit_signal(
210 "org.freedesktop.import1.Transfer",
216 log_error_errno(r
, "Cannot emit message: %m");
219 static void transfer_send_logs(Transfer
*t
, bool flush
) {
222 /* Try to send out all log messages, if we can. But if we
223 * can't we remove the messages from the buffer, but don't
226 while (t
->log_message_size
> 0) {
227 _cleanup_free_
char *n
= NULL
;
230 if (t
->log_message_size
>= sizeof(t
->log_message
))
231 e
= t
->log_message
+ sizeof(t
->log_message
);
235 a
= memchr(t
->log_message
, 0, t
->log_message_size
);
236 b
= memchr(t
->log_message
, '\n', t
->log_message_size
);
250 e
= t
->log_message
+ t
->log_message_size
;
253 n
= strndup(t
->log_message
, e
- t
->log_message
);
255 /* Skip over NUL and newlines */
256 while ((e
< t
->log_message
+ t
->log_message_size
) && (*e
== 0 || *e
== '\n'))
259 memmove(t
->log_message
, e
, t
->log_message
+ sizeof(t
->log_message
) - e
);
260 t
->log_message_size
-= e
- t
->log_message
;
270 transfer_send_log_line(t
, n
);
274 static int transfer_finalize(Transfer
*t
, bool success
) {
279 transfer_send_logs(t
, true);
281 r
= sd_bus_emit_signal(
283 "/org/freedesktop/import1",
284 "org.freedesktop.import1.Manager",
290 t
->n_canceled
> 0 ? "canceled" : "failed");
293 log_error_errno(r
, "Cannot emit message: %m");
299 static int transfer_cancel(Transfer
*t
) {
304 r
= kill_and_sigcont(t
->pid
, t
->n_canceled
< 3 ? SIGTERM
: SIGKILL
);
312 static int transfer_on_pid(sd_event_source
*s
, const siginfo_t
*si
, void *userdata
) {
313 Transfer
*t
= userdata
;
314 bool success
= false;
319 if (si
->si_code
== CLD_EXITED
) {
320 if (si
->si_status
!= 0)
321 log_error("Import process failed with exit code %i.", si
->si_status
);
323 log_debug("Import process succeeded.");
327 } else if (si
->si_code
== CLD_KILLED
||
328 si
->si_code
== CLD_DUMPED
)
330 log_error("Import process terminated by signal %s.", signal_to_string(si
->si_status
));
332 log_error("Import process failed due to unknown reason.");
336 return transfer_finalize(t
, success
);
339 static int transfer_on_log(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
340 Transfer
*t
= userdata
;
346 l
= read(fd
, t
->log_message
+ t
->log_message_size
, sizeof(t
->log_message
) - t
->log_message_size
);
348 /* EOF/read error. We just close the pipe here, and
349 * close the watch, waiting for the SIGCHLD to arrive,
350 * before we do anything else. */
353 log_error_errno(errno
, "Failed to read log message: %m");
355 t
->log_event_source
= sd_event_source_unref(t
->log_event_source
);
359 t
->log_message_size
+= l
;
361 transfer_send_logs(t
, false);
366 static int transfer_start(Transfer
*t
) {
367 _cleanup_close_pair_
int pipefd
[2] = { -1, -1 };
373 if (pipe2(pipefd
, O_CLOEXEC
) < 0)
380 const char *cmd
[] = {
381 NULL
, /* systemd-import, systemd-export or systemd-pull */
383 NULL
, /* --verify= */
384 NULL
, /* verify argument */
385 NULL
, /* maybe --force */
386 NULL
, /* maybe --read-only */
387 NULL
, /* if so: the actual URL */
388 NULL
, /* maybe --format= */
389 NULL
, /* if so: the actual format */
398 (void) reset_all_signal_handlers();
399 (void) reset_signal_mask();
400 assert_se(prctl(PR_SET_PDEATHSIG
, SIGTERM
) == 0);
402 pipefd
[0] = safe_close(pipefd
[0]);
404 if (dup2(pipefd
[1], STDERR_FILENO
) != STDERR_FILENO
) {
405 log_error_errno(errno
, "Failed to dup2() fd: %m");
409 if (t
->stdout_fd
>= 0) {
410 if (dup2(t
->stdout_fd
, STDOUT_FILENO
) != STDOUT_FILENO
) {
411 log_error_errno(errno
, "Failed to dup2() fd: %m");
415 if (t
->stdout_fd
!= STDOUT_FILENO
)
416 safe_close(t
->stdout_fd
);
418 if (dup2(pipefd
[1], STDOUT_FILENO
) != STDOUT_FILENO
) {
419 log_error_errno(errno
, "Failed to dup2() fd: %m");
424 if (pipefd
[1] != STDOUT_FILENO
&& pipefd
[1] != STDERR_FILENO
)
425 pipefd
[1] = safe_close(pipefd
[1]);
427 if (t
->stdin_fd
>= 0) {
428 if (dup2(t
->stdin_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
429 log_error_errno(errno
, "Failed to dup2() fd: %m");
433 if (t
->stdin_fd
!= STDIN_FILENO
)
434 safe_close(t
->stdin_fd
);
438 null_fd
= open("/dev/null", O_RDONLY
|O_NOCTTY
);
440 log_error_errno(errno
, "Failed to open /dev/null: %m");
444 if (dup2(null_fd
, STDIN_FILENO
) != STDIN_FILENO
) {
445 log_error_errno(errno
, "Failed to dup2() fd: %m");
449 if (null_fd
!= STDIN_FILENO
)
453 fd_cloexec(STDIN_FILENO
, false);
454 fd_cloexec(STDOUT_FILENO
, false);
455 fd_cloexec(STDERR_FILENO
, false);
457 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
458 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
460 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_IMPORT_RAW
))
461 cmd
[k
++] = SYSTEMD_IMPORT_PATH
;
462 else if (IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
))
463 cmd
[k
++] = SYSTEMD_EXPORT_PATH
;
465 cmd
[k
++] = SYSTEMD_PULL_PATH
;
467 if (IN_SET(t
->type
, TRANSFER_IMPORT_TAR
, TRANSFER_EXPORT_TAR
, TRANSFER_PULL_TAR
))
472 if (t
->verify
!= _IMPORT_VERIFY_INVALID
) {
473 cmd
[k
++] = "--verify";
474 cmd
[k
++] = import_verify_to_string(t
->verify
);
478 cmd
[k
++] = "--force";
480 cmd
[k
++] = "--read-only";
483 cmd
[k
++] = "--format";
484 cmd
[k
++] = t
->format
;
487 if (!IN_SET(t
->type
, TRANSFER_EXPORT_TAR
, TRANSFER_EXPORT_RAW
)) {
489 cmd
[k
++] = t
->remote
;
498 execv(cmd
[0], (char * const *) cmd
);
499 log_error_errno(errno
, "Failed to execute %s tool: %m", cmd
[0]);
503 pipefd
[1] = safe_close(pipefd
[1]);
504 t
->log_fd
= pipefd
[0];
507 t
->stdin_fd
= safe_close(t
->stdin_fd
);
509 r
= sd_event_add_child(t
->manager
->event
, &t
->pid_event_source
, t
->pid
, WEXITED
, transfer_on_pid
, t
);
513 r
= sd_event_add_io(t
->manager
->event
, &t
->log_event_source
, t
->log_fd
, EPOLLIN
, transfer_on_log
, t
);
517 /* Make sure always process logging before SIGCHLD */
518 r
= sd_event_source_set_priority(t
->log_event_source
, SD_EVENT_PRIORITY_NORMAL
-5);
522 r
= sd_bus_emit_signal(
524 "/org/freedesktop/import1",
525 "org.freedesktop.import1.Manager",
536 static Manager
*manager_unref(Manager
*m
) {
542 sd_event_source_unref(m
->notify_event_source
);
543 safe_close(m
->notify_fd
);
545 while ((t
= hashmap_first(m
->transfers
)))
548 hashmap_free(m
->transfers
);
550 bus_verify_polkit_async_registry_free(m
->polkit_registry
);
552 m
->bus
= sd_bus_flush_close_unref(m
->bus
);
553 sd_event_unref(m
->event
);
559 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager
*, manager_unref
);
561 static int manager_on_notify(sd_event_source
*s
, int fd
, uint32_t revents
, void *userdata
) {
563 char buf
[NOTIFY_BUFFER_MAX
+1];
564 struct iovec iovec
= {
566 .iov_len
= sizeof(buf
)-1,
569 struct cmsghdr cmsghdr
;
570 uint8_t buf
[CMSG_SPACE(sizeof(struct ucred
)) +
571 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX
)];
573 struct msghdr msghdr
= {
576 .msg_control
= &control
,
577 .msg_controllen
= sizeof(control
),
579 struct ucred
*ucred
= NULL
;
580 Manager
*m
= userdata
;
581 struct cmsghdr
*cmsg
;
589 n
= recvmsg(fd
, &msghdr
, MSG_DONTWAIT
|MSG_CMSG_CLOEXEC
);
591 if (errno
== EAGAIN
|| errno
== EINTR
)
597 cmsg_close_all(&msghdr
);
599 CMSG_FOREACH(cmsg
, &msghdr
)
600 if (cmsg
->cmsg_level
== SOL_SOCKET
&&
601 cmsg
->cmsg_type
== SCM_CREDENTIALS
&&
602 cmsg
->cmsg_len
== CMSG_LEN(sizeof(struct ucred
)))
603 ucred
= (struct ucred
*) CMSG_DATA(cmsg
);
605 if (msghdr
.msg_flags
& MSG_TRUNC
) {
606 log_warning("Got overly long notification datagram, ignoring.");
610 if (!ucred
|| ucred
->pid
<= 0) {
611 log_warning("Got notification datagram lacking credential information, ignoring.");
615 HASHMAP_FOREACH(t
, m
->transfers
, i
)
616 if (ucred
->pid
== t
->pid
)
620 log_warning("Got notification datagram from unexpected peer, ignoring.");
626 p
= startswith(buf
, "X_IMPORT_PROGRESS=");
628 p
= strstr(buf
, "\nX_IMPORT_PROGRESS=");
635 e
= strchrnul(p
, '\n');
638 r
= safe_atou(p
, &percent
);
639 if (r
< 0 || percent
> 100) {
640 log_warning("Got invalid percent value, ignoring.");
644 t
->progress_percent
= percent
;
646 log_debug("Got percentage from client: %u%%", percent
);
650 static int manager_new(Manager
**ret
) {
651 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
652 static const union sockaddr_union sa
= {
653 .un
.sun_family
= AF_UNIX
,
654 .un
.sun_path
= "/run/systemd/import/notify",
656 static const int one
= 1;
661 m
= new0(Manager
, 1);
665 r
= sd_event_default(&m
->event
);
669 sd_event_set_watchdog(m
->event
, true);
671 r
= sd_bus_default_system(&m
->bus
);
675 m
->notify_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
676 if (m
->notify_fd
< 0)
679 (void) mkdir_parents_label(sa
.un
.sun_path
, 0755);
680 (void) unlink(sa
.un
.sun_path
);
682 if (bind(m
->notify_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
)) < 0)
685 if (setsockopt(m
->notify_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
)) < 0)
688 r
= sd_event_add_io(m
->event
, &m
->notify_event_source
, m
->notify_fd
, EPOLLIN
, manager_on_notify
, m
);
698 static Transfer
*manager_find(Manager
*m
, TransferType type
, const char *remote
) {
704 assert(type
< _TRANSFER_TYPE_MAX
);
706 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
708 if (t
->type
== type
&&
709 streq_ptr(t
->remote
, remote
))
716 static int method_import_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
717 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
718 int fd
, force
, read_only
, r
;
719 const char *local
, *object
;
720 Manager
*m
= userdata
;
727 r
= bus_verify_polkit_async(
730 "org.freedesktop.import1.import",
739 return 1; /* Will call us back */
741 r
= sd_bus_message_read(msg
, "hsbb", &fd
, &local
, &force
, &read_only
);
745 if (!machine_name_is_valid(local
))
746 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
748 r
= setup_machine_directory((uint64_t) -1, error
);
752 type
= streq_ptr(sd_bus_message_get_member(msg
), "ImportTar") ? TRANSFER_IMPORT_TAR
: TRANSFER_IMPORT_RAW
;
754 r
= transfer_new(m
, &t
);
759 t
->force_local
= force
;
760 t
->read_only
= read_only
;
762 t
->local
= strdup(local
);
766 t
->stdin_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
770 r
= transfer_start(t
);
774 object
= t
->object_path
;
778 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
781 static int method_export_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
782 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
784 const char *local
, *object
, *format
;
785 Manager
*m
= userdata
;
792 r
= bus_verify_polkit_async(
795 "org.freedesktop.import1.export",
804 return 1; /* Will call us back */
806 r
= sd_bus_message_read(msg
, "shs", &local
, &fd
, &format
);
810 if (!machine_name_is_valid(local
))
811 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
813 type
= streq_ptr(sd_bus_message_get_member(msg
), "ExportTar") ? TRANSFER_EXPORT_TAR
: TRANSFER_EXPORT_RAW
;
815 r
= transfer_new(m
, &t
);
821 if (!isempty(format
)) {
822 t
->format
= strdup(format
);
827 t
->local
= strdup(local
);
831 t
->stdout_fd
= fcntl(fd
, F_DUPFD_CLOEXEC
, 3);
832 if (t
->stdout_fd
< 0)
835 r
= transfer_start(t
);
839 object
= t
->object_path
;
843 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
846 static int method_pull_tar_or_raw(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
847 _cleanup_(transfer_unrefp
) Transfer
*t
= NULL
;
848 const char *remote
, *local
, *verify
, *object
;
849 Manager
*m
= userdata
;
858 r
= bus_verify_polkit_async(
861 "org.freedesktop.import1.pull",
870 return 1; /* Will call us back */
872 r
= sd_bus_message_read(msg
, "sssb", &remote
, &local
, &verify
, &force
);
876 if (!http_url_is_valid(remote
))
877 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "URL %s is invalid", remote
);
881 else if (!machine_name_is_valid(local
))
882 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Local name %s is invalid", local
);
885 v
= IMPORT_VERIFY_SIGNATURE
;
887 v
= import_verify_from_string(verify
);
889 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Unknown verification mode %s", verify
);
891 r
= setup_machine_directory((uint64_t) -1, error
);
895 type
= streq_ptr(sd_bus_message_get_member(msg
), "PullTar") ? TRANSFER_PULL_TAR
: TRANSFER_PULL_RAW
;
897 if (manager_find(m
, type
, remote
))
898 return sd_bus_error_setf(error
, BUS_ERROR_TRANSFER_IN_PROGRESS
, "Transfer for %s already in progress.", remote
);
900 r
= transfer_new(m
, &t
);
906 t
->force_local
= force
;
908 t
->remote
= strdup(remote
);
913 t
->local
= strdup(local
);
918 r
= transfer_start(t
);
922 object
= t
->object_path
;
926 return sd_bus_reply_method_return(msg
, "uo", id
, object
);
929 static int method_list_transfers(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
930 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
931 Manager
*m
= userdata
;
939 r
= sd_bus_message_new_method_return(msg
, &reply
);
943 r
= sd_bus_message_open_container(reply
, 'a', "(usssdo)");
947 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
949 r
= sd_bus_message_append(
953 transfer_type_to_string(t
->type
),
956 (double) t
->progress_percent
/ 100.0,
962 r
= sd_bus_message_close_container(reply
);
966 return sd_bus_send(NULL
, reply
, NULL
);
969 static int method_cancel(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
970 Transfer
*t
= userdata
;
976 r
= bus_verify_polkit_async(
979 "org.freedesktop.import1.pull",
983 &t
->manager
->polkit_registry
,
988 return 1; /* Will call us back */
990 r
= transfer_cancel(t
);
994 return sd_bus_reply_method_return(msg
, NULL
);
997 static int method_cancel_transfer(sd_bus_message
*msg
, void *userdata
, sd_bus_error
*error
) {
998 Manager
*m
= userdata
;
1006 r
= bus_verify_polkit_async(
1009 "org.freedesktop.import1.pull",
1013 &m
->polkit_registry
,
1018 return 1; /* Will call us back */
1020 r
= sd_bus_message_read(msg
, "u", &id
);
1024 return sd_bus_error_setf(error
, SD_BUS_ERROR_INVALID_ARGS
, "Invalid transfer id");
1026 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1028 return sd_bus_error_setf(error
, BUS_ERROR_NO_SUCH_TRANSFER
, "No transfer by id %" PRIu32
, id
);
1030 r
= transfer_cancel(t
);
1034 return sd_bus_reply_method_return(msg
, NULL
);
1037 static int property_get_progress(
1040 const char *interface
,
1041 const char *property
,
1042 sd_bus_message
*reply
,
1044 sd_bus_error
*error
) {
1046 Transfer
*t
= userdata
;
1052 return sd_bus_message_append(reply
, "d", (double) t
->progress_percent
/ 100.0);
1055 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type
, transfer_type
, TransferType
);
1056 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify
, import_verify
, ImportVerify
);
1058 static const sd_bus_vtable transfer_vtable
[] = {
1059 SD_BUS_VTABLE_START(0),
1060 SD_BUS_PROPERTY("Id", "u", NULL
, offsetof(Transfer
, id
), SD_BUS_VTABLE_PROPERTY_CONST
),
1061 SD_BUS_PROPERTY("Local", "s", NULL
, offsetof(Transfer
, local
), SD_BUS_VTABLE_PROPERTY_CONST
),
1062 SD_BUS_PROPERTY("Remote", "s", NULL
, offsetof(Transfer
, remote
), SD_BUS_VTABLE_PROPERTY_CONST
),
1063 SD_BUS_PROPERTY("Type", "s", property_get_type
, offsetof(Transfer
, type
), SD_BUS_VTABLE_PROPERTY_CONST
),
1064 SD_BUS_PROPERTY("Verify", "s", property_get_verify
, offsetof(Transfer
, verify
), SD_BUS_VTABLE_PROPERTY_CONST
),
1065 SD_BUS_PROPERTY("Progress", "d", property_get_progress
, 0, 0),
1066 SD_BUS_METHOD("Cancel", NULL
, NULL
, method_cancel
, SD_BUS_VTABLE_UNPRIVILEGED
),
1067 SD_BUS_SIGNAL("LogMessage", "us", 0),
1071 static const sd_bus_vtable manager_vtable
[] = {
1072 SD_BUS_VTABLE_START(0),
1073 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1074 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1075 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1076 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1077 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1078 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw
, SD_BUS_VTABLE_UNPRIVILEGED
),
1079 SD_BUS_METHOD("ListTransfers", NULL
, "a(usssdo)", method_list_transfers
, SD_BUS_VTABLE_UNPRIVILEGED
),
1080 SD_BUS_METHOD("CancelTransfer", "u", NULL
, method_cancel_transfer
, SD_BUS_VTABLE_UNPRIVILEGED
),
1081 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1082 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1086 static int transfer_object_find(sd_bus
*bus
, const char *path
, const char *interface
, void *userdata
, void **found
, sd_bus_error
*error
) {
1087 Manager
*m
= userdata
;
1099 p
= startswith(path
, "/org/freedesktop/import1/transfer/_");
1103 r
= safe_atou32(p
, &id
);
1104 if (r
< 0 || id
== 0)
1107 t
= hashmap_get(m
->transfers
, UINT32_TO_PTR(id
));
1115 static int transfer_node_enumerator(sd_bus
*bus
, const char *path
, void *userdata
, char ***nodes
, sd_bus_error
*error
) {
1116 _cleanup_strv_free_
char **l
= NULL
;
1117 Manager
*m
= userdata
;
1122 l
= new0(char*, hashmap_size(m
->transfers
) + 1);
1126 HASHMAP_FOREACH(t
, m
->transfers
, i
) {
1128 l
[k
] = strdup(t
->object_path
);
1141 static int manager_add_bus_objects(Manager
*m
) {
1146 r
= sd_bus_add_object_vtable(m
->bus
, NULL
, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable
, m
);
1148 return log_error_errno(r
, "Failed to register object: %m");
1150 r
= sd_bus_add_fallback_vtable(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable
, transfer_object_find
, m
);
1152 return log_error_errno(r
, "Failed to register object: %m");
1154 r
= sd_bus_add_node_enumerator(m
->bus
, NULL
, "/org/freedesktop/import1/transfer", transfer_node_enumerator
, m
);
1156 return log_error_errno(r
, "Failed to add transfer enumerator: %m");
1158 r
= sd_bus_request_name(m
->bus
, "org.freedesktop.import1", 0);
1160 return log_error_errno(r
, "Failed to register name: %m");
1162 r
= sd_bus_attach_event(m
->bus
, m
->event
, 0);
1164 return log_error_errno(r
, "Failed to attach bus to event loop: %m");
1169 static bool manager_check_idle(void *userdata
) {
1170 Manager
*m
= userdata
;
1172 return hashmap_isempty(m
->transfers
);
1175 static int manager_run(Manager
*m
) {
1178 return bus_event_loop_with_idle(
1181 "org.freedesktop.import1",
1187 int main(int argc
, char *argv
[]) {
1188 _cleanup_(manager_unrefp
) Manager
*m
= NULL
;
1191 log_set_target(LOG_TARGET_AUTO
);
1192 log_parse_environment();
1198 log_error("This program takes no arguments.");
1203 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGCHLD
, -1) >= 0);
1205 r
= manager_new(&m
);
1207 log_error_errno(r
, "Failed to allocate manager object: %m");
1211 r
= manager_add_bus_objects(m
);
1217 log_error_errno(r
, "Failed to run event loop: %m");
1222 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;