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