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