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