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