chiark / gitweb /
f44d47df9d23eeadff1b5f9f2e7538b4005b92f9
[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 2014 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-tar.h"
30 #include "import-raw.h"
31 #include "import-dkr.h"
32 #include "import-util.h"
33
34 static bool arg_force = false;
35 static const char *arg_image_root = "/var/lib/machines";
36 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
37 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
38
39 static void on_tar_finished(TarImport *import, int error, void *userdata) {
40         sd_event *event = userdata;
41         assert(import);
42
43         if (error == 0)
44                 log_info("Operation completed successfully.");
45         else
46                 log_error_errno(error, "Operation failed: %m");
47
48         sd_event_exit(event, error);
49 }
50
51 static int strip_tar_suffixes(const char *name, char **ret) {
52         const char *e;
53         char *s;
54
55         e = endswith(name, ".tar");
56         if (!e)
57                 e = endswith(name, ".tar.gz");
58         if (!e)
59                 e = endswith(name, ".tar.xz");
60         if (!e)
61                 e = endswith(name, ".tgz");
62         if (!e)
63                 e = strchr(name, 0);
64
65         if (e <= name)
66                 return -EINVAL;
67
68         s = strndup(name, e - name);
69         if (!s)
70                 return -ENOMEM;
71
72         *ret = s;
73         return 0;
74 }
75
76 static int pull_tar(int argc, char *argv[], void *userdata) {
77         _cleanup_(tar_import_unrefp) TarImport *import = NULL;
78         _cleanup_event_unref_ sd_event *event = NULL;
79         const char *url, *local;
80         _cleanup_free_ char *l = NULL, *ll = NULL;
81         int r;
82
83         url = argv[1];
84         if (!http_url_is_valid(url)) {
85                 log_error("URL '%s' is not valid.", url);
86                 return -EINVAL;
87         }
88
89         if (argc >= 3)
90                 local = argv[2];
91         else {
92                 r = import_url_last_component(url, &l);
93                 if (r < 0)
94                         return log_error_errno(r, "Failed get final component of URL: %m");
95
96                 local = l;
97         }
98
99         if (isempty(local) || streq(local, "-"))
100                 local = NULL;
101
102         if (local) {
103                 r = strip_tar_suffixes(local, &ll);
104                 if (r < 0)
105                         return log_oom();
106
107                 local = ll;
108
109                 if (!machine_name_is_valid(local)) {
110                         log_error("Local image name '%s' is not valid.", local);
111                         return -EINVAL;
112                 }
113
114                 if (!arg_force) {
115                         r = image_find(local, NULL);
116                         if (r < 0)
117                                 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
118                         else if (r > 0) {
119                                 log_error_errno(EEXIST, "Image '%s' already exists.", local);
120                                 return -EEXIST;
121                         }
122                 }
123
124                 log_info("Pulling '%s', saving as '%s'.", url, local);
125         } else
126                 log_info("Pulling '%s'.", url);
127
128         r = sd_event_default(&event);
129         if (r < 0)
130                 return log_error_errno(r, "Failed to allocate event loop: %m");
131
132         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
133         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
134         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
135
136         r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
137         if (r < 0)
138                 return log_error_errno(r, "Failed to allocate importer: %m");
139
140         r = tar_import_pull(import, url, local, arg_force);
141         if (r < 0)
142                 return log_error_errno(r, "Failed to pull image: %m");
143
144         r = sd_event_loop(event);
145         if (r < 0)
146                 return log_error_errno(r, "Failed to run event loop: %m");
147
148         log_info("Exiting.");
149
150         return 0;
151 }
152
153 static void on_raw_finished(RawImport *import, int error, void *userdata) {
154         sd_event *event = userdata;
155         assert(import);
156
157         if (error == 0)
158                 log_info("Operation completed successfully.");
159         else
160                 log_error_errno(error, "Operation failed: %m");
161
162         sd_event_exit(event, error);
163 }
164
165 static int strip_raw_suffixes(const char *p, char **ret) {
166         static const char suffixes[] =
167                 ".xz\0"
168                 ".raw\0"
169                 ".qcow2\0"
170                 ".img\0";
171
172         _cleanup_free_ char *q = NULL;
173
174         q = strdup(p);
175         if (!q)
176                 return -ENOMEM;
177
178         for (;;) {
179                 const char *sfx;
180                 bool changed = false;
181
182                 NULSTR_FOREACH(sfx, suffixes) {
183                         char *e;
184
185                         e = endswith(q, sfx);
186                         if (e) {
187                                 *e = 0;
188                                 changed = true;
189                         }
190                 }
191
192                 if (!changed)
193                         break;
194         }
195
196         *ret = q;
197         q = NULL;
198
199         return 0;
200 }
201
202 static int pull_raw(int argc, char *argv[], void *userdata) {
203         _cleanup_(raw_import_unrefp) RawImport *import = NULL;
204         _cleanup_event_unref_ sd_event *event = NULL;
205         const char *url, *local;
206         _cleanup_free_ char *l = NULL, *ll = NULL;
207         int r;
208
209         url = argv[1];
210         if (!http_url_is_valid(url)) {
211                 log_error("URL '%s' is not valid.", url);
212                 return -EINVAL;
213         }
214
215         if (argc >= 3)
216                 local = argv[2];
217         else {
218                 r = import_url_last_component(url, &l);
219                 if (r < 0)
220                         return log_error_errno(r, "Failed get final component of URL: %m");
221
222                 local = l;
223         }
224
225         if (isempty(local) || streq(local, "-"))
226                 local = NULL;
227
228         if (local) {
229                 r = strip_raw_suffixes(local, &ll);
230                 if (r < 0)
231                         return log_oom();
232
233                 local = ll;
234
235                 if (!machine_name_is_valid(local)) {
236                         log_error("Local image name '%s' is not valid.", local);
237                         return -EINVAL;
238                 }
239
240                 if (!arg_force) {
241                         r = image_find(local, NULL);
242                         if (r < 0)
243                                 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
244                         else if (r > 0) {
245                                 log_error_errno(EEXIST, "Image '%s' already exists.", local);
246                                 return -EEXIST;
247                         }
248                 }
249
250                 log_info("Pulling '%s', saving as '%s'.", url, local);
251         } else
252                 log_info("Pulling '%s'.", url);
253
254         r = sd_event_default(&event);
255         if (r < 0)
256                 return log_error_errno(r, "Failed to allocate event loop: %m");
257
258         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
259         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
260         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
261
262         r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
263         if (r < 0)
264                 return log_error_errno(r, "Failed to allocate importer: %m");
265
266         r = raw_import_pull(import, url, local, arg_force, arg_verify);
267         if (r < 0)
268                 return log_error_errno(r, "Failed to pull image: %m");
269
270         r = sd_event_loop(event);
271         if (r < 0)
272                 return log_error_errno(r, "Failed to run event loop: %m");
273
274         log_info("Exiting.");
275
276         return 0;
277 }
278
279 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
280         sd_event *event = userdata;
281         assert(import);
282
283         if (error == 0)
284                 log_info("Operation completed successfully.");
285         else
286                 log_error_errno(error, "Operation failed: %m");
287
288         sd_event_exit(event, error);
289 }
290
291 static int pull_dkr(int argc, char *argv[], void *userdata) {
292         _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
293         _cleanup_event_unref_ sd_event *event = NULL;
294         const char *name, *tag, *local;
295         int r;
296
297         if (!arg_dkr_index_url) {
298                 log_error("Please specify an index URL with --dkr-index-url=");
299                 return -EINVAL;
300         }
301
302         if (arg_verify != IMPORT_VERIFY_NO) {
303                 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
304                 return -EINVAL;
305         }
306
307         tag = strchr(argv[1], ':');
308         if (tag) {
309                 name = strndupa(argv[1], tag - argv[1]);
310                 tag++;
311         } else {
312                 name = argv[1];
313                 tag = "latest";
314         }
315
316         if (!dkr_name_is_valid(name)) {
317                 log_error("Remote name '%s' is not valid.", name);
318                 return -EINVAL;
319         }
320
321         if (!dkr_tag_is_valid(tag)) {
322                 log_error("Tag name '%s' is not valid.", tag);
323                 return -EINVAL;
324         }
325
326         if (argc >= 3)
327                 local = argv[2];
328         else {
329                 local = strchr(name, '/');
330                 if (local)
331                         local++;
332                 else
333                         local = name;
334         }
335
336         if (isempty(local) || streq(local, "-"))
337                 local = NULL;
338
339         if (local) {
340                 const char *p;
341
342                 if (!machine_name_is_valid(local)) {
343                         log_error("Local image name '%s' is not valid.", local);
344                         return -EINVAL;
345                 }
346
347                 p = strappenda(arg_image_root, "/", local);
348                 if (laccess(p, F_OK) >= 0) {
349                         if (!arg_force) {
350                                 log_info("Image '%s' already exists.", local);
351                                 return 0;
352                         }
353                 } else if (errno != ENOENT)
354                         return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
355
356                 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
357         } else
358                 log_info("Pulling '%s' with tag '%s'.", name, tag);
359
360         r = sd_event_default(&event);
361         if (r < 0)
362                 return log_error_errno(r, "Failed to allocate event loop: %m");
363
364         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
365         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
366         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
367
368         r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
369         if (r < 0)
370                 return log_error_errno(r, "Failed to allocate importer: %m");
371
372         r = dkr_import_pull(import, name, tag, local, arg_force);
373         if (r < 0)
374                 return log_error_errno(r, "Failed to pull image: %m");
375
376         r = sd_event_loop(event);
377         if (r < 0)
378                 return log_error_errno(r, "Failed to run event loop: %m");
379
380         log_info("Exiting.");
381
382         return 0;
383 }
384
385 static int help(int argc, char *argv[], void *userdata) {
386
387         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
388                "Import container or virtual machine image.\n\n"
389                "  -h --help                   Show this help\n"
390                "     --version                Show package version\n"
391                "     --force                  Force creation of image\n"
392                "     --verify=                Verify downloaded image, one of: 'no', 'sum'\n"
393                "                              'signature'.\n"
394                "     --image-root=            Image root directory\n"
395                "     --dkr-index-url=URL      Specify index URL to use for downloads\n\n"
396                "Commands:\n"
397                "  pull-tar URL [NAME]         Download a TAR image\n"
398                "  pull-raw URL [NAME]         Download a RAW image\n"
399                "  pull-dkr REMOTE [NAME]      Download a DKR image\n",
400                program_invocation_short_name);
401
402         return 0;
403 }
404
405 static int parse_argv(int argc, char *argv[]) {
406
407         enum {
408                 ARG_VERSION = 0x100,
409                 ARG_FORCE,
410                 ARG_DKR_INDEX_URL,
411                 ARG_IMAGE_ROOT,
412                 ARG_VERIFY,
413         };
414
415         static const struct option options[] = {
416                 { "help",            no_argument,       NULL, 'h'                 },
417                 { "version",         no_argument,       NULL, ARG_VERSION         },
418                 { "force",           no_argument,       NULL, ARG_FORCE           },
419                 { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
420                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
421                 { "verify",          required_argument, NULL, ARG_VERIFY          },
422                 {}
423         };
424
425         int c;
426
427         assert(argc >= 0);
428         assert(argv);
429
430         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
431
432                 switch (c) {
433
434                 case 'h':
435                         return help(0, NULL, NULL);
436
437                 case ARG_VERSION:
438                         puts(PACKAGE_STRING);
439                         puts(SYSTEMD_FEATURES);
440                         return 0;
441
442                 case ARG_FORCE:
443                         arg_force = true;
444                         break;
445
446                 case ARG_DKR_INDEX_URL:
447                         if (!dkr_url_is_valid(optarg)) {
448                                 log_error("Index URL is not valid: %s", optarg);
449                                 return -EINVAL;
450                         }
451
452                         arg_dkr_index_url = optarg;
453                         break;
454
455                 case ARG_IMAGE_ROOT:
456                         arg_image_root = optarg;
457                         break;
458
459                 case ARG_VERIFY:
460                         arg_verify = import_verify_from_string(optarg);
461                         if (arg_verify < 0) {
462                                 log_error("Invalid verification setting '%s'", optarg);
463                                 return -EINVAL;
464                         }
465
466                         break;
467
468                 case '?':
469                         return -EINVAL;
470
471                 default:
472                         assert_not_reached("Unhandled option");
473                 }
474
475         return 1;
476 }
477
478 static int import_main(int argc, char *argv[]) {
479
480         static const Verb verbs[] = {
481                 { "help",     VERB_ANY, VERB_ANY, 0, help     },
482                 { "pull-tar", 2,        3,        0, pull_tar },
483                 { "pull-raw", 2,        3,        0, pull_raw },
484                 { "pull-dkr", 2,        3,        0, pull_dkr },
485                 {}
486         };
487
488         return dispatch_verb(argc, argv, verbs, NULL);
489 }
490
491 int main(int argc, char *argv[]) {
492         int r;
493
494         setlocale(LC_ALL, "");
495         log_parse_environment();
496         log_open();
497
498         r = parse_argv(argc, argv);
499         if (r <= 0)
500                 goto finish;
501
502         r = import_main(argc, argv);
503
504 finish:
505         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
506 }