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