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