]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/importd.c
tree-wise: several cleanups for logging
[thirdparty/systemd.git] / src / import / importd.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
3d7415f4
LP
2
3#include <sys/prctl.h>
d7e454ba 4#include <sys/wait.h>
3d7415f4
LP
5
6#include "sd-bus.h"
3ffd4af2 7
b5efdb8a 8#include "alloc-util.h"
a0afd4e7 9#include "build-path.h"
3d7415f4 10#include "bus-common-errors.h"
40af3d02 11#include "bus-get-properties.h"
ac9f55ed 12#include "bus-log-control-api.h"
269e4d2d 13#include "bus-polkit.h"
82fa9f28 14#include "common-signal.h"
28db6fbf 15#include "constants.h"
deb86997 16#include "daemon-util.h"
7af5785d 17#include "discover-image.h"
d7548908 18#include "env-util.h"
09232207 19#include "event-util.h"
3ffd4af2 20#include "fd-util.h"
1d7579c4 21#include "float.h"
3ffd4af2 22#include "hostname-util.h"
7af5785d 23#include "import-common.h"
3ffd4af2 24#include "import-util.h"
432cea00 25#include "machine-pool.h"
5e332028 26#include "main-func.h"
f5947a5e 27#include "missing_capability.h"
35cd0ba5 28#include "mkdir-label.h"
7af5785d 29#include "os-util.h"
6bedfcbb 30#include "parse-util.h"
113b3fc1 31#include "path-util.h"
ed5033fd 32#include "percent-util.h"
0b452006 33#include "process-util.h"
fc021a5b 34#include "service-util.h"
40af3d02 35#include "signal-util.h"
3ffd4af2 36#include "socket-util.h"
176a05c2 37#include "stat-util.h"
8b43440b 38#include "string-table.h"
3ffd4af2 39#include "strv.h"
7ccbd1ae 40#include "syslog-util.h"
ee104e11 41#include "user-util.h"
49cf4170 42#include "web-util.h"
3d7415f4
LP
43
44typedef struct Transfer Transfer;
45typedef struct Manager Manager;
46
47typedef enum TransferType {
b6e676ce
LP
48 TRANSFER_IMPORT_TAR,
49 TRANSFER_IMPORT_RAW,
1d7579c4 50 TRANSFER_IMPORT_FS,
587fec42
LP
51 TRANSFER_EXPORT_TAR,
52 TRANSFER_EXPORT_RAW,
b6e676ce
LP
53 TRANSFER_PULL_TAR,
54 TRANSFER_PULL_RAW,
3d7415f4 55 _TRANSFER_TYPE_MAX,
2d93c20e 56 _TRANSFER_TYPE_INVALID = -EINVAL,
3d7415f4
LP
57} TransferType;
58
59struct Transfer {
60 Manager *manager;
61
62 uint32_t id;
63 char *object_path;
64
65 TransferType type;
66 ImportVerify verify;
67
68 char *remote;
69 char *local;
7af5785d
LP
70 ImageClass class;
71 ImportFlags flags;
587fec42 72 char *format;
3d7415f4 73
09232207 74 PidRef pidref;
3d7415f4
LP
75
76 int log_fd;
77
78 char log_message[LINE_MAX];
79 size_t log_message_size;
80
81 sd_event_source *pid_event_source;
82 sd_event_source *log_event_source;
83
84 unsigned n_canceled;
7079cfef 85 unsigned progress_percent;
71cb203a 86 unsigned progress_percent_sent;
b6e676ce
LP
87
88 int stdin_fd;
587fec42 89 int stdout_fd;
3d7415f4
LP
90};
91
92struct Manager {
93 sd_event *event;
94 sd_bus *bus;
95
96 uint32_t current_transfer_id;
97 Hashmap *transfers;
98
99 Hashmap *polkit_registry;
7079cfef
LP
100
101 int notify_fd;
102
103 sd_event_source *notify_event_source;
c7779a61
IS
104
105 bool use_btrfs_subvol;
106 bool use_btrfs_quota;
3d7415f4
LP
107};
108
109#define TRANSFERS_MAX 64
110
111static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
b6e676ce
LP
112 [TRANSFER_IMPORT_TAR] = "import-tar",
113 [TRANSFER_IMPORT_RAW] = "import-raw",
b37ec1e7 114 [TRANSFER_IMPORT_FS] = "import-fs",
587fec42
LP
115 [TRANSFER_EXPORT_TAR] = "export-tar",
116 [TRANSFER_EXPORT_RAW] = "export-raw",
b37ec1e7
LP
117 [TRANSFER_PULL_TAR] = "pull-tar",
118 [TRANSFER_PULL_RAW] = "pull-raw",
3d7415f4
LP
119};
120
b9a5f858 121DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(transfer_type, TransferType);
3d7415f4
LP
122
123static Transfer *transfer_unref(Transfer *t) {
124 if (!t)
125 return NULL;
126
127 if (t->manager)
128 hashmap_remove(t->manager->transfers, UINT32_TO_PTR(t->id));
129
130 sd_event_source_unref(t->pid_event_source);
131 sd_event_source_unref(t->log_event_source);
132
133 free(t->remote);
134 free(t->local);
587fec42 135 free(t->format);
3d7415f4
LP
136 free(t->object_path);
137
09232207 138 pidref_done_sigkill_wait(&t->pidref);
3d7415f4
LP
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
0d94088e 160 t = new(Transfer, 1);
3d7415f4
LP
161 if (!t)
162 return -ENOMEM;
163
0d94088e
YW
164 *t = (Transfer) {
165 .type = _TRANSFER_TYPE_INVALID,
254d1313
ZJS
166 .log_fd = -EBADF,
167 .stdin_fd = -EBADF,
168 .stdout_fd = -EBADF,
0d94088e 169 .verify = _IMPORT_VERIFY_INVALID,
71cb203a
LP
170 .progress_percent = UINT_MAX,
171 .progress_percent_sent = UINT_MAX,
0d94088e 172 };
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
df24a407 179 r = hashmap_ensure_put(&m->transfers, &trivial_hash_ops, UINT32_TO_PTR(id), t);
3d7415f4
LP
180 if (r < 0)
181 return r;
182
183 m->current_transfer_id = id;
184
185 t->manager = m;
186 t->id = id;
187
1cc6c93a 188 *ret = TAKE_PTR(t);
3d7415f4
LP
189
190 return 0;
191}
192
1d7579c4
LP
193static double transfer_percent_as_double(Transfer *t) {
194 assert(t);
195
f5fbe71d 196 if (t->progress_percent == UINT_MAX)
1d7579c4
LP
197 return -DBL_MAX;
198
199 return (double) t->progress_percent / 100.0;
200}
201
3d7415f4
LP
202static void transfer_send_log_line(Transfer *t, const char *line) {
203 int r, priority = LOG_INFO;
204
205 assert(t);
206 assert(line);
207
208 syslog_parse_priority(&line, &priority, true);
209
09d46cfd 210 log_full(priority, "(transfer%" PRIu32 ") %s", t->id, line);
3d7415f4
LP
211
212 r = sd_bus_emit_signal(
213 t->manager->bus,
214 t->object_path,
215 "org.freedesktop.import1.Transfer",
216 "LogMessage",
217 "us",
218 priority,
219 line);
220 if (r < 0)
5b6299ee 221 log_warning_errno(r, "Cannot emit log message signal, ignoring: %m");
71cb203a
LP
222}
223
224static void transfer_send_progress_update(Transfer *t) {
225 int r;
226
227 assert(t);
228
229 if (t->progress_percent_sent == t->progress_percent)
230 return;
231
232 r = sd_bus_emit_signal(
233 t->manager->bus,
234 t->object_path,
235 "org.freedesktop.import1.Transfer",
236 "ProgressUpdate",
237 "d",
238 transfer_percent_as_double(t));
239 if (r < 0)
240 log_warning_errno(r, "Cannot emit progress update signal, ignoring: %m");
241
242 t->progress_percent_sent = t->progress_percent;
243}
3d7415f4
LP
244
245static void transfer_send_logs(Transfer *t, bool flush) {
246 assert(t);
247
248 /* Try to send out all log messages, if we can. But if we
249 * can't we remove the messages from the buffer, but don't
250 * fail */
251
252 while (t->log_message_size > 0) {
253 _cleanup_free_ char *n = NULL;
254 char *e;
255
256 if (t->log_message_size >= sizeof(t->log_message))
257 e = t->log_message + sizeof(t->log_message);
258 else {
259 char *a, *b;
260
261 a = memchr(t->log_message, 0, t->log_message_size);
262 b = memchr(t->log_message, '\n', t->log_message_size);
263
264 if (a && b)
265 e = a < b ? a : b;
266 else if (a)
267 e = a;
268 else
269 e = b;
270 }
271
272 if (!e) {
273 if (!flush)
274 return;
275
276 e = t->log_message + t->log_message_size;
277 }
278
279 n = strndup(t->log_message, e - t->log_message);
280
281 /* Skip over NUL and newlines */
ed0cb346 282 while (e < t->log_message + t->log_message_size && IN_SET(*e, 0, '\n'))
3d7415f4
LP
283 e++;
284
285 memmove(t->log_message, e, t->log_message + sizeof(t->log_message) - e);
286 t->log_message_size -= e - t->log_message;
287
288 if (!n) {
289 log_oom();
290 continue;
291 }
292
293 if (isempty(n))
294 continue;
295
296 transfer_send_log_line(t, n);
297 }
298}
299
300static int transfer_finalize(Transfer *t, bool success) {
301 int r;
302
303 assert(t);
304
305 transfer_send_logs(t, true);
306
307 r = sd_bus_emit_signal(
308 t->manager->bus,
309 "/org/freedesktop/import1",
310 "org.freedesktop.import1.Manager",
311 "TransferRemoved",
312 "uos",
313 t->id,
314 t->object_path,
315 success ? "done" :
316 t->n_canceled > 0 ? "canceled" : "failed");
317
318 if (r < 0)
319 log_error_errno(r, "Cannot emit message: %m");
320
321 transfer_unref(t);
322 return 0;
323}
324
325static int transfer_cancel(Transfer *t) {
326 int r;
327
328 assert(t);
329
09232207 330 r = pidref_kill_and_sigcont(&t->pidref, t->n_canceled < 3 ? SIGTERM : SIGKILL);
3d7415f4
LP
331 if (r < 0)
332 return r;
333
334 t->n_canceled++;
335 return 0;
336}
337
338static int transfer_on_pid(sd_event_source *s, const siginfo_t *si, void *userdata) {
99534007 339 Transfer *t = ASSERT_PTR(userdata);
3d7415f4
LP
340 bool success = false;
341
342 assert(s);
3d7415f4
LP
343
344 if (si->si_code == CLD_EXITED) {
345 if (si->si_status != 0)
4b71c27b 346 log_error("Transfer process failed with exit code %i.", si->si_status);
3d7415f4 347 else {
4b71c27b 348 log_debug("Transfer process succeeded.");
3d7415f4
LP
349 success = true;
350 }
351
3742095b 352 } else if (IN_SET(si->si_code, CLD_KILLED, CLD_DUMPED))
4b71c27b 353 log_error("Transfer process terminated by signal %s.", signal_to_string(si->si_status));
3d7415f4 354 else
4b71c27b 355 log_error("Transfer process failed due to unknown reason.");
3d7415f4 356
09232207 357 pidref_done(&t->pidref);
3d7415f4
LP
358
359 return transfer_finalize(t, success);
360}
361
362static int transfer_on_log(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
99534007 363 Transfer *t = ASSERT_PTR(userdata);
3d7415f4
LP
364 ssize_t l;
365
366 assert(s);
3d7415f4
LP
367
368 l = read(fd, t->log_message + t->log_message_size, sizeof(t->log_message) - t->log_message_size);
b0325c99
LP
369 if (l < 0)
370 log_error_errno(errno, "Failed to read log message: %m");
3d7415f4
LP
371 if (l <= 0) {
372 /* EOF/read error. We just close the pipe here, and
373 * close the watch, waiting for the SIGCHLD to arrive,
374 * before we do anything else. */
3d7415f4
LP
375 t->log_event_source = sd_event_source_unref(t->log_event_source);
376 return 0;
377 }
378
379 t->log_message_size += l;
380
381 transfer_send_logs(t, false);
382
383 return 0;
384}
385
386static int transfer_start(Transfer *t) {
71136404 387 _cleanup_close_pair_ int pipefd[2] = EBADF_PAIR;
3d7415f4
LP
388 int r;
389
390 assert(t);
09232207 391 assert(!pidref_is_set(&t->pidref));
3d7415f4
LP
392
393 if (pipe2(pipefd, O_CLOEXEC) < 0)
394 return -errno;
395
09232207
LP
396 r = pidref_safe_fork_full(
397 "(sd-transfer)",
398 (int[]) { t->stdin_fd, t->stdout_fd < 0 ? pipefd[1] : t->stdout_fd, pipefd[1] },
399 /* except_fds= */ NULL, /* n_except_fds= */ 0,
1a176d5b 400 FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_REOPEN_LOG,
09232207 401 &t->pidref);
4c253ed1
LP
402 if (r < 0)
403 return r;
404 if (r == 0) {
3d7415f4 405 const char *cmd[] = {
1d7579c4 406 NULL, /* systemd-import, systemd-import-fs, systemd-export or systemd-pull */
b43d75c3 407 NULL, /* tar, raw */
b6e676ce 408 NULL, /* --verify= */
3d7415f4 409 NULL, /* verify argument */
7af5785d
LP
410 NULL, /* --class= */
411 NULL, /* class argument */
b146afc4 412 NULL, /* --keep-download= */
3d7415f4 413 NULL, /* maybe --force */
b6e676ce 414 NULL, /* maybe --read-only */
587fec42
LP
415 NULL, /* if so: the actual URL */
416 NULL, /* maybe --format= */
417 NULL, /* if so: the actual format */
3d7415f4
LP
418 NULL, /* remote */
419 NULL, /* local */
420 NULL
421 };
b37ec1e7 422 size_t k = 0;
3d7415f4
LP
423
424 /* Child */
425
df8067ef
ZJS
426 if (setenv("SYSTEMD_LOG_TARGET", "console-prefixed", 1) < 0 ||
427 setenv("NOTIFY_SOCKET", "/run/systemd/import/notify", 1) < 0) {
428 log_error_errno(errno, "setenv() failed: %m");
429 _exit(EXIT_FAILURE);
430 }
3d7415f4 431
5a985dd0
LP
432 r = setenv_systemd_log_level();
433 if (r < 0)
434 log_warning_errno(r, "Failed to update $SYSTEMD_LOG_LEVEL, ignoring: %m");
435
d7548908
YW
436 r = setenv_systemd_exec_pid(true);
437 if (r < 0)
438 log_warning_errno(r, "Failed to update $SYSTEMD_EXEC_PID, ignoring: %m");
439
1d7579c4
LP
440 switch (t->type) {
441
442 case TRANSFER_IMPORT_TAR:
443 case TRANSFER_IMPORT_RAW:
b6e676ce 444 cmd[k++] = SYSTEMD_IMPORT_PATH;
1d7579c4
LP
445 break;
446
447 case TRANSFER_IMPORT_FS:
448 cmd[k++] = SYSTEMD_IMPORT_FS_PATH;
449 break;
450
451 case TRANSFER_EXPORT_TAR:
452 case TRANSFER_EXPORT_RAW:
587fec42 453 cmd[k++] = SYSTEMD_EXPORT_PATH;
1d7579c4
LP
454 break;
455
456 case TRANSFER_PULL_TAR:
457 case TRANSFER_PULL_RAW:
b6e676ce 458 cmd[k++] = SYSTEMD_PULL_PATH;
1d7579c4
LP
459 break;
460
461 default:
04499a70 462 assert_not_reached();
1d7579c4
LP
463 }
464
465 switch (t->type) {
b6e676ce 466
1d7579c4
LP
467 case TRANSFER_IMPORT_TAR:
468 case TRANSFER_EXPORT_TAR:
469 case TRANSFER_PULL_TAR:
b6e676ce 470 cmd[k++] = "tar";
1d7579c4
LP
471 break;
472
473 case TRANSFER_IMPORT_RAW:
474 case TRANSFER_EXPORT_RAW:
475 case TRANSFER_PULL_RAW:
b43d75c3 476 cmd[k++] = "raw";
1d7579c4
LP
477 break;
478
479 case TRANSFER_IMPORT_FS:
480 cmd[k++] = "run";
481 break;
482
483 default:
484 break;
485 }
b6e676ce
LP
486
487 if (t->verify != _IMPORT_VERIFY_INVALID) {
488 cmd[k++] = "--verify";
489 cmd[k++] = import_verify_to_string(t->verify);
490 }
491
7af5785d
LP
492 if (t->class != IMAGE_MACHINE) {
493 cmd[k++] = "--class";
494 cmd[k++] = image_class_to_string(t->class);
495 }
496
b146afc4
LP
497 if (IN_SET(t->type, TRANSFER_PULL_TAR, TRANSFER_PULL_RAW))
498 cmd[k++] = FLAGS_SET(t->flags, IMPORT_PULL_KEEP_DOWNLOAD) ?
499 "--keep-download=yes" : "--keep-download=no";
500
7af5785d 501 if (FLAGS_SET(t->flags, IMPORT_FORCE))
3d7415f4 502 cmd[k++] = "--force";
7af5785d 503 if (FLAGS_SET(t->flags, IMPORT_READ_ONLY))
b6e676ce 504 cmd[k++] = "--read-only";
3d7415f4 505
587fec42
LP
506 if (t->format) {
507 cmd[k++] = "--format";
508 cmd[k++] = t->format;
509 }
510
511 if (!IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW)) {
512 if (t->remote)
513 cmd[k++] = t->remote;
514 else
515 cmd[k++] = "-";
516 }
b6e676ce 517
3d7415f4
LP
518 if (t->local)
519 cmd[k++] = t->local;
520 cmd[k] = NULL;
521
a0afd4e7
LP
522 assert(k < ELEMENTSOF(cmd));
523
1a176d5b
LP
524 if (DEBUG_LOGGING) {
525 _cleanup_free_ char *joined = strv_join((char**) cmd, " ");
526 log_debug("Calling: %s", strnull(joined));
527 }
528
a0afd4e7
LP
529 r = invoke_callout_binary(cmd[0], (char * const *) cmd);
530 log_error_errno(r, "Failed to execute %s tool: %m", cmd[0]);
3d7415f4
LP
531 _exit(EXIT_FAILURE);
532 }
533
534 pipefd[1] = safe_close(pipefd[1]);
c10d6bdb 535 t->log_fd = TAKE_FD(pipefd[0]);
3d7415f4 536
b6e676ce
LP
537 t->stdin_fd = safe_close(t->stdin_fd);
538
09232207
LP
539 r = event_add_child_pidref(
540 t->manager->event,
541 &t->pid_event_source,
542 &t->pidref,
543 WEXITED,
544 transfer_on_pid,
545 t);
3d7415f4
LP
546 if (r < 0)
547 return r;
548
ab09bf90
ZJS
549 r = sd_event_add_io(t->manager->event, &t->log_event_source,
550 t->log_fd, EPOLLIN, transfer_on_log, t);
3d7415f4
LP
551 if (r < 0)
552 return r;
553
554 /* Make sure always process logging before SIGCHLD */
555 r = sd_event_source_set_priority(t->log_event_source, SD_EVENT_PRIORITY_NORMAL -5);
556 if (r < 0)
557 return r;
558
559 r = sd_bus_emit_signal(
560 t->manager->bus,
561 "/org/freedesktop/import1",
562 "org.freedesktop.import1.Manager",
563 "TransferNew",
564 "uo",
565 t->id,
566 t->object_path);
567 if (r < 0)
568 return r;
569
570 return 0;
571}
572
573static Manager *manager_unref(Manager *m) {
574 Transfer *t;
575
576 if (!m)
577 return NULL;
578
7079cfef
LP
579 sd_event_source_unref(m->notify_event_source);
580 safe_close(m->notify_fd);
581
3d7415f4
LP
582 while ((t = hashmap_first(m->transfers)))
583 transfer_unref(t);
584
585 hashmap_free(m->transfers);
586
2a1ffd3e 587 hashmap_free(m->polkit_registry);
3d7415f4 588
03976f7b 589 m->bus = sd_bus_flush_close_unref(m->bus);
3d7415f4
LP
590 sd_event_unref(m->event);
591
6b430fdb 592 return mfree(m);
3d7415f4
LP
593}
594
595DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref);
596
7079cfef
LP
597static int manager_on_notify(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
598
599 char buf[NOTIFY_BUFFER_MAX+1];
600 struct iovec iovec = {
601 .iov_base = buf,
602 .iov_len = sizeof(buf)-1,
603 };
fb29cdbe
LP
604 CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred)) +
605 CMSG_SPACE(sizeof(int) * NOTIFY_FD_MAX)) control;
7079cfef
LP
606 struct msghdr msghdr = {
607 .msg_iov = &iovec,
608 .msg_iovlen = 1,
609 .msg_control = &control,
610 .msg_controllen = sizeof(control),
611 };
371d72e0 612 struct ucred *ucred;
7079cfef 613 Manager *m = userdata;
7079cfef 614 Transfer *t;
7079cfef 615 ssize_t n;
90f5f355 616 char *p;
7079cfef
LP
617 int r;
618
3691bcf3 619 n = recvmsg_safe(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC);
8add30a0
YW
620 if (n < 0) {
621 if (ERRNO_IS_TRANSIENT(n))
622 return 0;
3691bcf3 623 return (int) n;
8add30a0 624 }
7079cfef 625
1c8da044
LP
626 cmsg_close_all(&msghdr);
627
7079cfef
LP
628 if (msghdr.msg_flags & MSG_TRUNC) {
629 log_warning("Got overly long notification datagram, ignoring.");
630 return 0;
631 }
632
371d72e0 633 ucred = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_CREDENTIALS, struct ucred);
7079cfef
LP
634 if (!ucred || ucred->pid <= 0) {
635 log_warning("Got notification datagram lacking credential information, ignoring.");
636 return 0;
637 }
638
90e74a66 639 HASHMAP_FOREACH(t, m->transfers)
09232207 640 if (ucred->pid == t->pidref.pid)
7079cfef
LP
641 break;
642
643 if (!t) {
644 log_warning("Got notification datagram from unexpected peer, ignoring.");
645 return 0;
646 }
647
648 buf[n] = 0;
649
50ed5cbf
LP
650 p = find_line_startswith(buf, "X_IMPORT_PROGRESS=");
651 if (!p)
652 return 0;
7079cfef 653
90f5f355 654 truncate_nl(p);
7079cfef 655
5a8582fb
LP
656 r = parse_percent(p);
657 if (r < 0) {
a986de68 658 log_warning("Got invalid percent value '%s', ignoring.", p);
7079cfef
LP
659 return 0;
660 }
661
5a8582fb 662 t->progress_percent = (unsigned) r;
7079cfef 663
5a8582fb 664 log_debug("Got percentage from client: %u%%", t->progress_percent);
71cb203a
LP
665
666 transfer_send_progress_update(t);
7079cfef
LP
667 return 0;
668}
669
3d7415f4
LP
670static int manager_new(Manager **ret) {
671 _cleanup_(manager_unrefp) Manager *m = NULL;
7079cfef
LP
672 static const union sockaddr_union sa = {
673 .un.sun_family = AF_UNIX,
674 .un.sun_path = "/run/systemd/import/notify",
675 };
3d7415f4
LP
676 int r;
677
678 assert(ret);
679
c7779a61 680 m = new(Manager, 1);
3d7415f4
LP
681 if (!m)
682 return -ENOMEM;
683
c7779a61
IS
684 *m = (Manager) {
685 .use_btrfs_subvol = true,
686 .use_btrfs_quota = true,
687 };
688
3d7415f4
LP
689 r = sd_event_default(&m->event);
690 if (r < 0)
691 return r;
692
423bba99 693 r = sd_event_set_signal_exit(m->event, true);
82fa9f28
LP
694 if (r < 0)
695 return r;
696
423bba99 697 r = sd_event_add_signal(m->event, NULL, (SIGRTMIN+18)|SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
82fa9f28
LP
698 if (r < 0)
699 return r;
700
701 r = sd_event_add_memory_pressure(m->event, NULL, NULL, NULL);
702 if (r < 0)
703 log_debug_errno(r, "Failed allocate memory pressure event source, ignoring: %m");
3d7415f4 704
423bba99
LP
705 r = sd_event_set_watchdog(m->event, true);
706 if (r < 0)
707 log_debug_errno(r, "Failed to enable watchdog logic, ignoring: %m");
708
3d7415f4
LP
709 r = sd_bus_default_system(&m->bus);
710 if (r < 0)
711 return r;
712
7079cfef
LP
713 m->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
714 if (m->notify_fd < 0)
715 return -errno;
716
717 (void) mkdir_parents_label(sa.un.sun_path, 0755);
155b6876 718 (void) sockaddr_un_unlink(&sa.un);
7079cfef 719
fc2fffe7 720 if (bind(m->notify_fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
7079cfef
LP
721 return -errno;
722
2ff48e98
LP
723 r = setsockopt_int(m->notify_fd, SOL_SOCKET, SO_PASSCRED, true);
724 if (r < 0)
725 return r;
7079cfef 726
ab09bf90
ZJS
727 r = sd_event_add_io(m->event, &m->notify_event_source,
728 m->notify_fd, EPOLLIN, manager_on_notify, m);
7079cfef
LP
729 if (r < 0)
730 return r;
731
1cc6c93a 732 *ret = TAKE_PTR(m);
3d7415f4
LP
733
734 return 0;
735}
736
b43d75c3 737static Transfer *manager_find(Manager *m, TransferType type, const char *remote) {
3d7415f4 738 Transfer *t;
3d7415f4
LP
739
740 assert(m);
741 assert(type >= 0);
742 assert(type < _TRANSFER_TYPE_MAX);
743
90e74a66 744 HASHMAP_FOREACH(t, m->transfers)
b0325c99 745 if (t->type == type && streq_ptr(t->remote, remote))
3d7415f4 746 return t;
3d7415f4
LP
747
748 return NULL;
749}
750
19070062 751static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
b6e676ce 752 _cleanup_(transfer_unrefp) Transfer *t = NULL;
7af5785d 753 ImageClass class = _IMAGE_CLASS_INVALID;
99534007 754 Manager *m = ASSERT_PTR(userdata);
7af5785d 755 const char *local;
b6e676ce 756 TransferType type;
1209ef94 757 struct stat st;
7af5785d
LP
758 uint64_t flags;
759 int fd, r;
b6e676ce 760
19070062 761 assert(msg);
19070062 762
b6e676ce
LP
763 r = bus_verify_polkit_async(
764 msg,
b6e676ce 765 "org.freedesktop.import1.import",
7b36fb9f 766 /* details= */ NULL,
b6e676ce
LP
767 &m->polkit_registry,
768 error);
769 if (r < 0)
770 return r;
771 if (r == 0)
772 return 1; /* Will call us back */
773
7af5785d
LP
774 if (endswith(sd_bus_message_get_member(msg), "Ex")) {
775 const char *sclass;
776
777 r = sd_bus_message_read(msg, "hsst", &fd, &local, &sclass, &flags);
778 if (r < 0)
779 return r;
780
781 class = image_class_from_string(sclass);
782 if (class < 0)
783 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
784 "Image class '%s' not known", sclass);
785
786 if (flags & ~(IMPORT_READ_ONLY|IMPORT_FORCE))
787 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
788 "Flags 0x%" PRIx64 " invalid", flags);
789 } else {
790 int force, read_only;
791
792 r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
793 if (r < 0)
794 return r;
795
796 class = IMAGE_MACHINE;
797
798 flags = 0;
799 SET_FLAG(flags, IMPORT_FORCE, force);
800 SET_FLAG(flags, IMPORT_READ_ONLY, read_only);
801 }
b6e676ce 802
c3c892b4
LP
803 r = fd_verify_safe_flags(fd);
804 if (r < 0)
805 return r;
806
1209ef94
AZ
807 if (fstat(fd, &st) < 0)
808 return -errno;
809
810 if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
811 return -EINVAL;
176a05c2 812
8f20b498 813 if (!image_name_is_valid(local))
ab09bf90 814 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
8f20b498 815 "Local image name %s is invalid", local);
b6e676ce 816
7af5785d
LP
817 if (class == IMAGE_MACHINE) {
818 r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
819 if (r < 0)
820 return r;
821 }
b6e676ce 822
7af5785d 823 type = startswith(sd_bus_message_get_member(msg), "ImportTar") ?
ab09bf90 824 TRANSFER_IMPORT_TAR : TRANSFER_IMPORT_RAW;
b6e676ce
LP
825
826 r = transfer_new(m, &t);
827 if (r < 0)
828 return r;
829
830 t->type = type;
7af5785d
LP
831 t->class = class;
832 t->flags = flags;
b6e676ce
LP
833
834 t->local = strdup(local);
835 if (!t->local)
836 return -ENOMEM;
837
838 t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
839 if (t->stdin_fd < 0)
840 return -errno;
841
842 r = transfer_start(t);
843 if (r < 0)
844 return r;
845
7af5785d
LP
846 r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
847 if (r < 0)
848 return r;
b6e676ce 849
7af5785d
LP
850 TAKE_PTR(t);
851 return 1;
b6e676ce
LP
852}
853
1d7579c4
LP
854static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
855 _cleanup_(transfer_unrefp) Transfer *t = NULL;
7af5785d 856 ImageClass class = _IMAGE_CLASS_INVALID;
99534007 857 Manager *m = ASSERT_PTR(userdata);
7af5785d
LP
858 const char *local;
859 uint64_t flags;
860 int fd, r;
1d7579c4
LP
861
862 assert(msg);
1d7579c4
LP
863
864 r = bus_verify_polkit_async(
865 msg,
1d7579c4 866 "org.freedesktop.import1.import",
7b36fb9f 867 /* details= */ NULL,
1d7579c4
LP
868 &m->polkit_registry,
869 error);
870 if (r < 0)
871 return r;
872 if (r == 0)
873 return 1; /* Will call us back */
874
7af5785d
LP
875 if (endswith(sd_bus_message_get_member(msg), "Ex")) {
876 const char *sclass;
877
878 r = sd_bus_message_read(msg, "hsst", &fd, &local, &sclass, &flags);
879 if (r < 0)
880 return r;
881
882 class = image_class_from_string(sclass);
883 if (class < 0)
884 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
885 "Image class '%s' not known", sclass);
886
887 if (flags & ~(IMPORT_READ_ONLY|IMPORT_FORCE))
888 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
889 "Flags 0x%" PRIx64 " invalid", flags);
890 } else {
891 int force, read_only;
892
893 r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
894 if (r < 0)
895 return r;
896
897 class = IMAGE_MACHINE;
898
899 flags = 0;
900 SET_FLAG(flags, IMPORT_FORCE, force);
901 SET_FLAG(flags, IMPORT_READ_ONLY, read_only);
902 }
1d7579c4 903
9f65355b 904 r = fd_verify_safe_flags_full(fd, O_DIRECTORY);
c3c892b4
LP
905 if (r < 0)
906 return r;
907
176a05c2
LP
908 r = fd_verify_directory(fd);
909 if (r < 0)
910 return r;
911
8f20b498 912 if (!image_name_is_valid(local))
ab09bf90 913 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
8f20b498 914 "Local image name %s is invalid", local);
1d7579c4 915
7af5785d
LP
916 if (class == IMAGE_MACHINE) {
917 r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
918 if (r < 0)
919 return r;
920 }
1d7579c4
LP
921
922 r = transfer_new(m, &t);
923 if (r < 0)
924 return r;
925
926 t->type = TRANSFER_IMPORT_FS;
7af5785d
LP
927 t->class = class;
928 t->flags = flags;
1d7579c4
LP
929
930 t->local = strdup(local);
931 if (!t->local)
932 return -ENOMEM;
933
934 t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
935 if (t->stdin_fd < 0)
936 return -errno;
937
938 r = transfer_start(t);
939 if (r < 0)
940 return r;
941
7af5785d
LP
942 r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
943 if (r < 0)
944 return r;
1d7579c4 945
7af5785d
LP
946 TAKE_PTR(t);
947 return 1;
1d7579c4
LP
948}
949
19070062 950static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
587fec42 951 _cleanup_(transfer_unrefp) Transfer *t = NULL;
7af5785d 952 ImageClass class = _IMAGE_CLASS_INVALID;
99534007 953 Manager *m = ASSERT_PTR(userdata);
7af5785d 954 const char *local, *format;
587fec42 955 TransferType type;
7af5785d 956 uint64_t flags;
1209ef94 957 struct stat st;
7af5785d 958 int fd, r;
587fec42 959
19070062 960 assert(msg);
19070062 961
587fec42
LP
962 r = bus_verify_polkit_async(
963 msg,
587fec42 964 "org.freedesktop.import1.export",
7b36fb9f 965 /* details= */ NULL,
587fec42
LP
966 &m->polkit_registry,
967 error);
968 if (r < 0)
969 return r;
970 if (r == 0)
971 return 1; /* Will call us back */
972
7af5785d
LP
973 if (endswith(sd_bus_message_get_member(msg), "Ex")) {
974 const char *sclass;
975
976 r = sd_bus_message_read(msg, "sshst", &local, &sclass, &fd, &format, &flags);
977 if (r < 0)
978 return r;
979
980 class = image_class_from_string(sclass);
981 if (class < 0)
982 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
983 "Image class '%s' not known", sclass);
984
985 if (flags != 0)
986 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
987 "Flags 0x%" PRIx64 " invalid", flags);
988 } else {
989 r = sd_bus_message_read(msg, "shs", &local, &fd, &format);
990 if (r < 0)
991 return r;
992
993 class = IMAGE_MACHINE;
994 flags = 0;
995 }
587fec42 996
8f20b498 997 if (!image_name_is_valid(local))
ab09bf90 998 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
8f20b498 999 "Local image name %s is invalid", local);
587fec42 1000
c3c892b4
LP
1001 r = fd_verify_safe_flags(fd);
1002 if (r < 0)
1003 return r;
1004
1209ef94
AZ
1005 if (fstat(fd, &st) < 0)
1006 return -errno;
1007
1008 if (!S_ISREG(st.st_mode) && !S_ISFIFO(st.st_mode))
1009 return -EINVAL;
176a05c2 1010
7af5785d 1011 type = startswith(sd_bus_message_get_member(msg), "ExportTar") ?
ab09bf90 1012 TRANSFER_EXPORT_TAR : TRANSFER_EXPORT_RAW;
587fec42
LP
1013
1014 r = transfer_new(m, &t);
1015 if (r < 0)
1016 return r;
1017
1018 t->type = type;
7af5785d
LP
1019 t->class = class;
1020 t->flags = flags;
587fec42
LP
1021
1022 if (!isempty(format)) {
1023 t->format = strdup(format);
1024 if (!t->format)
1025 return -ENOMEM;
1026 }
1027
1028 t->local = strdup(local);
1029 if (!t->local)
1030 return -ENOMEM;
1031
1032 t->stdout_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
1033 if (t->stdout_fd < 0)
1034 return -errno;
1035
1036 r = transfer_start(t);
1037 if (r < 0)
1038 return r;
1039
7af5785d
LP
1040 r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
1041 if (r < 0)
1042 return r;
587fec42 1043
7af5785d
LP
1044 TAKE_PTR(t);
1045 return 1;
587fec42
LP
1046}
1047
19070062 1048static int method_pull_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
3d7415f4 1049 _cleanup_(transfer_unrefp) Transfer *t = NULL;
7af5785d
LP
1050 ImageClass class = _IMAGE_CLASS_INVALID;
1051 const char *remote, *local, *verify;
99534007 1052 Manager *m = ASSERT_PTR(userdata);
3d7415f4 1053 TransferType type;
7af5785d
LP
1054 uint64_t flags;
1055 ImportVerify v;
1056 int r;
3d7415f4 1057
3d7415f4 1058 assert(msg);
3d7415f4
LP
1059
1060 r = bus_verify_polkit_async(
1061 msg,
3d7415f4 1062 "org.freedesktop.import1.pull",
7b36fb9f 1063 /* details= */ NULL,
3d7415f4
LP
1064 &m->polkit_registry,
1065 error);
1066 if (r < 0)
1067 return r;
1068 if (r == 0)
1069 return 1; /* Will call us back */
1070
7af5785d
LP
1071 if (endswith(sd_bus_message_get_member(msg), "Ex")) {
1072 const char *sclass;
1073
1074 r = sd_bus_message_read(msg, "sssst", &remote, &local, &sclass, &verify, &flags);
1075 if (r < 0)
1076 return r;
1077
1078 class = image_class_from_string(sclass);
1079 if (class < 0)
1080 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1081 "Image class '%s' not known", sclass);
1082
b146afc4 1083 if (flags & ~(IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD))
7af5785d
LP
1084 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1085 "Flags 0x%" PRIx64 " invalid", flags);
1086 } else {
1087 int force;
1088
1089 r = sd_bus_message_read(msg, "sssb", &remote, &local, &verify, &force);
1090 if (r < 0)
1091 return r;
1092
1093 class = IMAGE_MACHINE;
1094
1095 flags = 0;
1096 SET_FLAG(flags, IMPORT_FORCE, force);
1097 }
3d7415f4 1098
c456862f 1099 if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
ab09bf90
ZJS
1100 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1101 "URL %s is invalid", remote);
3d7415f4
LP
1102
1103 if (isempty(local))
1104 local = NULL;
8f20b498 1105 else if (!image_name_is_valid(local))
ab09bf90 1106 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
8f20b498 1107 "Local image name %s is invalid", local);
3d7415f4
LP
1108
1109 if (isempty(verify))
1110 v = IMPORT_VERIFY_SIGNATURE;
1111 else
1112 v = import_verify_from_string(verify);
1113 if (v < 0)
ab09bf90
ZJS
1114 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1115 "Unknown verification mode %s", verify);
3d7415f4 1116
7af5785d
LP
1117 if (class == IMAGE_MACHINE) {
1118 r = setup_machine_directory(error, m->use_btrfs_subvol, m->use_btrfs_quota);
1119 if (r < 0)
1120 return r;
1121 }
ce06fdfb 1122
7af5785d 1123 type = startswith(sd_bus_message_get_member(msg), "PullTar") ?
ab09bf90 1124 TRANSFER_PULL_TAR : TRANSFER_PULL_RAW;
3d7415f4 1125
b43d75c3 1126 if (manager_find(m, type, remote))
ab09bf90
ZJS
1127 return sd_bus_error_setf(error, BUS_ERROR_TRANSFER_IN_PROGRESS,
1128 "Transfer for %s already in progress.", remote);
3d7415f4
LP
1129
1130 r = transfer_new(m, &t);
1131 if (r < 0)
1132 return r;
1133
1134 t->type = type;
1135 t->verify = v;
7af5785d
LP
1136 t->flags = flags;
1137 t->class = class;
3d7415f4
LP
1138
1139 t->remote = strdup(remote);
1140 if (!t->remote)
1141 return -ENOMEM;
1142
b6e676ce
LP
1143 if (local) {
1144 t->local = strdup(local);
1145 if (!t->local)
1146 return -ENOMEM;
1147 }
3d7415f4
LP
1148
1149 r = transfer_start(t);
1150 if (r < 0)
1151 return r;
1152
7af5785d
LP
1153 r = sd_bus_reply_method_return(msg, "uo", t->id, t->object_path);
1154 if (r < 0)
1155 return r;
3d7415f4 1156
7af5785d
LP
1157 TAKE_PTR(t);
1158 return 1;
3d7415f4
LP
1159}
1160
19070062 1161static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
4afd3348 1162 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
99534007 1163 Manager *m = ASSERT_PTR(userdata);
7af5785d 1164 ImageClass class = _IMAGE_CLASS_INVALID;
3d7415f4 1165 Transfer *t;
3d7415f4
LP
1166 int r;
1167
3d7415f4 1168 assert(msg);
3d7415f4 1169
7af5785d
LP
1170 bool ex = endswith(sd_bus_message_get_member(msg), "Ex");
1171 if (ex) {
1172 const char *sclass;
1173 uint64_t flags;
1174
1175 r = sd_bus_message_read(msg, "st", &sclass, &flags);
1176 if (r < 0)
1177 return bus_log_parse_error(r);
1178
1179 if (!isempty(sclass)) {
1180 class = image_class_from_string(sclass);
1181 if (class < 0)
1182 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1183 "Image class '%s' not known", sclass);
1184 }
1185
1186 if (flags != 0)
1187 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1188 "Flags 0x%" PRIx64 " invalid", flags);
1189 }
1190
3d7415f4
LP
1191 r = sd_bus_message_new_method_return(msg, &reply);
1192 if (r < 0)
1193 return r;
1194
7af5785d
LP
1195 if (ex)
1196 r = sd_bus_message_open_container(reply, 'a', "(ussssdo)");
1197 else
1198 r = sd_bus_message_open_container(reply, 'a', "(usssdo)");
3d7415f4
LP
1199 if (r < 0)
1200 return r;
1201
90e74a66 1202 HASHMAP_FOREACH(t, m->transfers) {
3d7415f4 1203
7af5785d
LP
1204 if (class >= 0 && class != t->class)
1205 continue;
1206
1207 if (ex)
1208 r = sd_bus_message_append(
1209 reply,
1210 "(ussssdo)",
1211 t->id,
1212 transfer_type_to_string(t->type),
1213 t->remote,
1214 t->local,
1215 image_class_to_string(t->class),
1216 transfer_percent_as_double(t),
1217 t->object_path);
1218 else
1219 r = sd_bus_message_append(
1220 reply,
1221 "(usssdo)",
1222 t->id,
1223 transfer_type_to_string(t->type),
1224 t->remote,
1225 t->local,
1226 transfer_percent_as_double(t),
1227 t->object_path);
3d7415f4
LP
1228 if (r < 0)
1229 return r;
1230 }
1231
1232 r = sd_bus_message_close_container(reply);
1233 if (r < 0)
1234 return r;
1235
9030ca46 1236 return sd_bus_send(NULL, reply, NULL);
3d7415f4
LP
1237}
1238
19070062 1239static int method_cancel(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
99534007 1240 Transfer *t = ASSERT_PTR(userdata);
3d7415f4
LP
1241 int r;
1242
3d7415f4 1243 assert(msg);
3d7415f4
LP
1244
1245 r = bus_verify_polkit_async(
1246 msg,
3d7415f4 1247 "org.freedesktop.import1.pull",
7b36fb9f 1248 /* details= */ NULL,
3d7415f4
LP
1249 &t->manager->polkit_registry,
1250 error);
1251 if (r < 0)
1252 return r;
1253 if (r == 0)
1254 return 1; /* Will call us back */
1255
1256 r = transfer_cancel(t);
1257 if (r < 0)
1258 return r;
1259
1260 return sd_bus_reply_method_return(msg, NULL);
1261}
1262
19070062 1263static int method_cancel_transfer(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
99534007 1264 Manager *m = ASSERT_PTR(userdata);
3d7415f4
LP
1265 Transfer *t;
1266 uint32_t id;
1267 int r;
1268
3d7415f4 1269 assert(msg);
3d7415f4
LP
1270
1271 r = bus_verify_polkit_async(
1272 msg,
7af5785d 1273 "org.freedesktop.import1.cancel",
7b36fb9f 1274 /* details= */ NULL,
3d7415f4
LP
1275 &m->polkit_registry,
1276 error);
1277 if (r < 0)
1278 return r;
1279 if (r == 0)
1280 return 1; /* Will call us back */
1281
1282 r = sd_bus_message_read(msg, "u", &id);
1283 if (r < 0)
1284 return r;
1285 if (id <= 0)
1b09b81c 1286 return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid transfer id");
3d7415f4
LP
1287
1288 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1289 if (!t)
09d46cfd 1290 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_TRANSFER, "No transfer by id %" PRIu32, id);
3d7415f4
LP
1291
1292 r = transfer_cancel(t);
1293 if (r < 0)
1294 return r;
1295
1296 return sd_bus_reply_method_return(msg, NULL);
1297}
1298
5b7bfe06
LP
1299static int method_list_images(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
1300 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1301 ImageClass class = _IMAGE_CLASS_INVALID;
1302 int r;
1303
1304 assert(msg);
1305
1306 const char *sclass;
1307 uint64_t flags;
1308
1309 r = sd_bus_message_read(msg, "st", &sclass, &flags);
1310 if (r < 0)
1311 return r;
1312
1313 if (!isempty(sclass)) {
1314 class = image_class_from_string(sclass);
1315 if (class < 0)
1316 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1317 "Image class '%s' not known", sclass);
1318 }
1319
1320 if (flags != 0)
1321 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
1322 "Flags 0x%" PRIx64 " invalid", flags);
1323
1324 r = sd_bus_message_new_method_return(msg, &reply);
1325 if (r < 0)
1326 return r;
1327
1328 r = sd_bus_message_open_container(reply, 'a', "(ssssbtttttt)");
1329 if (r < 0)
1330 return r;
1331
1332 for (ImageClass c = class < 0 ? 0 : class;
1333 class < 0 ? (c < _IMAGE_CLASS_MAX) : (c == class);
1334 c++) {
1335
1336 _cleanup_(hashmap_freep) Hashmap *h = NULL;
1337
1338 h = hashmap_new(&image_hash_ops);
1339 if (!h)
1340 return -ENOMEM;
1341
1342 r = image_discover(c, /* root= */ NULL, h);
1343 if (r < 0) {
1344 if (class >= 0)
1345 return r;
1346
1347 log_warning_errno(r, "Failed to discover images of type %s: %m", image_class_to_string(c));
1348 continue;
1349 }
1350
1351 Image *i;
1352 HASHMAP_FOREACH(i, h) {
1353 r = sd_bus_message_append(
1354 reply,
1355 "(ssssbtttttt)",
1356 image_class_to_string(i->class),
1357 i->name,
1358 image_type_to_string(i->type),
1359 i->path,
1360 i->read_only,
1361 i->crtime,
1362 i->mtime,
1363 i->usage,
1364 i->usage_exclusive,
1365 i->limit,
1366 i->limit_exclusive);
1367 if (r < 0)
1368 return r;
1369 }
1370 }
1371
1372 r = sd_bus_message_close_container(reply);
1373 if (r < 0)
1374 return r;
1375
1376 return sd_bus_send(NULL, reply, NULL);
1377}
1378
7079cfef
LP
1379static int property_get_progress(
1380 sd_bus *bus,
1381 const char *path,
1382 const char *interface,
1383 const char *property,
1384 sd_bus_message *reply,
1385 void *userdata,
1386 sd_bus_error *error) {
1387
99534007 1388 Transfer *t = ASSERT_PTR(userdata);
7079cfef
LP
1389
1390 assert(bus);
1391 assert(reply);
7079cfef 1392
1d7579c4 1393 return sd_bus_message_append(reply, "d", transfer_percent_as_double(t));
7079cfef
LP
1394}
1395
3d7415f4
LP
1396static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
1397static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_verify, import_verify, ImportVerify);
1398
a3b7cf50
ZJS
1399static int transfer_object_find(
1400 sd_bus *bus,
1401 const char *path,
1402 const char *interface,
1403 void *userdata,
1404 void **found,
1405 sd_bus_error *error) {
1406
99534007 1407 Manager *m = ASSERT_PTR(userdata);
a3b7cf50
ZJS
1408 Transfer *t;
1409 const char *p;
1410 uint32_t id;
1411 int r;
1412
1413 assert(bus);
1414 assert(path);
1415 assert(interface);
1416 assert(found);
a3b7cf50
ZJS
1417
1418 p = startswith(path, "/org/freedesktop/import1/transfer/_");
1419 if (!p)
1420 return 0;
1421
1422 r = safe_atou32(p, &id);
1423 if (r < 0 || id == 0)
1424 return 0;
1425
1426 t = hashmap_get(m->transfers, UINT32_TO_PTR(id));
1427 if (!t)
1428 return 0;
1429
1430 *found = t;
1431 return 1;
1432}
1433
1434static int transfer_node_enumerator(
1435 sd_bus *bus,
1436 const char *path,
1437 void *userdata,
1438 char ***nodes,
1439 sd_bus_error *error) {
1440
1441 _cleanup_strv_free_ char **l = NULL;
1442 Manager *m = userdata;
1443 Transfer *t;
1444 unsigned k = 0;
a3b7cf50
ZJS
1445
1446 l = new0(char*, hashmap_size(m->transfers) + 1);
1447 if (!l)
1448 return -ENOMEM;
1449
90e74a66 1450 HASHMAP_FOREACH(t, m->transfers) {
a3b7cf50
ZJS
1451
1452 l[k] = strdup(t->object_path);
1453 if (!l[k])
1454 return -ENOMEM;
1455
1456 k++;
1457 }
1458
1459 *nodes = TAKE_PTR(l);
1460
1461 return 1;
1462}
1463
3d7415f4
LP
1464static const sd_bus_vtable transfer_vtable[] = {
1465 SD_BUS_VTABLE_START(0),
956ecd3c 1466
3d7415f4
LP
1467 SD_BUS_PROPERTY("Id", "u", NULL, offsetof(Transfer, id), SD_BUS_VTABLE_PROPERTY_CONST),
1468 SD_BUS_PROPERTY("Local", "s", NULL, offsetof(Transfer, local), SD_BUS_VTABLE_PROPERTY_CONST),
1469 SD_BUS_PROPERTY("Remote", "s", NULL, offsetof(Transfer, remote), SD_BUS_VTABLE_PROPERTY_CONST),
1470 SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Transfer, type), SD_BUS_VTABLE_PROPERTY_CONST),
1471 SD_BUS_PROPERTY("Verify", "s", property_get_verify, offsetof(Transfer, verify), SD_BUS_VTABLE_PROPERTY_CONST),
7079cfef 1472 SD_BUS_PROPERTY("Progress", "d", property_get_progress, 0, 0),
956ecd3c 1473
3d7415f4 1474 SD_BUS_METHOD("Cancel", NULL, NULL, method_cancel, SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1475
1476 SD_BUS_SIGNAL_WITH_NAMES("LogMessage",
1477 "us",
1478 SD_BUS_PARAM(priority)
1479 SD_BUS_PARAM(line),
1480 0),
71cb203a
LP
1481 SD_BUS_SIGNAL_WITH_NAMES("ProgressUpdate",
1482 "d",
1483 SD_BUS_PARAM(progress),
1484 0),
956ecd3c 1485
3d7415f4
LP
1486 SD_BUS_VTABLE_END,
1487};
1488
a3b7cf50
ZJS
1489static const BusObjectImplementation transfer_object = {
1490 "/org/freedesktop/import1/transfer",
1491 "org.freedesktop.import1.Transfer",
1492 .fallback_vtables = BUS_FALLBACK_VTABLES({transfer_vtable, transfer_object_find}),
1493 .node_enumerator = transfer_node_enumerator,
1494};
1495
3d7415f4
LP
1496static const sd_bus_vtable manager_vtable[] = {
1497 SD_BUS_VTABLE_START(0),
956ecd3c
ZJS
1498
1499 SD_BUS_METHOD_WITH_NAMES("ImportTar",
1500 "hsbb",
1501 SD_BUS_PARAM(fd)
1502 SD_BUS_PARAM(local_name)
1503 SD_BUS_PARAM(force)
1504 SD_BUS_PARAM(read_only),
1505 "uo",
1506 SD_BUS_PARAM(transfer_id)
1507 SD_BUS_PARAM(transfer_path),
1508 method_import_tar_or_raw,
1509 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1510 SD_BUS_METHOD_WITH_NAMES("ImportTarEx",
1511 "hsst",
1512 SD_BUS_PARAM(fd)
1513 SD_BUS_PARAM(local_name)
1514 SD_BUS_PARAM(class)
1515 SD_BUS_PARAM(flags),
1516 "uo",
1517 SD_BUS_PARAM(transfer_id)
1518 SD_BUS_PARAM(transfer_path),
1519 method_import_tar_or_raw,
1520 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1521 SD_BUS_METHOD_WITH_NAMES("ImportRaw",
1522 "hsbb",
1523 SD_BUS_PARAM(fd)
1524 SD_BUS_PARAM(local_name)
1525 SD_BUS_PARAM(force)
1526 SD_BUS_PARAM(read_only),
1527 "uo",
1528 SD_BUS_PARAM(transfer_id)
1529 SD_BUS_PARAM(transfer_path),
1530 method_import_tar_or_raw,
1531 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1532 SD_BUS_METHOD_WITH_NAMES("ImportRawEx",
1533 "hsst",
1534 SD_BUS_PARAM(fd)
1535 SD_BUS_PARAM(local_name)
1536 SD_BUS_PARAM(class)
1537 SD_BUS_PARAM(flags),
1538 "uo",
1539 SD_BUS_PARAM(transfer_id)
1540 SD_BUS_PARAM(transfer_path),
1541 method_import_tar_or_raw,
1542 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1543 SD_BUS_METHOD_WITH_NAMES("ImportFileSystem",
1544 "hsbb",
1545 SD_BUS_PARAM(fd)
1546 SD_BUS_PARAM(local_name)
1547 SD_BUS_PARAM(force)
1548 SD_BUS_PARAM(read_only),
1549 "uo",
1550 SD_BUS_PARAM(transfer_id)
1551 SD_BUS_PARAM(transfer_path),
1552 method_import_fs,
1553 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1554 SD_BUS_METHOD_WITH_NAMES("ImportFileSystemEx",
1555 "hsst",
1556 SD_BUS_PARAM(fd)
1557 SD_BUS_PARAM(local_name)
1558 SD_BUS_PARAM(class)
1559 SD_BUS_PARAM(flags),
1560 "uo",
1561 SD_BUS_PARAM(transfer_id)
1562 SD_BUS_PARAM(transfer_path),
1563 method_import_fs,
1564 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1565 SD_BUS_METHOD_WITH_NAMES("ExportTar",
1566 "shs",
1567 SD_BUS_PARAM(local_name)
1568 SD_BUS_PARAM(fd)
1569 SD_BUS_PARAM(format),
1570 "uo",
1571 SD_BUS_PARAM(transfer_id)
1572 SD_BUS_PARAM(transfer_path),
1573 method_export_tar_or_raw,
1574 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1575 SD_BUS_METHOD_WITH_NAMES("ExportTarEx",
1576 "sshst",
1577 SD_BUS_PARAM(local_name)
1578 SD_BUS_PARAM(class)
1579 SD_BUS_PARAM(fd)
1580 SD_BUS_PARAM(format)
1581 SD_BUS_PARAM(flags),
1582 "uo",
1583 SD_BUS_PARAM(transfer_id)
1584 SD_BUS_PARAM(transfer_path),
1585 method_export_tar_or_raw,
1586 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1587 SD_BUS_METHOD_WITH_NAMES("ExportRaw",
1588 "shs",
1589 SD_BUS_PARAM(local_name)
1590 SD_BUS_PARAM(fd)
1591 SD_BUS_PARAM(format),
1592 "uo",
1593 SD_BUS_PARAM(transfer_id)
1594 SD_BUS_PARAM(transfer_path),
1595 method_export_tar_or_raw,
1596 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1597 SD_BUS_METHOD_WITH_NAMES("ExportRawEx",
1598 "sshst",
1599 SD_BUS_PARAM(local_name)
1600 SD_BUS_PARAM(class)
1601 SD_BUS_PARAM(fd)
1602 SD_BUS_PARAM(format)
1603 SD_BUS_PARAM(flags),
1604 "uo",
1605 SD_BUS_PARAM(transfer_id)
1606 SD_BUS_PARAM(transfer_path),
1607 method_export_tar_or_raw,
1608 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1609 SD_BUS_METHOD_WITH_NAMES("PullTar",
1610 "sssb",
1611 SD_BUS_PARAM(url)
1612 SD_BUS_PARAM(local_name)
1613 SD_BUS_PARAM(verify_mode)
1614 SD_BUS_PARAM(force),
1615 "uo",
1616 SD_BUS_PARAM(transfer_id)
1617 SD_BUS_PARAM(transfer_path),
1618 method_pull_tar_or_raw,
1619 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1620 SD_BUS_METHOD_WITH_NAMES("PullTarEx",
1621 "sssst",
1622 SD_BUS_PARAM(url)
1623 SD_BUS_PARAM(local_name)
1624 SD_BUS_PARAM(class)
1625 SD_BUS_PARAM(verify_mode)
1626 SD_BUS_PARAM(flags),
1627 "uo",
1628 SD_BUS_PARAM(transfer_id)
1629 SD_BUS_PARAM(transfer_path),
1630 method_pull_tar_or_raw,
1631 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1632 SD_BUS_METHOD_WITH_NAMES("PullRaw",
1633 "sssb",
1634 SD_BUS_PARAM(url)
1635 SD_BUS_PARAM(local_name)
1636 SD_BUS_PARAM(verify_mode)
1637 SD_BUS_PARAM(force),
1638 "uo",
1639 SD_BUS_PARAM(transfer_id)
1640 SD_BUS_PARAM(transfer_path),
1641 method_pull_tar_or_raw,
1642 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1643 SD_BUS_METHOD_WITH_NAMES("PullRawEx",
1644 "sssst",
1645 SD_BUS_PARAM(url)
1646 SD_BUS_PARAM(local_name)
1647 SD_BUS_PARAM(class)
1648 SD_BUS_PARAM(verify_mode)
1649 SD_BUS_PARAM(flags),
1650 "uo",
1651 SD_BUS_PARAM(transfer_id)
1652 SD_BUS_PARAM(transfer_path),
1653 method_pull_tar_or_raw,
1654 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1655 SD_BUS_METHOD_WITH_NAMES("ListTransfers",
1656 NULL,,
1657 "a(usssdo)",
1658 SD_BUS_PARAM(transfers),
1659 method_list_transfers,
1660 SD_BUS_VTABLE_UNPRIVILEGED),
7af5785d
LP
1661 SD_BUS_METHOD_WITH_NAMES("ListTransfersEx",
1662 "st",
1663 SD_BUS_PARAM(class)
1664 SD_BUS_PARAM(flags),
1665 "a(ussssdo)",
1666 SD_BUS_PARAM(transfers),
1667 method_list_transfers,
1668 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1669 SD_BUS_METHOD_WITH_NAMES("CancelTransfer",
1670 "u",
1671 SD_BUS_PARAM(transfer_id),
1672 NULL,,
1673 method_cancel_transfer,
1674 SD_BUS_VTABLE_UNPRIVILEGED),
5b7bfe06
LP
1675 SD_BUS_METHOD_WITH_NAMES("ListImages",
1676 "st",
1677 SD_BUS_PARAM(class)
1678 SD_BUS_PARAM(flags),
1679 "a(ssssbtttttt)",
1680 SD_BUS_PARAM(images),
1681 method_list_images,
1682 SD_BUS_VTABLE_UNPRIVILEGED),
956ecd3c
ZJS
1683
1684 SD_BUS_SIGNAL_WITH_NAMES("TransferNew",
1685 "uo",
1686 SD_BUS_PARAM(transfer_id)
1687 SD_BUS_PARAM(transfer_path),
1688 0),
1689 SD_BUS_SIGNAL_WITH_NAMES("TransferRemoved",
1690 "uos",
1691 SD_BUS_PARAM(transfer_id)
1692 SD_BUS_PARAM(transfer_path)
1693 SD_BUS_PARAM(result),
1694 0),
1695
3d7415f4
LP
1696 SD_BUS_VTABLE_END,
1697};
1698
a3b7cf50
ZJS
1699static const BusObjectImplementation manager_object = {
1700 "/org/freedesktop/import1",
1701 "org.freedesktop.import1.Manager",
1702 .vtables = BUS_VTABLES(manager_vtable),
1703 .children = BUS_IMPLEMENTATIONS(&transfer_object),
1704};
3d7415f4
LP
1705
1706static int manager_add_bus_objects(Manager *m) {
1707 int r;
1708
1709 assert(m);
1710
a3b7cf50 1711 r = bus_add_implementation(m->bus, &manager_object, m);
3d7415f4 1712 if (r < 0)
a3b7cf50 1713 return r;
3d7415f4 1714
ac9f55ed
LP
1715 r = bus_log_control_api_register(m->bus);
1716 if (r < 0)
1717 return r;
1718
0c0b9306 1719 r = sd_bus_request_name_async(m->bus, NULL, "org.freedesktop.import1", 0, NULL, NULL);
3d7415f4 1720 if (r < 0)
0c0b9306 1721 return log_error_errno(r, "Failed to request name: %m");
3d7415f4
LP
1722
1723 r = sd_bus_attach_event(m->bus, m->event, 0);
1724 if (r < 0)
1725 return log_error_errno(r, "Failed to attach bus to event loop: %m");
1726
1727 return 0;
1728}
1729
1730static bool manager_check_idle(void *userdata) {
1731 Manager *m = userdata;
1732
1733 return hashmap_isempty(m->transfers);
1734}
1735
c7779a61
IS
1736static void manager_parse_env(Manager *m) {
1737 int r;
1738
1739 assert(m);
1740
1741 /* Same as src/import/{import,pull}.c:
1742 * Let's make these relatively low-level settings also controllable via env vars. User can then set
1743 * them for systemd-importd.service if they like to tweak behaviour */
1744
1745 r = getenv_bool("SYSTEMD_IMPORT_BTRFS_SUBVOL");
1746 if (r >= 0)
1747 m->use_btrfs_subvol = r;
1748 else if (r != -ENXIO)
1749 log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_SUBVOL: %m");
1750
1751 r = getenv_bool("SYSTEMD_IMPORT_BTRFS_QUOTA");
1752 if (r >= 0)
1753 m->use_btrfs_quota = r;
1754 else if (r != -ENXIO)
1755 log_warning_errno(r, "Failed to parse $SYSTEMD_IMPORT_BTRFS_QUOTA: %m");
1756}
1757
5272ae42 1758static int run(int argc, char *argv[]) {
3d7415f4
LP
1759 _cleanup_(manager_unrefp) Manager *m = NULL;
1760 int r;
1761
d2acb93d 1762 log_setup();
3d7415f4 1763
fc021a5b
ZJS
1764 r = service_parse_argv("systemd-importd.service",
1765 "VM and container image import and export service.",
d4cc0edf
ZJS
1766 BUS_IMPLEMENTATIONS(&manager_object,
1767 &log_control_object),
fc021a5b
ZJS
1768 argc, argv);
1769 if (r <= 0)
1770 return r;
3d7415f4 1771
fc021a5b 1772 umask(0022);
3d7415f4 1773
423bba99 1774 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD) >= 0);
3d7415f4
LP
1775
1776 r = manager_new(&m);
5272ae42
ZJS
1777 if (r < 0)
1778 return log_error_errno(r, "Failed to allocate manager object: %m");
3d7415f4 1779
c7779a61
IS
1780 manager_parse_env(m);
1781
3d7415f4
LP
1782 r = manager_add_bus_objects(m);
1783 if (r < 0)
5272ae42 1784 return r;
3d7415f4 1785
deb86997
MY
1786 r = sd_notify(false, NOTIFY_READY);
1787 if (r < 0)
1788 log_warning_errno(r, "Failed to send readiness notification, ignoring: %m");
1789
1790 r = bus_event_loop_with_idle(
1791 m->event,
1792 m->bus,
1793 "org.freedesktop.import1",
1794 DEFAULT_EXIT_USEC,
1795 manager_check_idle,
1796 m);
5272ae42
ZJS
1797 if (r < 0)
1798 return log_error_errno(r, "Failed to run event loop: %m");
3d7415f4 1799
5272ae42 1800 return 0;
3d7415f4 1801}
5272ae42
ZJS
1802
1803DEFINE_MAIN_FUNCTION(run);