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