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