]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/importd.c
importd: drop dkr support
[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,
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 75
587fec42 76 char *format;
3d7415f4
LP
77
78 pid_t pid;
79
80 int log_fd;
81
82 char log_message[LINE_MAX];
83 size_t log_message_size;
84
85 sd_event_source *pid_event_source;
86 sd_event_source *log_event_source;
87
88 unsigned n_canceled;
7079cfef 89 unsigned progress_percent;
b6e676ce
LP
90
91 int stdin_fd;
587fec42 92 int stdout_fd;
3d7415f4
LP
93};
94
95struct Manager {
96 sd_event *event;
97 sd_bus *bus;
98
99 uint32_t current_transfer_id;
100 Hashmap *transfers;
101
102 Hashmap *polkit_registry;
7079cfef
LP
103
104 int notify_fd;
105
106 sd_event_source *notify_event_source;
3d7415f4
LP
107};
108
109#define TRANSFERS_MAX 64
110
111static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
b6e676ce
LP
112 [TRANSFER_IMPORT_TAR] = "import-tar",
113 [TRANSFER_IMPORT_RAW] = "import-raw",
587fec42
LP
114 [TRANSFER_EXPORT_TAR] = "export-tar",
115 [TRANSFER_EXPORT_RAW] = "export-raw",
b6e676ce
LP
116 [TRANSFER_PULL_TAR] = "pull-tar",
117 [TRANSFER_PULL_RAW] = "pull-raw",
3d7415f4
LP
118};
119
b9a5f858 120DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
3d7415f4
LP
121
122static Transfer *transfer_unref(Transfer *t) {
123 if (!t)
124 return NULL;
125
126 if (t->manager)
127 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
128
129 sd_event_source_unref(t->pid_event_source);
130 sd_event_source_unref(t->log_event_source);
131
132 free(t->remote);
133 free(t->local);
587fec42 134 free(t->format);
3d7415f4
LP
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);
b6e676ce 143 safe_close(t->stdin_fd);
587fec42 144 safe_close(t->stdout_fd);
3d7415f4
LP
145
146 free(t);
147 return NULL;
148}
149
150DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
151
152static 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;
b6e676ce 173 t->stdin_fd = -1;
3be78ab2 174 t->stdout_fd = -1;
b6e676ce 175 t->verify = _IMPORT_VERIFY_INVALID;
3d7415f4
LP
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
197static 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
09d46cfd 205 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
3d7415f4
LP
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
219static 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 */
be3ce301 256 while ((e < t->log_message + t->log_message_size) && (*e == 0 || *e == '\n'))
3d7415f4
LP
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
274static 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
299static 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
312static 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
339static 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
366static 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[] = {
587fec42 381 NULL, /* systemd-import, systemd-export or systemd-pull */
b43d75c3 382 NULL, /* tar, raw */
b6e676ce 383 NULL, /* --verify= */
3d7415f4
LP
384 NULL, /* verify argument */
385 NULL, /* maybe --force */
b6e676ce 386 NULL, /* maybe --read-only */
587fec42
LP
387 NULL, /* if so: the actual URL */
388 NULL, /* maybe --format= */
389 NULL, /* if so: the actual format */
3d7415f4
LP
390 NULL, /* remote */
391 NULL, /* local */
392 NULL
393 };
b6e676ce 394 unsigned k = 0;
3d7415f4
LP
395
396 /* Child */
397
ce30c8dc
LP
398 (void) reset_all_signal_handlers();
399 (void) reset_signal_mask();
3d7415f4
LP
400 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
401
402 pipefd[0] = safe_close(pipefd[0]);
403
587fec42 404 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
3d7415f4
LP
405 log_error_errno(errno, "Failed to dup2() fd: %m");
406 _exit(EXIT_FAILURE);
407 }
408
587fec42
LP
409 if (t->stdout_fd >= 0) {
410 if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) {
411 log_error_errno(errno, "Failed to dup2() fd: %m");
412 _exit(EXIT_FAILURE);
413 }
414
415 if (t->stdout_fd != STDOUT_FILENO)
416 safe_close(t->stdout_fd);
417 } else {
418 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
419 log_error_errno(errno, "Failed to dup2() fd: %m");
420 _exit(EXIT_FAILURE);
421 }
3d7415f4
LP
422 }
423
424 if (pipefd[1] != STDOUT_FILENO && pipefd[1] != STDERR_FILENO)
425 pipefd[1] = safe_close(pipefd[1]);
426
b6e676ce
LP
427 if (t->stdin_fd >= 0) {
428 if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) {
429 log_error_errno(errno, "Failed to dup2() fd: %m");
430 _exit(EXIT_FAILURE);
431 }
432
433 if (t->stdin_fd != STDIN_FILENO)
434 safe_close(t->stdin_fd);
435 } else {
436 int null_fd;
437
438 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
439 if (null_fd < 0) {
440 log_error_errno(errno, "Failed to open /dev/null: %m");
441 _exit(EXIT_FAILURE);
442 }
443
444 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
445 log_error_errno(errno, "Failed to dup2() fd: %m");
446 _exit(EXIT_FAILURE);
447 }
448
449 if (null_fd != STDIN_FILENO)
450 safe_close(null_fd);
3d7415f4
LP
451 }
452
3d7415f4
LP
453 fd_cloexec(STDIN_FILENO, false);
454 fd_cloexec(STDOUT_FILENO, false);
455 fd_cloexec(STDERR_FILENO, false);
456
7079cfef
LP
457 setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1);
458 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1);
3d7415f4 459
b6e676ce
LP
460 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
461 cmd[k++] = SYSTEMD_IMPORT_PATH;
587fec42
LP
462 else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
463 cmd[k++] = SYSTEMD_EXPORT_PATH;
b6e676ce
LP
464 else
465 cmd[k++] = SYSTEMD_PULL_PATH;
466
587fec42 467 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
b6e676ce 468 cmd[k++] = "tar";
b6e676ce 469 else
b43d75c3 470 cmd[k++] = "raw";
b6e676ce
LP
471
472 if (t->verify != _IMPORT_VERIFY_INVALID) {
473 cmd[k++] = "--verify";
474 cmd[k++] = import_verify_to_string(t->verify);
475 }
476
3d7415f4
LP
477 if (t->force_local)
478 cmd[k++] = "--force";
b6e676ce
LP
479 if (t->read_only)
480 cmd[k++] = "--read-only";
3d7415f4 481
587fec42
LP
482 if (t->format) {
483 cmd[k++] = "--format";
484 cmd[k++] = t->format;
485 }
486
487 if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) {
488 if (t->remote)
489 cmd[k++] = t->remote;
490 else
491 cmd[k++] = "-";
492 }
b6e676ce 493
3d7415f4
LP
494 if (t->local)
495 cmd[k++] = t->local;
496 cmd[k] = NULL;
497
b6e676ce
LP
498 execv(cmd[0], (char * const *) cmd);
499 log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]);
3d7415f4
LP
500 _exit(EXIT_FAILURE);
501 }
502
503 pipefd[1] = safe_close(pipefd[1]);
504 t->log_fd = pipefd[0];
505 pipefd[0] = -1;
506
b6e676ce
LP
507 t->stdin_fd = safe_close(t->stdin_fd);
508
3d7415f4
LP
509 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
510 if (r < 0)
511 return r;
512
513 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
514 if (r < 0)
515 return r;
516
517 /* Make sure always process logging before SIGCHLD */
518 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
519 if (r < 0)
520 return r;
521
522 r = sd_bus_emit_signal(
523 t->manager->bus,
524 "/org/freedesktop/import1",
525 "org.freedesktop.import1.Manager",
526 "TransferNew",
527 "uo",
528 t->id,
529 t->object_path);
530 if (r < 0)
531 return r;
532
533 return 0;
534}
535
536static Manager *manager_unref(Manager *m) {
537 Transfer *t;
538
539 if (!m)
540 return NULL;
541
7079cfef
LP
542 sd_event_source_unref(m->notify_event_source);
543 safe_close(m->notify_fd);
544
3d7415f4
LP
545 while ((t = hashmap_first(m->transfers)))
546 transfer_unref(t);
547
548 hashmap_free(m->transfers);
549
550 bus_verify_polkit_async_registry_free(m->polkit_registry);
551
03976f7b 552 m->bus = sd_bus_flush_close_unref(m->bus);
3d7415f4
LP
553 sd_event_unref(m->event);
554
555 free(m);
556 return NULL;
557}
558
559DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
560
7079cfef
LP
561static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
562
563 char buf[NOTIFY_BUFFER_MAX+1];
564 struct iovec iovec = {
565 .iov_base = buf,
566 .iov_len = sizeof(buf)-1,
567 };
568 union {
569 struct cmsghdr cmsghdr;
570 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
571 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
572 } control = {};
573 struct msghdr msghdr = {
574 .msg_iov = &iovec,
575 .msg_iovlen = 1,
576 .msg_control = &control,
577 .msg_controllen = sizeof(control),
578 };
579 struct ucred *ucred = NULL;
580 Manager *m = userdata;
581 struct cmsghdr *cmsg;
582 unsigned percent;
583 char *p, *e;
584 Transfer *t;
585 Iterator i;
586 ssize_t n;
587 int r;
588
589 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
590 if (n < 0) {
591 if (errno == EAGAIN || errno == EINTR)
592 return 0;
593
594 return -errno;
595 }
596
1c8da044
LP
597 cmsg_close_all(&msghdr);
598
3ee897d6
LP
599 CMSG_FOREACH(cmsg, &msghdr)
600 if (cmsg->cmsg_level == SOL_SOCKET &&
601 cmsg->cmsg_type == SCM_CREDENTIALS &&
602 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
7079cfef 603 ucred = (struct ucred*) CMSG_DATA(cmsg);
7079cfef
LP
604
605 if (msghdr.msg_flags & MSG_TRUNC) {
606 log_warning("Got overly long notification datagram, ignoring.");
607 return 0;
608 }
609
610 if (!ucred || ucred->pid <= 0) {
611 log_warning("Got notification datagram lacking credential information, ignoring.");
612 return 0;
613 }
614
615 HASHMAP_FOREACH(t, m->transfers, i)
616 if (ucred->pid == t->pid)
617 break;
618
619 if (!t) {
620 log_warning("Got notification datagram from unexpected peer, ignoring.");
621 return 0;
622 }
623
624 buf[n] = 0;
625
626 p = startswith(buf, "X_IMPORT_PROGRESS=");
627 if (!p) {
628 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
629 if (!p)
630 return 0;
631
632 p += 19;
633 }
634
635 e = strchrnul(p, '\n');
636 *e = 0;
637
638 r = safe_atou(p, &percent);
639 if (r < 0 || percent > 100) {
640 log_warning("Got invalid percent value, ignoring.");
641 return 0;
642 }
643
644 t->progress_percent = percent;
645
646 log_debug("Got percentage from client: %u%%", percent);
647 return 0;
648}
649
3d7415f4
LP
650static int manager_new(Manager **ret) {
651 _cleanup_(manager_unrefp) Manager *m = NULL;
7079cfef
LP
652 static const union sockaddr_union sa = {
653 .un.sun_family = AF_UNIX,
654 .un.sun_path = "/run/systemd/import/notify",
655 };
656 static const int one = 1;
3d7415f4
LP
657 int r;
658
659 assert(ret);
660
661 m = new0(Manager, 1);
662 if (!m)
663 return -ENOMEM;
664
665 r = sd_event_default(&m->event);
666 if (r < 0)
667 return r;
668
669 sd_event_set_watchdog(m->event, true);
670
671 r = sd_bus_default_system(&m->bus);
672 if (r < 0)
673 return r;
674
7079cfef
LP
675 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
676 if (m->notify_fd < 0)
677 return -errno;
678
679 (void) mkdir_parents_label(sa.un.sun_path, 0755);
680 (void) unlink(sa.un.sun_path);
681
682 if (bind(m->notify_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)) < 0)
683 return -errno;
684
685 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
686 return -errno;
687
688 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
689 if (r < 0)
690 return r;
691
3d7415f4
LP
692 *ret = m;
693 m = NULL;
694
695 return 0;
696}
697
b43d75c3 698static Transfer *manager_find(Manager *m, TransferType type, const char *remote) {
3d7415f4
LP
699 Transfer *t;
700 Iterator i;
701
702 assert(m);
703 assert(type >= 0);
704 assert(type < _TRANSFER_TYPE_MAX);
705
706 HASHMAP_FOREACH(t, m->transfers, i) {
707
708 if (t->type == type &&
b43d75c3 709 streq_ptr(t->remote, remote))
3d7415f4
LP
710 return t;
711 }
712
713 return NULL;
714}
715
19070062 716static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
b6e676ce
LP
717 _cleanup_(transfer_unrefp) Transfer *t = NULL;
718 int fd, force, read_only, r;
719 const char *local, *object;
720 Manager *m = userdata;
721 TransferType type;
722 uint32_t id;
723
19070062
LP
724 assert(msg);
725 assert(m);
726
b6e676ce
LP
727 r = bus_verify_polkit_async(
728 msg,
729 CAP_SYS_ADMIN,
730 "org.freedesktop.import1.import",
403ed0e5 731 NULL,
b6e676ce
LP
732 false,
733 UID_INVALID,
734 &m->polkit_registry,
735 error);
736 if (r < 0)
737 return r;
738 if (r == 0)
739 return 1; /* Will call us back */
740
741 r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
742 if (r < 0)
743 return r;
744
745 if (!machine_name_is_valid(local))
746 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
747
748 r = setup_machine_directory((uint64_t) -1, error);
749 if (r < 0)
750 return r;
751
752 type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
753
754 r = transfer_new(m, &t);
755 if (r < 0)
756 return r;
757
758 t->type = type;
759 t->force_local = force;
760 t->read_only = read_only;
761
762 t->local = strdup(local);
763 if (!t->local)
764 return -ENOMEM;
765
766 t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
767 if (t->stdin_fd < 0)
768 return -errno;
769
770 r = transfer_start(t);
771 if (r < 0)
772 return r;
773
774 object = t->object_path;
775 id = t->id;
776 t = NULL;
777
778 return sd_bus_reply_method_return(msg, "uo", id, object);
779}
780
19070062 781static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
587fec42
LP
782 _cleanup_(transfer_unrefp) Transfer *t = NULL;
783 int fd, r;
784 const char *local, *object, *format;
785 Manager *m = userdata;
786 TransferType type;
787 uint32_t id;
788
19070062
LP
789 assert(msg);
790 assert(m);
791
587fec42
LP
792 r = bus_verify_polkit_async(
793 msg,
794 CAP_SYS_ADMIN,
795 "org.freedesktop.import1.export",
403ed0e5 796 NULL,
587fec42
LP
797 false,
798 UID_INVALID,
799 &m->polkit_registry,
800 error);
801 if (r < 0)
802 return r;
803 if (r == 0)
804 return 1; /* Will call us back */
805
806 r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
807 if (r < 0)
808 return r;
809
810 if (!machine_name_is_valid(local))
811 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
812
813 type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
814
815 r = transfer_new(m, &t);
816 if (r < 0)
817 return r;
818
819 t->type = type;
820
821 if (!isempty(format)) {
822 t->format = strdup(format);
823 if (!t->format)
824 return -ENOMEM;
825 }
826
827 t->local = strdup(local);
828 if (!t->local)
829 return -ENOMEM;
830
831 t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
832 if (t->stdout_fd < 0)
833 return -errno;
834
835 r = transfer_start(t);
836 if (r < 0)
837 return r;
838
839 object = t->object_path;
840 id = t->id;
841 t = NULL;
842
843 return sd_bus_reply_method_return(msg, "uo", id, object);
844}
845
19070062 846static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
3d7415f4
LP
847 _cleanup_(transfer_unrefp) Transfer *t = NULL;
848 const char *remote, *local, *verify, *object;
849 Manager *m = userdata;
850 ImportVerify v;
851 TransferType type;
852 int force, r;
853 uint32_t id;
854
3d7415f4
LP
855 assert(msg);
856 assert(m);
857
858 r = bus_verify_polkit_async(
859 msg,
860 CAP_SYS_ADMIN,
861 "org.freedesktop.import1.pull",
403ed0e5 862 NULL,
3d7415f4 863 false,
c529695e 864 UID_INVALID,
3d7415f4
LP
865 &m->polkit_registry,
866 error);
867 if (r < 0)
868 return r;
869 if (r == 0)
870 return 1; /* Will call us back */
871
872 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
873 if (r < 0)
874 return r;
875
876 if (!http_url_is_valid(remote))
877 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
878
879 if (isempty(local))
880 local = NULL;
881 else if (!machine_name_is_valid(local))
882 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
883
884 if (isempty(verify))
885 v = IMPORT_VERIFY_SIGNATURE;
886 else
887 v = import_verify_from_string(verify);
888 if (v < 0)
889 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
890
4cee5eed 891 r = setup_machine_directory((uint64_t) -1, error);
ce06fdfb
LP
892 if (r < 0)
893 return r;
894
b6e676ce 895 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
3d7415f4 896
b43d75c3 897 if (manager_find(m, type, remote))
3d7415f4
LP
898 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
899
900 r = transfer_new(m, &t);
901 if (r < 0)
902 return r;
903
904 t->type = type;
905 t->verify = v;
906 t->force_local = force;
907
908 t->remote = strdup(remote);
909 if (!t->remote)
910 return -ENOMEM;
911
b6e676ce
LP
912 if (local) {
913 t->local = strdup(local);
914 if (!t->local)
915 return -ENOMEM;
916 }
3d7415f4
LP
917
918 r = transfer_start(t);
919 if (r < 0)
920 return r;
921
922 object = t->object_path;
923 id = t->id;
924 t = NULL;
925
926 return sd_bus_reply_method_return(msg, "uo", id, object);
927}
928
19070062 929static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
4afd3348 930 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3d7415f4
LP
931 Manager *m = userdata;
932 Transfer *t;
933 Iterator i;
934 int r;
935
3d7415f4
LP
936 assert(msg);
937 assert(m);
938
939 r = sd_bus_message_new_method_return(msg, &reply);
940 if (r < 0)
941 return r;
942
7079cfef 943 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
3d7415f4
LP
944 if (r < 0)
945 return r;
946
947 HASHMAP_FOREACH(t, m->transfers, i) {
948
949 r = sd_bus_message_append(
950 reply,
7079cfef 951 "(usssdo)",
3d7415f4
LP
952 t->id,
953 transfer_type_to_string(t->type),
954 t->remote,
955 t->local,
7079cfef 956 (double) t->progress_percent / 100.0,
3d7415f4
LP
957 t->object_path);
958 if (r < 0)
959 return r;
960 }
961
962 r = sd_bus_message_close_container(reply);
963 if (r < 0)
964 return r;
965
9030ca46 966 return sd_bus_send(NULL, reply, NULL);
3d7415f4
LP
967}
968
19070062 969static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
3d7415f4
LP
970 Transfer *t = userdata;
971 int r;
972
3d7415f4
LP
973 assert(msg);
974 assert(t);
975
976 r = bus_verify_polkit_async(
977 msg,
978 CAP_SYS_ADMIN,
979 "org.freedesktop.import1.pull",
403ed0e5 980 NULL,
3d7415f4 981 false,
c529695e 982 UID_INVALID,
3d7415f4
LP
983 &t->manager->polkit_registry,
984 error);
985 if (r < 0)
986 return r;
987 if (r == 0)
988 return 1; /* Will call us back */
989
990 r = transfer_cancel(t);
991 if (r < 0)
992 return r;
993
994 return sd_bus_reply_method_return(msg, NULL);
995}
996
19070062 997static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
3d7415f4
LP
998 Manager *m = userdata;
999 Transfer *t;
1000 uint32_t id;
1001 int r;
1002
3d7415f4
LP
1003 assert(msg);
1004 assert(m);
1005
1006 r = bus_verify_polkit_async(
1007 msg,
1008 CAP_SYS_ADMIN,
1009 "org.freedesktop.import1.pull",
403ed0e5 1010 NULL,
3d7415f4 1011 false,
c529695e 1012 UID_INVALID,
3d7415f4
LP
1013 &m->polkit_registry,
1014 error);
1015 if (r < 0)
1016 return r;
1017 if (r == 0)
1018 return 1; /* Will call us back */
1019
1020 r = sd_bus_message_read(msg, "u", &id);
1021 if (r < 0)
1022 return r;
1023 if (id <= 0)
1024 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
1025
1026 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1027 if (!t)
09d46cfd 1028 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
3d7415f4
LP
1029
1030 r = transfer_cancel(t);
1031 if (r < 0)
1032 return r;
1033
1034 return sd_bus_reply_method_return(msg, NULL);
1035}
1036
7079cfef
LP
1037static int property_get_progress(
1038 sd_bus *bus,
1039 const char *path,
1040 const char *interface,
1041 const char *property,
1042 sd_bus_message *reply,
1043 void *userdata,
1044 sd_bus_error *error) {
1045
1046 Transfer *t = userdata;
1047
1048 assert(bus);
1049 assert(reply);
1050 assert(t);
1051
1052 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
1053}
1054
3d7415f4
LP
1055static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1056static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1057
1058static const sd_bus_vtable transfer_vtable[] = {
1059 SD_BUS_VTABLE_START(0),
1060 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1061 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1062 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1063 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1064 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
7079cfef 1065 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
3d7415f4
LP
1066 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
1067 SD_BUS_SIGNAL("LogMessage", "us", 0),
1068 SD_BUS_VTABLE_END,
1069};
1070
1071static const sd_bus_vtable manager_vtable[] = {
1072 SD_BUS_VTABLE_START(0),
b6e676ce
LP
1073 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1074 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
587fec42
LP
1075 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1076 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
3d7415f4
LP
1077 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1078 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
7079cfef 1079 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
3d7415f4
LP
1080 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
1081 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1082 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1083 SD_BUS_VTABLE_END,
1084};
1085
1086static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1087 Manager *m = userdata;
1088 Transfer *t;
1089 const char *p;
1090 uint32_t id;
1091 int r;
1092
1093 assert(bus);
1094 assert(path);
1095 assert(interface);
1096 assert(found);
1097 assert(m);
1098
1099 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1100 if (!p)
1101 return 0;
1102
1103 r = safe_atou32(p, &id);
1104 if (r < 0 || id == 0)
1105 return 0;
1106
1107 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1108 if (!t)
1109 return 0;
1110
1111 *found = t;
1112 return 1;
1113}
1114
1115static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1116 _cleanup_strv_free_ char **l = NULL;
1117 Manager *m = userdata;
1118 Transfer *t;
1119 unsigned k = 0;
1120 Iterator i;
1121
1122 l = new0(char*, hashmap_size(m->transfers) + 1);
1123 if (!l)
1124 return -ENOMEM;
1125
1126 HASHMAP_FOREACH(t, m->transfers, i) {
1127
1128 l[k] = strdup(t->object_path);
1129 if (!l[k])
1130 return -ENOMEM;
1131
1132 k++;
1133 }
1134
1135 *nodes = l;
1136 l = NULL;
1137
1138 return 1;
1139}
1140
1141static int manager_add_bus_objects(Manager *m) {
1142 int r;
1143
1144 assert(m);
1145
1146 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1147 if (r < 0)
1148 return log_error_errno(r, "Failed to register object: %m");
1149
1150 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1151 if (r < 0)
1152 return log_error_errno(r, "Failed to register object: %m");
1153
1154 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1155 if (r < 0)
1156 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1157
1158 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1159 if (r < 0)
1160 return log_error_errno(r, "Failed to register name: %m");
1161
1162 r = sd_bus_attach_event(m->bus, m->event, 0);
1163 if (r < 0)
1164 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1165
1166 return 0;
1167}
1168
1169static bool manager_check_idle(void *userdata) {
1170 Manager *m = userdata;
1171
1172 return hashmap_isempty(m->transfers);
1173}
1174
1175static int manager_run(Manager *m) {
1176 assert(m);
1177
1178 return bus_event_loop_with_idle(
1179 m->event,
1180 m->bus,
1181 "org.freedesktop.import1",
1182 DEFAULT_EXIT_USEC,
1183 manager_check_idle,
1184 m);
1185}
1186
1187int main(int argc, char *argv[]) {
1188 _cleanup_(manager_unrefp) Manager *m = NULL;
1189 int r;
1190
1191 log_set_target(LOG_TARGET_AUTO);
1192 log_parse_environment();
1193 log_open();
1194
1195 umask(0022);
1196
1197 if (argc != 1) {
1198 log_error("This program takes no arguments.");
1199 r = -EINVAL;
1200 goto finish;
1201 }
1202
72c0a2c2 1203 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
3d7415f4
LP
1204
1205 r = manager_new(&m);
1206 if (r < 0) {
1207 log_error_errno(r, "Failed to allocate manager object: %m");
1208 goto finish;
1209 }
1210
1211 r = manager_add_bus_objects(m);
1212 if (r < 0)
1213 goto finish;
1214
1215 r = manager_run(m);
1216 if (r < 0) {
1217 log_error_errno(r, "Failed to run event loop: %m");
1218 goto finish;
1219 }
1220
1221finish:
1222 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1223}