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