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