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