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