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 {
50 _TRANSFER_TYPE_INVALID = -1,
74 char log_message[LINE_MAX];
75 size_t log_message_size;
77 sd_event_source *pid_event_source;
78 sd_event_source *log_event_source;
81 unsigned progress_percent;
91 uint32_t current_transfer_id;
94 Hashmap *polkit_registry;
98 sd_event_source *notify_event_source;
101 #define TRANSFERS_MAX 64
103 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
104 [TRANSFER_IMPORT_TAR] = "import-tar",
105 [TRANSFER_IMPORT_RAW] = "import-raw",
106 [TRANSFER_EXPORT_TAR] = "export-tar",
107 [TRANSFER_EXPORT_RAW] = "export-raw",
108 [TRANSFER_PULL_TAR] = "pull-tar",
109 [TRANSFER_PULL_RAW] = "pull-raw",
110 [TRANSFER_PULL_DKR] = "pull-dkr",
113 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
115 static Transfer *transfer_unref(Transfer *t) {
120 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
122 sd_event_source_unref(t->pid_event_source);
123 sd_event_source_unref(t->log_event_source);
127 free(t->dkr_index_url);
129 free(t->object_path);
132 (void) kill_and_sigcont(t->pid, SIGKILL);
133 (void) wait_for_terminate(t->pid, NULL);
136 safe_close(t->log_fd);
137 safe_close(t->stdin_fd);
138 safe_close(t->stdout_fd);
144 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
146 static int transfer_new(Manager *m, Transfer **ret) {
147 _cleanup_(transfer_unrefp) Transfer *t = NULL;
154 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
157 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
161 t = new0(Transfer, 1);
165 t->type = _TRANSFER_TYPE_INVALID;
168 t->verify = _IMPORT_VERIFY_INVALID;
170 id = m->current_transfer_id + 1;
172 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
175 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
179 m->current_transfer_id = id;
190 static void transfer_send_log_line(Transfer *t, const char *line) {
191 int r, priority = LOG_INFO;
196 syslog_parse_priority(&line, &priority, true);
198 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
200 r = sd_bus_emit_signal(
203 "org.freedesktop.import1.Transfer",
209 log_error_errno(r, "Cannot emit message: %m");
212 static void transfer_send_logs(Transfer *t, bool flush) {
215 /* Try to send out all log messages, if we can. But if we
216 * can't we remove the messages from the buffer, but don't
219 while (t->log_message_size > 0) {
220 _cleanup_free_ char *n = NULL;
223 if (t->log_message_size >= sizeof(t->log_message))
224 e = t->log_message + sizeof(t->log_message);
228 a = memchr(t->log_message, 0, t->log_message_size);
229 b = memchr(t->log_message, '\n', t->log_message_size);
243 e = t->log_message + t->log_message_size;
246 n = strndup(t->log_message, e - t->log_message);
248 /* Skip over NUL and newlines */
249 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
252 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
253 t->log_message_size -= e - t->log_message;
263 transfer_send_log_line(t, n);
267 static int transfer_finalize(Transfer *t, bool success) {
272 transfer_send_logs(t, true);
274 r = sd_bus_emit_signal(
276 "/org/freedesktop/import1",
277 "org.freedesktop.import1.Manager",
283 t->n_canceled > 0 ? "canceled" : "failed");
286 log_error_errno(r, "Cannot emit message: %m");
292 static int transfer_cancel(Transfer *t) {
297 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
305 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
306 Transfer *t = userdata;
307 bool success = false;
312 if (si->si_code == CLD_EXITED) {
313 if (si->si_status != 0)
314 log_error("Import process failed with exit code %i.", si->si_status);
316 log_debug("Import process succeeded.");
320 } else if (si->si_code == CLD_KILLED ||
321 si->si_code == CLD_DUMPED)
323 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
325 log_error("Import process failed due to unknown reason.");
329 return transfer_finalize(t, success);
332 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
333 Transfer *t = userdata;
339 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
341 /* EOF/read error. We just close the pipe here, and
342 * close the watch, waiting for the SIGCHLD to arrive,
343 * before we do anything else. */
346 log_error_errno(errno, "Failed to read log message: %m");
348 t->log_event_source = sd_event_source_unref(t->log_event_source);
352 t->log_message_size += l;
354 transfer_send_logs(t, false);
359 static int transfer_start(Transfer *t) {
360 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
366 if (pipe2(pipefd, O_CLOEXEC) < 0)
373 const char *cmd[] = {
374 NULL, /* systemd-import, systemd-export or systemd-pull */
375 NULL, /* tar, raw, dkr */
376 NULL, /* --verify= */
377 NULL, /* verify argument */
378 NULL, /* maybe --force */
379 NULL, /* maybe --read-only */
380 NULL, /* maybe --dkr-index-url */
381 NULL, /* if so: the actual URL */
382 NULL, /* maybe --format= */
383 NULL, /* if so: the actual format */
392 reset_all_signal_handlers();
394 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
396 pipefd[0] = safe_close(pipefd[0]);
398 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
399 log_error_errno(errno, "Failed to dup2() fd: %m");
403 if (t->stdout_fd >= 0) {
404 if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) {
405 log_error_errno(errno, "Failed to dup2() fd: %m");
409 if (t->stdout_fd != STDOUT_FILENO)
410 safe_close(t->stdout_fd);
412 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
413 log_error_errno(errno, "Failed to dup2() fd: %m");
418 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
419 pipefd[1] = safe_close(pipefd[1]);
421 if (t->stdin_fd >= 0) {
422 if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) {
423 log_error_errno(errno, "Failed to dup2() fd: %m");
427 if (t->stdin_fd != STDIN_FILENO)
428 safe_close(t->stdin_fd);
432 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
434 log_error_errno(errno, "Failed to open /dev/null: %m");
438 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
439 log_error_errno(errno, "Failed to dup2() fd: %m");
443 if (null_fd != STDIN_FILENO)
447 fd_cloexec(STDIN_FILENO, false);
448 fd_cloexec(STDOUT_FILENO, false);
449 fd_cloexec(STDERR_FILENO, false);
451 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
452 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
454 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
455 cmd[k++] = SYSTEMD_IMPORT_PATH;
456 else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
457 cmd[k++] = SYSTEMD_EXPORT_PATH;
459 cmd[k++] = SYSTEMD_PULL_PATH;
461 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
463 else if (IN_SET(t->type, TRANSFER_IMPORT_RAW, TRANSFER_EXPORT_RAW, TRANSFER_PULL_RAW))
468 if (t->verify != _IMPORT_VERIFY_INVALID) {
469 cmd[k++] = "--verify";
470 cmd[k++] = import_verify_to_string(t->verify);
474 cmd[k++] = "--force";
476 cmd[k++] = "--read-only";
478 if (t->dkr_index_url) {
479 cmd[k++] = "--dkr-index-url";
480 cmd[k++] = t->dkr_index_url;
484 cmd[k++] = "--format";
485 cmd[k++] = t->format;
488 if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) {
490 cmd[k++] = t->remote;
499 execv(cmd[0], (char * const *) cmd);
500 log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]);
504 pipefd[1] = safe_close(pipefd[1]);
505 t->log_fd = pipefd[0];
508 t->stdin_fd = safe_close(t->stdin_fd);
510 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
514 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
518 /* Make sure always process logging before SIGCHLD */
519 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
523 r = sd_bus_emit_signal(
525 "/org/freedesktop/import1",
526 "org.freedesktop.import1.Manager",
537 static Manager *manager_unref(Manager *m) {
543 sd_event_source_unref(m->notify_event_source);
544 safe_close(m->notify_fd);
546 while ((t = hashmap_first(m->transfers)))
549 hashmap_free(m->transfers);
551 bus_verify_polkit_async_registry_free(m->polkit_registry);
553 sd_bus_close(m->bus);
554 sd_bus_unref(m->bus);
555 sd_event_unref(m->event);
561 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
563 static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
565 char buf[NOTIFY_BUFFER_MAX+1];
566 struct iovec iovec = {
568 .iov_len = sizeof(buf)-1,
571 struct cmsghdr cmsghdr;
572 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
573 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
575 struct msghdr msghdr = {
578 .msg_control = &control,
579 .msg_controllen = sizeof(control),
581 struct ucred *ucred = NULL;
582 Manager *m = userdata;
583 struct cmsghdr *cmsg;
591 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
593 if (errno == EAGAIN || errno == EINTR)
599 cmsg_close_all(&msghdr);
601 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
602 if (cmsg->cmsg_level == SOL_SOCKET &&
603 cmsg->cmsg_type == SCM_CREDENTIALS &&
604 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
606 ucred = (struct ucred*) CMSG_DATA(cmsg);
610 if (msghdr.msg_flags & MSG_TRUNC) {
611 log_warning("Got overly long notification datagram, ignoring.");
615 if (!ucred || ucred->pid <= 0) {
616 log_warning("Got notification datagram lacking credential information, ignoring.");
620 HASHMAP_FOREACH(t, m->transfers, i)
621 if (ucred->pid == t->pid)
625 log_warning("Got notification datagram from unexpected peer, ignoring.");
631 p = startswith(buf, "X_IMPORT_PROGRESS=");
633 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
640 e = strchrnul(p, '\n');
643 r = safe_atou(p, &percent);
644 if (r < 0 || percent > 100) {
645 log_warning("Got invalid percent value, ignoring.");
649 t->progress_percent = percent;
651 log_debug("Got percentage from client: %u%%", percent);
655 static int manager_new(Manager **ret) {
656 _cleanup_(manager_unrefp) Manager *m = NULL;
657 static const union sockaddr_union sa = {
658 .un.sun_family = AF_UNIX,
659 .un.sun_path = "/run/systemd/import/notify",
661 static const int one = 1;
666 m = new0(Manager, 1);
670 r = sd_event_default(&m->event);
674 sd_event_set_watchdog(m->event, true);
676 r = sd_bus_default_system(&m->bus);
680 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
681 if (m->notify_fd < 0)
684 (void) mkdir_parents_label(sa.un.sun_path, 0755);
685 (void) unlink(sa.un.sun_path);
687 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
690 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
693 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
703 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
709 assert(type < _TRANSFER_TYPE_MAX);
711 HASHMAP_FOREACH(t, m->transfers, i) {
713 if (t->type == type &&
714 streq_ptr(t->remote, remote) &&
715 streq_ptr(t->dkr_index_url, dkr_index_url))
722 static int method_import_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
723 _cleanup_(transfer_unrefp) Transfer *t = NULL;
724 int fd, force, read_only, r;
725 const char *local, *object;
726 Manager *m = userdata;
730 r = bus_verify_polkit_async(
733 "org.freedesktop.import1.import",
741 return 1; /* Will call us back */
743 r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
747 if (!machine_name_is_valid(local))
748 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
750 r = setup_machine_directory((uint64_t) -1, error);
754 type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
756 r = transfer_new(m, &t);
761 t->force_local = force;
762 t->read_only = read_only;
764 t->local = strdup(local);
768 t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
772 r = transfer_start(t);
776 object = t->object_path;
780 return sd_bus_reply_method_return(msg, "uo", id, object);
783 static int method_export_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
784 _cleanup_(transfer_unrefp) Transfer *t = NULL;
786 const char *local, *object, *format;
787 Manager *m = userdata;
791 r = bus_verify_polkit_async(
794 "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 *bus, 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;
857 r = bus_verify_polkit_async(
860 "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, NULL, 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_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
928 _cleanup_(transfer_unrefp) Transfer *t = NULL;
929 const char *index_url, *remote, *tag, *local, *verify, *object;
930 Manager *m = userdata;
939 r = bus_verify_polkit_async(
942 "org.freedesktop.import1.pull",
950 return 1; /* Will call us back */
952 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
956 if (isempty(index_url))
957 index_url = DEFAULT_DKR_INDEX_URL;
959 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
960 if (!http_url_is_valid(index_url))
961 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
963 if (!dkr_name_is_valid(remote))
964 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
968 else if (!dkr_tag_is_valid(tag))
969 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
973 else if (!machine_name_is_valid(local))
974 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
977 v = IMPORT_VERIFY_SIGNATURE;
979 v = import_verify_from_string(verify);
981 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
983 if (v != IMPORT_VERIFY_NO)
984 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
986 r = setup_machine_directory((uint64_t) -1, error);
990 if (manager_find(m, TRANSFER_PULL_DKR, index_url, remote))
991 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
993 r = transfer_new(m, &t);
997 t->type = TRANSFER_PULL_DKR;
999 t->force_local = force;
1001 t->dkr_index_url = strdup(index_url);
1002 if (!t->dkr_index_url)
1005 t->remote = strjoin(remote, ":", tag, NULL);
1010 t->local = strdup(local);
1015 r = transfer_start(t);
1019 object = t->object_path;
1023 return sd_bus_reply_method_return(msg, "uo", id, object);
1026 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1027 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1028 Manager *m = userdata;
1037 r = sd_bus_message_new_method_return(msg, &reply);
1041 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
1045 HASHMAP_FOREACH(t, m->transfers, i) {
1047 r = sd_bus_message_append(
1051 transfer_type_to_string(t->type),
1054 (double) t->progress_percent / 100.0,
1060 r = sd_bus_message_close_container(reply);
1064 return sd_bus_send(bus, reply, NULL);
1067 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1068 Transfer *t = userdata;
1075 r = bus_verify_polkit_async(
1078 "org.freedesktop.import1.pull",
1081 &t->manager->polkit_registry,
1086 return 1; /* Will call us back */
1088 r = transfer_cancel(t);
1092 return sd_bus_reply_method_return(msg, NULL);
1095 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1096 Manager *m = userdata;
1105 r = bus_verify_polkit_async(
1108 "org.freedesktop.import1.pull",
1111 &m->polkit_registry,
1116 return 1; /* Will call us back */
1118 r = sd_bus_message_read(msg, "u", &id);
1122 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
1124 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1126 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
1128 r = transfer_cancel(t);
1132 return sd_bus_reply_method_return(msg, NULL);
1135 static int property_get_progress(
1138 const char *interface,
1139 const char *property,
1140 sd_bus_message *reply,
1142 sd_bus_error *error) {
1144 Transfer *t = userdata;
1150 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
1153 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1154 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1156 static const sd_bus_vtable transfer_vtable[] = {
1157 SD_BUS_VTABLE_START(0),
1158 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1159 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1160 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1161 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1162 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
1163 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
1164 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
1165 SD_BUS_SIGNAL("LogMessage", "us", 0),
1169 static const sd_bus_vtable manager_vtable[] = {
1170 SD_BUS_VTABLE_START(0),
1171 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1172 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1173 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1174 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1175 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1176 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1177 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
1178 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
1179 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
1180 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1181 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1185 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1186 Manager *m = userdata;
1198 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1202 r = safe_atou32(p, &id);
1203 if (r < 0 || id == 0)
1206 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1214 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1215 _cleanup_strv_free_ char **l = NULL;
1216 Manager *m = userdata;
1221 l = new0(char*, hashmap_size(m->transfers) + 1);
1225 HASHMAP_FOREACH(t, m->transfers, i) {
1227 l[k] = strdup(t->object_path);
1240 static int manager_add_bus_objects(Manager *m) {
1245 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1247 return log_error_errno(r, "Failed to register object: %m");
1249 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1251 return log_error_errno(r, "Failed to register object: %m");
1253 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1255 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1257 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1259 return log_error_errno(r, "Failed to register name: %m");
1261 r = sd_bus_attach_event(m->bus, m->event, 0);
1263 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1268 static bool manager_check_idle(void *userdata) {
1269 Manager *m = userdata;
1271 return hashmap_isempty(m->transfers);
1274 static int manager_run(Manager *m) {
1277 return bus_event_loop_with_idle(
1280 "org.freedesktop.import1",
1286 int main(int argc, char *argv[]) {
1287 _cleanup_(manager_unrefp) Manager *m = NULL;
1290 log_set_target(LOG_TARGET_AUTO);
1291 log_parse_environment();
1297 log_error("This program takes no arguments.");
1302 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1304 r = manager_new(&m);
1306 log_error_errno(r, "Failed to allocate manager object: %m");
1310 r = manager_add_bus_objects(m);
1316 log_error_errno(r, "Failed to run event loop: %m");
1321 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;