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