]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import.c
import: make verification code generic, in preparation for using it pull-tar
[thirdparty/systemd.git] / src / import / import.c
CommitLineData
72648326
LP
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"
56ebfaf1
LP
28#include "machine-image.h"
29#include "import-tar.h"
aceac2f0 30#include "import-raw.h"
91f4347e 31#include "import-dkr.h"
85dbc41d 32#include "import-util.h"
72648326
LP
33
34static bool arg_force = false;
5f129649 35static const char *arg_image_root = "/var/lib/machines";
8f695058 36static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
91f4347e
LP
37static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
38
56ebfaf1
LP
39static 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.");
56ebfaf1 45
5a3b1abd 46 sd_event_exit(event, EXIT_FAILURE);
56ebfaf1
LP
47}
48
56ebfaf1
LP
49static 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.gz");
56 if (!e)
57 e = endswith(name, ".tar.xz");
58 if (!e)
59 e = endswith(name, ".tgz");
60 if (!e)
61 e = strchr(name, 0);
62
63 if (e <= name)
64 return -EINVAL;
65
66 s = strndup(name, e - name);
67 if (!s)
68 return -ENOMEM;
69
70 *ret = s;
71 return 0;
72}
73
74static int pull_tar(int argc, char *argv[], void *userdata) {
75 _cleanup_(tar_import_unrefp) TarImport *import = NULL;
76 _cleanup_event_unref_ sd_event *event = NULL;
77 const char *url, *local;
78 _cleanup_free_ char *l = NULL, *ll = NULL;
79 int r;
80
81 url = argv[1];
82 if (!http_url_is_valid(url)) {
83 log_error("URL '%s' is not valid.", url);
84 return -EINVAL;
85 }
86
87 if (argc >= 3)
88 local = argv[2];
89 else {
85dbc41d 90 r = import_url_last_component(url, &l);
56ebfaf1
LP
91 if (r < 0)
92 return log_error_errno(r, "Failed get final component of URL: %m");
93
94 local = l;
95 }
96
97 if (isempty(local) || streq(local, "-"))
98 local = NULL;
99
100 if (local) {
101 r = strip_tar_suffixes(local, &ll);
102 if (r < 0)
103 return log_oom();
104
105 local = ll;
106
107 if (!machine_name_is_valid(local)) {
108 log_error("Local image name '%s' is not valid.", local);
109 return -EINVAL;
110 }
111
112 if (!arg_force) {
113 r = image_find(local, NULL);
114 if (r < 0)
115 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
116 else if (r > 0) {
117 log_error_errno(EEXIST, "Image '%s' already exists.", local);
118 return -EEXIST;
119 }
120 }
121
122 log_info("Pulling '%s', saving as '%s'.", url, local);
123 } else
124 log_info("Pulling '%s'.", url);
125
126 r = sd_event_default(&event);
127 if (r < 0)
128 return log_error_errno(r, "Failed to allocate event loop: %m");
129
130 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
131 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
132 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
133
134 r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
135 if (r < 0)
136 return log_error_errno(r, "Failed to allocate importer: %m");
137
138 r = tar_import_pull(import, url, local, arg_force);
139 if (r < 0)
140 return log_error_errno(r, "Failed to pull image: %m");
141
142 r = sd_event_loop(event);
143 if (r < 0)
144 return log_error_errno(r, "Failed to run event loop: %m");
145
146 log_info("Exiting.");
147
5a3b1abd 148 return r;
56ebfaf1
LP
149}
150
aceac2f0 151static void on_raw_finished(RawImport *import, int error, void *userdata) {
90199220
LP
152 sd_event *event = userdata;
153 assert(import);
154
155 if (error == 0)
156 log_info("Operation completed successfully.");
90199220 157
5a3b1abd 158 sd_event_exit(event, EXIT_FAILURE);
90199220
LP
159}
160
edce2aed
LP
161static int strip_raw_suffixes(const char *p, char **ret) {
162 static const char suffixes[] =
163 ".xz\0"
c660bb09 164 ".gz\0"
edce2aed
LP
165 ".raw\0"
166 ".qcow2\0"
c660bb09
LP
167 ".img\0"
168 ".bin\0";
edce2aed
LP
169
170 _cleanup_free_ char *q = NULL;
171
172 q = strdup(p);
173 if (!q)
174 return -ENOMEM;
175
176 for (;;) {
177 const char *sfx;
178 bool changed = false;
179
180 NULSTR_FOREACH(sfx, suffixes) {
181 char *e;
182
183 e = endswith(q, sfx);
184 if (e) {
185 *e = 0;
186 changed = true;
187 }
188 }
189
190 if (!changed)
191 break;
192 }
193
194 *ret = q;
195 q = NULL;
196
197 return 0;
198}
199
aceac2f0
LP
200static int pull_raw(int argc, char *argv[], void *userdata) {
201 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
90199220 202 _cleanup_event_unref_ sd_event *event = NULL;
edce2aed 203 const char *url, *local;
0d6e763b 204 _cleanup_free_ char *l = NULL, *ll = NULL;
90199220
LP
205 int r;
206
207 url = argv[1];
a2e03378 208 if (!http_url_is_valid(url)) {
90199220
LP
209 log_error("URL '%s' is not valid.", url);
210 return -EINVAL;
211 }
212
8620a9a3
LP
213 if (argc >= 3)
214 local = argv[2];
215 else {
85dbc41d 216 r = import_url_last_component(url, &l);
0d6e763b
LP
217 if (r < 0)
218 return log_error_errno(r, "Failed get final component of URL: %m");
90199220 219
0d6e763b 220 local = l;
90199220
LP
221 }
222
8620a9a3
LP
223 if (isempty(local) || streq(local, "-"))
224 local = NULL;
90199220 225
8620a9a3 226 if (local) {
0d6e763b 227 r = strip_raw_suffixes(local, &ll);
edce2aed
LP
228 if (r < 0)
229 return log_oom();
230
0d6e763b 231 local = ll;
8620a9a3
LP
232
233 if (!machine_name_is_valid(local)) {
234 log_error("Local image name '%s' is not valid.", local);
235 return -EINVAL;
236 }
237
0d6e763b
LP
238 if (!arg_force) {
239 r = image_find(local, NULL);
240 if (r < 0)
241 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
242 else if (r > 0) {
243 log_error_errno(EEXIST, "Image '%s' already exists.", local);
244 return -EEXIST;
8620a9a3 245 }
0d6e763b 246 }
90199220 247
8620a9a3
LP
248 log_info("Pulling '%s', saving as '%s'.", url, local);
249 } else
250 log_info("Pulling '%s'.", url);
90199220
LP
251
252 r = sd_event_default(&event);
253 if (r < 0)
254 return log_error_errno(r, "Failed to allocate event loop: %m");
255
256 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
257 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
258 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
259
aceac2f0 260 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
90199220
LP
261 if (r < 0)
262 return log_error_errno(r, "Failed to allocate importer: %m");
263
8f695058 264 r = raw_import_pull(import, url, local, arg_force, arg_verify);
90199220
LP
265 if (r < 0)
266 return log_error_errno(r, "Failed to pull image: %m");
267
268 r = sd_event_loop(event);
269 if (r < 0)
270 return log_error_errno(r, "Failed to run event loop: %m");
271
272 log_info("Exiting.");
273
5a3b1abd 274 return r;
90199220
LP
275}
276
277static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
72648326
LP
278 sd_event *event = userdata;
279 assert(import);
280
281 if (error == 0)
282 log_info("Operation completed successfully.");
283 else
56ebfaf1 284 log_error_errno(error, "Operation failed: %m");
72648326
LP
285
286 sd_event_exit(event, error);
287}
288
91f4347e
LP
289static int pull_dkr(int argc, char *argv[], void *userdata) {
290 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
72648326
LP
291 _cleanup_event_unref_ sd_event *event = NULL;
292 const char *name, *tag, *local;
293 int r;
294
91f4347e
LP
295 if (!arg_dkr_index_url) {
296 log_error("Please specify an index URL with --dkr-index-url=");
297 return -EINVAL;
298 }
299
8f695058
LP
300 if (arg_verify != IMPORT_VERIFY_NO) {
301 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
302 return -EINVAL;
303 }
304
72648326
LP
305 tag = strchr(argv[1], ':');
306 if (tag) {
307 name = strndupa(argv[1], tag - argv[1]);
308 tag++;
309 } else {
310 name = argv[1];
311 tag = "latest";
312 }
313
8620a9a3
LP
314 if (!dkr_name_is_valid(name)) {
315 log_error("Remote name '%s' is not valid.", name);
316 return -EINVAL;
317 }
318
319 if (!dkr_tag_is_valid(tag)) {
320 log_error("Tag name '%s' is not valid.", tag);
321 return -EINVAL;
322 }
323
72648326
LP
324 if (argc >= 3)
325 local = argv[2];
326 else {
327 local = strchr(name, '/');
328 if (local)
329 local++;
330 else
331 local = name;
332 }
333
0c7bf33a 334 if (isempty(local) || streq(local, "-"))
72648326
LP
335 local = NULL;
336
72648326
LP
337 if (local) {
338 const char *p;
339
0c7bf33a 340 if (!machine_name_is_valid(local)) {
72648326
LP
341 log_error("Local image name '%s' is not valid.", local);
342 return -EINVAL;
343 }
344
087682d1 345 p = strappenda(arg_image_root, "/", local);
72648326
LP
346 if (laccess(p, F_OK) >= 0) {
347 if (!arg_force) {
348 log_info("Image '%s' already exists.", local);
349 return 0;
350 }
351 } else if (errno != ENOENT)
352 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
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
087682d1 366 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
72648326
LP
367 if (r < 0)
368 return log_error_errno(r, "Failed to allocate importer: %m");
369
ea1ae8c3 370 r = dkr_import_pull(import, name, tag, local, arg_force);
72648326
LP
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
383static 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"
91f4347e 389 " --force Force creation of image\n"
8f695058
LP
390 " --verify= Verify downloaded image, one of: 'no', 'sum'\n"
391 " 'signature'.\n"
087682d1 392 " --image-root= Image root directory\n"
91f4347e 393 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
72648326 394 "Commands:\n"
0d6e763b 395 " pull-tar URL [NAME] Download a TAR image\n"
56ebfaf1
LP
396 " pull-raw URL [NAME] Download a RAW image\n"
397 " pull-dkr REMOTE [NAME] Download a DKR image\n",
72648326
LP
398 program_invocation_short_name);
399
400 return 0;
401}
402
403static int parse_argv(int argc, char *argv[]) {
404
405 enum {
406 ARG_VERSION = 0x100,
407 ARG_FORCE,
91f4347e 408 ARG_DKR_INDEX_URL,
087682d1 409 ARG_IMAGE_ROOT,
8f695058 410 ARG_VERIFY,
72648326
LP
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 },
91f4347e 417 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
087682d1 418 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
8f695058 419 { "verify", required_argument, NULL, ARG_VERIFY },
72648326
LP
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':
7eeeb28e 433 return help(0, NULL, NULL);
72648326
LP
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
91f4347e
LP
444 case ARG_DKR_INDEX_URL:
445 if (!dkr_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
087682d1
LP
453 case ARG_IMAGE_ROOT:
454 arg_image_root = optarg;
455 break;
456
8f695058
LP
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
72648326
LP
466 case '?':
467 return -EINVAL;
468
469 default:
470 assert_not_reached("Unhandled option");
471 }
472
473 return 1;
474}
475
476static int import_main(int argc, char *argv[]) {
477
7eeeb28e 478 static const Verb verbs[] = {
72648326 479 { "help", VERB_ANY, VERB_ANY, 0, help },
56ebfaf1 480 { "pull-tar", 2, 3, 0, pull_tar },
aceac2f0 481 { "pull-raw", 2, 3, 0, pull_raw },
56ebfaf1 482 { "pull-dkr", 2, 3, 0, pull_dkr },
72648326
LP
483 {}
484 };
485
486 return dispatch_verb(argc, argv, verbs, NULL);
487}
488
489int 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
502finish:
503 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
504}