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