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