1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include "alloc-util.h"
10 #include "export-raw.h"
11 #include "export-tar.h"
14 #include "hostname-util.h"
15 #include "import-util.h"
16 #include "machine-image.h"
17 #include "main-func.h"
18 #include "signal-util.h"
19 #include "string-util.h"
22 static ImportCompressType arg_compress
= IMPORT_COMPRESS_UNKNOWN
;
24 static void determine_compression_from_filename(const char *p
) {
26 if (arg_compress
!= IMPORT_COMPRESS_UNKNOWN
)
30 arg_compress
= IMPORT_COMPRESS_UNCOMPRESSED
;
34 if (endswith(p
, ".xz"))
35 arg_compress
= IMPORT_COMPRESS_XZ
;
36 else if (endswith(p
, ".gz"))
37 arg_compress
= IMPORT_COMPRESS_GZIP
;
38 else if (endswith(p
, ".bz2"))
39 arg_compress
= IMPORT_COMPRESS_BZIP2
;
41 arg_compress
= IMPORT_COMPRESS_UNCOMPRESSED
;
44 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
45 log_notice("Transfer aborted.");
46 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
50 static void on_tar_finished(TarExport
*export
, int error
, void *userdata
) {
51 sd_event
*event
= userdata
;
55 log_info("Operation completed successfully.");
57 sd_event_exit(event
, abs(error
));
60 static int export_tar(int argc
, char *argv
[], void *userdata
) {
61 _cleanup_(tar_export_unrefp
) TarExport
*export
= NULL
;
62 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
63 _cleanup_(image_unrefp
) Image
*image
= NULL
;
64 const char *path
= NULL
, *local
= NULL
;
65 _cleanup_close_
int open_fd
= -1;
68 if (hostname_is_valid(argv
[1], 0)) {
69 r
= image_find(IMAGE_MACHINE
, argv
[1], NULL
, &image
);
71 return log_error_errno(r
, "Machine image %s not found.", argv
[1]);
73 return log_error_errno(r
, "Failed to look for machine %s: %m", argv
[1]);
81 path
= empty_or_dash_to_null(path
);
83 determine_compression_from_filename(path
);
86 open_fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
88 return log_error_errno(errno
, "Failed to open tar image for export: %m");
92 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local
, path
, import_compress_type_to_string(arg_compress
));
94 _cleanup_free_
char *pretty
= NULL
;
98 (void) fd_get_path(fd
, &pretty
);
99 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local
, strna(pretty
), import_compress_type_to_string(arg_compress
));
102 r
= sd_event_default(&event
);
104 return log_error_errno(r
, "Failed to allocate event loop: %m");
106 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
107 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
108 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
110 r
= tar_export_new(&export
, event
, on_tar_finished
, event
);
112 return log_error_errno(r
, "Failed to allocate exporter: %m");
114 r
= tar_export_start(export
, local
, fd
, arg_compress
);
116 return log_error_errno(r
, "Failed to export image: %m");
118 r
= sd_event_loop(event
);
120 return log_error_errno(r
, "Failed to run event loop: %m");
122 log_info("Exiting.");
126 static void on_raw_finished(RawExport
*export
, int error
, void *userdata
) {
127 sd_event
*event
= userdata
;
131 log_info("Operation completed successfully.");
133 sd_event_exit(event
, abs(error
));
136 static int export_raw(int argc
, char *argv
[], void *userdata
) {
137 _cleanup_(raw_export_unrefp
) RawExport
*export
= NULL
;
138 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
139 _cleanup_(image_unrefp
) Image
*image
= NULL
;
140 const char *path
= NULL
, *local
= NULL
;
141 _cleanup_close_
int open_fd
= -1;
144 if (hostname_is_valid(argv
[1], 0)) {
145 r
= image_find(IMAGE_MACHINE
, argv
[1], NULL
, &image
);
147 return log_error_errno(r
, "Machine image %s not found.", argv
[1]);
149 return log_error_errno(r
, "Failed to look for machine %s: %m", argv
[1]);
157 path
= empty_or_dash_to_null(path
);
159 determine_compression_from_filename(path
);
162 open_fd
= open(path
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_CLOEXEC
|O_NOCTTY
, 0666);
164 return log_error_errno(errno
, "Failed to open raw image for export: %m");
168 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local
, path
, import_compress_type_to_string(arg_compress
));
170 _cleanup_free_
char *pretty
= NULL
;
174 (void) fd_get_path(fd
, &pretty
);
175 log_info("Exporting '%s', saving to '%s' with compression '%s'.", local
, strna(pretty
), import_compress_type_to_string(arg_compress
));
178 r
= sd_event_default(&event
);
180 return log_error_errno(r
, "Failed to allocate event loop: %m");
182 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
183 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
184 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
186 r
= raw_export_new(&export
, event
, on_raw_finished
, event
);
188 return log_error_errno(r
, "Failed to allocate exporter: %m");
190 r
= raw_export_start(export
, local
, fd
, arg_compress
);
192 return log_error_errno(r
, "Failed to export image: %m");
194 r
= sd_event_loop(event
);
196 return log_error_errno(r
, "Failed to run event loop: %m");
198 log_info("Exiting.");
202 static int help(int argc
, char *argv
[], void *userdata
) {
204 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
205 "Export container or virtual machine images.\n\n"
206 " -h --help Show this help\n"
207 " --version Show package version\n"
208 " --format=FORMAT Select format\n\n"
210 " tar NAME [FILE] Export a TAR image\n"
211 " raw NAME [FILE] Export a RAW image\n",
212 program_invocation_short_name
);
217 static int parse_argv(int argc
, char *argv
[]) {
224 static const struct option options
[] = {
225 { "help", no_argument
, NULL
, 'h' },
226 { "version", no_argument
, NULL
, ARG_VERSION
},
227 { "format", required_argument
, NULL
, ARG_FORMAT
},
236 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
241 return help(0, NULL
, NULL
);
247 if (streq(optarg
, "uncompressed"))
248 arg_compress
= IMPORT_COMPRESS_UNCOMPRESSED
;
249 else if (streq(optarg
, "xz"))
250 arg_compress
= IMPORT_COMPRESS_XZ
;
251 else if (streq(optarg
, "gzip"))
252 arg_compress
= IMPORT_COMPRESS_GZIP
;
253 else if (streq(optarg
, "bzip2"))
254 arg_compress
= IMPORT_COMPRESS_BZIP2
;
256 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
257 "Unknown format: %s", optarg
);
264 assert_not_reached("Unhandled option");
270 static int export_main(int argc
, char *argv
[]) {
271 static const Verb verbs
[] = {
272 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
273 { "tar", 2, 3, 0, export_tar
},
274 { "raw", 2, 3, 0, export_raw
},
278 return dispatch_verb(argc
, argv
, verbs
, NULL
);
281 static int run(int argc
, char *argv
[]) {
284 setlocale(LC_ALL
, "");
285 log_parse_environment();
288 r
= parse_argv(argc
, argv
);
292 (void) ignore_signals(SIGPIPE
, -1);
294 return export_main(argc
, argv
);
297 DEFINE_MAIN_FUNCTION(run
);