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