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