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