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