Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <getopt.h>
4 : #include <locale.h>
5 :
6 : #include "sd-event.h"
7 : #include "sd-id128.h"
8 :
9 : #include "alloc-util.h"
10 : #include "export-raw.h"
11 : #include "export-tar.h"
12 : #include "fd-util.h"
13 : #include "fs-util.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"
20 : #include "verbs.h"
21 :
22 : static ImportCompressType arg_compress = IMPORT_COMPRESS_UNKNOWN;
23 :
24 0 : static void determine_compression_from_filename(const char *p) {
25 :
26 0 : if (arg_compress != IMPORT_COMPRESS_UNKNOWN)
27 0 : return;
28 :
29 0 : if (!p) {
30 0 : arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
31 0 : return;
32 : }
33 :
34 0 : if (endswith(p, ".xz"))
35 0 : arg_compress = IMPORT_COMPRESS_XZ;
36 0 : else if (endswith(p, ".gz"))
37 0 : arg_compress = IMPORT_COMPRESS_GZIP;
38 0 : else if (endswith(p, ".bz2"))
39 0 : arg_compress = IMPORT_COMPRESS_BZIP2;
40 : else
41 0 : arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
42 : }
43 :
44 0 : static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
45 0 : log_notice("Transfer aborted.");
46 0 : sd_event_exit(sd_event_source_get_event(s), EINTR);
47 0 : return 0;
48 : }
49 :
50 0 : static void on_tar_finished(TarExport *export, int error, void *userdata) {
51 0 : sd_event *event = userdata;
52 0 : assert(export);
53 :
54 0 : if (error == 0)
55 0 : log_info("Operation completed successfully.");
56 :
57 0 : sd_event_exit(event, abs(error));
58 0 : }
59 :
60 0 : static int export_tar(int argc, char *argv[], void *userdata) {
61 0 : _cleanup_(tar_export_unrefp) TarExport *export = NULL;
62 0 : _cleanup_(sd_event_unrefp) sd_event *event = NULL;
63 0 : _cleanup_(image_unrefp) Image *image = NULL;
64 0 : const char *path = NULL, *local = NULL;
65 0 : _cleanup_close_ int open_fd = -1;
66 : int r, fd;
67 :
68 0 : if (machine_name_is_valid(argv[1])) {
69 0 : r = image_find(IMAGE_MACHINE, argv[1], &image);
70 0 : if (r == -ENOENT)
71 0 : return log_error_errno(r, "Machine image %s not found.", argv[1]);
72 0 : if (r < 0)
73 0 : return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
74 :
75 0 : local = image->path;
76 : } else
77 0 : local = argv[1];
78 :
79 0 : if (argc >= 3)
80 0 : path = argv[2];
81 0 : path = empty_or_dash_to_null(path);
82 :
83 0 : determine_compression_from_filename(path);
84 :
85 0 : if (path) {
86 0 : open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
87 0 : if (open_fd < 0)
88 0 : return log_error_errno(errno, "Failed to open tar image for export: %m");
89 :
90 0 : fd = open_fd;
91 :
92 0 : log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress));
93 : } else {
94 0 : _cleanup_free_ char *pretty = NULL;
95 :
96 0 : fd = STDOUT_FILENO;
97 :
98 0 : (void) fd_get_path(fd, &pretty);
99 0 : log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
100 : }
101 :
102 0 : r = sd_event_default(&event);
103 0 : if (r < 0)
104 0 : return log_error_errno(r, "Failed to allocate event loop: %m");
105 :
106 0 : assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
107 0 : (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
108 0 : (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
109 :
110 0 : r = tar_export_new(&export, event, on_tar_finished, event);
111 0 : if (r < 0)
112 0 : return log_error_errno(r, "Failed to allocate exporter: %m");
113 :
114 0 : r = tar_export_start(export, local, fd, arg_compress);
115 0 : if (r < 0)
116 0 : return log_error_errno(r, "Failed to export image: %m");
117 :
118 0 : r = sd_event_loop(event);
119 0 : if (r < 0)
120 0 : return log_error_errno(r, "Failed to run event loop: %m");
121 :
122 0 : log_info("Exiting.");
123 0 : return -r;
124 : }
125 :
126 0 : static void on_raw_finished(RawExport *export, int error, void *userdata) {
127 0 : sd_event *event = userdata;
128 0 : assert(export);
129 :
130 0 : if (error == 0)
131 0 : log_info("Operation completed successfully.");
132 :
133 0 : sd_event_exit(event, abs(error));
134 0 : }
135 :
136 0 : static int export_raw(int argc, char *argv[], void *userdata) {
137 0 : _cleanup_(raw_export_unrefp) RawExport *export = NULL;
138 0 : _cleanup_(sd_event_unrefp) sd_event *event = NULL;
139 0 : _cleanup_(image_unrefp) Image *image = NULL;
140 0 : const char *path = NULL, *local = NULL;
141 0 : _cleanup_close_ int open_fd = -1;
142 : int r, fd;
143 :
144 0 : if (machine_name_is_valid(argv[1])) {
145 0 : r = image_find(IMAGE_MACHINE, argv[1], &image);
146 0 : if (r == -ENOENT)
147 0 : return log_error_errno(r, "Machine image %s not found.", argv[1]);
148 0 : if (r < 0)
149 0 : return log_error_errno(r, "Failed to look for machine %s: %m", argv[1]);
150 :
151 0 : local = image->path;
152 : } else
153 0 : local = argv[1];
154 :
155 0 : if (argc >= 3)
156 0 : path = argv[2];
157 0 : path = empty_or_dash_to_null(path);
158 :
159 0 : determine_compression_from_filename(path);
160 :
161 0 : if (path) {
162 0 : open_fd = open(path, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC|O_NOCTTY, 0666);
163 0 : if (open_fd < 0)
164 0 : return log_error_errno(errno, "Failed to open raw image for export: %m");
165 :
166 0 : fd = open_fd;
167 :
168 0 : log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, path, import_compress_type_to_string(arg_compress));
169 : } else {
170 0 : _cleanup_free_ char *pretty = NULL;
171 :
172 0 : fd = STDOUT_FILENO;
173 :
174 0 : (void) fd_get_path(fd, &pretty);
175 0 : log_info("Exporting '%s', saving to '%s' with compression '%s'.", local, strna(pretty), import_compress_type_to_string(arg_compress));
176 : }
177 :
178 0 : r = sd_event_default(&event);
179 0 : if (r < 0)
180 0 : return log_error_errno(r, "Failed to allocate event loop: %m");
181 :
182 0 : assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
183 0 : (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
184 0 : (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
185 :
186 0 : r = raw_export_new(&export, event, on_raw_finished, event);
187 0 : if (r < 0)
188 0 : return log_error_errno(r, "Failed to allocate exporter: %m");
189 :
190 0 : r = raw_export_start(export, local, fd, arg_compress);
191 0 : if (r < 0)
192 0 : return log_error_errno(r, "Failed to export image: %m");
193 :
194 0 : r = sd_event_loop(event);
195 0 : if (r < 0)
196 0 : return log_error_errno(r, "Failed to run event loop: %m");
197 :
198 0 : log_info("Exiting.");
199 0 : return -r;
200 : }
201 :
202 3 : static int help(int argc, char *argv[], void *userdata) {
203 :
204 3 : 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"
209 : "Commands:\n"
210 : " tar NAME [FILE] Export a TAR image\n"
211 : " raw NAME [FILE] Export a RAW image\n",
212 : program_invocation_short_name);
213 :
214 3 : return 0;
215 : }
216 :
217 4 : static int parse_argv(int argc, char *argv[]) {
218 :
219 : enum {
220 : ARG_VERSION = 0x100,
221 : ARG_FORMAT,
222 : };
223 :
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 },
228 : {}
229 : };
230 :
231 : int c;
232 :
233 4 : assert(argc >= 0);
234 4 : assert(argv);
235 :
236 4 : while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
237 :
238 4 : switch (c) {
239 :
240 3 : case 'h':
241 3 : return help(0, NULL, NULL);
242 :
243 0 : case ARG_VERSION:
244 0 : return version();
245 :
246 0 : case ARG_FORMAT:
247 0 : if (streq(optarg, "uncompressed"))
248 0 : arg_compress = IMPORT_COMPRESS_UNCOMPRESSED;
249 0 : else if (streq(optarg, "xz"))
250 0 : arg_compress = IMPORT_COMPRESS_XZ;
251 0 : else if (streq(optarg, "gzip"))
252 0 : arg_compress = IMPORT_COMPRESS_GZIP;
253 0 : else if (streq(optarg, "bzip2"))
254 0 : arg_compress = IMPORT_COMPRESS_BZIP2;
255 : else
256 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
257 : "Unknown format: %s", optarg);
258 0 : break;
259 :
260 1 : case '?':
261 1 : return -EINVAL;
262 :
263 0 : default:
264 0 : assert_not_reached("Unhandled option");
265 : }
266 :
267 0 : return 1;
268 : }
269 :
270 0 : 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 },
275 : {}
276 : };
277 :
278 0 : return dispatch_verb(argc, argv, verbs, NULL);
279 : }
280 :
281 4 : static int run(int argc, char *argv[]) {
282 : int r;
283 :
284 4 : setlocale(LC_ALL, "");
285 4 : log_parse_environment();
286 4 : log_open();
287 :
288 4 : r = parse_argv(argc, argv);
289 4 : if (r <= 0)
290 4 : return r;
291 :
292 0 : (void) ignore_signals(SIGPIPE, -1);
293 :
294 0 : return export_main(argc, argv);
295 : }
296 :
297 4 : DEFINE_MAIN_FUNCTION(run);
|