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