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