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