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