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