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