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