]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
b6e676ce LP |
2 | |
3 | #include <getopt.h> | |
4 | ||
5 | #include "sd-event.h" | |
dccca82b | 6 | #include "sd-id128.h" |
3f6fd1ba | 7 | |
b5efdb8a | 8 | #include "alloc-util.h" |
3ffd4af2 | 9 | #include "fd-util.h" |
f4f15635 | 10 | #include "fs-util.h" |
25300b5a | 11 | #include "hostname-util.h" |
b6e676ce | 12 | #include "import-raw.h" |
3f6fd1ba LP |
13 | #include "import-tar.h" |
14 | #include "import-util.h" | |
15 | #include "machine-image.h" | |
5e332028 | 16 | #include "main-func.h" |
3f6fd1ba | 17 | #include "signal-util.h" |
07630cea | 18 | #include "string-util.h" |
3f6fd1ba | 19 | #include "verbs.h" |
b6e676ce LP |
20 | |
21 | static bool arg_force = false; | |
22 | static bool arg_read_only = false; | |
23 | static const char *arg_image_root = "/var/lib/machines"; | |
24 | ||
25 | static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { | |
26 | log_notice("Transfer aborted."); | |
27 | sd_event_exit(sd_event_source_get_event(s), EINTR); | |
28 | return 0; | |
29 | } | |
30 | ||
31 | static void on_tar_finished(TarImport *import, int error, void *userdata) { | |
32 | sd_event *event = userdata; | |
33 | assert(import); | |
34 | ||
35 | if (error == 0) | |
36 | log_info("Operation completed successfully."); | |
37 | ||
38 | sd_event_exit(event, abs(error)); | |
39 | } | |
40 | ||
41 | static int import_tar(int argc, char *argv[], void *userdata) { | |
42 | _cleanup_(tar_import_unrefp) TarImport *import = NULL; | |
4afd3348 | 43 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; |
b6e676ce LP |
44 | const char *path = NULL, *local = NULL; |
45 | _cleanup_free_ char *ll = NULL; | |
46 | _cleanup_close_ int open_fd = -1; | |
47 | int r, fd; | |
48 | ||
49 | if (argc >= 2) | |
50 | path = argv[1]; | |
51 | if (isempty(path) || streq(path, "-")) | |
52 | path = NULL; | |
53 | ||
54 | if (argc >= 3) | |
55 | local = argv[2]; | |
56 | else if (path) | |
57 | local = basename(path); | |
58 | if (isempty(local) || streq(local, "-")) | |
59 | local = NULL; | |
60 | ||
61 | if (local) { | |
62 | r = tar_strip_suffixes(local, &ll); | |
63 | if (r < 0) | |
64 | return log_oom(); | |
65 | ||
66 | local = ll; | |
67 | ||
68 | if (!machine_name_is_valid(local)) { | |
69 | log_error("Local image name '%s' is not valid.", local); | |
70 | return -EINVAL; | |
71 | } | |
72 | ||
73 | if (!arg_force) { | |
5ef46e5f | 74 | r = image_find(IMAGE_MACHINE, local, NULL); |
3a6ce860 LP |
75 | if (r < 0) { |
76 | if (r != -ENOENT) | |
77 | return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); | |
78 | } else { | |
92dfd88b | 79 | log_error("Image '%s' already exists.", local); |
b6e676ce LP |
80 | return -EEXIST; |
81 | } | |
82 | } | |
83 | } else | |
84 | local = "imported"; | |
85 | ||
86 | if (path) { | |
87 | open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); | |
88 | if (open_fd < 0) | |
89 | return log_error_errno(errno, "Failed to open tar image to import: %m"); | |
90 | ||
91 | fd = open_fd; | |
92 | ||
93 | log_info("Importing '%s', saving as '%s'.", path, local); | |
94 | } else { | |
95 | _cleanup_free_ char *pretty = NULL; | |
96 | ||
97 | fd = STDIN_FILENO; | |
98 | ||
1295c906 | 99 | (void) fd_get_path(fd, &pretty); |
b6e676ce LP |
100 | log_info("Importing '%s', saving as '%s'.", strna(pretty), local); |
101 | } | |
102 | ||
103 | r = sd_event_default(&event); | |
104 | if (r < 0) | |
105 | return log_error_errno(r, "Failed to allocate event loop: %m"); | |
106 | ||
72c0a2c2 LP |
107 | assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); |
108 | (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); | |
109 | (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); | |
b6e676ce LP |
110 | |
111 | r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event); | |
112 | if (r < 0) | |
113 | return log_error_errno(r, "Failed to allocate importer: %m"); | |
114 | ||
115 | r = tar_import_start(import, fd, local, arg_force, arg_read_only); | |
116 | if (r < 0) | |
117 | return log_error_errno(r, "Failed to import image: %m"); | |
118 | ||
119 | r = sd_event_loop(event); | |
120 | if (r < 0) | |
121 | return log_error_errno(r, "Failed to run event loop: %m"); | |
122 | ||
123 | log_info("Exiting."); | |
124 | return -r; | |
125 | } | |
126 | ||
127 | static void on_raw_finished(RawImport *import, int error, void *userdata) { | |
128 | sd_event *event = userdata; | |
129 | assert(import); | |
130 | ||
131 | if (error == 0) | |
132 | log_info("Operation completed successfully."); | |
133 | ||
134 | sd_event_exit(event, abs(error)); | |
135 | } | |
136 | ||
137 | static int import_raw(int argc, char *argv[], void *userdata) { | |
138 | _cleanup_(raw_import_unrefp) RawImport *import = NULL; | |
4afd3348 | 139 | _cleanup_(sd_event_unrefp) sd_event *event = NULL; |
b6e676ce LP |
140 | const char *path = NULL, *local = NULL; |
141 | _cleanup_free_ char *ll = NULL; | |
142 | _cleanup_close_ int open_fd = -1; | |
143 | int r, fd; | |
144 | ||
145 | if (argc >= 2) | |
146 | path = argv[1]; | |
147 | if (isempty(path) || streq(path, "-")) | |
148 | path = NULL; | |
149 | ||
150 | if (argc >= 3) | |
151 | local = argv[2]; | |
152 | else if (path) | |
153 | local = basename(path); | |
154 | if (isempty(local) || streq(local, "-")) | |
155 | local = NULL; | |
156 | ||
157 | if (local) { | |
158 | r = raw_strip_suffixes(local, &ll); | |
159 | if (r < 0) | |
160 | return log_oom(); | |
161 | ||
162 | local = ll; | |
163 | ||
164 | if (!machine_name_is_valid(local)) { | |
165 | log_error("Local image name '%s' is not valid.", local); | |
166 | return -EINVAL; | |
167 | } | |
168 | ||
169 | if (!arg_force) { | |
5ef46e5f | 170 | r = image_find(IMAGE_MACHINE, local, NULL); |
3a6ce860 LP |
171 | if (r < 0) { |
172 | if (r != -ENOENT) | |
173 | return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local); | |
174 | } else { | |
92dfd88b | 175 | log_error("Image '%s' already exists.", local); |
b6e676ce LP |
176 | return -EEXIST; |
177 | } | |
178 | } | |
179 | } else | |
180 | local = "imported"; | |
181 | ||
182 | if (path) { | |
183 | open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); | |
184 | if (open_fd < 0) | |
185 | return log_error_errno(errno, "Failed to open raw image to import: %m"); | |
186 | ||
187 | fd = open_fd; | |
188 | ||
189 | log_info("Importing '%s', saving as '%s'.", path, local); | |
190 | } else { | |
191 | _cleanup_free_ char *pretty = NULL; | |
192 | ||
193 | fd = STDIN_FILENO; | |
194 | ||
1295c906 | 195 | (void) fd_get_path(fd, &pretty); |
fe4377fa | 196 | log_info("Importing '%s', saving as '%s'.", strempty(pretty), local); |
b6e676ce LP |
197 | } |
198 | ||
199 | r = sd_event_default(&event); | |
200 | if (r < 0) | |
201 | return log_error_errno(r, "Failed to allocate event loop: %m"); | |
202 | ||
72c0a2c2 LP |
203 | assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); |
204 | (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL); | |
205 | (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL); | |
b6e676ce LP |
206 | |
207 | r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event); | |
208 | if (r < 0) | |
209 | return log_error_errno(r, "Failed to allocate importer: %m"); | |
210 | ||
211 | r = raw_import_start(import, fd, local, arg_force, arg_read_only); | |
212 | if (r < 0) | |
213 | return log_error_errno(r, "Failed to import image: %m"); | |
214 | ||
215 | r = sd_event_loop(event); | |
216 | if (r < 0) | |
217 | return log_error_errno(r, "Failed to run event loop: %m"); | |
218 | ||
219 | log_info("Exiting."); | |
220 | return -r; | |
221 | } | |
222 | ||
223 | static int help(int argc, char *argv[], void *userdata) { | |
224 | ||
225 | printf("%s [OPTIONS...] {COMMAND} ...\n\n" | |
587fec42 | 226 | "Import container or virtual machine images.\n\n" |
b6e676ce LP |
227 | " -h --help Show this help\n" |
228 | " --version Show package version\n" | |
229 | " --force Force creation of image\n" | |
230 | " --image-root=PATH Image root directory\n" | |
231 | " --read-only Create a read-only image\n\n" | |
232 | "Commands:\n" | |
587fec42 LP |
233 | " tar FILE [NAME] Import a TAR image\n" |
234 | " raw FILE [NAME] Import a RAW image\n", | |
b6e676ce LP |
235 | program_invocation_short_name); |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | static int parse_argv(int argc, char *argv[]) { | |
241 | ||
242 | enum { | |
243 | ARG_VERSION = 0x100, | |
244 | ARG_FORCE, | |
245 | ARG_IMAGE_ROOT, | |
246 | ARG_READ_ONLY, | |
247 | }; | |
248 | ||
249 | static const struct option options[] = { | |
250 | { "help", no_argument, NULL, 'h' }, | |
251 | { "version", no_argument, NULL, ARG_VERSION }, | |
252 | { "force", no_argument, NULL, ARG_FORCE }, | |
253 | { "image-root", required_argument, NULL, ARG_IMAGE_ROOT }, | |
254 | { "read-only", no_argument, NULL, ARG_READ_ONLY }, | |
255 | {} | |
256 | }; | |
257 | ||
258 | int c; | |
259 | ||
260 | assert(argc >= 0); | |
261 | assert(argv); | |
262 | ||
263 | while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) | |
264 | ||
265 | switch (c) { | |
266 | ||
267 | case 'h': | |
268 | return help(0, NULL, NULL); | |
269 | ||
270 | case ARG_VERSION: | |
3f6fd1ba | 271 | return version(); |
b6e676ce LP |
272 | |
273 | case ARG_FORCE: | |
274 | arg_force = true; | |
275 | break; | |
276 | ||
277 | case ARG_IMAGE_ROOT: | |
278 | arg_image_root = optarg; | |
279 | break; | |
280 | ||
281 | case ARG_READ_ONLY: | |
282 | arg_read_only = true; | |
283 | break; | |
284 | ||
285 | case '?': | |
286 | return -EINVAL; | |
287 | ||
288 | default: | |
289 | assert_not_reached("Unhandled option"); | |
290 | } | |
291 | ||
292 | return 1; | |
293 | } | |
294 | ||
295 | static int import_main(int argc, char *argv[]) { | |
b6e676ce LP |
296 | static const Verb verbs[] = { |
297 | { "help", VERB_ANY, VERB_ANY, 0, help }, | |
298 | { "tar", 2, 3, 0, import_tar }, | |
299 | { "raw", 2, 3, 0, import_raw }, | |
300 | {} | |
301 | }; | |
302 | ||
303 | return dispatch_verb(argc, argv, verbs, NULL); | |
304 | } | |
305 | ||
5272ae42 | 306 | static int run(int argc, char *argv[]) { |
b6e676ce LP |
307 | int r; |
308 | ||
309 | setlocale(LC_ALL, ""); | |
310 | log_parse_environment(); | |
311 | log_open(); | |
312 | ||
313 | r = parse_argv(argc, argv); | |
314 | if (r <= 0) | |
5272ae42 | 315 | return 0; |
b6e676ce | 316 | |
ce30c8dc | 317 | (void) ignore_signals(SIGPIPE, -1); |
b6e676ce | 318 | |
5272ae42 | 319 | return import_main(argc, argv); |
b6e676ce | 320 | } |
5272ae42 ZJS |
321 | |
322 | DEFINE_MAIN_FUNCTION(run); |