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