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");
798 if (path_is_mount_point("/var/lib/machines", true) > 0 ||
799 dir_is_empty("/var/lib/machines") == 0)
800 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.");
802 fd = setup_machine_raw(error);
806 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
808 return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
810 nr = ioctl(control, LOOP_CTL_GET_FREE);
812 return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
814 if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
819 loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
821 r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
825 if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
826 r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
830 if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
831 r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
835 /* We need to make sure the new /var/lib/machines directory
836 * has an access mode of 0700 at the time it is first made
837 * available. mkfs will create it with 0755 however. Hence,
838 * let's mount the directory into an inaccessible directory
839 * below /tmp first, fix the access mode, and move it to the
840 * public place then. */
842 if (!mkdtemp(tmpdir)) {
843 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
848 mntdir = strjoina(tmpdir, "/mnt");
849 if (mkdir(mntdir, 0700) < 0) {
850 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
855 if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
856 r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
859 mntdir_mounted = true;
861 if (chmod(mntdir, 0700) < 0) {
862 r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
866 (void) mkdir_p_label("/var/lib/machines", 0700);
868 if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
869 r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
873 (void) umount2(mntdir, MNT_DETACH);
874 (void) rmdir(mntdir);
875 (void) rmdir(tmpdir);
881 (void) umount2(mntdir, MNT_DETACH);
884 (void) rmdir(mntdir);
886 (void) rmdir(tmpdir);
889 (void) ioctl(loop, LOOP_CLR_FD);
890 loop = safe_close(loop);
893 if (control >= 0 && nr >= 0)
894 (void) ioctl(control, LOOP_CTL_REMOVE, nr);
899 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
900 _cleanup_(transfer_unrefp) Transfer *t = NULL;
901 const char *remote, *local, *verify, *object;
902 Manager *m = userdata;
912 r = bus_verify_polkit_async(
915 "org.freedesktop.import1.pull",
923 return 1; /* Will call us back */
925 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
929 if (!http_url_is_valid(remote))
930 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
934 else if (!machine_name_is_valid(local))
935 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
938 v = IMPORT_VERIFY_SIGNATURE;
940 v = import_verify_from_string(verify);
942 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
944 r = setup_machine_directory(error);
948 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
950 if (manager_find(m, type, NULL, remote))
951 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
953 r = transfer_new(m, &t);
959 t->force_local = force;
961 t->remote = strdup(remote);
965 t->local = strdup(local);
969 r = transfer_start(t);
973 object = t->object_path;
977 return sd_bus_reply_method_return(msg, "uo", id, object);
980 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
981 _cleanup_(transfer_unrefp) Transfer *t = NULL;
982 const char *index_url, *remote, *tag, *local, *verify, *object;
983 Manager *m = userdata;
992 r = bus_verify_polkit_async(
995 "org.freedesktop.import1.pull",
1003 return 1; /* Will call us back */
1005 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
1009 if (isempty(index_url))
1010 index_url = DEFAULT_DKR_INDEX_URL;
1012 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
1013 if (!http_url_is_valid(index_url))
1014 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
1016 if (!dkr_name_is_valid(remote))
1017 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
1021 else if (!dkr_tag_is_valid(tag))
1022 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
1026 else if (!machine_name_is_valid(local))
1027 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
1029 if (isempty(verify))
1030 v = IMPORT_VERIFY_SIGNATURE;
1032 v = import_verify_from_string(verify);
1034 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
1036 if (v != IMPORT_VERIFY_NO)
1037 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
1039 r = setup_machine_directory(error);
1043 if (manager_find(m, TRANSFER_DKR, index_url, remote))
1044 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
1046 r = transfer_new(m, &t);
1050 t->type = TRANSFER_DKR;
1052 t->force_local = force;
1054 t->dkr_index_url = strdup(index_url);
1055 if (!t->dkr_index_url)
1058 t->remote = strjoin(remote, ":", tag, NULL);
1062 t->local = strdup(local);
1066 r = transfer_start(t);
1070 object = t->object_path;
1074 return sd_bus_reply_method_return(msg, "uo", id, object);
1077 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1078 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1079 Manager *m = userdata;
1088 r = sd_bus_message_new_method_return(msg, &reply);
1092 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
1096 HASHMAP_FOREACH(t, m->transfers, i) {
1098 r = sd_bus_message_append(
1102 transfer_type_to_string(t->type),
1105 (double) t->progress_percent / 100.0,
1111 r = sd_bus_message_close_container(reply);
1115 return sd_bus_send(bus, reply, NULL);
1118 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1119 Transfer *t = userdata;
1126 r = bus_verify_polkit_async(
1129 "org.freedesktop.import1.pull",
1132 &t->manager->polkit_registry,
1137 return 1; /* Will call us back */
1139 r = transfer_cancel(t);
1143 return sd_bus_reply_method_return(msg, NULL);
1146 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1147 Manager *m = userdata;
1156 r = bus_verify_polkit_async(
1159 "org.freedesktop.import1.pull",
1162 &m->polkit_registry,
1167 return 1; /* Will call us back */
1169 r = sd_bus_message_read(msg, "u", &id);
1173 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
1175 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1177 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
1179 r = transfer_cancel(t);
1183 return sd_bus_reply_method_return(msg, NULL);
1186 static int property_get_progress(
1189 const char *interface,
1190 const char *property,
1191 sd_bus_message *reply,
1193 sd_bus_error *error) {
1195 Transfer *t = userdata;
1201 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
1204 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1205 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1207 static const sd_bus_vtable transfer_vtable[] = {
1208 SD_BUS_VTABLE_START(0),
1209 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1210 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1211 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1212 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1213 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
1214 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
1215 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
1216 SD_BUS_SIGNAL("LogMessage", "us", 0),
1220 static const sd_bus_vtable manager_vtable[] = {
1221 SD_BUS_VTABLE_START(0),
1222 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1223 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1224 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
1225 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
1226 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
1227 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1228 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1232 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1233 Manager *m = userdata;
1245 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1249 r = safe_atou32(p, &id);
1250 if (r < 0 || id == 0)
1253 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1261 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1262 _cleanup_strv_free_ char **l = NULL;
1263 Manager *m = userdata;
1268 l = new0(char*, hashmap_size(m->transfers) + 1);
1272 HASHMAP_FOREACH(t, m->transfers, i) {
1274 l[k] = strdup(t->object_path);
1287 static int manager_add_bus_objects(Manager *m) {
1292 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1294 return log_error_errno(r, "Failed to register object: %m");
1296 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1298 return log_error_errno(r, "Failed to register object: %m");
1300 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1302 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1304 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1306 return log_error_errno(r, "Failed to register name: %m");
1308 r = sd_bus_attach_event(m->bus, m->event, 0);
1310 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1315 static bool manager_check_idle(void *userdata) {
1316 Manager *m = userdata;
1318 return hashmap_isempty(m->transfers);
1321 static int manager_run(Manager *m) {
1324 return bus_event_loop_with_idle(
1327 "org.freedesktop.import1",
1333 int main(int argc, char *argv[]) {
1334 _cleanup_(manager_unrefp) Manager *m = NULL;
1337 log_set_target(LOG_TARGET_AUTO);
1338 log_parse_environment();
1344 log_error("This program takes no arguments.");
1349 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1351 r = manager_new(&m);
1353 log_error_errno(r, "Failed to allocate manager object: %m");
1357 r = manager_add_bus_objects(m);
1363 log_error_errno(r, "Failed to run event loop: %m");
1368 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;