]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/importd.c
remove duplicated includes
[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 #include "util.h"
26 #include "strv.h"
27 #include "bus-util.h"
28 #include "bus-common-errors.h"
29 #include "socket-util.h"
30 #include "mkdir.h"
31 #include "def.h"
32 #include "missing.h"
33 #include "machine-pool.h"
34 #include "path-util.h"
35 #include "import-util.h"
36
37 typedef struct Transfer Transfer;
38 typedef struct Manager Manager;
39
40 typedef enum TransferType {
41 TRANSFER_IMPORT_TAR,
42 TRANSFER_IMPORT_RAW,
43 TRANSFER_EXPORT_TAR,
44 TRANSFER_EXPORT_RAW,
45 TRANSFER_PULL_TAR,
46 TRANSFER_PULL_RAW,
47 TRANSFER_PULL_DKR,
48 _TRANSFER_TYPE_MAX,
49 _TRANSFER_TYPE_INVALID = -1,
50 } TransferType;
51
52 struct Transfer {
53 Manager *manager;
54
55 uint32_t id;
56 char *object_path;
57
58 TransferType type;
59 ImportVerify verify;
60
61 char *remote;
62 char *local;
63 bool force_local;
64 bool read_only;
65
66 char *dkr_index_url;
67 char *format;
68
69 pid_t pid;
70
71 int log_fd;
72
73 char log_message[LINE_MAX];
74 size_t log_message_size;
75
76 sd_event_source *pid_event_source;
77 sd_event_source *log_event_source;
78
79 unsigned n_canceled;
80 unsigned progress_percent;
81
82 int stdin_fd;
83 int stdout_fd;
84 };
85
86 struct Manager {
87 sd_event *event;
88 sd_bus *bus;
89
90 uint32_t current_transfer_id;
91 Hashmap *transfers;
92
93 Hashmap *polkit_registry;
94
95 int notify_fd;
96
97 sd_event_source *notify_event_source;
98 };
99
100 #define TRANSFERS_MAX 64
101
102 static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
103 [TRANSFER_IMPORT_TAR] = "import-tar",
104 [TRANSFER_IMPORT_RAW] = "import-raw",
105 [TRANSFER_EXPORT_TAR] = "export-tar",
106 [TRANSFER_EXPORT_RAW] = "export-raw",
107 [TRANSFER_PULL_TAR] = "pull-tar",
108 [TRANSFER_PULL_RAW] = "pull-raw",
109 [TRANSFER_PULL_DKR] = "pull-dkr",
110 };
111
112 DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
113
114 static Transfer *transfer_unref(Transfer *t) {
115 if (!t)
116 return NULL;
117
118 if (t->manager)
119 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
120
121 sd_event_source_unref(t->pid_event_source);
122 sd_event_source_unref(t->log_event_source);
123
124 free(t->remote);
125 free(t->local);
126 free(t->dkr_index_url);
127 free(t->format);
128 free(t->object_path);
129
130 if (t->pid > 0) {
131 (void) kill_and_sigcont(t->pid, SIGKILL);
132 (void) wait_for_terminate(t->pid, NULL);
133 }
134
135 safe_close(t->log_fd);
136 safe_close(t->stdin_fd);
137 safe_close(t->stdout_fd);
138
139 free(t);
140 return NULL;
141 }
142
143 DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
144
145 static int transfer_new(Manager *m, Transfer **ret) {
146 _cleanup_(transfer_unrefp) Transfer *t = NULL;
147 uint32_t id;
148 int r;
149
150 assert(m);
151 assert(ret);
152
153 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
154 return -E2BIG;
155
156 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
157 if (r < 0)
158 return r;
159
160 t = new0(Transfer, 1);
161 if (!t)
162 return -ENOMEM;
163
164 t->type = _TRANSFER_TYPE_INVALID;
165 t->log_fd = -1;
166 t->stdin_fd = -1;
167 t->verify = _IMPORT_VERIFY_INVALID;
168
169 id = m->current_transfer_id + 1;
170
171 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
172 return -ENOMEM;
173
174 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
175 if (r < 0)
176 return r;
177
178 m->current_transfer_id = id;
179
180 t->manager = m;
181 t->id = id;
182
183 *ret = t;
184 t = NULL;
185
186 return 0;
187 }
188
189 static void transfer_send_log_line(Transfer *t, const char *line) {
190 int r, priority = LOG_INFO;
191
192 assert(t);
193 assert(line);
194
195 syslog_parse_priority(&line, &priority, true);
196
197 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
198
199 r = sd_bus_emit_signal(
200 t->manager->bus,
201 t->object_path,
202 "org.freedesktop.import1.Transfer",
203 "LogMessage",
204 "us",
205 priority,
206 line);
207 if (r < 0)
208 log_error_errno(r, "Cannot emit message: %m");
209 }
210
211 static void transfer_send_logs(Transfer *t, bool flush) {
212 assert(t);
213
214 /* Try to send out all log messages, if we can. But if we
215 * can't we remove the messages from the buffer, but don't
216 * fail */
217
218 while (t->log_message_size > 0) {
219 _cleanup_free_ char *n = NULL;
220 char *e;
221
222 if (t->log_message_size >= sizeof(t->log_message))
223 e = t->log_message + sizeof(t->log_message);
224 else {
225 char *a, *b;
226
227 a = memchr(t->log_message, 0, t->log_message_size);
228 b = memchr(t->log_message, '\n', t->log_message_size);
229
230 if (a && b)
231 e = a < b ? a : b;
232 else if (a)
233 e = a;
234 else
235 e = b;
236 }
237
238 if (!e) {
239 if (!flush)
240 return;
241
242 e = t->log_message + t->log_message_size;
243 }
244
245 n = strndup(t->log_message, e - t->log_message);
246
247 /* Skip over NUL and newlines */
248 while ((e < t->log_message + t->log_message_size) && (*e == 0 || *e == '\n'))
249 e++;
250
251 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
252 t->log_message_size -= e - t->log_message;
253
254 if (!n) {
255 log_oom();
256 continue;
257 }
258
259 if (isempty(n))
260 continue;
261
262 transfer_send_log_line(t, n);
263 }
264 }
265
266 static int transfer_finalize(Transfer *t, bool success) {
267 int r;
268
269 assert(t);
270
271 transfer_send_logs(t, true);
272
273 r = sd_bus_emit_signal(
274 t->manager->bus,
275 "/org/freedesktop/import1",
276 "org.freedesktop.import1.Manager",
277 "TransferRemoved",
278 "uos",
279 t->id,
280 t->object_path,
281 success ? "done" :
282 t->n_canceled > 0 ? "canceled" : "failed");
283
284 if (r < 0)
285 log_error_errno(r, "Cannot emit message: %m");
286
287 transfer_unref(t);
288 return 0;
289 }
290
291 static int transfer_cancel(Transfer *t) {
292 int r;
293
294 assert(t);
295
296 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
297 if (r < 0)
298 return r;
299
300 t->n_canceled++;
301 return 0;
302 }
303
304 static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
305 Transfer *t = userdata;
306 bool success = false;
307
308 assert(s);
309 assert(t);
310
311 if (si->si_code == CLD_EXITED) {
312 if (si->si_status != 0)
313 log_error("Import process failed with exit code %i.", si->si_status);
314 else {
315 log_debug("Import process succeeded.");
316 success = true;
317 }
318
319 } else if (si->si_code == CLD_KILLED ||
320 si->si_code == CLD_DUMPED)
321
322 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
323 else
324 log_error("Import process failed due to unknown reason.");
325
326 t->pid = 0;
327
328 return transfer_finalize(t, success);
329 }
330
331 static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
332 Transfer *t = userdata;
333 ssize_t l;
334
335 assert(s);
336 assert(t);
337
338 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
339 if (l <= 0) {
340 /* EOF/read error. We just close the pipe here, and
341 * close the watch, waiting for the SIGCHLD to arrive,
342 * before we do anything else. */
343
344 if (l < 0)
345 log_error_errno(errno, "Failed to read log message: %m");
346
347 t->log_event_source = sd_event_source_unref(t->log_event_source);
348 return 0;
349 }
350
351 t->log_message_size += l;
352
353 transfer_send_logs(t, false);
354
355 return 0;
356 }
357
358 static int transfer_start(Transfer *t) {
359 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
360 int r;
361
362 assert(t);
363 assert(t->pid <= 0);
364
365 if (pipe2(pipefd, O_CLOEXEC) < 0)
366 return -errno;
367
368 t->pid = fork();
369 if (t->pid < 0)
370 return -errno;
371 if (t->pid == 0) {
372 const char *cmd[] = {
373 NULL, /* systemd-import, systemd-export or systemd-pull */
374 NULL, /* tar, raw, dkr */
375 NULL, /* --verify= */
376 NULL, /* verify argument */
377 NULL, /* maybe --force */
378 NULL, /* maybe --read-only */
379 NULL, /* maybe --dkr-index-url */
380 NULL, /* if so: the actual URL */
381 NULL, /* maybe --format= */
382 NULL, /* if so: the actual format */
383 NULL, /* remote */
384 NULL, /* local */
385 NULL
386 };
387 unsigned k = 0;
388
389 /* Child */
390
391 reset_all_signal_handlers();
392 reset_signal_mask();
393 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
394
395 pipefd[0] = safe_close(pipefd[0]);
396
397 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
398 log_error_errno(errno, "Failed to dup2() fd: %m");
399 _exit(EXIT_FAILURE);
400 }
401
402 if (t->stdout_fd >= 0) {
403 if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) {
404 log_error_errno(errno, "Failed to dup2() fd: %m");
405 _exit(EXIT_FAILURE);
406 }
407
408 if (t->stdout_fd != STDOUT_FILENO)
409 safe_close(t->stdout_fd);
410 } else {
411 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
412 log_error_errno(errno, "Failed to dup2() fd: %m");
413 _exit(EXIT_FAILURE);
414 }
415 }
416
417 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
418 pipefd[1] = safe_close(pipefd[1]);
419
420 if (t->stdin_fd >= 0) {
421 if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) {
422 log_error_errno(errno, "Failed to dup2() fd: %m");
423 _exit(EXIT_FAILURE);
424 }
425
426 if (t->stdin_fd != STDIN_FILENO)
427 safe_close(t->stdin_fd);
428 } else {
429 int null_fd;
430
431 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
432 if (null_fd < 0) {
433 log_error_errno(errno, "Failed to open /dev/null: %m");
434 _exit(EXIT_FAILURE);
435 }
436
437 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
438 log_error_errno(errno, "Failed to dup2() fd: %m");
439 _exit(EXIT_FAILURE);
440 }
441
442 if (null_fd != STDIN_FILENO)
443 safe_close(null_fd);
444 }
445
446 fd_cloexec(STDIN_FILENO, false);
447 fd_cloexec(STDOUT_FILENO, false);
448 fd_cloexec(STDERR_FILENO, false);
449
450 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
451 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
452
453 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
454 cmd[k++] = SYSTEMD_IMPORT_PATH;
455 else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
456 cmd[k++] = SYSTEMD_EXPORT_PATH;
457 else
458 cmd[k++] = SYSTEMD_PULL_PATH;
459
460 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
461 cmd[k++] = "tar";
462 else if (IN_SET(t->type, TRANSFER_IMPORT_RAW, TRANSFER_EXPORT_RAW, TRANSFER_PULL_RAW))
463 cmd[k++] = "raw";
464 else
465 cmd[k++] = "dkr";
466
467 if (t->verify != _IMPORT_VERIFY_INVALID) {
468 cmd[k++] = "--verify";
469 cmd[k++] = import_verify_to_string(t->verify);
470 }
471
472 if (t->force_local)
473 cmd[k++] = "--force";
474 if (t->read_only)
475 cmd[k++] = "--read-only";
476
477 if (t->dkr_index_url) {
478 cmd[k++] = "--dkr-index-url";
479 cmd[k++] = t->dkr_index_url;
480 }
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 sd_bus_close(m->bus);
553 sd_bus_unref(m->bus);
554 sd_event_unref(m->event);
555
556 free(m);
557 return NULL;
558 }
559
560 DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
561
562 static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
563
564 char buf[NOTIFY_BUFFER_MAX+1];
565 struct iovec iovec = {
566 .iov_base = buf,
567 .iov_len = sizeof(buf)-1,
568 };
569 union {
570 struct cmsghdr cmsghdr;
571 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
572 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
573 } control = {};
574 struct msghdr msghdr = {
575 .msg_iov = &iovec,
576 .msg_iovlen = 1,
577 .msg_control = &control,
578 .msg_controllen = sizeof(control),
579 };
580 struct ucred *ucred = NULL;
581 Manager *m = userdata;
582 struct cmsghdr *cmsg;
583 unsigned percent;
584 char *p, *e;
585 Transfer *t;
586 Iterator i;
587 ssize_t n;
588 int r;
589
590 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
591 if (n < 0) {
592 if (errno == EAGAIN || errno == EINTR)
593 return 0;
594
595 return -errno;
596 }
597
598 cmsg_close_all(&msghdr);
599
600 for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg; cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
601 if (cmsg->cmsg_level == SOL_SOCKET &&
602 cmsg->cmsg_type == SCM_CREDENTIALS &&
603 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) {
604
605 ucred = (struct ucred*) CMSG_DATA(cmsg);
606 }
607 }
608
609 if (msghdr.msg_flags & MSG_TRUNC) {
610 log_warning("Got overly long notification datagram, ignoring.");
611 return 0;
612 }
613
614 if (!ucred || ucred->pid <= 0) {
615 log_warning("Got notification datagram lacking credential information, ignoring.");
616 return 0;
617 }
618
619 HASHMAP_FOREACH(t, m->transfers, i)
620 if (ucred->pid == t->pid)
621 break;
622
623 if (!t) {
624 log_warning("Got notification datagram from unexpected peer, ignoring.");
625 return 0;
626 }
627
628 buf[n] = 0;
629
630 p = startswith(buf, "X_IMPORT_PROGRESS=");
631 if (!p) {
632 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
633 if (!p)
634 return 0;
635
636 p += 19;
637 }
638
639 e = strchrnul(p, '\n');
640 *e = 0;
641
642 r = safe_atou(p, &percent);
643 if (r < 0 || percent > 100) {
644 log_warning("Got invalid percent value, ignoring.");
645 return 0;
646 }
647
648 t->progress_percent = percent;
649
650 log_debug("Got percentage from client: %u%%", percent);
651 return 0;
652 }
653
654 static int manager_new(Manager **ret) {
655 _cleanup_(manager_unrefp) Manager *m = NULL;
656 static const union sockaddr_union sa = {
657 .un.sun_family = AF_UNIX,
658 .un.sun_path = "/run/systemd/import/notify",
659 };
660 static const int one = 1;
661 int r;
662
663 assert(ret);
664
665 m = new0(Manager, 1);
666 if (!m)
667 return -ENOMEM;
668
669 r = sd_event_default(&m->event);
670 if (r < 0)
671 return r;
672
673 sd_event_set_watchdog(m->event, true);
674
675 r = sd_bus_default_system(&m->bus);
676 if (r < 0)
677 return r;
678
679 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
680 if (m->notify_fd < 0)
681 return -errno;
682
683 (void) mkdir_parents_label(sa.un.sun_path, 0755);
684 (void) unlink(sa.un.sun_path);
685
686 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
687 return -errno;
688
689 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
690 return -errno;
691
692 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
693 if (r < 0)
694 return r;
695
696 *ret = m;
697 m = NULL;
698
699 return 0;
700 }
701
702 static Transfer *manager_find(Manager *m, TransferType type, const char *dkr_index_url, const char *remote) {
703 Transfer *t;
704 Iterator i;
705
706 assert(m);
707 assert(type >= 0);
708 assert(type < _TRANSFER_TYPE_MAX);
709
710 HASHMAP_FOREACH(t, m->transfers, i) {
711
712 if (t->type == type &&
713 streq_ptr(t->remote, remote) &&
714 streq_ptr(t->dkr_index_url, dkr_index_url))
715 return t;
716 }
717
718 return NULL;
719 }
720
721 static int method_import_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
722 _cleanup_(transfer_unrefp) Transfer *t = NULL;
723 int fd, force, read_only, r;
724 const char *local, *object;
725 Manager *m = userdata;
726 TransferType type;
727 uint32_t id;
728
729 r = bus_verify_polkit_async(
730 msg,
731 CAP_SYS_ADMIN,
732 "org.freedesktop.import1.import",
733 false,
734 UID_INVALID,
735 &m->polkit_registry,
736 error);
737 if (r < 0)
738 return r;
739 if (r == 0)
740 return 1; /* Will call us back */
741
742 r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
743 if (r < 0)
744 return r;
745
746 if (!machine_name_is_valid(local))
747 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
748
749 r = setup_machine_directory((uint64_t) -1, error);
750 if (r < 0)
751 return r;
752
753 type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
754
755 r = transfer_new(m, &t);
756 if (r < 0)
757 return r;
758
759 t->type = type;
760 t->force_local = force;
761 t->read_only = read_only;
762
763 t->local = strdup(local);
764 if (!t->local)
765 return -ENOMEM;
766
767 t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
768 if (t->stdin_fd < 0)
769 return -errno;
770
771 r = transfer_start(t);
772 if (r < 0)
773 return r;
774
775 object = t->object_path;
776 id = t->id;
777 t = NULL;
778
779 return sd_bus_reply_method_return(msg, "uo", id, object);
780 }
781
782 static int method_export_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
783 _cleanup_(transfer_unrefp) Transfer *t = NULL;
784 int fd, r;
785 const char *local, *object, *format;
786 Manager *m = userdata;
787 TransferType type;
788 uint32_t id;
789
790 r = bus_verify_polkit_async(
791 msg,
792 CAP_SYS_ADMIN,
793 "org.freedesktop.import1.export",
794 false,
795 UID_INVALID,
796 &m->polkit_registry,
797 error);
798 if (r < 0)
799 return r;
800 if (r == 0)
801 return 1; /* Will call us back */
802
803 r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
804 if (r < 0)
805 return r;
806
807 if (!machine_name_is_valid(local))
808 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
809
810 type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
811
812 r = transfer_new(m, &t);
813 if (r < 0)
814 return r;
815
816 t->type = type;
817
818 if (!isempty(format)) {
819 t->format = strdup(format);
820 if (!t->format)
821 return -ENOMEM;
822 }
823
824 t->local = strdup(local);
825 if (!t->local)
826 return -ENOMEM;
827
828 t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
829 if (t->stdout_fd < 0)
830 return -errno;
831
832 r = transfer_start(t);
833 if (r < 0)
834 return r;
835
836 object = t->object_path;
837 id = t->id;
838 t = NULL;
839
840 return sd_bus_reply_method_return(msg, "uo", id, object);
841 }
842
843 static int method_pull_tar_or_raw(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
844 _cleanup_(transfer_unrefp) Transfer *t = NULL;
845 const char *remote, *local, *verify, *object;
846 Manager *m = userdata;
847 ImportVerify v;
848 TransferType type;
849 int force, r;
850 uint32_t id;
851
852 assert(bus);
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 false,
861 UID_INVALID,
862 &m->polkit_registry,
863 error);
864 if (r < 0)
865 return r;
866 if (r == 0)
867 return 1; /* Will call us back */
868
869 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
870 if (r < 0)
871 return r;
872
873 if (!http_url_is_valid(remote))
874 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
875
876 if (isempty(local))
877 local = NULL;
878 else if (!machine_name_is_valid(local))
879 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
880
881 if (isempty(verify))
882 v = IMPORT_VERIFY_SIGNATURE;
883 else
884 v = import_verify_from_string(verify);
885 if (v < 0)
886 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
887
888 r = setup_machine_directory((uint64_t) -1, error);
889 if (r < 0)
890 return r;
891
892 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
893
894 if (manager_find(m, type, NULL, remote))
895 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
896
897 r = transfer_new(m, &t);
898 if (r < 0)
899 return r;
900
901 t->type = type;
902 t->verify = v;
903 t->force_local = force;
904
905 t->remote = strdup(remote);
906 if (!t->remote)
907 return -ENOMEM;
908
909 if (local) {
910 t->local = strdup(local);
911 if (!t->local)
912 return -ENOMEM;
913 }
914
915 r = transfer_start(t);
916 if (r < 0)
917 return r;
918
919 object = t->object_path;
920 id = t->id;
921 t = NULL;
922
923 return sd_bus_reply_method_return(msg, "uo", id, object);
924 }
925
926 static int method_pull_dkr(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
927 _cleanup_(transfer_unrefp) Transfer *t = NULL;
928 const char *index_url, *remote, *tag, *local, *verify, *object;
929 Manager *m = userdata;
930 ImportVerify v;
931 int force, r;
932 uint32_t id;
933
934 assert(bus);
935 assert(msg);
936 assert(m);
937
938 r = bus_verify_polkit_async(
939 msg,
940 CAP_SYS_ADMIN,
941 "org.freedesktop.import1.pull",
942 false,
943 UID_INVALID,
944 &m->polkit_registry,
945 error);
946 if (r < 0)
947 return r;
948 if (r == 0)
949 return 1; /* Will call us back */
950
951 r = sd_bus_message_read(msg, "sssssb", &index_url, &remote, &tag, &local, &verify, &force);
952 if (r < 0)
953 return r;
954
955 if (isempty(index_url))
956 index_url = DEFAULT_DKR_INDEX_URL;
957 if (!index_url)
958 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL must be specified.");
959 if (!http_url_is_valid(index_url))
960 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Index URL %s is invalid", index_url);
961
962 if (!dkr_name_is_valid(remote))
963 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Remote name %s is not valid", remote);
964
965 if (isempty(tag))
966 tag = "latest";
967 else if (!dkr_tag_is_valid(tag))
968 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Tag %s is not valid", tag);
969
970 if (isempty(local))
971 local = NULL;
972 else if (!machine_name_is_valid(local))
973 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
974
975 if (isempty(verify))
976 v = IMPORT_VERIFY_SIGNATURE;
977 else
978 v = import_verify_from_string(verify);
979 if (v < 0)
980 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
981
982 if (v != IMPORT_VERIFY_NO)
983 return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "DKR does not support verification.");
984
985 r = setup_machine_directory((uint64_t) -1, error);
986 if (r < 0)
987 return r;
988
989 if (manager_find(m, TRANSFER_PULL_DKR, index_url, remote))
990 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
991
992 r = transfer_new(m, &t);
993 if (r < 0)
994 return r;
995
996 t->type = TRANSFER_PULL_DKR;
997 t->verify = v;
998 t->force_local = force;
999
1000 t->dkr_index_url = strdup(index_url);
1001 if (!t->dkr_index_url)
1002 return -ENOMEM;
1003
1004 t->remote = strjoin(remote, ":", tag, NULL);
1005 if (!t->remote)
1006 return -ENOMEM;
1007
1008 if (local) {
1009 t->local = strdup(local);
1010 if (!t->local)
1011 return -ENOMEM;
1012 }
1013
1014 r = transfer_start(t);
1015 if (r < 0)
1016 return r;
1017
1018 object = t->object_path;
1019 id = t->id;
1020 t = NULL;
1021
1022 return sd_bus_reply_method_return(msg, "uo", id, object);
1023 }
1024
1025 static int method_list_transfers(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1026 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1027 Manager *m = userdata;
1028 Transfer *t;
1029 Iterator i;
1030 int r;
1031
1032 assert(bus);
1033 assert(msg);
1034 assert(m);
1035
1036 r = sd_bus_message_new_method_return(msg, &reply);
1037 if (r < 0)
1038 return r;
1039
1040 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
1041 if (r < 0)
1042 return r;
1043
1044 HASHMAP_FOREACH(t, m->transfers, i) {
1045
1046 r = sd_bus_message_append(
1047 reply,
1048 "(usssdo)",
1049 t->id,
1050 transfer_type_to_string(t->type),
1051 t->remote,
1052 t->local,
1053 (double) t->progress_percent / 100.0,
1054 t->object_path);
1055 if (r < 0)
1056 return r;
1057 }
1058
1059 r = sd_bus_message_close_container(reply);
1060 if (r < 0)
1061 return r;
1062
1063 return sd_bus_send(bus, reply, NULL);
1064 }
1065
1066 static int method_cancel(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1067 Transfer *t = userdata;
1068 int r;
1069
1070 assert(bus);
1071 assert(msg);
1072 assert(t);
1073
1074 r = bus_verify_polkit_async(
1075 msg,
1076 CAP_SYS_ADMIN,
1077 "org.freedesktop.import1.pull",
1078 false,
1079 UID_INVALID,
1080 &t->manager->polkit_registry,
1081 error);
1082 if (r < 0)
1083 return r;
1084 if (r == 0)
1085 return 1; /* Will call us back */
1086
1087 r = transfer_cancel(t);
1088 if (r < 0)
1089 return r;
1090
1091 return sd_bus_reply_method_return(msg, NULL);
1092 }
1093
1094 static int method_cancel_transfer(sd_bus *bus, sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1095 Manager *m = userdata;
1096 Transfer *t;
1097 uint32_t id;
1098 int r;
1099
1100 assert(bus);
1101 assert(msg);
1102 assert(m);
1103
1104 r = bus_verify_polkit_async(
1105 msg,
1106 CAP_SYS_ADMIN,
1107 "org.freedesktop.import1.pull",
1108 false,
1109 UID_INVALID,
1110 &m->polkit_registry,
1111 error);
1112 if (r < 0)
1113 return r;
1114 if (r == 0)
1115 return 1; /* Will call us back */
1116
1117 r = sd_bus_message_read(msg, "u", &id);
1118 if (r < 0)
1119 return r;
1120 if (id <= 0)
1121 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
1122
1123 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1124 if (!t)
1125 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
1126
1127 r = transfer_cancel(t);
1128 if (r < 0)
1129 return r;
1130
1131 return sd_bus_reply_method_return(msg, NULL);
1132 }
1133
1134 static int property_get_progress(
1135 sd_bus *bus,
1136 const char *path,
1137 const char *interface,
1138 const char *property,
1139 sd_bus_message *reply,
1140 void *userdata,
1141 sd_bus_error *error) {
1142
1143 Transfer *t = userdata;
1144
1145 assert(bus);
1146 assert(reply);
1147 assert(t);
1148
1149 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
1150 }
1151
1152 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1153 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1154
1155 static const sd_bus_vtable transfer_vtable[] = {
1156 SD_BUS_VTABLE_START(0),
1157 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1158 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1159 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1160 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1161 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
1162 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
1163 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
1164 SD_BUS_SIGNAL("LogMessage", "us", 0),
1165 SD_BUS_VTABLE_END,
1166 };
1167
1168 static const sd_bus_vtable manager_vtable[] = {
1169 SD_BUS_VTABLE_START(0),
1170 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1171 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1172 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1173 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1174 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1175 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1176 SD_BUS_METHOD("PullDkr", "sssssb", "uo", method_pull_dkr, SD_BUS_VTABLE_UNPRIVILEGED),
1177 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
1178 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
1179 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1180 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1181 SD_BUS_VTABLE_END,
1182 };
1183
1184 static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1185 Manager *m = userdata;
1186 Transfer *t;
1187 const char *p;
1188 uint32_t id;
1189 int r;
1190
1191 assert(bus);
1192 assert(path);
1193 assert(interface);
1194 assert(found);
1195 assert(m);
1196
1197 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1198 if (!p)
1199 return 0;
1200
1201 r = safe_atou32(p, &id);
1202 if (r < 0 || id == 0)
1203 return 0;
1204
1205 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1206 if (!t)
1207 return 0;
1208
1209 *found = t;
1210 return 1;
1211 }
1212
1213 static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1214 _cleanup_strv_free_ char **l = NULL;
1215 Manager *m = userdata;
1216 Transfer *t;
1217 unsigned k = 0;
1218 Iterator i;
1219
1220 l = new0(char*, hashmap_size(m->transfers) + 1);
1221 if (!l)
1222 return -ENOMEM;
1223
1224 HASHMAP_FOREACH(t, m->transfers, i) {
1225
1226 l[k] = strdup(t->object_path);
1227 if (!l[k])
1228 return -ENOMEM;
1229
1230 k++;
1231 }
1232
1233 *nodes = l;
1234 l = NULL;
1235
1236 return 1;
1237 }
1238
1239 static int manager_add_bus_objects(Manager *m) {
1240 int r;
1241
1242 assert(m);
1243
1244 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1245 if (r < 0)
1246 return log_error_errno(r, "Failed to register object: %m");
1247
1248 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1249 if (r < 0)
1250 return log_error_errno(r, "Failed to register object: %m");
1251
1252 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1253 if (r < 0)
1254 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1255
1256 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1257 if (r < 0)
1258 return log_error_errno(r, "Failed to register name: %m");
1259
1260 r = sd_bus_attach_event(m->bus, m->event, 0);
1261 if (r < 0)
1262 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1263
1264 return 0;
1265 }
1266
1267 static bool manager_check_idle(void *userdata) {
1268 Manager *m = userdata;
1269
1270 return hashmap_isempty(m->transfers);
1271 }
1272
1273 static int manager_run(Manager *m) {
1274 assert(m);
1275
1276 return bus_event_loop_with_idle(
1277 m->event,
1278 m->bus,
1279 "org.freedesktop.import1",
1280 DEFAULT_EXIT_USEC,
1281 manager_check_idle,
1282 m);
1283 }
1284
1285 int main(int argc, char *argv[]) {
1286 _cleanup_(manager_unrefp) Manager *m = NULL;
1287 int r;
1288
1289 log_set_target(LOG_TARGET_AUTO);
1290 log_parse_environment();
1291 log_open();
1292
1293 umask(0022);
1294
1295 if (argc != 1) {
1296 log_error("This program takes no arguments.");
1297 r = -EINVAL;
1298 goto finish;
1299 }
1300
1301 assert_se(sigprocmask_many(SIG_BLOCK, SIGCHLD, -1) >= 0);
1302
1303 r = manager_new(&m);
1304 if (r < 0) {
1305 log_error_errno(r, "Failed to allocate manager object: %m");
1306 goto finish;
1307 }
1308
1309 r = manager_add_bus_objects(m);
1310 if (r < 0)
1311 goto finish;
1312
1313 r = manager_run(m);
1314 if (r < 0) {
1315 log_error_errno(r, "Failed to run event loop: %m");
1316 goto finish;
1317 }
1318
1319 finish:
1320 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1321 }