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>
24 #include <sys/statvfs.h>
25 #include <sys/mount.h>
31 #include "bus-common-errors.h"
33 #include "socket-util.h"
37 #include "btrfs-util.h"
38 #include "path-util.h"
39 #include "import-util.h"
41 #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
42 #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
44 typedef struct Transfer Transfer;
45 typedef struct Manager Manager;
47 typedef enum TransferType {
52 _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;
88 uint32_t current_transfer_id;
91 Hashmap *polkit_registry;
95 sd_event_source *notify_event_source;
98 #define TRANSFERS_MAX 64
100 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
101 [TRANSFER_TAR] = "tar",
102 [TRANSFER_RAW] = "raw",
103 [TRANSFER_DKR] = "dkr",
106 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
108 static Transfer *transfer_unref(Transfer *t) {
113 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
115 sd_event_source_unref(t->pid_event_source);
116 sd_event_source_unref(t->log_event_source);
120 free(t->dkr_index_url);
121 free(t->object_path);
124 (void) kill_and_sigcont(t->pid, SIGKILL);
125 (void) wait_for_terminate(t->pid, NULL);
128 safe_close(t->log_fd);
134 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
136 static int transfer_new(Manager *m, Transfer **ret) {
137 _cleanup_(transfer_unrefp) Transfer *t = NULL;
144 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
147 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
151 t = new0(Transfer, 1);
155 t->type = _TRANSFER_TYPE_INVALID;
158 id = m->current_transfer_id + 1;
160 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
163 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
167 m->current_transfer_id = id;
178 static void transfer_send_log_line(Transfer *t, const char *line) {
179 int r, priority = LOG_INFO;
184 syslog_parse_priority(&line, &priority, true);
186 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
188 r = sd_bus_emit_signal(
191 "org.freedesktop.import1.Transfer",
197 log_error_errno(r, "Cannot emit message: %m");
200 static void transfer_send_logs(Transfer *t, bool flush) {
203 /* Try to send out all log messages, if we can. But if we
204 * can't we remove the messages from the buffer, but don't
207 while (t->log_message_size > 0) {
208 _cleanup_free_ char *n = NULL;
211 if (t->log_message_size >= sizeof(t->log_message))
212 e = t->log_message + sizeof(t->log_message);
216 a = memchr(t->log_message, 0, t->log_message_size);
217 b = memchr(t->log_message, '\n', t->log_message_size);
231 e = t->log_message + t->log_message_size;
234 n = strndup(t->log_message, e - t->log_message);
236 /* Skip over NUL and newlines */
237 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
240 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
241 t->log_message_size -= e - t->log_message;
251 transfer_send_log_line(t, n);
255 static int transfer_finalize(Transfer *t, bool success) {
260 transfer_send_logs(t, true);
262 r = sd_bus_emit_signal(
264 "/org/freedesktop/import1",
265 "org.freedesktop.import1.Manager",
271 t->n_canceled > 0 ? "canceled" : "failed");
274 log_error_errno(r, "Cannot emit message: %m");
280 static int transfer_cancel(Transfer *t) {
285 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
293 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
294 Transfer *t = userdata;
295 bool success = false;
300 if (si->si_code == CLD_EXITED) {
301 if (si->si_status != 0)
302 log_error("Import process failed with exit code %i.", si->si_status);
304 log_debug("Import process succeeded.");
308 } else if (si->si_code == CLD_KILLED ||
309 si->si_code == CLD_DUMPED)
311 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
313 log_error("Import process failed due to unknown reason.");
317 return transfer_finalize(t, success);
320 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
321 Transfer *t = userdata;
327 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
329 /* EOF/read error. We just close the pipe here, and
330 * close the watch, waiting for the SIGCHLD to arrive,
331 * before we do anything else. */
334 log_error_errno(errno, "Failed to read log message: %m");
336 t->log_event_source = sd_event_source_unref(t->log_event_source);
340 t->log_message_size += l;
342 transfer_send_logs(t, false);
347 static int transfer_start(Transfer *t) {
348 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
354 if (pipe2(pipefd, O_CLOEXEC) < 0)
361 const char *cmd[] = {
363 transfer_type_to_string(t->type),
365 NULL, /* verify argument */
366 NULL, /* maybe --force */
367 NULL, /* maybe --dkr-index-url */
368 NULL, /* the actual URL */
378 reset_all_signal_handlers();
380 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
382 pipefd[0] = safe_close(pipefd[0]);
384 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
385 log_error_errno(errno, "Failed to dup2() fd: %m");
389 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
390 log_error_errno(errno, "Failed to dup2() fd: %m");
394 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
395 pipefd[1] = safe_close(pipefd[1]);
397 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
399 log_error_errno(errno, "Failed to open /dev/null: %m");
403 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
404 log_error_errno(errno, "Failed to dup2() fd: %m");
408 if (null_fd != STDIN_FILENO)
411 fd_cloexec(STDIN_FILENO, false);
412 fd_cloexec(STDOUT_FILENO, false);
413 fd_cloexec(STDERR_FILENO, false);
415 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
416 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
418 cmd[k++] = import_verify_to_string(t->verify);
420 cmd[k++] = "--force";
422 if (t->dkr_index_url) {
423 cmd[k++] = "--dkr-index-url";
424 cmd[k++] = t->dkr_index_url;
427 cmd[k++] = t->remote;
432 execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
433 log_error_errno(errno, "Failed to execute import tool: %m");
437 pipefd[1] = safe_close(pipefd[1]);
438 t->log_fd = pipefd[0];
441 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
445 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
449 /* Make sure always process logging before SIGCHLD */
450 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
454 r = sd_bus_emit_signal(
456 "/org/freedesktop/import1",
457 "org.freedesktop.import1.Manager",
468 static Manager *manager_unref(Manager *m) {
474 sd_event_source_unref(m->notify_event_source);
475 safe_close(m->notify_fd);
477 while ((t = hashmap_first(m->transfers)))
480 hashmap_free(m->transfers);
482 bus_verify_polkit_async_registry_free(m->polkit_registry);
484 sd_bus_close(m->bus);
485 sd_bus_unref(m->bus);
486 sd_event_unref(m->event);
492 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
494 static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
496 char buf[NOTIFY_BUFFER_MAX+1];
497 struct iovec iovec = {
499 .iov_len = sizeof(buf)-1,
502 struct cmsghdr cmsghdr;
503 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
504 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
506 struct msghdr msghdr = {
509 .msg_control = &control,
510 .msg_controllen = sizeof(control),
512 struct ucred *ucred = NULL;
513 Manager *m = userdata;
514 struct cmsghdr *cmsg;
522 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
524 if (errno == EAGAIN || errno == EINTR)
530 cmsg_close_all(&msghdr);
532 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
533 if (cmsg->cmsg_level == SOL_SOCKET &&
534 cmsg->cmsg_type == SCM_CREDENTIALS &&
535 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
537 ucred = (struct ucred*) CMSG_DATA(cmsg);
541 if (msghdr.msg_flags & MSG_TRUNC) {
542 log_warning("Got overly long notification datagram, ignoring.");
546 if (!ucred || ucred->pid <= 0) {
547 log_warning("Got notification datagram lacking credential information, ignoring.");
551 HASHMAP_FOREACH(t, m->transfers, i)
552 if (ucred->pid == t->pid)
556 log_warning("Got notification datagram from unexpected peer, ignoring.");
562 p = startswith(buf, "X_IMPORT_PROGRESS=");
564 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
571 e = strchrnul(p, '\n');
574 r = safe_atou(p, &percent);
575 if (r < 0 || percent > 100) {
576 log_warning("Got invalid percent value, ignoring.");
580 t->progress_percent = percent;
582 log_debug("Got percentage from client: %u%%", percent);
586 static int manager_new(Manager **ret) {
587 _cleanup_(manager_unrefp) Manager *m = NULL;
588 static const union sockaddr_union sa = {
589 .un.sun_family = AF_UNIX,
590 .un.sun_path = "/run/systemd/import/notify",
592 static const int one = 1;
597 m = new0(Manager, 1);
601 r = sd_event_default(&m->event);
605 sd_event_set_watchdog(m->event, true);
607 r = sd_bus_default_system(&m->bus);
611 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
612 if (m->notify_fd < 0)
615 (void) mkdir_parents_label(sa.un.sun_path, 0755);
616 (void) unlink(sa.un.sun_path);
618 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
621 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
624 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
634 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
640 assert(type < _TRANSFER_TYPE_MAX);
642 HASHMAP_FOREACH(t, m->transfers, i) {
644 if (t->type == type &&
645 streq_ptr(t->remote, remote) &&
646 streq_ptr(t->dkr_index_url, dkr_index_url))
653 static int check_btrfs(void) {
656 if (statfs("/var/lib/machines", &sfs) < 0) {
660 if (statfs("/var/lib", &sfs) < 0)
664 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
667 static int setup_machine_raw(sd_bus_error *error) {
668 _cleanup_free_ char *tmp = NULL;
669 _cleanup_close_ int fd = -1;
675 /* We want to be able to make use of btrfs-specific file
676 * system features, in particular subvolumes, reflinks and
677 * quota. Hence, if we detect that /var/lib/machines.raw is
678 * not located on btrfs, let's create a loopback file, place a
679 * btrfs file system into it, and mount it to
680 * /var/lib/machines. */
682 fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
690 return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
692 r = tempfn_xxxxxx("/var/lib/machines.raw", &tmp);
696 (void) mkdir_p_label("/var/lib", 0755);
697 fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
699 return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
701 if (fstatvfs(fd, &ss) < 0) {
702 r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
706 if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
707 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
711 if (ftruncate(fd, VAR_LIB_MACHINES_SIZE_START) < 0) {
712 r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
718 r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m");
726 reset_all_signal_handlers();
728 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
732 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
739 r = wait_for_terminate(pid, &si);
741 sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
747 if (si.si_code != CLD_EXITED) {
748 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally.");
751 if (si.si_status == 99) {
752 r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
755 if (si.si_status != 0) {
756 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status);
760 if (renameat2(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw", RENAME_NOREPLACE) < 0) {
761 r = sd_bus_error_set_errnof(error, errno, "Failed to move /var/lib/machines.raw into place: %m");
775 kill_and_sigcont(pid, SIGKILL);
780 static int setup_machine_directory(sd_bus_error *error) {
781 struct loop_info64 info = {
782 .lo_flags = LO_FLAGS_AUTOCLEAR,
784 _cleanup_close_ int fd = -1, control = -1, loop = -1;
785 _cleanup_free_ char* loopdev = NULL;
786 char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL;
787 bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
792 return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
794 (void) btrfs_subvol_make_label("/var/lib/machines");
796 r = btrfs_quota_enable("/var/lib/machines", true);
798 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
803 if (path_is_mount_point("/var/lib/machines", true) > 0 ||
804 dir_is_empty("/var/lib/machines") == 0)
805 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
807 fd = setup_machine_raw(error);
811 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
813 return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
815 nr = ioctl(control, LOOP_CTL_GET_FREE);
817 return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
819 if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
824 loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
826 r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
830 if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
831 r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
835 if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
836 r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
840 /* We need to make sure the new /var/lib/machines directory
841 * has an access mode of 0700 at the time it is first made
842 * available. mkfs will create it with 0755 however. Hence,
843 * let's mount the directory into an inaccessible directory
844 * below /tmp first, fix the access mode, and move it to the
845 * public place then. */
847 if (!mkdtemp(tmpdir)) {
848 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
853 mntdir = strjoina(tmpdir, "/mnt");
854 if (mkdir(mntdir, 0700) < 0) {
855 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
860 if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
861 r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
864 mntdir_mounted = true;
866 r = btrfs_quota_enable(mntdir, true);
868 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
870 if (chmod(mntdir, 0700) < 0) {
871 r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
875 (void) mkdir_p_label("/var/lib/machines", 0700);
877 if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
878 r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
882 (void) umount2(mntdir, MNT_DETACH);
883 (void) rmdir(mntdir);
884 (void) rmdir(tmpdir);
890 (void) umount2(mntdir, MNT_DETACH);
893 (void) rmdir(mntdir);
895 (void) rmdir(tmpdir);
898 (void) ioctl(loop, LOOP_CLR_FD);
899 loop = safe_close(loop);
902 if (control >= 0 && nr >= 0)
903 (void) ioctl(control, LOOP_CTL_REMOVE, nr);
908 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
909 _cleanup_(transfer_unrefp) Transfer *t = NULL;
910 const char *remote, *local, *verify, *object;
911 Manager *m = userdata;
921 r = bus_verify_polkit_async(
924 "org.freedesktop.import1.pull",
932 return 1; /* Will call us back */
934 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
938 if (!http_url_is_valid(remote))
939 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
943 else if (!machine_name_is_valid(local))
944 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
947 v = IMPORT_VERIFY_SIGNATURE;
949 v = import_verify_from_string(verify);
951 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
953 r = setup_machine_directory(error);
957 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
959 if (manager_find(m, type, NULL, remote))
960 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
962 r = transfer_new(m, &t);
968 t->force_local = force;
970 t->remote = strdup(remote);
974 t->local = strdup(local);
978 r = transfer_start(t);
982 object = t->object_path;
986 return sd_bus_reply_method_return(msg, "uo", id, object);
989 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
990 _cleanup_(transfer_unrefp) Transfer *t = NULL;
991 const char *index_url, *remote, *tag, *local, *verify, *object;
992 Manager *m = userdata;
1001 r = bus_verify_polkit_async(
1004 "org.freedesktop.import1.pull",
1007 &m->polkit_registry,
1012 return 1; /* Will call us back */
1014 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
1018 if (isempty(index_url))
1019 index_url = DEFAULT_DKR_INDEX_URL;
1021 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
1022 if (!http_url_is_valid(index_url))
1023 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
1025 if (!dkr_name_is_valid(remote))
1026 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
1030 else if (!dkr_tag_is_valid(tag))
1031 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
1035 else if (!machine_name_is_valid(local))
1036 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
1038 if (isempty(verify))
1039 v = IMPORT_VERIFY_SIGNATURE;
1041 v = import_verify_from_string(verify);
1043 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
1045 if (v != IMPORT_VERIFY_NO)
1046 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
1048 r = setup_machine_directory(error);
1052 if (manager_find(m, TRANSFER_DKR, index_url, remote))
1053 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
1055 r = transfer_new(m, &t);
1059 t->type = TRANSFER_DKR;
1061 t->force_local = force;
1063 t->dkr_index_url = strdup(index_url);
1064 if (!t->dkr_index_url)
1067 t->remote = strjoin(remote, ":", tag, NULL);
1071 t->local = strdup(local);
1075 r = transfer_start(t);
1079 object = t->object_path;
1083 return sd_bus_reply_method_return(msg, "uo", id, object);
1086 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1087 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1088 Manager *m = userdata;
1097 r = sd_bus_message_new_method_return(msg, &reply);
1101 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
1105 HASHMAP_FOREACH(t, m->transfers, i) {
1107 r = sd_bus_message_append(
1111 transfer_type_to_string(t->type),
1114 (double) t->progress_percent / 100.0,
1120 r = sd_bus_message_close_container(reply);
1124 return sd_bus_send(bus, reply, NULL);
1127 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1128 Transfer *t = userdata;
1135 r = bus_verify_polkit_async(
1138 "org.freedesktop.import1.pull",
1141 &t->manager->polkit_registry,
1146 return 1; /* Will call us back */
1148 r = transfer_cancel(t);
1152 return sd_bus_reply_method_return(msg, NULL);
1155 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1156 Manager *m = userdata;
1165 r = bus_verify_polkit_async(
1168 "org.freedesktop.import1.pull",
1171 &m->polkit_registry,
1176 return 1; /* Will call us back */
1178 r = sd_bus_message_read(msg, "u", &id);
1182 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
1184 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1186 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
1188 r = transfer_cancel(t);
1192 return sd_bus_reply_method_return(msg, NULL);
1195 static int property_get_progress(
1198 const char *interface,
1199 const char *property,
1200 sd_bus_message *reply,
1202 sd_bus_error *error) {
1204 Transfer *t = userdata;
1210 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
1213 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1214 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1216 static const sd_bus_vtable transfer_vtable[] = {
1217 SD_BUS_VTABLE_START(0),
1218 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1219 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1220 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1221 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1222 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
1223 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
1224 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
1225 SD_BUS_SIGNAL("LogMessage", "us", 0),
1229 static const sd_bus_vtable manager_vtable[] = {
1230 SD_BUS_VTABLE_START(0),
1231 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1232 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1233 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
1234 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
1235 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
1236 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1237 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1241 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1242 Manager *m = userdata;
1254 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1258 r = safe_atou32(p, &id);
1259 if (r < 0 || id == 0)
1262 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1270 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1271 _cleanup_strv_free_ char **l = NULL;
1272 Manager *m = userdata;
1277 l = new0(char*, hashmap_size(m->transfers) + 1);
1281 HASHMAP_FOREACH(t, m->transfers, i) {
1283 l[k] = strdup(t->object_path);
1296 static int manager_add_bus_objects(Manager *m) {
1301 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1303 return log_error_errno(r, "Failed to register object: %m");
1305 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1307 return log_error_errno(r, "Failed to register object: %m");
1309 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1311 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1313 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1315 return log_error_errno(r, "Failed to register name: %m");
1317 r = sd_bus_attach_event(m->bus, m->event, 0);
1319 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1324 static bool manager_check_idle(void *userdata) {
1325 Manager *m = userdata;
1327 return hashmap_isempty(m->transfers);
1330 static int manager_run(Manager *m) {
1333 return bus_event_loop_with_idle(
1336 "org.freedesktop.import1",
1342 int main(int argc, char *argv[]) {
1343 _cleanup_(manager_unrefp) Manager *m = NULL;
1346 log_set_target(LOG_TARGET_AUTO);
1347 log_parse_environment();
1353 log_error("This program takes no arguments.");
1358 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1360 r = manager_new(&m);
1362 log_error_errno(r, "Failed to allocate manager object: %m");
1366 r = manager_add_bus_objects(m);
1372 log_error_errno(r, "Failed to run event loop: %m");
1377 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;