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