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