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