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