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