Branch data 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 : 12 : static int help(int argc, char *argv[], void *userdata) {
203 : :
204 : 12 : 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 : 12 : return 0;
215 : : }
216 : :
217 : 16 : 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 [ - + ]: 16 : assert(argc >= 0);
234 [ - + ]: 16 : assert(argv);
235 : :
236 [ + - ]: 16 : while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
237 : :
238 [ + - - + : 16 : switch (c) {
- ]
239 : :
240 : 12 : case 'h':
241 : 12 : 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 : 4 : case '?':
261 : 4 : 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 : 16 : static int run(int argc, char *argv[]) {
282 : : int r;
283 : :
284 : 16 : setlocale(LC_ALL, "");
285 : 16 : log_parse_environment();
286 : 16 : log_open();
287 : :
288 : 16 : r = parse_argv(argc, argv);
289 [ + - ]: 16 : if (r <= 0)
290 : 16 : return r;
291 : :
292 : 0 : (void) ignore_signals(SIGPIPE, -1);
293 : :
294 : 0 : return export_main(argc, argv);
295 : : }
296 : :
297 : 16 : DEFINE_MAIN_FUNCTION(run);
|