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