]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import.c
util: make machine_name_is_valid() a macro and move it to hostname-util.h
[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"
25300b5a 29#include "hostname-util.h"
b6e676ce
LP
30#include "machine-image.h"
31#include "import-util.h"
32#include "import-tar.h"
33#include "import-raw.h"
34
35static bool arg_force = false;
36static bool arg_read_only = false;
37static const char *arg_image_root = "/var/lib/machines";
38
39static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
40 log_notice("Transfer aborted.");
41 sd_event_exit(sd_event_source_get_event(s), EINTR);
42 return 0;
43}
44
45static void on_tar_finished(TarImport *import, int error, void *userdata) {
46 sd_event *event = userdata;
47 assert(import);
48
49 if (error == 0)
50 log_info("Operation completed successfully.");
51
52 sd_event_exit(event, abs(error));
53}
54
55static int import_tar(int argc, char *argv[], void *userdata) {
56 _cleanup_(tar_import_unrefp) TarImport *import = NULL;
57 _cleanup_event_unref_ sd_event *event = NULL;
58 const char *path = NULL, *local = NULL;
59 _cleanup_free_ char *ll = NULL;
60 _cleanup_close_ int open_fd = -1;
61 int r, fd;
62
63 if (argc >= 2)
64 path = argv[1];
65 if (isempty(path) || streq(path, "-"))
66 path = NULL;
67
68 if (argc >= 3)
69 local = argv[2];
70 else if (path)
71 local = basename(path);
72 if (isempty(local) || streq(local, "-"))
73 local = NULL;
74
75 if (local) {
76 r = tar_strip_suffixes(local, &ll);
77 if (r < 0)
78 return log_oom();
79
80 local = ll;
81
82 if (!machine_name_is_valid(local)) {
83 log_error("Local image name '%s' is not valid.", local);
84 return -EINVAL;
85 }
86
87 if (!arg_force) {
88 r = image_find(local, NULL);
89 if (r < 0)
90 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
91 else if (r > 0) {
92 log_error_errno(EEXIST, "Image '%s' already exists.", local);
93 return -EEXIST;
94 }
95 }
96 } else
97 local = "imported";
98
99 if (path) {
100 open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
101 if (open_fd < 0)
102 return log_error_errno(errno, "Failed to open tar image to import: %m");
103
104 fd = open_fd;
105
106 log_info("Importing '%s', saving as '%s'.", path, local);
107 } else {
108 _cleanup_free_ char *pretty = NULL;
109
110 fd = STDIN_FILENO;
111
112 (void) readlink_malloc("/proc/self/fd/0", &pretty);
113 log_info("Importing '%s', saving as '%s'.", strna(pretty), local);
114 }
115
116 r = sd_event_default(&event);
117 if (r < 0)
118 return log_error_errno(r, "Failed to allocate event loop: %m");
119
72c0a2c2
LP
120 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
121 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
122 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
b6e676ce
LP
123
124 r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
125 if (r < 0)
126 return log_error_errno(r, "Failed to allocate importer: %m");
127
128 r = tar_import_start(import, fd, local, arg_force, arg_read_only);
129 if (r < 0)
130 return log_error_errno(r, "Failed to import image: %m");
131
132 r = sd_event_loop(event);
133 if (r < 0)
134 return log_error_errno(r, "Failed to run event loop: %m");
135
136 log_info("Exiting.");
137 return -r;
138}
139
140static void on_raw_finished(RawImport *import, int error, void *userdata) {
141 sd_event *event = userdata;
142 assert(import);
143
144 if (error == 0)
145 log_info("Operation completed successfully.");
146
147 sd_event_exit(event, abs(error));
148}
149
150static int import_raw(int argc, char *argv[], void *userdata) {
151 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
152 _cleanup_event_unref_ sd_event *event = NULL;
153 const char *path = NULL, *local = NULL;
154 _cleanup_free_ char *ll = NULL;
155 _cleanup_close_ int open_fd = -1;
156 int r, fd;
157
158 if (argc >= 2)
159 path = argv[1];
160 if (isempty(path) || streq(path, "-"))
161 path = NULL;
162
163 if (argc >= 3)
164 local = argv[2];
165 else if (path)
166 local = basename(path);
167 if (isempty(local) || streq(local, "-"))
168 local = NULL;
169
170 if (local) {
171 r = raw_strip_suffixes(local, &ll);
172 if (r < 0)
173 return log_oom();
174
175 local = ll;
176
177 if (!machine_name_is_valid(local)) {
178 log_error("Local image name '%s' is not valid.", local);
179 return -EINVAL;
180 }
181
182 if (!arg_force) {
183 r = image_find(local, NULL);
184 if (r < 0)
185 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
186 else if (r > 0) {
187 log_error_errno(EEXIST, "Image '%s' already exists.", local);
188 return -EEXIST;
189 }
190 }
191 } else
192 local = "imported";
193
194 if (path) {
195 open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY);
196 if (open_fd < 0)
197 return log_error_errno(errno, "Failed to open raw image to import: %m");
198
199 fd = open_fd;
200
201 log_info("Importing '%s', saving as '%s'.", path, local);
202 } else {
203 _cleanup_free_ char *pretty = NULL;
204
205 fd = STDIN_FILENO;
206
207 (void) readlink_malloc("/proc/self/fd/0", &pretty);
208 log_info("Importing '%s', saving as '%s'.", pretty, local);
209 }
210
211 r = sd_event_default(&event);
212 if (r < 0)
213 return log_error_errno(r, "Failed to allocate event loop: %m");
214
72c0a2c2
LP
215 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
216 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
217 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
b6e676ce
LP
218
219 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
220 if (r < 0)
221 return log_error_errno(r, "Failed to allocate importer: %m");
222
223 r = raw_import_start(import, fd, local, arg_force, arg_read_only);
224 if (r < 0)
225 return log_error_errno(r, "Failed to import image: %m");
226
227 r = sd_event_loop(event);
228 if (r < 0)
229 return log_error_errno(r, "Failed to run event loop: %m");
230
231 log_info("Exiting.");
232 return -r;
233}
234
235static int help(int argc, char *argv[], void *userdata) {
236
237 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
587fec42 238 "Import container or virtual machine images.\n\n"
b6e676ce
LP
239 " -h --help Show this help\n"
240 " --version Show package version\n"
241 " --force Force creation of image\n"
242 " --image-root=PATH Image root directory\n"
243 " --read-only Create a read-only image\n\n"
244 "Commands:\n"
587fec42
LP
245 " tar FILE [NAME] Import a TAR image\n"
246 " raw FILE [NAME] Import a RAW image\n",
b6e676ce
LP
247 program_invocation_short_name);
248
249 return 0;
250}
251
252static int parse_argv(int argc, char *argv[]) {
253
254 enum {
255 ARG_VERSION = 0x100,
256 ARG_FORCE,
257 ARG_IMAGE_ROOT,
258 ARG_READ_ONLY,
259 };
260
261 static const struct option options[] = {
262 { "help", no_argument, NULL, 'h' },
263 { "version", no_argument, NULL, ARG_VERSION },
264 { "force", no_argument, NULL, ARG_FORCE },
265 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
266 { "read-only", no_argument, NULL, ARG_READ_ONLY },
267 {}
268 };
269
270 int c;
271
272 assert(argc >= 0);
273 assert(argv);
274
275 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
276
277 switch (c) {
278
279 case 'h':
280 return help(0, NULL, NULL);
281
282 case ARG_VERSION:
283 puts(PACKAGE_STRING);
284 puts(SYSTEMD_FEATURES);
285 return 0;
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}