]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/importd.c
test-hashmap: fix gcc5 warning
[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
1c8da044
LP
521 cmsg_close_all(&msghdr);
522
7079cfef 523 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
1c8da044 524 if (cmsg->cmsg_level == SOL_SOCKET &&
7079cfef
LP
525 cmsg->cmsg_type == SCM_CREDENTIALS &&
526 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
527
528 ucred = (struct ucred*) CMSG_DATA(cmsg);
529 }
530 }
531
532 if (msghdr.msg_flags & MSG_TRUNC) {
533 log_warning("Got overly long notification datagram, ignoring.");
534 return 0;
535 }
536
537 if (!ucred || ucred->pid <= 0) {
538 log_warning("Got notification datagram lacking credential information, ignoring.");
539 return 0;
540 }
541
542 HASHMAP_FOREACH(t, m->transfers, i)
543 if (ucred->pid == t->pid)
544 break;
545
546 if (!t) {
547 log_warning("Got notification datagram from unexpected peer, ignoring.");
548 return 0;
549 }
550
551 buf[n] = 0;
552
553 p = startswith(buf, "X_IMPORT_PROGRESS=");
554 if (!p) {
555 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
556 if (!p)
557 return 0;
558
559 p += 19;
560 }
561
562 e = strchrnul(p, '\n');
563 *e = 0;
564
565 r = safe_atou(p, &percent);
566 if (r < 0 || percent > 100) {
567 log_warning("Got invalid percent value, ignoring.");
568 return 0;
569 }
570
571 t->progress_percent = percent;
572
573 log_debug("Got percentage from client: %u%%", percent);
574 return 0;
575}
576
3d7415f4
LP
577static int manager_new(Manager **ret) {
578 _cleanup_(manager_unrefp) Manager *m = NULL;
7079cfef
LP
579 static const union sockaddr_union sa = {
580 .un.sun_family = AF_UNIX,
581 .un.sun_path = "/run/systemd/import/notify",
582 };
583 static const int one = 1;
3d7415f4
LP
584 int r;
585
586 assert(ret);
587
588 m = new0(Manager, 1);
589 if (!m)
590 return -ENOMEM;
591
592 r = sd_event_default(&m->event);
593 if (r < 0)
594 return r;
595
596 sd_event_set_watchdog(m->event, true);
597
598 r = sd_bus_default_system(&m->bus);
599 if (r < 0)
600 return r;
601
7079cfef
LP
602 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
603 if (m->notify_fd < 0)
604 return -errno;
605
606 (void) mkdir_parents_label(sa.un.sun_path, 0755);
607 (void) unlink(sa.un.sun_path);
608
609 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
610 return -errno;
611
612 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
613 return -errno;
614
615 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
616 if (r < 0)
617 return r;
618
3d7415f4
LP
619 *ret = m;
620 m = NULL;
621
622 return 0;
623}
624
625static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
626 Transfer *t;
627 Iterator i;
628
629 assert(m);
630 assert(type >= 0);
631 assert(type < _TRANSFER_TYPE_MAX);
632
633 HASHMAP_FOREACH(t, m->transfers, i) {
634
635 if (t->type == type &&
636 streq_ptr(t->remote, remote) &&
637 streq_ptr(t->dkr_index_url, dkr_index_url))
638 return t;
639 }
640
641 return NULL;
642}
643
644static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
645 _cleanup_(transfer_unrefp) Transfer *t = NULL;
646 const char *remote, *local, *verify, *object;
647 Manager *m = userdata;
648 ImportVerify v;
649 TransferType type;
650 int force, r;
651 uint32_t id;
652
653 assert(bus);
654 assert(msg);
655 assert(m);
656
657 r = bus_verify_polkit_async(
658 msg,
659 CAP_SYS_ADMIN,
660 "org.freedesktop.import1.pull",
661 false,
c529695e 662 UID_INVALID,
3d7415f4
LP
663 &m->polkit_registry,
664 error);
665 if (r < 0)
666 return r;
667 if (r == 0)
668 return 1; /* Will call us back */
669
670 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
671 if (r < 0)
672 return r;
673
674 if (!http_url_is_valid(remote))
675 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
676
677 if (isempty(local))
678 local = NULL;
679 else if (!machine_name_is_valid(local))
680 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
681
682 if (isempty(verify))
683 v = IMPORT_VERIFY_SIGNATURE;
684 else
685 v = import_verify_from_string(verify);
686 if (v < 0)
687 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
688
689 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_TAR : TRANSFER_RAW;
690
691 if (manager_find(m, type, NULL, remote))
692 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
693
694 r = transfer_new(m, &t);
695 if (r < 0)
696 return r;
697
698 t->type = type;
699 t->verify = v;
700 t->force_local = force;
701
702 t->remote = strdup(remote);
703 if (!t->remote)
704 return -ENOMEM;
705
706 t->local = strdup(local);
707 if (!t->local)
708 return -ENOMEM;
709
710 r = transfer_start(t);
711 if (r < 0)
712 return r;
713
714 object = t->object_path;
715 id = t->id;
716 t = NULL;
717
718 return sd_bus_reply_method_return(msg, "uo", id, object);
719}
720
721static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
722 _cleanup_(transfer_unrefp) Transfer *t = NULL;
723 const char *index_url, *remote, *tag, *local, *verify, *object;
724 Manager *m = userdata;
725 ImportVerify v;
726 int force, r;
727 uint32_t id;
728
729 assert(bus);
730 assert(msg);
731 assert(m);
732
733 r = bus_verify_polkit_async(
734 msg,
735 CAP_SYS_ADMIN,
736 "org.freedesktop.import1.pull",
737 false,
c529695e 738 UID_INVALID,
3d7415f4
LP
739 &m->polkit_registry,
740 error);
741 if (r < 0)
742 return r;
743 if (r == 0)
744 return 1; /* Will call us back */
745
746 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
747 if (r < 0)
748 return r;
749
750 if (isempty(index_url))
751 index_url = DEFAULT_DKR_INDEX_URL;
752 if (!index_url)
753 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
754 if (!http_url_is_valid(index_url))
755 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
756
757 if (!dkr_name_is_valid(remote))
758 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
759
760 if (isempty(tag))
761 tag = "latest";
762 else if (!dkr_tag_is_valid(tag))
763 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
764
765 if (isempty(local))
766 local = NULL;
767 else if (!machine_name_is_valid(local))
768 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
769
770 if (isempty(verify))
771 v = IMPORT_VERIFY_SIGNATURE;
772 else
773 v = import_verify_from_string(verify);
774 if (v < 0)
775 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
776
777 if (v != IMPORT_VERIFY_NO)
778 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
779
780 if (manager_find(m, TRANSFER_DKR, index_url, remote))
781 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
782
783 r = transfer_new(m, &t);
784 if (r < 0)
785 return r;
786
787 t->type = TRANSFER_DKR;
788 t->verify = v;
789 t->force_local = force;
790
791 t->dkr_index_url = strdup(index_url);
792 if (!t->dkr_index_url)
793 return -ENOMEM;
794
795 t->remote = strjoin(remote, ":", tag, NULL);
796 if (!t->remote)
797 return -ENOMEM;
798
799 t->local = strdup(local);
800 if (!t->local)
801 return -ENOMEM;
802
803 r = transfer_start(t);
804 if (r < 0)
805 return r;
806
807 object = t->object_path;
808 id = t->id;
809 t = NULL;
810
811 return sd_bus_reply_method_return(msg, "uo", id, object);
812}
813
814static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
815 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
816 Manager *m = userdata;
817 Transfer *t;
818 Iterator i;
819 int r;
820
821 assert(bus);
822 assert(msg);
823 assert(m);
824
825 r = sd_bus_message_new_method_return(msg, &reply);
826 if (r < 0)
827 return r;
828
7079cfef 829 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
3d7415f4
LP
830 if (r < 0)
831 return r;
832
833 HASHMAP_FOREACH(t, m->transfers, i) {
834
835 r = sd_bus_message_append(
836 reply,
7079cfef 837 "(usssdo)",
3d7415f4
LP
838 t->id,
839 transfer_type_to_string(t->type),
840 t->remote,
841 t->local,
7079cfef 842 (double) t->progress_percent / 100.0,
3d7415f4
LP
843 t->object_path);
844 if (r < 0)
845 return r;
846 }
847
848 r = sd_bus_message_close_container(reply);
849 if (r < 0)
850 return r;
851
852 return sd_bus_send(bus, reply, NULL);
853}
854
855static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
856 Transfer *t = userdata;
857 int r;
858
859 assert(bus);
860 assert(msg);
861 assert(t);
862
863 r = bus_verify_polkit_async(
864 msg,
865 CAP_SYS_ADMIN,
866 "org.freedesktop.import1.pull",
867 false,
c529695e 868 UID_INVALID,
3d7415f4
LP
869 &t->manager->polkit_registry,
870 error);
871 if (r < 0)
872 return r;
873 if (r == 0)
874 return 1; /* Will call us back */
875
876 r = transfer_cancel(t);
877 if (r < 0)
878 return r;
879
880 return sd_bus_reply_method_return(msg, NULL);
881}
882
883static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
884 Manager *m = userdata;
885 Transfer *t;
886 uint32_t id;
887 int r;
888
889 assert(bus);
890 assert(msg);
891 assert(m);
892
893 r = bus_verify_polkit_async(
894 msg,
895 CAP_SYS_ADMIN,
896 "org.freedesktop.import1.pull",
897 false,
c529695e 898 UID_INVALID,
3d7415f4
LP
899 &m->polkit_registry,
900 error);
901 if (r < 0)
902 return r;
903 if (r == 0)
904 return 1; /* Will call us back */
905
906 r = sd_bus_message_read(msg, "u", &id);
907 if (r < 0)
908 return r;
909 if (id <= 0)
910 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
911
912 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
913 if (!t)
09d46cfd 914 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
3d7415f4
LP
915
916 r = transfer_cancel(t);
917 if (r < 0)
918 return r;
919
920 return sd_bus_reply_method_return(msg, NULL);
921}
922
7079cfef
LP
923static int property_get_progress(
924 sd_bus *bus,
925 const char *path,
926 const char *interface,
927 const char *property,
928 sd_bus_message *reply,
929 void *userdata,
930 sd_bus_error *error) {
931
932 Transfer *t = userdata;
933
934 assert(bus);
935 assert(reply);
936 assert(t);
937
938 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
939}
940
3d7415f4
LP
941static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
942static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
943
944static const sd_bus_vtable transfer_vtable[] = {
945 SD_BUS_VTABLE_START(0),
946 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
947 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
948 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
949 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
950 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
7079cfef 951 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
3d7415f4
LP
952 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
953 SD_BUS_SIGNAL("LogMessage", "us", 0),
954 SD_BUS_VTABLE_END,
955};
956
957static const sd_bus_vtable manager_vtable[] = {
958 SD_BUS_VTABLE_START(0),
959 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
960 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
961 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
7079cfef 962 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
3d7415f4
LP
963 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
964 SD_BUS_SIGNAL("TransferNew", "uo", 0),
965 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
966 SD_BUS_VTABLE_END,
967};
968
969static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
970 Manager *m = userdata;
971 Transfer *t;
972 const char *p;
973 uint32_t id;
974 int r;
975
976 assert(bus);
977 assert(path);
978 assert(interface);
979 assert(found);
980 assert(m);
981
982 p = startswith(path, "/org/freedesktop/import1/transfer/_");
983 if (!p)
984 return 0;
985
986 r = safe_atou32(p, &id);
987 if (r < 0 || id == 0)
988 return 0;
989
990 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
991 if (!t)
992 return 0;
993
994 *found = t;
995 return 1;
996}
997
998static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
999 _cleanup_strv_free_ char **l = NULL;
1000 Manager *m = userdata;
1001 Transfer *t;
1002 unsigned k = 0;
1003 Iterator i;
1004
1005 l = new0(char*, hashmap_size(m->transfers) + 1);
1006 if (!l)
1007 return -ENOMEM;
1008
1009 HASHMAP_FOREACH(t, m->transfers, i) {
1010
1011 l[k] = strdup(t->object_path);
1012 if (!l[k])
1013 return -ENOMEM;
1014
1015 k++;
1016 }
1017
1018 *nodes = l;
1019 l = NULL;
1020
1021 return 1;
1022}
1023
1024static int manager_add_bus_objects(Manager *m) {
1025 int r;
1026
1027 assert(m);
1028
1029 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1030 if (r < 0)
1031 return log_error_errno(r, "Failed to register object: %m");
1032
1033 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1034 if (r < 0)
1035 return log_error_errno(r, "Failed to register object: %m");
1036
1037 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1038 if (r < 0)
1039 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1040
1041 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1042 if (r < 0)
1043 return log_error_errno(r, "Failed to register name: %m");
1044
1045 r = sd_bus_attach_event(m->bus, m->event, 0);
1046 if (r < 0)
1047 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1048
1049 return 0;
1050}
1051
1052static bool manager_check_idle(void *userdata) {
1053 Manager *m = userdata;
1054
1055 return hashmap_isempty(m->transfers);
1056}
1057
1058static int manager_run(Manager *m) {
1059 assert(m);
1060
1061 return bus_event_loop_with_idle(
1062 m->event,
1063 m->bus,
1064 "org.freedesktop.import1",
1065 DEFAULT_EXIT_USEC,
1066 manager_check_idle,
1067 m);
1068}
1069
1070int main(int argc, char *argv[]) {
1071 _cleanup_(manager_unrefp) Manager *m = NULL;
1072 int r;
1073
1074 log_set_target(LOG_TARGET_AUTO);
1075 log_parse_environment();
1076 log_open();
1077
1078 umask(0022);
1079
1080 if (argc != 1) {
1081 log_error("This program takes no arguments.");
1082 r = -EINVAL;
1083 goto finish;
1084 }
1085
1086 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1087
1088 r = manager_new(&m);
1089 if (r < 0) {
1090 log_error_errno(r, "Failed to allocate manager object: %m");
1091 goto finish;
1092 }
1093
1094 r = manager_add_bus_objects(m);
1095 if (r < 0)
1096 goto finish;
1097
1098 r = manager_run(m);
1099 if (r < 0) {
1100 log_error_errno(r, "Failed to run event loop: %m");
1101 goto finish;
1102 }
1103
1104finish:
1105 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1106}