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