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