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