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