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