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