]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/importctl.c
7ada0e51df0be65adf74122e895b525b7ebbba47
[thirdparty/systemd.git] / src / import / importctl.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <getopt.h>
4
5 #include "sd-bus.h"
6
7 #include "alloc-util.h"
8 #include "build.h"
9 #include "bus-error.h"
10 #include "bus-locator.h"
11 #include "bus-util.h"
12 #include "discover-image.h"
13 #include "fd-util.h"
14 #include "format-table.h"
15 #include "hostname-util.h"
16 #include "import-common.h"
17 #include "import-util.h"
18 #include "locale-util.h"
19 #include "log.h"
20 #include "macro.h"
21 #include "main-func.h"
22 #include "os-util.h"
23 #include "pager.h"
24 #include "parse-argument.h"
25 #include "parse-util.h"
26 #include "path-util.h"
27 #include "pretty-print.h"
28 #include "signal-util.h"
29 #include "sort-util.h"
30 #include "spawn-polkit-agent.h"
31 #include "string-table.h"
32 #include "verbs.h"
33 #include "web-util.h"
34
35 static PagerFlags arg_pager_flags = 0;
36 static bool arg_legend = true;
37 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
38 static const char *arg_host = NULL;
39 static ImportFlags arg_import_flags = 0;
40 static ImportFlags arg_import_flags_mask = 0; /* Indicates which flags have been explicitly set to on or to off */
41 static bool arg_quiet = false;
42 static bool arg_ask_password = true;
43 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
44 static const char* arg_format = NULL;
45 static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF;
46 static ImageClass arg_image_class = _IMAGE_CLASS_INVALID;
47
48 #define PROGRESS_PREFIX "Total: "
49
50 static int settle_image_class(void) {
51
52 if (arg_image_class < 0) {
53 _cleanup_free_ char *j = NULL;
54
55 for (ImageClass class = 0; class < _IMAGE_CLASS_MAX; class++)
56 if (strextendf_with_separator(&j, ", ", "%s (downloads to %s/)",
57 image_class_to_string(class),
58 image_root_to_string(class)) < 0)
59 return log_oom();
60
61 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
62 "No image class specified, retry with --class= set to one of: %s.", j);
63 }
64
65 /* Keep the original pristine downloaded file as a copy only when dealing with machine images,
66 * because unlike sysext/confext/portable they are typically modified during runtime. */
67 if (!FLAGS_SET(arg_import_flags_mask, IMPORT_PULL_KEEP_DOWNLOAD))
68 SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, arg_image_class == IMAGE_MACHINE);
69
70 return 0;
71 }
72
73 typedef struct Context {
74 const char *object_path;
75 double progress;
76 } Context;
77
78 static int match_log_message(sd_bus_message *m, void *userdata, sd_bus_error *error) {
79 Context *c = ASSERT_PTR(userdata);
80 const char *line;
81 unsigned priority;
82 int r;
83
84 assert(m);
85
86 if (!streq_ptr(c->object_path, sd_bus_message_get_path(m)))
87 return 0;
88
89 r = sd_bus_message_read(m, "us", &priority, &line);
90 if (r < 0) {
91 bus_log_parse_error(r);
92 return 0;
93 }
94
95 if (arg_quiet && LOG_PRI(priority) >= LOG_INFO)
96 return 0;
97
98 if (!arg_quiet)
99 clear_progress_bar(PROGRESS_PREFIX);
100
101 log_full(priority, "%s", line);
102
103 if (!arg_quiet)
104 draw_progress_bar(PROGRESS_PREFIX, c->progress * 100);
105
106 return 0;
107 }
108
109 static int match_progress_update(sd_bus_message *m, void *userdata, sd_bus_error *error) {
110 Context *c = ASSERT_PTR(userdata);
111 int r;
112
113 assert(m);
114
115 if (!streq_ptr(c->object_path, sd_bus_message_get_path(m)))
116 return 0;
117
118 r = sd_bus_message_read(m, "d", &c->progress);
119 if (r < 0) {
120 bus_log_parse_error(r);
121 return 0;
122 }
123
124 if (!arg_quiet)
125 draw_progress_bar(PROGRESS_PREFIX, c->progress * 100);
126
127 return 0;
128 }
129
130 static int match_transfer_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
131 Context *c = ASSERT_PTR(userdata);
132 const char *path, *result;
133 uint32_t id;
134 int r;
135
136 assert(m);
137
138 if (!arg_quiet)
139 clear_progress_bar(PROGRESS_PREFIX);
140
141 r = sd_bus_message_read(m, "uos", &id, &path, &result);
142 if (r < 0) {
143 bus_log_parse_error(r);
144 return 0;
145 }
146
147 if (!streq_ptr(c->object_path, path))
148 return 0;
149
150 sd_event_exit(sd_bus_get_event(sd_bus_message_get_bus(m)), !streq_ptr(result, "done"));
151 return 0;
152 }
153
154 static int transfer_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
155 assert(s);
156 assert(si);
157
158 if (!arg_quiet)
159 clear_progress_bar(PROGRESS_PREFIX);
160
161 if (!arg_quiet)
162 log_info("Continuing download in the background. Use \"%s cancel-transfer %" PRIu32 "\" to abort transfer.",
163 program_invocation_short_name,
164 PTR_TO_UINT32(userdata));
165
166 sd_event_exit(sd_event_source_get_event(s), EINTR);
167 return 0;
168 }
169
170 static int transfer_image_common(sd_bus *bus, sd_bus_message *m) {
171 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_job_removed = NULL, *slot_log_message = NULL, *slot_progress_update = NULL;
172 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
173 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
174 _cleanup_(sd_event_unrefp) sd_event* event = NULL;
175 Context c = {};
176 uint32_t id;
177 int r;
178
179 assert(bus);
180 assert(m);
181
182 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
183
184 r = sd_event_default(&event);
185 if (r < 0)
186 return log_error_errno(r, "Failed to get event loop: %m");
187
188 r = sd_bus_attach_event(bus, event, 0);
189 if (r < 0)
190 return log_error_errno(r, "Failed to attach bus to event loop: %m");
191
192 r = bus_match_signal_async(
193 bus,
194 &slot_job_removed,
195 bus_import_mgr,
196 "TransferRemoved",
197 match_transfer_removed,
198 /* add_callback= */ NULL,
199 &c);
200 if (r < 0)
201 return log_error_errno(r, "Failed to request match: %m");
202
203 r = sd_bus_match_signal_async(
204 bus,
205 &slot_log_message,
206 "org.freedesktop.import1",
207 /* object_path= */ NULL,
208 "org.freedesktop.import1.Transfer",
209 "LogMessage",
210 match_log_message,
211 /* add_callback= */ NULL,
212 &c);
213 if (r < 0)
214 return log_error_errno(r, "Failed to request match: %m");
215
216 r = sd_bus_match_signal_async(
217 bus,
218 &slot_progress_update,
219 "org.freedesktop.import1",
220 /* object_path= */ NULL,
221 "org.freedesktop.import1.Transfer",
222 "ProgressUpdate",
223 match_progress_update,
224 /* add_callback= */ NULL,
225 &c);
226 if (r < 0)
227 return log_error_errno(r, "Failed to request match: %m");
228
229 r = sd_bus_call(bus, m, 0, &error, &reply);
230 if (r < 0)
231 return log_error_errno(r, "Failed to transfer image: %s", bus_error_message(&error, r));
232
233 r = sd_bus_message_read(reply, "uo", &id, &c.object_path);
234 if (r < 0)
235 return bus_log_parse_error(r);
236
237 if (!arg_quiet) {
238 clear_progress_bar(PROGRESS_PREFIX);
239 log_info("Enqueued transfer job %u. Press C-c to continue download in background.", id);
240 draw_progress_bar(PROGRESS_PREFIX, c.progress);
241 }
242
243 (void) sd_event_add_signal(event, NULL, SIGINT|SD_EVENT_SIGNAL_PROCMASK, transfer_signal_handler, UINT32_TO_PTR(id));
244 (void) sd_event_add_signal(event, NULL, SIGTERM|SD_EVENT_SIGNAL_PROCMASK, transfer_signal_handler, UINT32_TO_PTR(id));
245
246 r = sd_event_loop(event);
247 if (r < 0)
248 return log_error_errno(r, "Failed to run event loop: %m");
249
250 return -r;
251 }
252
253 static int import_tar(int argc, char *argv[], void *userdata) {
254 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
255 _cleanup_free_ char *ll = NULL, *fn = NULL;
256 const char *local = NULL, *path = NULL;
257 _cleanup_close_ int fd = -EBADF;
258 sd_bus *bus = ASSERT_PTR(userdata);
259 int r;
260
261 r = settle_image_class();
262 if (r < 0)
263 return r;
264
265 if (argc >= 2)
266 path = empty_or_dash_to_null(argv[1]);
267
268 if (argc >= 3)
269 local = empty_or_dash_to_null(argv[2]);
270 else if (path) {
271 r = path_extract_filename(path, &fn);
272 if (r < 0)
273 return log_error_errno(r, "Cannot extract container name from filename: %m");
274 if (r == O_DIRECTORY)
275 return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
276 "Path '%s' refers to directory, but we need a regular file: %m", path);
277
278 local = fn;
279 }
280 if (!local)
281 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
282 "Need either path or local name.");
283
284 r = tar_strip_suffixes(local, &ll);
285 if (r < 0)
286 return log_oom();
287
288 local = ll;
289
290 if (!image_name_is_valid(local))
291 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
292 "Local name %s is not a suitable image name.",
293 local);
294
295 if (path) {
296 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
297 if (fd < 0)
298 return log_error_errno(errno, "Failed to open %s: %m", path);
299 }
300
301 if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~(IMPORT_FORCE|IMPORT_READ_ONLY)) == 0) {
302 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportTar");
303 if (r < 0)
304 return bus_log_create_error(r);
305
306 r = sd_bus_message_append(
307 m,
308 "hsbb",
309 fd >= 0 ? fd : STDIN_FILENO,
310 local,
311 FLAGS_SET(arg_import_flags, IMPORT_FORCE),
312 FLAGS_SET(arg_import_flags, IMPORT_READ_ONLY));
313 } else {
314 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportTarEx");
315 if (r < 0)
316 return bus_log_create_error(r);
317
318 r = sd_bus_message_append(
319 m,
320 "hsst",
321 fd >= 0 ? fd : STDIN_FILENO,
322 local,
323 image_class_to_string(arg_image_class),
324 (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY));
325 }
326 if (r < 0)
327 return bus_log_create_error(r);
328
329 return transfer_image_common(bus, m);
330 }
331
332 static int import_raw(int argc, char *argv[], void *userdata) {
333 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
334 _cleanup_free_ char *ll = NULL, *fn = NULL;
335 const char *local = NULL, *path = NULL;
336 _cleanup_close_ int fd = -EBADF;
337 sd_bus *bus = ASSERT_PTR(userdata);
338 int r;
339
340 r = settle_image_class();
341 if (r < 0)
342 return r;
343
344 if (argc >= 2)
345 path = empty_or_dash_to_null(argv[1]);
346
347 if (argc >= 3)
348 local = empty_or_dash_to_null(argv[2]);
349 else if (path) {
350 r = path_extract_filename(path, &fn);
351 if (r < 0)
352 return log_error_errno(r, "Cannot extract container name from filename: %m");
353 if (r == O_DIRECTORY)
354 return log_error_errno(SYNTHETIC_ERRNO(EISDIR),
355 "Path '%s' refers to directory, but we need a regular file: %m", path);
356
357 local = fn;
358 }
359 if (!local)
360 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
361 "Need either path or local name.");
362
363 r = raw_strip_suffixes(local, &ll);
364 if (r < 0)
365 return log_oom();
366
367 local = ll;
368
369 if (!image_name_is_valid(local))
370 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
371 "Local name %s is not a suitable image name.",
372 local);
373
374 if (path) {
375 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
376 if (fd < 0)
377 return log_error_errno(errno, "Failed to open %s: %m", path);
378 }
379
380 if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~(IMPORT_FORCE|IMPORT_READ_ONLY)) == 0) {
381 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportRaw");
382 if (r < 0)
383 return bus_log_create_error(r);
384
385 r = sd_bus_message_append(
386 m,
387 "hsbb",
388 fd >= 0 ? fd : STDIN_FILENO,
389 local,
390 FLAGS_SET(arg_import_flags, IMPORT_FORCE),
391 FLAGS_SET(arg_import_flags, IMPORT_READ_ONLY));
392 } else {
393 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportRawEx");
394 if (r < 0)
395 return bus_log_create_error(r);
396
397 r = sd_bus_message_append(
398 m,
399 "hsst",
400 fd >= 0 ? fd : STDIN_FILENO,
401 local,
402 image_class_to_string(arg_image_class),
403 (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY));
404 }
405 if (r < 0)
406 return bus_log_create_error(r);
407
408 return transfer_image_common(bus, m);
409 }
410
411 static int import_fs(int argc, char *argv[], void *userdata) {
412 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
413 const char *local = NULL, *path = NULL;
414 _cleanup_free_ char *fn = NULL;
415 _cleanup_close_ int fd = -EBADF;
416 sd_bus *bus = ASSERT_PTR(userdata);
417 int r;
418
419 r = settle_image_class();
420 if (r < 0)
421 return r;
422
423 if (argc >= 2)
424 path = empty_or_dash_to_null(argv[1]);
425
426 if (argc >= 3)
427 local = empty_or_dash_to_null(argv[2]);
428 else if (path) {
429 r = path_extract_filename(path, &fn);
430 if (r < 0)
431 return log_error_errno(r, "Cannot extract container name from filename: %m");
432
433 local = fn;
434 }
435 if (!local)
436 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
437 "Need either path or local name.");
438
439 if (!image_name_is_valid(local))
440 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
441 "Local name %s is not a suitable image name.",
442 local);
443
444 if (path) {
445 fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
446 if (fd < 0)
447 return log_error_errno(errno, "Failed to open directory '%s': %m", path);
448 }
449
450 if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~(IMPORT_FORCE|IMPORT_READ_ONLY)) == 0) {
451 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportFileSystem");
452 if (r < 0)
453 return bus_log_create_error(r);
454
455 r = sd_bus_message_append(
456 m,
457 "hsbb",
458 fd >= 0 ? fd : STDIN_FILENO,
459 local,
460 FLAGS_SET(arg_import_flags, IMPORT_FORCE),
461 FLAGS_SET(arg_import_flags, IMPORT_READ_ONLY));
462 } else {
463 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ImportFileSystemEx");
464 if (r < 0)
465 return bus_log_create_error(r);
466
467 r = sd_bus_message_append(
468 m,
469 "hsst",
470 fd >= 0 ? fd : STDIN_FILENO,
471 local,
472 image_class_to_string(arg_image_class),
473 (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY));
474 }
475 if (r < 0)
476 return bus_log_create_error(r);
477
478
479 return transfer_image_common(bus, m);
480 }
481
482 static void determine_compression_from_filename(const char *p) {
483 if (arg_format)
484 return;
485
486 if (!p)
487 return;
488
489 if (endswith(p, ".xz"))
490 arg_format = "xz";
491 else if (endswith(p, ".gz"))
492 arg_format = "gzip";
493 else if (endswith(p, ".bz2"))
494 arg_format = "bzip2";
495 }
496
497 static int export_tar(int argc, char *argv[], void *userdata) {
498 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
499 _cleanup_close_ int fd = -EBADF;
500 const char *local = NULL, *path = NULL;
501 sd_bus *bus = ASSERT_PTR(userdata);
502 int r;
503
504 r = settle_image_class();
505 if (r < 0)
506 return r;
507
508 local = argv[1];
509 if (!image_name_is_valid(local))
510 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
511 "Image name %s is not valid.", local);
512
513 if (argc >= 3)
514 path = argv[2];
515 path = empty_or_dash_to_null(path);
516
517 if (path) {
518 determine_compression_from_filename(path);
519
520 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
521 if (fd < 0)
522 return log_error_errno(errno, "Failed to open %s: %m", path);
523 }
524
525 if (arg_image_class == IMAGE_MACHINE && arg_import_flags == 0) {
526 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportTar");
527 if (r < 0)
528 return bus_log_create_error(r);
529
530 r = sd_bus_message_append(
531 m,
532 "shs",
533 local,
534 fd >= 0 ? fd : STDOUT_FILENO,
535 arg_format);
536 } else {
537 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportTarEx");
538 if (r < 0)
539 return bus_log_create_error(r);
540
541 r = sd_bus_message_append(
542 m,
543 "sshst",
544 local,
545 image_class_to_string(arg_image_class),
546 fd >= 0 ? fd : STDOUT_FILENO,
547 arg_format,
548 /* flags= */ 0);
549 }
550 if (r < 0)
551 return bus_log_create_error(r);
552
553 return transfer_image_common(bus, m);
554 }
555
556 static int export_raw(int argc, char *argv[], void *userdata) {
557 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
558 _cleanup_close_ int fd = -EBADF;
559 const char *local = NULL, *path = NULL;
560 sd_bus *bus = ASSERT_PTR(userdata);
561 int r;
562
563 r = settle_image_class();
564 if (r < 0)
565 return r;
566
567 local = argv[1];
568 if (!image_name_is_valid(local))
569 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
570 "Image name %s is not valid.", local);
571
572 if (argc >= 3)
573 path = argv[2];
574 path = empty_or_dash_to_null(path);
575
576 if (path) {
577 determine_compression_from_filename(path);
578
579 fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
580 if (fd < 0)
581 return log_error_errno(errno, "Failed to open %s: %m", path);
582 }
583
584 if (arg_image_class == IMAGE_MACHINE && arg_import_flags == 0) {
585 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportRaw");
586 if (r < 0)
587 return bus_log_create_error(r);
588
589 r = sd_bus_message_append(
590 m,
591 "shs",
592 local,
593 fd >= 0 ? fd : STDOUT_FILENO,
594 arg_format);
595 } else {
596 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "ExportRawEx");
597 if (r < 0)
598 return bus_log_create_error(r);
599
600 r = sd_bus_message_append(
601 m,
602 "sshst",
603 local,
604 image_class_to_string(arg_image_class),
605 fd >= 0 ? fd : STDOUT_FILENO,
606 arg_format,
607 /* flags= */ 0);
608 }
609 if (r < 0)
610 return bus_log_create_error(r);
611
612 return transfer_image_common(bus, m);
613 }
614
615 static int pull_tar(int argc, char *argv[], void *userdata) {
616 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
617 _cleanup_free_ char *l = NULL, *ll = NULL;
618 const char *local, *remote;
619 sd_bus *bus = ASSERT_PTR(userdata);
620 int r;
621
622 r = settle_image_class();
623 if (r < 0)
624 return r;
625
626 remote = argv[1];
627 if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
628 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
629 "URL '%s' is not valid.", remote);
630
631 if (argc >= 3)
632 local = argv[2];
633 else {
634 r = import_url_last_component(remote, &l);
635 if (r < 0)
636 return log_error_errno(r, "Failed to get final component of URL: %m");
637
638 local = l;
639 }
640
641 local = empty_or_dash_to_null(local);
642
643 if (local) {
644 r = tar_strip_suffixes(local, &ll);
645 if (r < 0)
646 return log_oom();
647
648 local = ll;
649
650 if (!image_name_is_valid(local))
651 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
652 "Local name %s is not a suitable image name.",
653 local);
654 }
655
656 if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~IMPORT_FORCE) == 0) {
657 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTar");
658 if (r < 0)
659 return bus_log_create_error(r);
660
661 r = sd_bus_message_append(
662 m,
663 "sssb",
664 remote,
665 local,
666 import_verify_to_string(arg_verify),
667 FLAGS_SET(arg_import_flags, IMPORT_FORCE));
668 } else {
669 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullTarEx");
670 if (r < 0)
671 return bus_log_create_error(r);
672
673 r = sd_bus_message_append(
674 m,
675 "sssst",
676 remote,
677 local,
678 image_class_to_string(arg_image_class),
679 import_verify_to_string(arg_verify),
680 (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
681 }
682 if (r < 0)
683 return bus_log_create_error(r);
684
685 return transfer_image_common(bus, m);
686 }
687
688 static int pull_raw(int argc, char *argv[], void *userdata) {
689 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
690 _cleanup_free_ char *l = NULL, *ll = NULL;
691 const char *local, *remote;
692 sd_bus *bus = ASSERT_PTR(userdata);
693 int r;
694
695 r = settle_image_class();
696 if (r < 0)
697 return r;
698
699 remote = argv[1];
700 if (!http_url_is_valid(remote) && !file_url_is_valid(remote))
701 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
702 "URL '%s' is not valid.", remote);
703
704 if (argc >= 3)
705 local = argv[2];
706 else {
707 r = import_url_last_component(remote, &l);
708 if (r < 0)
709 return log_error_errno(r, "Failed to get final component of URL: %m");
710
711 local = l;
712 }
713
714 local = empty_or_dash_to_null(local);
715
716 if (local) {
717 r = raw_strip_suffixes(local, &ll);
718 if (r < 0)
719 return log_oom();
720
721 local = ll;
722
723 if (!image_name_is_valid(local))
724 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
725 "Local name %s is not a suitable image name.",
726 local);
727 }
728
729 if (arg_image_class == IMAGE_MACHINE && (arg_image_class & ~IMPORT_FORCE) == 0) {
730 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRaw");
731 if (r < 0)
732 return bus_log_create_error(r);
733
734 r = sd_bus_message_append(
735 m,
736 "sssb",
737 remote,
738 local,
739 import_verify_to_string(arg_verify),
740 FLAGS_SET(arg_import_flags, IMPORT_FORCE));
741 } else {
742 r = bus_message_new_method_call(bus, &m, bus_import_mgr, "PullRawEx");
743 if (r < 0)
744 return bus_log_create_error(r);
745
746 r = sd_bus_message_append(
747 m,
748 "sssst",
749 remote,
750 local,
751 image_class_to_string(arg_image_class),
752 import_verify_to_string(arg_verify),
753 (uint64_t) arg_import_flags & (IMPORT_FORCE|IMPORT_READ_ONLY|IMPORT_PULL_KEEP_DOWNLOAD));
754 }
755 if (r < 0)
756 return bus_log_create_error(r);
757
758 return transfer_image_common(bus, m);
759 }
760
761 static int list_transfers(int argc, char *argv[], void *userdata) {
762 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
763 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
764 _cleanup_(table_unrefp) Table *t = NULL;
765 sd_bus *bus = ASSERT_PTR(userdata);
766 int r;
767
768 pager_open(arg_pager_flags);
769
770 bool ex;
771 r = bus_call_method(bus, bus_import_mgr, "ListTransfersEx", &error, &reply, "st", image_class_to_string(arg_image_class), UINT64_C(0));
772 if (r < 0) {
773 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
774 sd_bus_error_free(&error);
775
776 r = bus_call_method(bus, bus_import_mgr, "ListTransfers", &error, &reply, NULL);
777 }
778 if (r < 0)
779 return log_error_errno(r, "Could not get transfers: %s", bus_error_message(&error, r));
780
781 ex = false;
782 r = sd_bus_message_enter_container(reply, 'a', "(usssdo)");
783 } else {
784 ex = true;
785 r = sd_bus_message_enter_container(reply, 'a', "(ussssdo)");
786 }
787 if (r < 0)
788 return bus_log_parse_error(r);
789
790 t = table_new("id", "progress", "type", "class", "local", "remote");
791 if (!t)
792 return log_oom();
793
794 (void) table_set_sort(t, (size_t) 4, (size_t) 0);
795 table_set_ersatz_string(t, TABLE_ERSATZ_DASH);
796
797 for (;;) {
798 const char *type, *remote, *local, *class = "machine";
799 double progress;
800 uint32_t id;
801
802 if (ex)
803 r = sd_bus_message_read(reply, "(ussssdo)", &id, &type, &remote, &local, &class, &progress, NULL);
804 else
805 r = sd_bus_message_read(reply, "(usssdo)", &id, &type, &remote, &local, &progress, NULL);
806 if (r < 0)
807 return bus_log_parse_error(r);
808 if (r == 0)
809 break;
810
811 /* Ideally we use server-side filtering. But if the server can't do it, we need to do it client side */
812 if (arg_image_class >= 0 && image_class_from_string(class) != arg_image_class)
813 continue;
814
815 r = table_add_many(
816 t,
817 TABLE_UINT32, id,
818 TABLE_SET_ALIGN_PERCENT, 100);
819 if (r < 0)
820 return table_log_add_error(r);
821
822 if (progress < 0)
823 r = table_add_many(
824 t,
825 TABLE_EMPTY,
826 TABLE_SET_ALIGN_PERCENT, 100);
827 else
828 r = table_add_many(
829 t,
830 TABLE_PERCENT, (int) (progress * 100),
831 TABLE_SET_ALIGN_PERCENT, 100);
832 if (r < 0)
833 return table_log_add_error(r);
834 r = table_add_many(
835 t,
836 TABLE_STRING, type,
837 TABLE_STRING, class,
838 TABLE_STRING, local,
839 TABLE_STRING, remote,
840 TABLE_SET_URL, remote);
841 if (r < 0)
842 return table_log_add_error(r);
843 }
844
845 r = sd_bus_message_exit_container(reply);
846 if (r < 0)
847 return bus_log_parse_error(r);
848
849 if (!table_isempty(t)) {
850 r = table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
851 if (r < 0)
852 return log_error_errno(r, "Failed to output table: %m");
853 }
854
855 if (arg_legend) {
856 if (!table_isempty(t))
857 printf("\n%zu transfers listed.\n", table_get_rows(t) - 1);
858 else
859 printf("No transfers.\n");
860 }
861
862 return 0;
863 }
864
865 static int cancel_transfer(int argc, char *argv[], void *userdata) {
866 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
867 sd_bus *bus = ASSERT_PTR(userdata);
868 int r;
869
870 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
871
872 for (int i = 1; i < argc; i++) {
873 uint32_t id;
874
875 r = safe_atou32(argv[i], &id);
876 if (r < 0)
877 return log_error_errno(r, "Failed to parse transfer id: %s", argv[i]);
878
879 r = bus_call_method(bus, bus_import_mgr, "CancelTransfer", &error, NULL, "u", id);
880 if (r < 0)
881 return log_error_errno(r, "Could not cancel transfer: %s", bus_error_message(&error, r));
882 }
883
884 return 0;
885 }
886
887 static int help(int argc, char *argv[], void *userdata) {
888 _cleanup_free_ char *link = NULL;
889 int r;
890
891 pager_open(arg_pager_flags);
892
893 r = terminal_urlify_man("importctl", "1", &link);
894 if (r < 0)
895 return log_oom();
896
897 printf("%1$s [OPTIONS...] COMMAND ...\n\n"
898 "%5$sDownload, import or export disk images%6$s\n"
899 "\n%3$sCommands:%4$s\n"
900 " pull-tar URL [NAME] Download a TAR container image\n"
901 " pull-raw URL [NAME] Download a RAW container or VM image\n"
902 " import-tar FILE [NAME] Import a local TAR container image\n"
903 " import-raw FILE [NAME] Import a local RAW container or VM image\n"
904 " import-fs DIRECTORY [NAME] Import a local directory container image\n"
905 " export-tar NAME [FILE] Export a TAR container image locally\n"
906 " export-raw NAME [FILE] Export a RAW container or VM image locally\n"
907 " list-transfers Show list of transfers in progress\n"
908 " cancel-transfer [ID...] Cancel a transfer\n"
909 "\n%3$sOptions:%4$s\n"
910 " -h --help Show this help\n"
911 " --version Show package version\n"
912 " --no-pager Do not pipe output into a pager\n"
913 " --no-legend Do not show the headers and footers\n"
914 " --no-ask-password Do not ask for system passwords\n"
915 " -H --host=[USER@]HOST Operate on remote host\n"
916 " -M --machine=CONTAINER Operate on local container\n"
917 " --read-only Create read-only image\n"
918 " -q --quiet Suppress output\n"
919 " --json=pretty|short|off Generate JSON output\n"
920 " -j Equvilant to --json=pretty on TTY, --json=short\n"
921 " otherwise\n"
922 " --verify=MODE Verification mode for downloaded images (no,\n"
923 " checksum, signature)\n"
924 " --format=xz|gzip|bzip2 Desired output format for export\n"
925 " --force Install image even if already exists\n"
926 " -m --class=machine Install as machine image\n"
927 " -P --class=portable Install as portable service image\n"
928 " -S --class=sysext Install as system extension image\n"
929 " -C --class=confext Install as configuration extension image\n"
930 " --keep-download=BOOL Control whether to keep pristine copy of download\n"
931 " -N Shortcut for --keep-download=no\n"
932 "\nSee the %2$s for details.\n",
933 program_invocation_short_name,
934 link,
935 ansi_underline(),
936 ansi_normal(),
937 ansi_highlight(),
938 ansi_normal());
939
940 return 0;
941 }
942
943 static int parse_argv(int argc, char *argv[]) {
944
945 enum {
946 ARG_VERSION = 0x100,
947 ARG_NO_PAGER,
948 ARG_NO_LEGEND,
949 ARG_NO_ASK_PASSWORD,
950 ARG_READ_ONLY,
951 ARG_JSON,
952 ARG_VERIFY,
953 ARG_FORCE,
954 ARG_FORMAT,
955 ARG_CLASS,
956 ARG_KEEP_DOWNLOAD,
957 };
958
959 static const struct option options[] = {
960 { "help", no_argument, NULL, 'h' },
961 { "version", no_argument, NULL, ARG_VERSION },
962 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
963 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
964 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
965 { "host", required_argument, NULL, 'H' },
966 { "machine", required_argument, NULL, 'M' },
967 { "read-only", no_argument, NULL, ARG_READ_ONLY },
968 { "json", required_argument, NULL, ARG_JSON },
969 { "quiet", no_argument, NULL, 'q' },
970 { "verify", required_argument, NULL, ARG_VERIFY },
971 { "force", no_argument, NULL, ARG_FORCE },
972 { "format", required_argument, NULL, ARG_FORMAT },
973 { "class", required_argument, NULL, ARG_CLASS },
974 { "keep-download", required_argument, NULL, ARG_KEEP_DOWNLOAD },
975 {}
976 };
977
978 int c, r;
979
980 assert(argc >= 0);
981 assert(argv);
982
983 for (;;) {
984 c = getopt_long(argc, argv, "hH:M:jqmPSCN", options, NULL);
985 if (c < 0)
986 break;
987
988 switch (c) {
989
990 case 'h':
991 return help(0, NULL, NULL);
992
993 case ARG_VERSION:
994 return version();
995
996 case ARG_NO_PAGER:
997 arg_pager_flags |= PAGER_DISABLE;
998 break;
999
1000 case ARG_NO_LEGEND:
1001 arg_legend = false;
1002 break;
1003
1004 case ARG_NO_ASK_PASSWORD:
1005 arg_ask_password = false;
1006 break;
1007
1008 case 'H':
1009 arg_transport = BUS_TRANSPORT_REMOTE;
1010 arg_host = optarg;
1011 break;
1012
1013 case 'M':
1014 arg_transport = BUS_TRANSPORT_MACHINE;
1015 arg_host = optarg;
1016 break;
1017
1018 case ARG_READ_ONLY:
1019 arg_import_flags |= IMPORT_READ_ONLY;
1020 arg_import_flags_mask |= IMPORT_READ_ONLY;
1021 break;
1022
1023 case 'q':
1024 arg_quiet = true;
1025 break;
1026
1027 case ARG_VERIFY:
1028 if (streq(optarg, "help")) {
1029 DUMP_STRING_TABLE(import_verify, ImportVerify, _IMPORT_VERIFY_MAX);
1030 return 0;
1031 }
1032
1033 r = import_verify_from_string(optarg);
1034 if (r < 0)
1035 return log_error_errno(r, "Failed to parse --verify= setting: %s", optarg);
1036 arg_verify = r;
1037 break;
1038
1039 case ARG_FORCE:
1040 arg_import_flags |= IMPORT_FORCE;
1041 arg_import_flags_mask |= IMPORT_FORCE;
1042 break;
1043
1044 case ARG_FORMAT:
1045 if (!STR_IN_SET(optarg, "uncompressed", "xz", "gzip", "bzip2"))
1046 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
1047 "Unknown format: %s", optarg);
1048
1049 arg_format = optarg;
1050 break;
1051
1052 case ARG_JSON:
1053 r = parse_json_argument(optarg, &arg_json_format_flags);
1054 if (r <= 0)
1055 return r;
1056
1057 arg_legend = false;
1058 break;
1059
1060 case 'j':
1061 arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO;
1062 arg_legend = false;
1063 break;
1064
1065 case ARG_CLASS:
1066 arg_image_class = image_class_from_string(optarg);
1067 if (arg_image_class < 0)
1068 return log_error_errno(arg_image_class, "Failed to parse --class= parameter: %s", optarg);
1069 break;
1070
1071 case 'm':
1072 arg_image_class = IMAGE_MACHINE;
1073 break;
1074
1075 case 'P':
1076 arg_image_class = IMAGE_PORTABLE;
1077 break;
1078
1079 case 'S':
1080 arg_image_class = IMAGE_SYSEXT;
1081 break;
1082
1083 case 'C':
1084 arg_image_class = IMAGE_CONFEXT;
1085 break;
1086
1087 case ARG_KEEP_DOWNLOAD:
1088 r = parse_boolean(optarg);
1089 if (r < 0)
1090 return log_error_errno(r, "Failed to parse --keep-download= value: %s", optarg);
1091
1092 SET_FLAG(arg_import_flags, IMPORT_PULL_KEEP_DOWNLOAD, r);
1093 arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD;
1094 break;
1095
1096 case 'N':
1097 arg_import_flags_mask &= ~IMPORT_PULL_KEEP_DOWNLOAD;
1098 arg_import_flags_mask |= IMPORT_PULL_KEEP_DOWNLOAD;
1099 break;
1100
1101 case '?':
1102 return -EINVAL;
1103
1104 default:
1105 assert_not_reached();
1106 }
1107 }
1108
1109 return 1;
1110 }
1111
1112 static int importctl_main(int argc, char *argv[], sd_bus *bus) {
1113
1114 static const Verb verbs[] = {
1115 { "help", VERB_ANY, VERB_ANY, 0, help },
1116 { "import-tar", 2, 3, 0, import_tar },
1117 { "import-raw", 2, 3, 0, import_raw },
1118 { "import-fs", 2, 3, 0, import_fs },
1119 { "export-tar", 2, 3, 0, export_tar },
1120 { "export-raw", 2, 3, 0, export_raw },
1121 { "pull-tar", 2, 3, 0, pull_tar },
1122 { "pull-raw", 2, 3, 0, pull_raw },
1123 { "list-transfers", VERB_ANY, 1, VERB_DEFAULT, list_transfers },
1124 { "cancel-transfer", 2, VERB_ANY, 0, cancel_transfer },
1125 {}
1126 };
1127
1128 return dispatch_verb(argc, argv, verbs, bus);
1129 }
1130
1131 static int run(int argc, char *argv[]) {
1132 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
1133 int r;
1134
1135 setlocale(LC_ALL, "");
1136 log_setup();
1137
1138 r = parse_argv(argc, argv);
1139 if (r <= 0)
1140 return r;
1141
1142 r = bus_connect_transport(arg_transport, arg_host, RUNTIME_SCOPE_SYSTEM, &bus);
1143 if (r < 0)
1144 return bus_log_connect_error(r, arg_transport);
1145
1146 (void) sd_bus_set_allow_interactive_authorization(bus, arg_ask_password);
1147
1148 return importctl_main(argc, argv, bus);
1149 }
1150
1151 DEFINE_MAIN_FUNCTION(run);