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