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