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"
30 #include "socket-util.h"
34 #include "machine-pool.h"
35 #include "path-util.h"
36 #include "import-util.h"
38 typedef struct Transfer Transfer;
39 typedef struct Manager Manager;
41 typedef enum TransferType {
48 _TRANSFER_TYPE_INVALID = -1,
71 char log_message[LINE_MAX];
72 size_t log_message_size;
74 sd_event_source *pid_event_source;
75 sd_event_source *log_event_source;
78 unsigned progress_percent;
87 uint32_t current_transfer_id;
90 Hashmap *polkit_registry;
94 sd_event_source *notify_event_source;
97 #define TRANSFERS_MAX 64
99 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
100 [TRANSFER_IMPORT_TAR] = "import-tar",
101 [TRANSFER_IMPORT_RAW] = "import-raw",
102 [TRANSFER_PULL_TAR] = "pull-tar",
103 [TRANSFER_PULL_RAW] = "pull-raw",
104 [TRANSFER_PULL_DKR] = "pull-dkr",
107 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
109 static Transfer *transfer_unref(Transfer *t) {
114 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
116 sd_event_source_unref(t->pid_event_source);
117 sd_event_source_unref(t->log_event_source);
121 free(t->dkr_index_url);
122 free(t->object_path);
125 (void) kill_and_sigcont(t->pid, SIGKILL);
126 (void) wait_for_terminate(t->pid, NULL);
129 safe_close(t->log_fd);
130 safe_close(t->stdin_fd);
136 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
138 static int transfer_new(Manager *m, Transfer **ret) {
139 _cleanup_(transfer_unrefp) Transfer *t = NULL;
146 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
149 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
153 t = new0(Transfer, 1);
157 t->type = _TRANSFER_TYPE_INVALID;
160 t->verify = _IMPORT_VERIFY_INVALID;
162 id = m->current_transfer_id + 1;
164 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
167 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
171 m->current_transfer_id = id;
182 static void transfer_send_log_line(Transfer *t, const char *line) {
183 int r, priority = LOG_INFO;
188 syslog_parse_priority(&line, &priority, true);
190 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
192 r = sd_bus_emit_signal(
195 "org.freedesktop.import1.Transfer",
201 log_error_errno(r, "Cannot emit message: %m");
204 static void transfer_send_logs(Transfer *t, bool flush) {
207 /* Try to send out all log messages, if we can. But if we
208 * can't we remove the messages from the buffer, but don't
211 while (t->log_message_size > 0) {
212 _cleanup_free_ char *n = NULL;
215 if (t->log_message_size >= sizeof(t->log_message))
216 e = t->log_message + sizeof(t->log_message);
220 a = memchr(t->log_message, 0, t->log_message_size);
221 b = memchr(t->log_message, '\n', t->log_message_size);
235 e = t->log_message + t->log_message_size;
238 n = strndup(t->log_message, e - t->log_message);
240 /* Skip over NUL and newlines */
241 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
244 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
245 t->log_message_size -= e - t->log_message;
255 transfer_send_log_line(t, n);
259 static int transfer_finalize(Transfer *t, bool success) {
264 transfer_send_logs(t, true);
266 r = sd_bus_emit_signal(
268 "/org/freedesktop/import1",
269 "org.freedesktop.import1.Manager",
275 t->n_canceled > 0 ? "canceled" : "failed");
278 log_error_errno(r, "Cannot emit message: %m");
284 static int transfer_cancel(Transfer *t) {
289 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
297 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
298 Transfer *t = userdata;
299 bool success = false;
304 if (si->si_code == CLD_EXITED) {
305 if (si->si_status != 0)
306 log_error("Import process failed with exit code %i.", si->si_status);
308 log_debug("Import process succeeded.");
312 } else if (si->si_code == CLD_KILLED ||
313 si->si_code == CLD_DUMPED)
315 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
317 log_error("Import process failed due to unknown reason.");
321 return transfer_finalize(t, success);
324 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
325 Transfer *t = userdata;
331 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
333 /* EOF/read error. We just close the pipe here, and
334 * close the watch, waiting for the SIGCHLD to arrive,
335 * before we do anything else. */
338 log_error_errno(errno, "Failed to read log message: %m");
340 t->log_event_source = sd_event_source_unref(t->log_event_source);
344 t->log_message_size += l;
346 transfer_send_logs(t, false);
351 static int transfer_start(Transfer *t) {
352 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
358 if (pipe2(pipefd, O_CLOEXEC) < 0)
365 const char *cmd[] = {
366 NULL, /* systemd-import or systemd-pull */
367 NULL, /* tar, raw, dkr */
368 NULL, /* --verify= */
369 NULL, /* verify argument */
370 NULL, /* maybe --force */
371 NULL, /* maybe --read-only */
372 NULL, /* maybe --dkr-index-url */
373 NULL, /* the actual URL */
382 reset_all_signal_handlers();
384 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
386 pipefd[0] = safe_close(pipefd[0]);
388 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
389 log_error_errno(errno, "Failed to dup2() fd: %m");
393 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
394 log_error_errno(errno, "Failed to dup2() fd: %m");
398 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
399 pipefd[1] = safe_close(pipefd[1]);
401 if (t->stdin_fd >= 0) {
402 if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) {
403 log_error_errno(errno, "Failed to dup2() fd: %m");
407 if (t->stdin_fd != STDIN_FILENO)
408 safe_close(t->stdin_fd);
412 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
414 log_error_errno(errno, "Failed to open /dev/null: %m");
418 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
419 log_error_errno(errno, "Failed to dup2() fd: %m");
423 if (null_fd != STDIN_FILENO)
427 fd_cloexec(STDIN_FILENO, false);
428 fd_cloexec(STDOUT_FILENO, false);
429 fd_cloexec(STDERR_FILENO, false);
431 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
432 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
434 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
435 cmd[k++] = SYSTEMD_IMPORT_PATH;
437 cmd[k++] = SYSTEMD_PULL_PATH;
439 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_PULL_TAR))
441 else if (IN_SET(t->type, TRANSFER_IMPORT_RAW, TRANSFER_PULL_RAW))
446 if (t->verify != _IMPORT_VERIFY_INVALID) {
447 cmd[k++] = "--verify";
448 cmd[k++] = import_verify_to_string(t->verify);
452 cmd[k++] = "--force";
454 cmd[k++] = "--read-only";
456 if (t->dkr_index_url) {
457 cmd[k++] = "--dkr-index-url";
458 cmd[k++] = t->dkr_index_url;
462 cmd[k++] = t->remote;
470 execv(cmd[0], (char * const *) cmd);
471 log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]);
475 pipefd[1] = safe_close(pipefd[1]);
476 t->log_fd = pipefd[0];
479 t->stdin_fd = safe_close(t->stdin_fd);
481 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
485 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
489 /* Make sure always process logging before SIGCHLD */
490 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
494 r = sd_bus_emit_signal(
496 "/org/freedesktop/import1",
497 "org.freedesktop.import1.Manager",
508 static Manager *manager_unref(Manager *m) {
514 sd_event_source_unref(m->notify_event_source);
515 safe_close(m->notify_fd);
517 while ((t = hashmap_first(m->transfers)))
520 hashmap_free(m->transfers);
522 bus_verify_polkit_async_registry_free(m->polkit_registry);
524 sd_bus_close(m->bus);
525 sd_bus_unref(m->bus);
526 sd_event_unref(m->event);
532 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
534 static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
536 char buf[NOTIFY_BUFFER_MAX+1];
537 struct iovec iovec = {
539 .iov_len = sizeof(buf)-1,
542 struct cmsghdr cmsghdr;
543 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
544 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
546 struct msghdr msghdr = {
549 .msg_control = &control,
550 .msg_controllen = sizeof(control),
552 struct ucred *ucred = NULL;
553 Manager *m = userdata;
554 struct cmsghdr *cmsg;
562 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
564 if (errno == EAGAIN || errno == EINTR)
570 cmsg_close_all(&msghdr);
572 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
573 if (cmsg->cmsg_level == SOL_SOCKET &&
574 cmsg->cmsg_type == SCM_CREDENTIALS &&
575 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
577 ucred = (struct ucred*) CMSG_DATA(cmsg);
581 if (msghdr.msg_flags & MSG_TRUNC) {
582 log_warning("Got overly long notification datagram, ignoring.");
586 if (!ucred || ucred->pid <= 0) {
587 log_warning("Got notification datagram lacking credential information, ignoring.");
591 HASHMAP_FOREACH(t, m->transfers, i)
592 if (ucred->pid == t->pid)
596 log_warning("Got notification datagram from unexpected peer, ignoring.");
602 p = startswith(buf, "X_IMPORT_PROGRESS=");
604 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
611 e = strchrnul(p, '\n');
614 r = safe_atou(p, &percent);
615 if (r < 0 || percent > 100) {
616 log_warning("Got invalid percent value, ignoring.");
620 t->progress_percent = percent;
622 log_debug("Got percentage from client: %u%%", percent);
626 static int manager_new(Manager **ret) {
627 _cleanup_(manager_unrefp) Manager *m = NULL;
628 static const union sockaddr_union sa = {
629 .un.sun_family = AF_UNIX,
630 .un.sun_path = "/run/systemd/import/notify",
632 static const int one = 1;
637 m = new0(Manager, 1);
641 r = sd_event_default(&m->event);
645 sd_event_set_watchdog(m->event, true);
647 r = sd_bus_default_system(&m->bus);
651 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
652 if (m->notify_fd < 0)
655 (void) mkdir_parents_label(sa.un.sun_path, 0755);
656 (void) unlink(sa.un.sun_path);
658 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
661 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
664 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
674 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
680 assert(type < _TRANSFER_TYPE_MAX);
682 HASHMAP_FOREACH(t, m->transfers, i) {
684 if (t->type == type &&
685 streq_ptr(t->remote, remote) &&
686 streq_ptr(t->dkr_index_url, dkr_index_url))
693 static int method_import_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
694 _cleanup_(transfer_unrefp) Transfer *t = NULL;
695 int fd, force, read_only, r;
696 const char *local, *object;
697 Manager *m = userdata;
701 r = bus_verify_polkit_async(
704 "org.freedesktop.import1.import",
712 return 1; /* Will call us back */
714 r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
718 if (!machine_name_is_valid(local))
719 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
721 r = setup_machine_directory((uint64_t) -1, error);
725 type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
727 r = transfer_new(m, &t);
732 t->force_local = force;
733 t->read_only = read_only;
735 t->local = strdup(local);
739 t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
743 r = transfer_start(t);
747 object = t->object_path;
751 return sd_bus_reply_method_return(msg, "uo", id, object);
754 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
755 _cleanup_(transfer_unrefp) Transfer *t = NULL;
756 const char *remote, *local, *verify, *object;
757 Manager *m = userdata;
767 r = bus_verify_polkit_async(
770 "org.freedesktop.import1.pull",
778 return 1; /* Will call us back */
780 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
784 if (!http_url_is_valid(remote))
785 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
789 else if (!machine_name_is_valid(local))
790 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
793 v = IMPORT_VERIFY_SIGNATURE;
795 v = import_verify_from_string(verify);
797 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
799 r = setup_machine_directory((uint64_t) -1, error);
803 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
805 if (manager_find(m, type, NULL, remote))
806 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
808 r = transfer_new(m, &t);
814 t->force_local = force;
816 t->remote = strdup(remote);
821 t->local = strdup(local);
826 r = transfer_start(t);
830 object = t->object_path;
834 return sd_bus_reply_method_return(msg, "uo", id, object);
837 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
838 _cleanup_(transfer_unrefp) Transfer *t = NULL;
839 const char *index_url, *remote, *tag, *local, *verify, *object;
840 Manager *m = userdata;
849 r = bus_verify_polkit_async(
852 "org.freedesktop.import1.pull",
860 return 1; /* Will call us back */
862 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
866 if (isempty(index_url))
867 index_url = DEFAULT_DKR_INDEX_URL;
869 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
870 if (!http_url_is_valid(index_url))
871 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
873 if (!dkr_name_is_valid(remote))
874 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
878 else if (!dkr_tag_is_valid(tag))
879 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
883 else if (!machine_name_is_valid(local))
884 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
887 v = IMPORT_VERIFY_SIGNATURE;
889 v = import_verify_from_string(verify);
891 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
893 if (v != IMPORT_VERIFY_NO)
894 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
896 r = setup_machine_directory((uint64_t) -1, error);
900 if (manager_find(m, TRANSFER_PULL_DKR, index_url, 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);
907 t->type = TRANSFER_PULL_DKR;
909 t->force_local = force;
911 t->dkr_index_url = strdup(index_url);
912 if (!t->dkr_index_url)
915 t->remote = strjoin(remote, ":", tag, NULL);
920 t->local = strdup(local);
925 r = transfer_start(t);
929 object = t->object_path;
933 return sd_bus_reply_method_return(msg, "uo", id, object);
936 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
937 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
938 Manager *m = userdata;
947 r = sd_bus_message_new_method_return(msg, &reply);
951 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
955 HASHMAP_FOREACH(t, m->transfers, i) {
957 r = sd_bus_message_append(
961 transfer_type_to_string(t->type),
964 (double) t->progress_percent / 100.0,
970 r = sd_bus_message_close_container(reply);
974 return sd_bus_send(bus, reply, NULL);
977 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
978 Transfer *t = userdata;
985 r = bus_verify_polkit_async(
988 "org.freedesktop.import1.pull",
991 &t->manager->polkit_registry,
996 return 1; /* Will call us back */
998 r = transfer_cancel(t);
1002 return sd_bus_reply_method_return(msg, NULL);
1005 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1006 Manager *m = userdata;
1015 r = bus_verify_polkit_async(
1018 "org.freedesktop.import1.pull",
1021 &m->polkit_registry,
1026 return 1; /* Will call us back */
1028 r = sd_bus_message_read(msg, "u", &id);
1032 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
1034 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1036 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
1038 r = transfer_cancel(t);
1042 return sd_bus_reply_method_return(msg, NULL);
1045 static int property_get_progress(
1048 const char *interface,
1049 const char *property,
1050 sd_bus_message *reply,
1052 sd_bus_error *error) {
1054 Transfer *t = userdata;
1060 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
1063 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1064 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1066 static const sd_bus_vtable transfer_vtable[] = {
1067 SD_BUS_VTABLE_START(0),
1068 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1069 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1070 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1071 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1072 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
1073 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
1074 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
1075 SD_BUS_SIGNAL("LogMessage", "us", 0),
1079 static const sd_bus_vtable manager_vtable[] = {
1080 SD_BUS_VTABLE_START(0),
1081 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1082 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1083 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1084 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1085 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
1086 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
1087 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
1088 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1089 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1093 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1094 Manager *m = userdata;
1106 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1110 r = safe_atou32(p, &id);
1111 if (r < 0 || id == 0)
1114 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1122 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1123 _cleanup_strv_free_ char **l = NULL;
1124 Manager *m = userdata;
1129 l = new0(char*, hashmap_size(m->transfers) + 1);
1133 HASHMAP_FOREACH(t, m->transfers, i) {
1135 l[k] = strdup(t->object_path);
1148 static int manager_add_bus_objects(Manager *m) {
1153 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1155 return log_error_errno(r, "Failed to register object: %m");
1157 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1159 return log_error_errno(r, "Failed to register object: %m");
1161 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1163 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1165 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1167 return log_error_errno(r, "Failed to register name: %m");
1169 r = sd_bus_attach_event(m->bus, m->event, 0);
1171 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1176 static bool manager_check_idle(void *userdata) {
1177 Manager *m = userdata;
1179 return hashmap_isempty(m->transfers);
1182 static int manager_run(Manager *m) {
1185 return bus_event_loop_with_idle(
1188 "org.freedesktop.import1",
1194 int main(int argc, char *argv[]) {
1195 _cleanup_(manager_unrefp) Manager *m = NULL;
1198 log_set_target(LOG_TARGET_AUTO);
1199 log_parse_environment();
1205 log_error("This program takes no arguments.");
1210 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1212 r = manager_new(&m);
1214 log_error_errno(r, "Failed to allocate manager object: %m");
1218 r = manager_add_bus_objects(m);
1224 log_error_errno(r, "Failed to run event loop: %m");
1229 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;