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