]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/importd.c
systemctl: allow interactive authorization for all bus calls
[thirdparty/systemd.git] / src / import / importd.c
CommitLineData
3d7415f4
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <sys/prctl.h>
23
24#include "sd-bus.h"
25#include "util.h"
26#include "strv.h"
27#include "bus-util.h"
28#include "bus-common-errors.h"
29#include "def.h"
7079cfef
LP
30#include "socket-util.h"
31#include "mkdir.h"
3d7415f4 32#include "import-util.h"
a97dcc12 33#include "def.h"
3d7415f4
LP
34
35typedef struct Transfer Transfer;
36typedef struct Manager Manager;
37
38typedef enum TransferType {
39 TRANSFER_TAR,
40 TRANSFER_RAW,
41 TRANSFER_DKR,
42 _TRANSFER_TYPE_MAX,
43 _TRANSFER_TYPE_INVALID = -1,
44} TransferType;
45
46struct Transfer {
47 Manager *manager;
48
49 uint32_t id;
50 char *object_path;
51
52 TransferType type;
53 ImportVerify verify;
54
55 char *remote;
56 char *local;
57 bool force_local;
58
59 char *dkr_index_url;
60
61 pid_t pid;
62
63 int log_fd;
64
65 char log_message[LINE_MAX];
66 size_t log_message_size;
67
68 sd_event_source *pid_event_source;
69 sd_event_source *log_event_source;
70
71 unsigned n_canceled;
7079cfef 72 unsigned progress_percent;
3d7415f4
LP
73};
74
75struct Manager {
76 sd_event *event;
77 sd_bus *bus;
78
79 uint32_t current_transfer_id;
80 Hashmap *transfers;
81
82 Hashmap *polkit_registry;
7079cfef
LP
83
84 int notify_fd;
85
86 sd_event_source *notify_event_source;
3d7415f4
LP
87};
88
89#define TRANSFERS_MAX 64
90
91static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
92 [TRANSFER_TAR] = "tar",
93 [TRANSFER_RAW] = "raw",
94 [TRANSFER_DKR] = "dkr",
95};
96
b9a5f858 97DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
3d7415f4
LP
98
99static Transfer *transfer_unref(Transfer *t) {
100 if (!t)
101 return NULL;
102
103 if (t->manager)
104 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
105
106 sd_event_source_unref(t->pid_event_source);
107 sd_event_source_unref(t->log_event_source);
108
109 free(t->remote);
110 free(t->local);
111 free(t->dkr_index_url);
112 free(t->object_path);
113
114 if (t->pid > 0) {
115 (void) kill_and_sigcont(t->pid, SIGKILL);
116 (void) wait_for_terminate(t->pid, NULL);
117 }
118
119 safe_close(t->log_fd);
120
121 free(t);
122 return NULL;
123}
124
125DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
126
127static int transfer_new(Manager *m, Transfer **ret) {
128 _cleanup_(transfer_unrefp) Transfer *t = NULL;
129 uint32_t id;
130 int r;
131
132 assert(m);
133 assert(ret);
134
135 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
136 return -E2BIG;
137
138 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
139 if (r < 0)
140 return r;
141
142 t = new0(Transfer, 1);
143 if (!t)
144 return -ENOMEM;
145
146 t->type = _TRANSFER_TYPE_INVALID;
147 t->log_fd = -1;
148
149 id = m->current_transfer_id + 1;
150
151 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
152 return -ENOMEM;
153
154 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
155 if (r < 0)
156 return r;
157
158 m->current_transfer_id = id;
159
160 t->manager = m;
161 t->id = id;
162
163 *ret = t;
164 t = NULL;
165
166 return 0;
167}
168
169static void transfer_send_log_line(Transfer *t, const char *line) {
170 int r, priority = LOG_INFO;
171
172 assert(t);
173 assert(line);
174
175 syslog_parse_priority(&line, &priority, true);
176
09d46cfd 177 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
3d7415f4
LP
178
179 r = sd_bus_emit_signal(
180 t->manager->bus,
181 t->object_path,
182 "org.freedesktop.import1.Transfer",
183 "LogMessage",
184 "us",
185 priority,
186 line);
187 if (r < 0)
188 log_error_errno(r, "Cannot emit message: %m");
189 }
190
191static void transfer_send_logs(Transfer *t, bool flush) {
192 assert(t);
193
194 /* Try to send out all log messages, if we can. But if we
195 * can't we remove the messages from the buffer, but don't
196 * fail */
197
198 while (t->log_message_size > 0) {
199 _cleanup_free_ char *n = NULL;
200 char *e;
201
202 if (t->log_message_size >= sizeof(t->log_message))
203 e = t->log_message + sizeof(t->log_message);
204 else {
205 char *a, *b;
206
207 a = memchr(t->log_message, 0, t->log_message_size);
208 b = memchr(t->log_message, '\n', t->log_message_size);
209
210 if (a && b)
211 e = a < b ? a : b;
212 else if (a)
213 e = a;
214 else
215 e = b;
216 }
217
218 if (!e) {
219 if (!flush)
220 return;
221
222 e = t->log_message + t->log_message_size;
223 }
224
225 n = strndup(t->log_message, e - t->log_message);
226
227 /* Skip over NUL and newlines */
228 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
229 e++;
230
231 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
232 t->log_message_size -= e - t->log_message;
233
234 if (!n) {
235 log_oom();
236 continue;
237 }
238
239 if (isempty(n))
240 continue;
241
242 transfer_send_log_line(t, n);
243 }
244}
245
246static int transfer_finalize(Transfer *t, bool success) {
247 int r;
248
249 assert(t);
250
251 transfer_send_logs(t, true);
252
253 r = sd_bus_emit_signal(
254 t->manager->bus,
255 "/org/freedesktop/import1",
256 "org.freedesktop.import1.Manager",
257 "TransferRemoved",
258 "uos",
259 t->id,
260 t->object_path,
261 success ? "done" :
262 t->n_canceled > 0 ? "canceled" : "failed");
263
264 if (r < 0)
265 log_error_errno(r, "Cannot emit message: %m");
266
267 transfer_unref(t);
268 return 0;
269}
270
271static int transfer_cancel(Transfer *t) {
272 int r;
273
274 assert(t);
275
276 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
277 if (r < 0)
278 return r;
279
280 t->n_canceled++;
281 return 0;
282}
283
284static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
285 Transfer *t = userdata;
286 bool success = false;
287
288 assert(s);
289 assert(t);
290
291 if (si->si_code == CLD_EXITED) {
292 if (si->si_status != 0)
293 log_error("Import process failed with exit code %i.", si->si_status);
294 else {
295 log_debug("Import process succeeded.");
296 success = true;
297 }
298
299 } else if (si->si_code == CLD_KILLED ||
300 si->si_code == CLD_DUMPED)
301
302 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
303 else
304 log_error("Import process failed due to unknown reason.");
305
306 t->pid = 0;
307
308 return transfer_finalize(t, success);
309}
310
311static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
312 Transfer *t = userdata;
313 ssize_t l;
314
315 assert(s);
316 assert(t);
317
318 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
319 if (l <= 0) {
320 /* EOF/read error. We just close the pipe here, and
321 * close the watch, waiting for the SIGCHLD to arrive,
322 * before we do anything else. */
323
324 if (l < 0)
325 log_error_errno(errno, "Failed to read log message: %m");
326
327 t->log_event_source = sd_event_source_unref(t->log_event_source);
328 return 0;
329 }
330
331 t->log_message_size += l;
332
333 transfer_send_logs(t, false);
334
335 return 0;
336}
337
338static int transfer_start(Transfer *t) {
339 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
340 int r;
341
342 assert(t);
343 assert(t->pid <= 0);
344
345 if (pipe2(pipefd, O_CLOEXEC) < 0)
346 return -errno;
347
348 t->pid = fork();
349 if (t->pid < 0)
350 return -errno;
351 if (t->pid == 0) {
352 const char *cmd[] = {
ff828763 353 "systemd-pull",
aa9bd499 354 transfer_type_to_string(t->type),
3d7415f4
LP
355 "--verify",
356 NULL, /* verify argument */
357 NULL, /* maybe --force */
358 NULL, /* maybe --dkr-index-url */
359 NULL, /* the actual URL */
360 NULL, /* remote */
361 NULL, /* local */
362 NULL
363 };
364 int null_fd;
365 unsigned k = 3;
366
367 /* Child */
368
369 reset_all_signal_handlers();
370 reset_signal_mask();
371 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
372
373 pipefd[0] = safe_close(pipefd[0]);
374
375 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
376 log_error_errno(errno, "Failed to dup2() fd: %m");
377 _exit(EXIT_FAILURE);
378 }
379
380 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
381 log_error_errno(errno, "Failed to dup2() fd: %m");
382 _exit(EXIT_FAILURE);
383 }
384
385 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
386 pipefd[1] = safe_close(pipefd[1]);
387
388 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
389 if (null_fd < 0) {
390 log_error_errno(errno, "Failed to open /dev/null: %m");
391 _exit(EXIT_FAILURE);
392 }
393
394 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
395 log_error_errno(errno, "Failed to dup2() fd: %m");
396 _exit(EXIT_FAILURE);
397 }
398
399 if (null_fd != STDIN_FILENO)
400 safe_close(null_fd);
401
402 fd_cloexec(STDIN_FILENO, false);
403 fd_cloexec(STDOUT_FILENO, false);
404 fd_cloexec(STDERR_FILENO, false);
405
7079cfef
LP
406 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
407 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
3d7415f4
LP
408
409 cmd[k++] = import_verify_to_string(t->verify);
410 if (t->force_local)
411 cmd[k++] = "--force";
412
413 if (t->dkr_index_url) {
414 cmd[k++] = "--dkr-index-url";
415 cmd[k++] = t->dkr_index_url;
416 }
417
418 cmd[k++] = t->remote;
419 if (t->local)
420 cmd[k++] = t->local;
421 cmd[k] = NULL;
422
ff828763 423 execv(SYSTEMD_PULL_PATH, (char * const *) cmd);
3d7415f4
LP
424 log_error_errno(errno, "Failed to execute import tool: %m");
425 _exit(EXIT_FAILURE);
426 }
427
428 pipefd[1] = safe_close(pipefd[1]);
429 t->log_fd = pipefd[0];
430 pipefd[0] = -1;
431
432 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
433 if (r < 0)
434 return r;
435
436 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
437 if (r < 0)
438 return r;
439
440 /* Make sure always process logging before SIGCHLD */
441 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
442 if (r < 0)
443 return r;
444
445 r = sd_bus_emit_signal(
446 t->manager->bus,
447 "/org/freedesktop/import1",
448 "org.freedesktop.import1.Manager",
449 "TransferNew",
450 "uo",
451 t->id,
452 t->object_path);
453 if (r < 0)
454 return r;
455
456 return 0;
457}
458
459static Manager *manager_unref(Manager *m) {
460 Transfer *t;
461
462 if (!m)
463 return NULL;
464
7079cfef
LP
465 sd_event_source_unref(m->notify_event_source);
466 safe_close(m->notify_fd);
467
3d7415f4
LP
468 while ((t = hashmap_first(m->transfers)))
469 transfer_unref(t);
470
471 hashmap_free(m->transfers);
472
473 bus_verify_polkit_async_registry_free(m->polkit_registry);
474
475 sd_bus_close(m->bus);
476 sd_bus_unref(m->bus);
477 sd_event_unref(m->event);
478
479 free(m);
480 return NULL;
481}
482
483DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
484
7079cfef
LP
485static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
486
487 char buf[NOTIFY_BUFFER_MAX+1];
488 struct iovec iovec = {
489 .iov_base = buf,
490 .iov_len = sizeof(buf)-1,
491 };
492 union {
493 struct cmsghdr cmsghdr;
494 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
495 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
496 } control = {};
497 struct msghdr msghdr = {
498 .msg_iov = &iovec,
499 .msg_iovlen = 1,
500 .msg_control = &control,
501 .msg_controllen = sizeof(control),
502 };
503 struct ucred *ucred = NULL;
504 Manager *m = userdata;
505 struct cmsghdr *cmsg;
506 unsigned percent;
507 char *p, *e;
508 Transfer *t;
509 Iterator i;
510 ssize_t n;
511 int r;
512
513 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
514 if (n < 0) {
515 if (errno == EAGAIN || errno == EINTR)
516 return 0;
517
518 return -errno;
519 }
520
521 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
522 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
523 close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
524 log_warning("Somebody sent us unexpected fds, ignoring.");
525 return 0;
526 } else if (cmsg->cmsg_level == SOL_SOCKET &&
527 cmsg->cmsg_type == SCM_CREDENTIALS &&
528 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
529
530 ucred = (struct ucred*) CMSG_DATA(cmsg);
531 }
532 }
533
534 if (msghdr.msg_flags & MSG_TRUNC) {
535 log_warning("Got overly long notification datagram, ignoring.");
536 return 0;
537 }
538
539 if (!ucred || ucred->pid <= 0) {
540 log_warning("Got notification datagram lacking credential information, ignoring.");
541 return 0;
542 }
543
544 HASHMAP_FOREACH(t, m->transfers, i)
545 if (ucred->pid == t->pid)
546 break;
547
548 if (!t) {
549 log_warning("Got notification datagram from unexpected peer, ignoring.");
550 return 0;
551 }
552
553 buf[n] = 0;
554
555 p = startswith(buf, "X_IMPORT_PROGRESS=");
556 if (!p) {
557 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
558 if (!p)
559 return 0;
560
561 p += 19;
562 }
563
564 e = strchrnul(p, '\n');
565 *e = 0;
566
567 r = safe_atou(p, &percent);
568 if (r < 0 || percent > 100) {
569 log_warning("Got invalid percent value, ignoring.");
570 return 0;
571 }
572
573 t->progress_percent = percent;
574
575 log_debug("Got percentage from client: %u%%", percent);
576 return 0;
577}
578
3d7415f4
LP
579static int manager_new(Manager **ret) {
580 _cleanup_(manager_unrefp) Manager *m = NULL;
7079cfef
LP
581 static const union sockaddr_union sa = {
582 .un.sun_family = AF_UNIX,
583 .un.sun_path = "/run/systemd/import/notify",
584 };
585 static const int one = 1;
3d7415f4
LP
586 int r;
587
588 assert(ret);
589
590 m = new0(Manager, 1);
591 if (!m)
592 return -ENOMEM;
593
594 r = sd_event_default(&m->event);
595 if (r < 0)
596 return r;
597
598 sd_event_set_watchdog(m->event, true);
599
600 r = sd_bus_default_system(&m->bus);
601 if (r < 0)
602 return r;
603
7079cfef
LP
604 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
605 if (m->notify_fd < 0)
606 return -errno;
607
608 (void) mkdir_parents_label(sa.un.sun_path, 0755);
609 (void) unlink(sa.un.sun_path);
610
611 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
612 return -errno;
613
614 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
615 return -errno;
616
617 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
618 if (r < 0)
619 return r;
620
3d7415f4
LP
621 *ret = m;
622 m = NULL;
623
624 return 0;
625}
626
627static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
628 Transfer *t;
629 Iterator i;
630
631 assert(m);
632 assert(type >= 0);
633 assert(type < _TRANSFER_TYPE_MAX);
634
635 HASHMAP_FOREACH(t, m->transfers, i) {
636
637 if (t->type == type &&
638 streq_ptr(t->remote, remote) &&
639 streq_ptr(t->dkr_index_url, dkr_index_url))
640 return t;
641 }
642
643 return NULL;
644}
645
646static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
647 _cleanup_(transfer_unrefp) Transfer *t = NULL;
648 const char *remote, *local, *verify, *object;
649 Manager *m = userdata;
650 ImportVerify v;
651 TransferType type;
652 int force, r;
653 uint32_t id;
654
655 assert(bus);
656 assert(msg);
657 assert(m);
658
659 r = bus_verify_polkit_async(
660 msg,
661 CAP_SYS_ADMIN,
662 "org.freedesktop.import1.pull",
663 false,
c529695e 664 UID_INVALID,
3d7415f4
LP
665 &m->polkit_registry,
666 error);
667 if (r < 0)
668 return r;
669 if (r == 0)
670 return 1; /* Will call us back */
671
672 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
673 if (r < 0)
674 return r;
675
676 if (!http_url_is_valid(remote))
677 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
678
679 if (isempty(local))
680 local = NULL;
681 else if (!machine_name_is_valid(local))
682 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
683
684 if (isempty(verify))
685 v = IMPORT_VERIFY_SIGNATURE;
686 else
687 v = import_verify_from_string(verify);
688 if (v < 0)
689 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
690
691 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
692
693 if (manager_find(m, type, NULL, remote))
694 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
695
696 r = transfer_new(m, &t);
697 if (r < 0)
698 return r;
699
700 t->type = type;
701 t->verify = v;
702 t->force_local = force;
703
704 t->remote = strdup(remote);
705 if (!t->remote)
706 return -ENOMEM;
707
708 t->local = strdup(local);
709 if (!t->local)
710 return -ENOMEM;
711
712 r = transfer_start(t);
713 if (r < 0)
714 return r;
715
716 object = t->object_path;
717 id = t->id;
718 t = NULL;
719
720 return sd_bus_reply_method_return(msg, "uo", id, object);
721}
722
723static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
724 _cleanup_(transfer_unrefp) Transfer *t = NULL;
725 const char *index_url, *remote, *tag, *local, *verify, *object;
726 Manager *m = userdata;
727 ImportVerify v;
728 int force, r;
729 uint32_t id;
730
731 assert(bus);
732 assert(msg);
733 assert(m);
734
735 r = bus_verify_polkit_async(
736 msg,
737 CAP_SYS_ADMIN,
738 "org.freedesktop.import1.pull",
739 false,
c529695e 740 UID_INVALID,
3d7415f4
LP
741 &m->polkit_registry,
742 error);
743 if (r < 0)
744 return r;
745 if (r == 0)
746 return 1; /* Will call us back */
747
748 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
749 if (r < 0)
750 return r;
751
752 if (isempty(index_url))
753 index_url = DEFAULT_DKR_INDEX_URL;
754 if (!index_url)
755 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
756 if (!http_url_is_valid(index_url))
757 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
758
759 if (!dkr_name_is_valid(remote))
760 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
761
762 if (isempty(tag))
763 tag = "latest";
764 else if (!dkr_tag_is_valid(tag))
765 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
766
767 if (isempty(local))
768 local = NULL;
769 else if (!machine_name_is_valid(local))
770 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
771
772 if (isempty(verify))
773 v = IMPORT_VERIFY_SIGNATURE;
774 else
775 v = import_verify_from_string(verify);
776 if (v < 0)
777 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
778
779 if (v != IMPORT_VERIFY_NO)
780 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
781
782 if (manager_find(m, TRANSFER_DKR, index_url, remote))
783 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
784
785 r = transfer_new(m, &t);
786 if (r < 0)
787 return r;
788
789 t->type = TRANSFER_DKR;
790 t->verify = v;
791 t->force_local = force;
792
793 t->dkr_index_url = strdup(index_url);
794 if (!t->dkr_index_url)
795 return -ENOMEM;
796
797 t->remote = strjoin(remote, ":", tag, NULL);
798 if (!t->remote)
799 return -ENOMEM;
800
801 t->local = strdup(local);
802 if (!t->local)
803 return -ENOMEM;
804
805 r = transfer_start(t);
806 if (r < 0)
807 return r;
808
809 object = t->object_path;
810 id = t->id;
811 t = NULL;
812
813 return sd_bus_reply_method_return(msg, "uo", id, object);
814}
815
816static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
817 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
818 Manager *m = userdata;
819 Transfer *t;
820 Iterator i;
821 int r;
822
823 assert(bus);
824 assert(msg);
825 assert(m);
826
827 r = sd_bus_message_new_method_return(msg, &reply);
828 if (r < 0)
829 return r;
830
7079cfef 831 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
3d7415f4
LP
832 if (r < 0)
833 return r;
834
835 HASHMAP_FOREACH(t, m->transfers, i) {
836
837 r = sd_bus_message_append(
838 reply,
7079cfef 839 "(usssdo)",
3d7415f4
LP
840 t->id,
841 transfer_type_to_string(t->type),
842 t->remote,
843 t->local,
7079cfef 844 (double) t->progress_percent / 100.0,
3d7415f4
LP
845 t->object_path);
846 if (r < 0)
847 return r;
848 }
849
850 r = sd_bus_message_close_container(reply);
851 if (r < 0)
852 return r;
853
854 return sd_bus_send(bus, reply, NULL);
855}
856
857static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
858 Transfer *t = userdata;
859 int r;
860
861 assert(bus);
862 assert(msg);
863 assert(t);
864
865 r = bus_verify_polkit_async(
866 msg,
867 CAP_SYS_ADMIN,
868 "org.freedesktop.import1.pull",
869 false,
c529695e 870 UID_INVALID,
3d7415f4
LP
871 &t->manager->polkit_registry,
872 error);
873 if (r < 0)
874 return r;
875 if (r == 0)
876 return 1; /* Will call us back */
877
878 r = transfer_cancel(t);
879 if (r < 0)
880 return r;
881
882 return sd_bus_reply_method_return(msg, NULL);
883}
884
885static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
886 Manager *m = userdata;
887 Transfer *t;
888 uint32_t id;
889 int r;
890
891 assert(bus);
892 assert(msg);
893 assert(m);
894
895 r = bus_verify_polkit_async(
896 msg,
897 CAP_SYS_ADMIN,
898 "org.freedesktop.import1.pull",
899 false,
c529695e 900 UID_INVALID,
3d7415f4
LP
901 &m->polkit_registry,
902 error);
903 if (r < 0)
904 return r;
905 if (r == 0)
906 return 1; /* Will call us back */
907
908 r = sd_bus_message_read(msg, "u", &id);
909 if (r < 0)
910 return r;
911 if (id <= 0)
912 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
913
914 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
915 if (!t)
09d46cfd 916 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
3d7415f4
LP
917
918 r = transfer_cancel(t);
919 if (r < 0)
920 return r;
921
922 return sd_bus_reply_method_return(msg, NULL);
923}
924
7079cfef
LP
925static int property_get_progress(
926 sd_bus *bus,
927 const char *path,
928 const char *interface,
929 const char *property,
930 sd_bus_message *reply,
931 void *userdata,
932 sd_bus_error *error) {
933
934 Transfer *t = userdata;
935
936 assert(bus);
937 assert(reply);
938 assert(t);
939
940 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
941}
942
3d7415f4
LP
943static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
944static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
945
946static const sd_bus_vtable transfer_vtable[] = {
947 SD_BUS_VTABLE_START(0),
948 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
949 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
950 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
951 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
952 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
7079cfef 953 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
3d7415f4
LP
954 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
955 SD_BUS_SIGNAL("LogMessage", "us", 0),
956 SD_BUS_VTABLE_END,
957};
958
959static const sd_bus_vtable manager_vtable[] = {
960 SD_BUS_VTABLE_START(0),
961 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
962 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
963 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
7079cfef 964 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
3d7415f4
LP
965 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
966 SD_BUS_SIGNAL("TransferNew", "uo", 0),
967 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
968 SD_BUS_VTABLE_END,
969};
970
971static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
972 Manager *m = userdata;
973 Transfer *t;
974 const char *p;
975 uint32_t id;
976 int r;
977
978 assert(bus);
979 assert(path);
980 assert(interface);
981 assert(found);
982 assert(m);
983
984 p = startswith(path, "/org/freedesktop/import1/transfer/_");
985 if (!p)
986 return 0;
987
988 r = safe_atou32(p, &id);
989 if (r < 0 || id == 0)
990 return 0;
991
992 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
993 if (!t)
994 return 0;
995
996 *found = t;
997 return 1;
998}
999
1000static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1001 _cleanup_strv_free_ char **l = NULL;
1002 Manager *m = userdata;
1003 Transfer *t;
1004 unsigned k = 0;
1005 Iterator i;
1006
1007 l = new0(char*, hashmap_size(m->transfers) + 1);
1008 if (!l)
1009 return -ENOMEM;
1010
1011 HASHMAP_FOREACH(t, m->transfers, i) {
1012
1013 l[k] = strdup(t->object_path);
1014 if (!l[k])
1015 return -ENOMEM;
1016
1017 k++;
1018 }
1019
1020 *nodes = l;
1021 l = NULL;
1022
1023 return 1;
1024}
1025
1026static int manager_add_bus_objects(Manager *m) {
1027 int r;
1028
1029 assert(m);
1030
1031 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1032 if (r < 0)
1033 return log_error_errno(r, "Failed to register object: %m");
1034
1035 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1036 if (r < 0)
1037 return log_error_errno(r, "Failed to register object: %m");
1038
1039 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1040 if (r < 0)
1041 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1042
1043 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1044 if (r < 0)
1045 return log_error_errno(r, "Failed to register name: %m");
1046
1047 r = sd_bus_attach_event(m->bus, m->event, 0);
1048 if (r < 0)
1049 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1050
1051 return 0;
1052}
1053
1054static bool manager_check_idle(void *userdata) {
1055 Manager *m = userdata;
1056
1057 return hashmap_isempty(m->transfers);
1058}
1059
1060static int manager_run(Manager *m) {
1061 assert(m);
1062
1063 return bus_event_loop_with_idle(
1064 m->event,
1065 m->bus,
1066 "org.freedesktop.import1",
1067 DEFAULT_EXIT_USEC,
1068 manager_check_idle,
1069 m);
1070}
1071
1072int main(int argc, char *argv[]) {
1073 _cleanup_(manager_unrefp) Manager *m = NULL;
1074 int r;
1075
1076 log_set_target(LOG_TARGET_AUTO);
1077 log_parse_environment();
1078 log_open();
1079
1080 umask(0022);
1081
1082 if (argc != 1) {
1083 log_error("This program takes no arguments.");
1084 r = -EINVAL;
1085 goto finish;
1086 }
1087
1088 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1089
1090 r = manager_new(&m);
1091 if (r < 0) {
1092 log_error_errno(r, "Failed to allocate manager object: %m");
1093 goto finish;
1094 }
1095
1096 r = manager_add_bus_objects(m);
1097 if (r < 0)
1098 goto finish;
1099
1100 r = manager_run(m);
1101 if (r < 0) {
1102 log_error_errno(r, "Failed to run event loop: %m");
1103 goto finish;
1104 }
1105
1106finish:
1107 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1108}