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