]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/importd.c
sd-bus: when disconnecting a slot, also reset its memory
[thirdparty/systemd.git] / src / import / importd.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
3d7415f4
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <sys/prctl.h>
d7e454ba 22#include <sys/wait.h>
3d7415f4
LP
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 145
6b430fdb 146 return mfree(t);
3d7415f4
LP
147}
148
149DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_unref);
150
151static int transfer_new(Manager *m, Transfer **ret) {
152 _cleanup_(transfer_unrefp) Transfer *t = NULL;
153 uint32_t id;
154 int r;
155
156 assert(m);
157 assert(ret);
158
159 if (hashmap_size(m->transfers) >= TRANSFERS_MAX)
160 return -E2BIG;
161
162 r = hashmap_ensure_allocated(&m->transfers, &trivial_hash_ops);
163 if (r < 0)
164 return r;
165
166 t = new0(Transfer, 1);
167 if (!t)
168 return -ENOMEM;
169
170 t->type = _TRANSFER_TYPE_INVALID;
171 t->log_fd = -1;
b6e676ce 172 t->stdin_fd = -1;
3be78ab2 173 t->stdout_fd = -1;
b6e676ce 174 t->verify = _IMPORT_VERIFY_INVALID;
3d7415f4
LP
175
176 id = m->current_transfer_id + 1;
177
178 if (asprintf(&t->object_path, "/org/freedesktop/import1/transfer/_%" PRIu32, id) < 0)
179 return -ENOMEM;
180
181 r = hashmap_put(m->transfers, UINT32_TO_PTR(id), t);
182 if (r < 0)
183 return r;
184
185 m->current_transfer_id = id;
186
187 t->manager = m;
188 t->id = id;
189
190 *ret = t;
191 t = NULL;
192
193 return 0;
194}
195
196static void transfer_send_log_line(Transfer *t, const char *line) {
197 int r, priority = LOG_INFO;
198
199 assert(t);
200 assert(line);
201
202 syslog_parse_priority(&line, &priority, true);
203
09d46cfd 204 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
3d7415f4
LP
205
206 r = sd_bus_emit_signal(
207 t->manager->bus,
208 t->object_path,
209 "org.freedesktop.import1.Transfer",
210 "LogMessage",
211 "us",
212 priority,
213 line);
214 if (r < 0)
215 log_error_errno(r, "Cannot emit message: %m");
216 }
217
218static void transfer_send_logs(Transfer *t, bool flush) {
219 assert(t);
220
221 /* Try to send out all log messages, if we can. But if we
222 * can't we remove the messages from the buffer, but don't
223 * fail */
224
225 while (t->log_message_size > 0) {
226 _cleanup_free_ char *n = NULL;
227 char *e;
228
229 if (t->log_message_size >= sizeof(t->log_message))
230 e = t->log_message + sizeof(t->log_message);
231 else {
232 char *a, *b;
233
234 a = memchr(t->log_message, 0, t->log_message_size);
235 b = memchr(t->log_message, '\n', t->log_message_size);
236
237 if (a && b)
238 e = a < b ? a : b;
239 else if (a)
240 e = a;
241 else
242 e = b;
243 }
244
245 if (!e) {
246 if (!flush)
247 return;
248
249 e = t->log_message + t->log_message_size;
250 }
251
252 n = strndup(t->log_message, e - t->log_message);
253
254 /* Skip over NUL and newlines */
3dad3203 255 while (e < t->log_message + t->log_message_size && (*e == 0 || *e == '\n'))
3d7415f4
LP
256 e++;
257
258 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
259 t->log_message_size -= e - t->log_message;
260
261 if (!n) {
262 log_oom();
263 continue;
264 }
265
266 if (isempty(n))
267 continue;
268
269 transfer_send_log_line(t, n);
270 }
271}
272
273static int transfer_finalize(Transfer *t, bool success) {
274 int r;
275
276 assert(t);
277
278 transfer_send_logs(t, true);
279
280 r = sd_bus_emit_signal(
281 t->manager->bus,
282 "/org/freedesktop/import1",
283 "org.freedesktop.import1.Manager",
284 "TransferRemoved",
285 "uos",
286 t->id,
287 t->object_path,
288 success ? "done" :
289 t->n_canceled > 0 ? "canceled" : "failed");
290
291 if (r < 0)
292 log_error_errno(r, "Cannot emit message: %m");
293
294 transfer_unref(t);
295 return 0;
296}
297
298static int transfer_cancel(Transfer *t) {
299 int r;
300
301 assert(t);
302
303 r = kill_and_sigcont(t->pid, t->n_canceled < 3 ? SIGTERM : SIGKILL);
304 if (r < 0)
305 return r;
306
307 t->n_canceled++;
308 return 0;
309}
310
311static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
312 Transfer *t = userdata;
313 bool success = false;
314
315 assert(s);
316 assert(t);
317
318 if (si->si_code == CLD_EXITED) {
319 if (si->si_status != 0)
320 log_error("Import process failed with exit code %i.", si->si_status);
321 else {
322 log_debug("Import process succeeded.");
323 success = true;
324 }
325
3742095b 326 } else if (IN_SET(si->si_code, CLD_KILLED, CLD_DUMPED))
3d7415f4
LP
327
328 log_error("Import process terminated by signal %s.", signal_to_string(si->si_status));
329 else
330 log_error("Import process failed due to unknown reason.");
331
332 t->pid = 0;
333
334 return transfer_finalize(t, success);
335}
336
337static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
338 Transfer *t = userdata;
339 ssize_t l;
340
341 assert(s);
342 assert(t);
343
344 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
345 if (l <= 0) {
346 /* EOF/read error. We just close the pipe here, and
347 * close the watch, waiting for the SIGCHLD to arrive,
348 * before we do anything else. */
349
350 if (l < 0)
351 log_error_errno(errno, "Failed to read log message: %m");
352
353 t->log_event_source = sd_event_source_unref(t->log_event_source);
354 return 0;
355 }
356
357 t->log_message_size += l;
358
359 transfer_send_logs(t, false);
360
361 return 0;
362}
363
364static int transfer_start(Transfer *t) {
365 _cleanup_close_pair_ int pipefd[2] = { -1, -1 };
366 int r;
367
368 assert(t);
369 assert(t->pid <= 0);
370
371 if (pipe2(pipefd, O_CLOEXEC) < 0)
372 return -errno;
373
4c253ed1
LP
374 r = safe_fork("(sd-transfer)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &t->pid);
375 if (r < 0)
376 return r;
377 if (r == 0) {
3d7415f4 378 const char *cmd[] = {
587fec42 379 NULL, /* systemd-import, systemd-export or systemd-pull */
b43d75c3 380 NULL, /* tar, raw */
b6e676ce 381 NULL, /* --verify= */
3d7415f4
LP
382 NULL, /* verify argument */
383 NULL, /* maybe --force */
b6e676ce 384 NULL, /* maybe --read-only */
587fec42
LP
385 NULL, /* if so: the actual URL */
386 NULL, /* maybe --format= */
387 NULL, /* if so: the actual format */
3d7415f4
LP
388 NULL, /* remote */
389 NULL, /* local */
390 NULL
391 };
b6e676ce 392 unsigned k = 0;
3d7415f4
LP
393
394 /* Child */
395
3d7415f4
LP
396 pipefd[0] = safe_close(pipefd[0]);
397
587fec42 398 if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO) {
3d7415f4
LP
399 log_error_errno(errno, "Failed to dup2() fd: %m");
400 _exit(EXIT_FAILURE);
401 }
402
587fec42
LP
403 if (t->stdout_fd >= 0) {
404 if (dup2(t->stdout_fd, STDOUT_FILENO) != STDOUT_FILENO) {
405 log_error_errno(errno, "Failed to dup2() fd: %m");
406 _exit(EXIT_FAILURE);
407 }
408
409 if (t->stdout_fd != STDOUT_FILENO)
410 safe_close(t->stdout_fd);
411 } else {
412 if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
413 log_error_errno(errno, "Failed to dup2() fd: %m");
414 _exit(EXIT_FAILURE);
415 }
3d7415f4
LP
416 }
417
ec2ce0c5 418 if (!IN_SET(pipefd[1], STDOUT_FILENO, STDERR_FILENO))
3d7415f4
LP
419 pipefd[1] = safe_close(pipefd[1]);
420
b6e676ce
LP
421 if (t->stdin_fd >= 0) {
422 if (dup2(t->stdin_fd, STDIN_FILENO) != STDIN_FILENO) {
423 log_error_errno(errno, "Failed to dup2() fd: %m");
424 _exit(EXIT_FAILURE);
425 }
426
427 if (t->stdin_fd != STDIN_FILENO)
428 safe_close(t->stdin_fd);
429 } else {
430 int null_fd;
431
432 null_fd = open("/dev/null", O_RDONLY|O_NOCTTY);
433 if (null_fd < 0) {
434 log_error_errno(errno, "Failed to open /dev/null: %m");
435 _exit(EXIT_FAILURE);
436 }
437
438 if (dup2(null_fd, STDIN_FILENO) != STDIN_FILENO) {
439 log_error_errno(errno, "Failed to dup2() fd: %m");
440 _exit(EXIT_FAILURE);
441 }
442
443 if (null_fd != STDIN_FILENO)
444 safe_close(null_fd);
3d7415f4
LP
445 }
446
913f38e4 447 stdio_unset_cloexec();
3d7415f4 448
df8067ef
ZJS
449 if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 ||
450 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) {
451 log_error_errno(errno, "setenv() failed: %m");
452 _exit(EXIT_FAILURE);
453 }
3d7415f4 454
b6e676ce
LP
455 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
456 cmd[k++] = SYSTEMD_IMPORT_PATH;
587fec42
LP
457 else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
458 cmd[k++] = SYSTEMD_EXPORT_PATH;
b6e676ce
LP
459 else
460 cmd[k++] = SYSTEMD_PULL_PATH;
461
587fec42 462 if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
b6e676ce 463 cmd[k++] = "tar";
b6e676ce 464 else
b43d75c3 465 cmd[k++] = "raw";
b6e676ce
LP
466
467 if (t->verify != _IMPORT_VERIFY_INVALID) {
468 cmd[k++] = "--verify";
469 cmd[k++] = import_verify_to_string(t->verify);
470 }
471
3d7415f4
LP
472 if (t->force_local)
473 cmd[k++] = "--force";
b6e676ce
LP
474 if (t->read_only)
475 cmd[k++] = "--read-only";
3d7415f4 476
587fec42
LP
477 if (t->format) {
478 cmd[k++] = "--format";
479 cmd[k++] = t->format;
480 }
481
482 if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) {
483 if (t->remote)
484 cmd[k++] = t->remote;
485 else
486 cmd[k++] = "-";
487 }
b6e676ce 488
3d7415f4
LP
489 if (t->local)
490 cmd[k++] = t->local;
491 cmd[k] = NULL;
492
b6e676ce
LP
493 execv(cmd[0], (char * const *) cmd);
494 log_error_errno(errno, "Failed to execute %s tool: %m", cmd[0]);
3d7415f4
LP
495 _exit(EXIT_FAILURE);
496 }
497
498 pipefd[1] = safe_close(pipefd[1]);
499 t->log_fd = pipefd[0];
500 pipefd[0] = -1;
501
b6e676ce
LP
502 t->stdin_fd = safe_close(t->stdin_fd);
503
3d7415f4
LP
504 r = sd_event_add_child(t->manager->event, &t->pid_event_source, t->pid, WEXITED, transfer_on_pid, t);
505 if (r < 0)
506 return r;
507
508 r = sd_event_add_io(t->manager->event, &t->log_event_source, t->log_fd, EPOLLIN, transfer_on_log, t);
509 if (r < 0)
510 return r;
511
512 /* Make sure always process logging before SIGCHLD */
513 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
514 if (r < 0)
515 return r;
516
517 r = sd_bus_emit_signal(
518 t->manager->bus,
519 "/org/freedesktop/import1",
520 "org.freedesktop.import1.Manager",
521 "TransferNew",
522 "uo",
523 t->id,
524 t->object_path);
525 if (r < 0)
526 return r;
527
528 return 0;
529}
530
531static Manager *manager_unref(Manager *m) {
532 Transfer *t;
533
534 if (!m)
535 return NULL;
536
7079cfef
LP
537 sd_event_source_unref(m->notify_event_source);
538 safe_close(m->notify_fd);
539
3d7415f4
LP
540 while ((t = hashmap_first(m->transfers)))
541 transfer_unref(t);
542
543 hashmap_free(m->transfers);
544
545 bus_verify_polkit_async_registry_free(m->polkit_registry);
546
03976f7b 547 m->bus = sd_bus_flush_close_unref(m->bus);
3d7415f4
LP
548 sd_event_unref(m->event);
549
6b430fdb 550 return mfree(m);
3d7415f4
LP
551}
552
553DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
554
7079cfef
LP
555static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
556
557 char buf[NOTIFY_BUFFER_MAX+1];
558 struct iovec iovec = {
559 .iov_base = buf,
560 .iov_len = sizeof(buf)-1,
561 };
562 union {
563 struct cmsghdr cmsghdr;
564 uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) +
565 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)];
566 } control = {};
567 struct msghdr msghdr = {
568 .msg_iov = &iovec,
569 .msg_iovlen = 1,
570 .msg_control = &control,
571 .msg_controllen = sizeof(control),
572 };
573 struct ucred *ucred = NULL;
574 Manager *m = userdata;
575 struct cmsghdr *cmsg;
576 unsigned percent;
577 char *p, *e;
578 Transfer *t;
579 Iterator i;
580 ssize_t n;
581 int r;
582
583 n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
584 if (n < 0) {
3742095b 585 if (IN_SET(errno, EAGAIN, EINTR))
7079cfef
LP
586 return 0;
587
588 return -errno;
589 }
590
1c8da044
LP
591 cmsg_close_all(&msghdr);
592
3ee897d6
LP
593 CMSG_FOREACH(cmsg, &msghdr)
594 if (cmsg->cmsg_level == SOL_SOCKET &&
595 cmsg->cmsg_type == SCM_CREDENTIALS &&
596 cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
7079cfef 597 ucred = (struct ucred*) CMSG_DATA(cmsg);
7079cfef
LP
598
599 if (msghdr.msg_flags & MSG_TRUNC) {
600 log_warning("Got overly long notification datagram, ignoring.");
601 return 0;
602 }
603
604 if (!ucred || ucred->pid <= 0) {
605 log_warning("Got notification datagram lacking credential information, ignoring.");
606 return 0;
607 }
608
609 HASHMAP_FOREACH(t, m->transfers, i)
610 if (ucred->pid == t->pid)
611 break;
612
613 if (!t) {
614 log_warning("Got notification datagram from unexpected peer, ignoring.");
615 return 0;
616 }
617
618 buf[n] = 0;
619
620 p = startswith(buf, "X_IMPORT_PROGRESS=");
621 if (!p) {
622 p = strstr(buf, "\nX_IMPORT_PROGRESS=");
623 if (!p)
624 return 0;
625
626 p += 19;
627 }
628
629 e = strchrnul(p, '\n');
630 *e = 0;
631
632 r = safe_atou(p, &percent);
633 if (r < 0 || percent > 100) {
634 log_warning("Got invalid percent value, ignoring.");
635 return 0;
636 }
637
638 t->progress_percent = percent;
639
640 log_debug("Got percentage from client: %u%%", percent);
641 return 0;
642}
643
3d7415f4
LP
644static int manager_new(Manager **ret) {
645 _cleanup_(manager_unrefp) Manager *m = NULL;
7079cfef
LP
646 static const union sockaddr_union sa = {
647 .un.sun_family = AF_UNIX,
648 .un.sun_path = "/run/systemd/import/notify",
649 };
650 static const int one = 1;
3d7415f4
LP
651 int r;
652
653 assert(ret);
654
655 m = new0(Manager, 1);
656 if (!m)
657 return -ENOMEM;
658
659 r = sd_event_default(&m->event);
660 if (r < 0)
661 return r;
662
663 sd_event_set_watchdog(m->event, true);
664
665 r = sd_bus_default_system(&m->bus);
666 if (r < 0)
667 return r;
668
7079cfef
LP
669 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
670 if (m->notify_fd < 0)
671 return -errno;
672
673 (void) mkdir_parents_label(sa.un.sun_path, 0755);
674 (void) unlink(sa.un.sun_path);
675
fc2fffe7 676 if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
7079cfef
LP
677 return -errno;
678
679 if (setsockopt(m->notify_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
680 return -errno;
681
682 r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_on_notify, m);
683 if (r < 0)
684 return r;
685
3d7415f4
LP
686 *ret = m;
687 m = NULL;
688
689 return 0;
690}
691
b43d75c3 692static Transfer *manager_find(Manager *m, TransferType type, const char *remote) {
3d7415f4
LP
693 Transfer *t;
694 Iterator i;
695
696 assert(m);
697 assert(type >= 0);
698 assert(type < _TRANSFER_TYPE_MAX);
699
700 HASHMAP_FOREACH(t, m->transfers, i) {
701
702 if (t->type == type &&
b43d75c3 703 streq_ptr(t->remote, remote))
3d7415f4
LP
704 return t;
705 }
706
707 return NULL;
708}
709
19070062 710static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
b6e676ce
LP
711 _cleanup_(transfer_unrefp) Transfer *t = NULL;
712 int fd, force, read_only, r;
713 const char *local, *object;
714 Manager *m = userdata;
715 TransferType type;
716 uint32_t id;
717
19070062
LP
718 assert(msg);
719 assert(m);
720
b6e676ce
LP
721 r = bus_verify_polkit_async(
722 msg,
723 CAP_SYS_ADMIN,
724 "org.freedesktop.import1.import",
403ed0e5 725 NULL,
b6e676ce
LP
726 false,
727 UID_INVALID,
728 &m->polkit_registry,
729 error);
730 if (r < 0)
731 return r;
732 if (r == 0)
733 return 1; /* Will call us back */
734
735 r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
736 if (r < 0)
737 return r;
738
739 if (!machine_name_is_valid(local))
740 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
741
742 r = setup_machine_directory((uint64_t) -1, error);
743 if (r < 0)
744 return r;
745
746 type = streq_ptr(sd_bus_message_get_member(msg), "ImportTar") ? TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
747
748 r = transfer_new(m, &t);
749 if (r < 0)
750 return r;
751
752 t->type = type;
753 t->force_local = force;
754 t->read_only = read_only;
755
756 t->local = strdup(local);
757 if (!t->local)
758 return -ENOMEM;
759
760 t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
761 if (t->stdin_fd < 0)
762 return -errno;
763
764 r = transfer_start(t);
765 if (r < 0)
766 return r;
767
768 object = t->object_path;
769 id = t->id;
770 t = NULL;
771
772 return sd_bus_reply_method_return(msg, "uo", id, object);
773}
774
19070062 775static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
587fec42
LP
776 _cleanup_(transfer_unrefp) Transfer *t = NULL;
777 int fd, r;
778 const char *local, *object, *format;
779 Manager *m = userdata;
780 TransferType type;
781 uint32_t id;
782
19070062
LP
783 assert(msg);
784 assert(m);
785
587fec42
LP
786 r = bus_verify_polkit_async(
787 msg,
788 CAP_SYS_ADMIN,
789 "org.freedesktop.import1.export",
403ed0e5 790 NULL,
587fec42
LP
791 false,
792 UID_INVALID,
793 &m->polkit_registry,
794 error);
795 if (r < 0)
796 return r;
797 if (r == 0)
798 return 1; /* Will call us back */
799
800 r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
801 if (r < 0)
802 return r;
803
804 if (!machine_name_is_valid(local))
805 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
806
807 type = streq_ptr(sd_bus_message_get_member(msg), "ExportTar") ? TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
808
809 r = transfer_new(m, &t);
810 if (r < 0)
811 return r;
812
813 t->type = type;
814
815 if (!isempty(format)) {
816 t->format = strdup(format);
817 if (!t->format)
818 return -ENOMEM;
819 }
820
821 t->local = strdup(local);
822 if (!t->local)
823 return -ENOMEM;
824
825 t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
826 if (t->stdout_fd < 0)
827 return -errno;
828
829 r = transfer_start(t);
830 if (r < 0)
831 return r;
832
833 object = t->object_path;
834 id = t->id;
835 t = NULL;
836
837 return sd_bus_reply_method_return(msg, "uo", id, object);
838}
839
19070062 840static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
3d7415f4
LP
841 _cleanup_(transfer_unrefp) Transfer *t = NULL;
842 const char *remote, *local, *verify, *object;
843 Manager *m = userdata;
844 ImportVerify v;
845 TransferType type;
846 int force, r;
847 uint32_t id;
848
3d7415f4
LP
849 assert(msg);
850 assert(m);
851
852 r = bus_verify_polkit_async(
853 msg,
854 CAP_SYS_ADMIN,
855 "org.freedesktop.import1.pull",
403ed0e5 856 NULL,
3d7415f4 857 false,
c529695e 858 UID_INVALID,
3d7415f4
LP
859 &m->polkit_registry,
860 error);
861 if (r < 0)
862 return r;
863 if (r == 0)
864 return 1; /* Will call us back */
865
866 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
867 if (r < 0)
868 return r;
869
870 if (!http_url_is_valid(remote))
871 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "URL %s is invalid", remote);
872
873 if (isempty(local))
874 local = NULL;
875 else if (!machine_name_is_valid(local))
876 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
877
878 if (isempty(verify))
879 v = IMPORT_VERIFY_SIGNATURE;
880 else
881 v = import_verify_from_string(verify);
882 if (v < 0)
883 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown verification mode %s", verify);
884
4cee5eed 885 r = setup_machine_directory((uint64_t) -1, error);
ce06fdfb
LP
886 if (r < 0)
887 return r;
888
b6e676ce 889 type = streq_ptr(sd_bus_message_get_member(msg), "PullTar") ? TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
3d7415f4 890
b43d75c3 891 if (manager_find(m, type, remote))
3d7415f4
LP
892 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS, "Transfer for %s already in progress.", remote);
893
894 r = transfer_new(m, &t);
895 if (r < 0)
896 return r;
897
898 t->type = type;
899 t->verify = v;
900 t->force_local = force;
901
902 t->remote = strdup(remote);
903 if (!t->remote)
904 return -ENOMEM;
905
b6e676ce
LP
906 if (local) {
907 t->local = strdup(local);
908 if (!t->local)
909 return -ENOMEM;
910 }
3d7415f4
LP
911
912 r = transfer_start(t);
913 if (r < 0)
914 return r;
915
916 object = t->object_path;
917 id = t->id;
918 t = NULL;
919
920 return sd_bus_reply_method_return(msg, "uo", id, object);
921}
922
19070062 923static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
4afd3348 924 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
3d7415f4
LP
925 Manager *m = userdata;
926 Transfer *t;
927 Iterator i;
928 int r;
929
3d7415f4
LP
930 assert(msg);
931 assert(m);
932
933 r = sd_bus_message_new_method_return(msg, &reply);
934 if (r < 0)
935 return r;
936
7079cfef 937 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
3d7415f4
LP
938 if (r < 0)
939 return r;
940
941 HASHMAP_FOREACH(t, m->transfers, i) {
942
943 r = sd_bus_message_append(
944 reply,
7079cfef 945 "(usssdo)",
3d7415f4
LP
946 t->id,
947 transfer_type_to_string(t->type),
948 t->remote,
949 t->local,
7079cfef 950 (double) t->progress_percent / 100.0,
3d7415f4
LP
951 t->object_path);
952 if (r < 0)
953 return r;
954 }
955
956 r = sd_bus_message_close_container(reply);
957 if (r < 0)
958 return r;
959
9030ca46 960 return sd_bus_send(NULL, reply, NULL);
3d7415f4
LP
961}
962
19070062 963static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
3d7415f4
LP
964 Transfer *t = userdata;
965 int r;
966
3d7415f4
LP
967 assert(msg);
968 assert(t);
969
970 r = bus_verify_polkit_async(
971 msg,
972 CAP_SYS_ADMIN,
973 "org.freedesktop.import1.pull",
403ed0e5 974 NULL,
3d7415f4 975 false,
c529695e 976 UID_INVALID,
3d7415f4
LP
977 &t->manager->polkit_registry,
978 error);
979 if (r < 0)
980 return r;
981 if (r == 0)
982 return 1; /* Will call us back */
983
984 r = transfer_cancel(t);
985 if (r < 0)
986 return r;
987
988 return sd_bus_reply_method_return(msg, NULL);
989}
990
19070062 991static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
3d7415f4
LP
992 Manager *m = userdata;
993 Transfer *t;
994 uint32_t id;
995 int r;
996
3d7415f4
LP
997 assert(msg);
998 assert(m);
999
1000 r = bus_verify_polkit_async(
1001 msg,
1002 CAP_SYS_ADMIN,
1003 "org.freedesktop.import1.pull",
403ed0e5 1004 NULL,
3d7415f4 1005 false,
c529695e 1006 UID_INVALID,
3d7415f4
LP
1007 &m->polkit_registry,
1008 error);
1009 if (r < 0)
1010 return r;
1011 if (r == 0)
1012 return 1; /* Will call us back */
1013
1014 r = sd_bus_message_read(msg, "u", &id);
1015 if (r < 0)
1016 return r;
1017 if (id <= 0)
1018 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
1019
1020 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1021 if (!t)
09d46cfd 1022 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
3d7415f4
LP
1023
1024 r = transfer_cancel(t);
1025 if (r < 0)
1026 return r;
1027
1028 return sd_bus_reply_method_return(msg, NULL);
1029}
1030
7079cfef
LP
1031static int property_get_progress(
1032 sd_bus *bus,
1033 const char *path,
1034 const char *interface,
1035 const char *property,
1036 sd_bus_message *reply,
1037 void *userdata,
1038 sd_bus_error *error) {
1039
1040 Transfer *t = userdata;
1041
1042 assert(bus);
1043 assert(reply);
1044 assert(t);
1045
1046 return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
1047}
1048
3d7415f4
LP
1049static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1050static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1051
1052static const sd_bus_vtable transfer_vtable[] = {
1053 SD_BUS_VTABLE_START(0),
1054 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1055 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1056 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1057 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1058 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
7079cfef 1059 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
3d7415f4
LP
1060 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
1061 SD_BUS_SIGNAL("LogMessage", "us", 0),
1062 SD_BUS_VTABLE_END,
1063};
1064
1065static const sd_bus_vtable manager_vtable[] = {
1066 SD_BUS_VTABLE_START(0),
b6e676ce
LP
1067 SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1068 SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
587fec42
LP
1069 SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1070 SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
3d7415f4
LP
1071 SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
1072 SD_BUS_METHOD("PullRaw", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
7079cfef 1073 SD_BUS_METHOD("ListTransfers", NULL, "a(usssdo)", method_list_transfers, SD_BUS_VTABLE_UNPRIVILEGED),
3d7415f4
LP
1074 SD_BUS_METHOD("CancelTransfer", "u", NULL, method_cancel_transfer, SD_BUS_VTABLE_UNPRIVILEGED),
1075 SD_BUS_SIGNAL("TransferNew", "uo", 0),
1076 SD_BUS_SIGNAL("TransferRemoved", "uos", 0),
1077 SD_BUS_VTABLE_END,
1078};
1079
1080static int transfer_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
1081 Manager *m = userdata;
1082 Transfer *t;
1083 const char *p;
1084 uint32_t id;
1085 int r;
1086
1087 assert(bus);
1088 assert(path);
1089 assert(interface);
1090 assert(found);
1091 assert(m);
1092
1093 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1094 if (!p)
1095 return 0;
1096
1097 r = safe_atou32(p, &id);
1098 if (r < 0 || id == 0)
1099 return 0;
1100
1101 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1102 if (!t)
1103 return 0;
1104
1105 *found = t;
1106 return 1;
1107}
1108
1109static int transfer_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) {
1110 _cleanup_strv_free_ char **l = NULL;
1111 Manager *m = userdata;
1112 Transfer *t;
1113 unsigned k = 0;
1114 Iterator i;
1115
1116 l = new0(char*, hashmap_size(m->transfers) + 1);
1117 if (!l)
1118 return -ENOMEM;
1119
1120 HASHMAP_FOREACH(t, m->transfers, i) {
1121
1122 l[k] = strdup(t->object_path);
1123 if (!l[k])
1124 return -ENOMEM;
1125
1126 k++;
1127 }
1128
1129 *nodes = l;
1130 l = NULL;
1131
1132 return 1;
1133}
1134
1135static int manager_add_bus_objects(Manager *m) {
1136 int r;
1137
1138 assert(m);
1139
1140 r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/import1", "org.freedesktop.import1.Manager", manager_vtable, m);
1141 if (r < 0)
1142 return log_error_errno(r, "Failed to register object: %m");
1143
1144 r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/import1/transfer", "org.freedesktop.import1.Transfer", transfer_vtable, transfer_object_find, m);
1145 if (r < 0)
1146 return log_error_errno(r, "Failed to register object: %m");
1147
1148 r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/import1/transfer", transfer_node_enumerator, m);
1149 if (r < 0)
1150 return log_error_errno(r, "Failed to add transfer enumerator: %m");
1151
1152 r = sd_bus_request_name(m->bus, "org.freedesktop.import1", 0);
1153 if (r < 0)
1154 return log_error_errno(r, "Failed to register name: %m");
1155
1156 r = sd_bus_attach_event(m->bus, m->event, 0);
1157 if (r < 0)
1158 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1159
1160 return 0;
1161}
1162
1163static bool manager_check_idle(void *userdata) {
1164 Manager *m = userdata;
1165
1166 return hashmap_isempty(m->transfers);
1167}
1168
1169static int manager_run(Manager *m) {
1170 assert(m);
1171
1172 return bus_event_loop_with_idle(
1173 m->event,
1174 m->bus,
1175 "org.freedesktop.import1",
1176 DEFAULT_EXIT_USEC,
1177 manager_check_idle,
1178 m);
1179}
1180
1181int main(int argc, char *argv[]) {
1182 _cleanup_(manager_unrefp) Manager *m = NULL;
1183 int r;
1184
1185 log_set_target(LOG_TARGET_AUTO);
1186 log_parse_environment();
1187 log_open();
1188
1189 umask(0022);
1190
1191 if (argc != 1) {
1192 log_error("This program takes no arguments.");
1193 r = -EINVAL;
1194 goto finish;
1195 }
1196
72c0a2c2 1197 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
3d7415f4
LP
1198
1199 r = manager_new(&m);
1200 if (r < 0) {
1201 log_error_errno(r, "Failed to allocate manager object: %m");
1202 goto finish;
1203 }
1204
1205 r = manager_add_bus_objects(m);
1206 if (r < 0)
1207 goto finish;
1208
1209 r = manager_run(m);
1210 if (r < 0) {
1211 log_error_errno(r, "Failed to run event loop: %m");
1212 goto finish;
1213 }
1214
1215finish:
1216 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1217}