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