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