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