chiark / gitweb /
3362f4a9efa2d547a4076bf613cb4643d07b3c02
[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
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);
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         tag = strchr(argv[1], ':');
303         if (tag) {
304                 name = strndupa(argv[1], tag - argv[1]);
305                 tag++;
306         } else {
307                 name = argv[1];
308                 tag = "latest";
309         }
310
311         if (!dkr_name_is_valid(name)) {
312                 log_error("Remote name '%s' is not valid.", name);
313                 return -EINVAL;
314         }
315
316         if (!dkr_tag_is_valid(tag)) {
317                 log_error("Tag name '%s' is not valid.", tag);
318                 return -EINVAL;
319         }
320
321         if (argc >= 3)
322                 local = argv[2];
323         else {
324                 local = strchr(name, '/');
325                 if (local)
326                         local++;
327                 else
328                         local = name;
329         }
330
331         if (isempty(local) || streq(local, "-"))
332                 local = NULL;
333
334         if (local) {
335                 const char *p;
336
337                 if (!machine_name_is_valid(local)) {
338                         log_error("Local image name '%s' is not valid.", local);
339                         return -EINVAL;
340                 }
341
342                 p = strappenda(arg_image_root, "/", local);
343                 if (laccess(p, F_OK) >= 0) {
344                         if (!arg_force) {
345                                 log_info("Image '%s' already exists.", local);
346                                 return 0;
347                         }
348                 } else if (errno != ENOENT)
349                         return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
350
351                 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
352         } else
353                 log_info("Pulling '%s' with tag '%s'.", name, tag);
354
355         r = sd_event_default(&event);
356         if (r < 0)
357                 return log_error_errno(r, "Failed to allocate event loop: %m");
358
359         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
360         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
361         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
362
363         r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
364         if (r < 0)
365                 return log_error_errno(r, "Failed to allocate importer: %m");
366
367         r = dkr_import_pull(import, name, tag, local, arg_force);
368         if (r < 0)
369                 return log_error_errno(r, "Failed to pull image: %m");
370
371         r = sd_event_loop(event);
372         if (r < 0)
373                 return log_error_errno(r, "Failed to run event loop: %m");
374
375         log_info("Exiting.");
376
377         return 0;
378 }
379
380 static int help(int argc, char *argv[], void *userdata) {
381
382         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
383                "Import container or virtual machine image.\n\n"
384                "  -h --help                   Show this help\n"
385                "     --version                Show package version\n"
386                "     --force                  Force creation of image\n"
387                "     --image-root=            Image root directory\n"
388                "     --dkr-index-url=URL      Specify index URL to use for downloads\n\n"
389                "Commands:\n"
390                "  pull-tar URL [NAME]         Download a TAR image\n"
391                "  pull-raw URL [NAME]         Download a RAW image\n"
392                "  pull-dkr REMOTE [NAME]      Download a DKR image\n",
393                program_invocation_short_name);
394
395         return 0;
396 }
397
398 static int parse_argv(int argc, char *argv[]) {
399
400         enum {
401                 ARG_VERSION = 0x100,
402                 ARG_FORCE,
403                 ARG_DKR_INDEX_URL,
404                 ARG_IMAGE_ROOT,
405         };
406
407         static const struct option options[] = {
408                 { "help",            no_argument,       NULL, 'h'                 },
409                 { "version",         no_argument,       NULL, ARG_VERSION         },
410                 { "force",           no_argument,       NULL, ARG_FORCE           },
411                 { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
412                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
413                 {}
414         };
415
416         int c;
417
418         assert(argc >= 0);
419         assert(argv);
420
421         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
422
423                 switch (c) {
424
425                 case 'h':
426                         return help(0, NULL, NULL);
427
428                 case ARG_VERSION:
429                         puts(PACKAGE_STRING);
430                         puts(SYSTEMD_FEATURES);
431                         return 0;
432
433                 case ARG_FORCE:
434                         arg_force = true;
435                         break;
436
437                 case ARG_DKR_INDEX_URL:
438                         if (!dkr_url_is_valid(optarg)) {
439                                 log_error("Index URL is not valid: %s", optarg);
440                                 return -EINVAL;
441                         }
442
443                         arg_dkr_index_url = optarg;
444                         break;
445
446                 case ARG_IMAGE_ROOT:
447                         arg_image_root = optarg;
448                         break;
449
450                 case '?':
451                         return -EINVAL;
452
453                 default:
454                         assert_not_reached("Unhandled option");
455                 }
456
457         return 1;
458 }
459
460 static int import_main(int argc, char *argv[]) {
461
462         static const Verb verbs[] = {
463                 { "help",     VERB_ANY, VERB_ANY, 0, help     },
464                 { "pull-tar", 2,        3,        0, pull_tar },
465                 { "pull-raw", 2,        3,        0, pull_raw },
466                 { "pull-dkr", 2,        3,        0, pull_dkr },
467                 {}
468         };
469
470         return dispatch_verb(argc, argv, verbs, NULL);
471 }
472
473 int main(int argc, char *argv[]) {
474         int r;
475
476         setlocale(LC_ALL, "");
477         log_parse_environment();
478         log_open();
479
480         r = parse_argv(argc, argv);
481         if (r <= 0)
482                 goto finish;
483
484         r = import_main(argc, argv);
485
486 finish:
487         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
488 }