]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import.c
import: minor cleanups for the tar and raw importers
[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");
8af3cf74
LP
54 if (!e)
55 e = endswith(name, ".tar.xz");
56ebfaf1
LP
56 if (!e)
57 e = endswith(name, ".tar.gz");
58 if (!e)
8af3cf74 59 e = endswith(name, ".tar.bz2");
56ebfaf1
LP
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
76static 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 {
85dbc41d 92 r = import_url_last_component(url, &l);
56ebfaf1
LP
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
0100b6e1 140 r = tar_import_pull(import, url, local, arg_force, arg_verify);
56ebfaf1
LP
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
5a3b1abd 150 return r;
56ebfaf1
LP
151}
152
aceac2f0 153static void on_raw_finished(RawImport *import, int error, void *userdata) {
90199220
LP
154 sd_event *event = userdata;
155 assert(import);
156
157 if (error == 0)
158 log_info("Operation completed successfully.");
90199220 159
5a3b1abd 160 sd_event_exit(event, EXIT_FAILURE);
90199220
LP
161}
162
edce2aed
LP
163static int strip_raw_suffixes(const char *p, char **ret) {
164 static const char suffixes[] =
165 ".xz\0"
c660bb09 166 ".gz\0"
8af3cf74 167 ".bz2\0"
edce2aed
LP
168 ".raw\0"
169 ".qcow2\0"
c660bb09
LP
170 ".img\0"
171 ".bin\0";
edce2aed
LP
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
aceac2f0
LP
203static int pull_raw(int argc, char *argv[], void *userdata) {
204 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
90199220 205 _cleanup_event_unref_ sd_event *event = NULL;
edce2aed 206 const char *url, *local;
0d6e763b 207 _cleanup_free_ char *l = NULL, *ll = NULL;
90199220
LP
208 int r;
209
210 url = argv[1];
a2e03378 211 if (!http_url_is_valid(url)) {
90199220
LP
212 log_error("URL '%s' is not valid.", url);
213 return -EINVAL;
214 }
215
8620a9a3
LP
216 if (argc >= 3)
217 local = argv[2];
218 else {
85dbc41d 219 r = import_url_last_component(url, &l);
0d6e763b
LP
220 if (r < 0)
221 return log_error_errno(r, "Failed get final component of URL: %m");
90199220 222
0d6e763b 223 local = l;
90199220
LP
224 }
225
8620a9a3
LP
226 if (isempty(local) || streq(local, "-"))
227 local = NULL;
90199220 228
8620a9a3 229 if (local) {
0d6e763b 230 r = strip_raw_suffixes(local, &ll);
edce2aed
LP
231 if (r < 0)
232 return log_oom();
233
0d6e763b 234 local = ll;
8620a9a3
LP
235
236 if (!machine_name_is_valid(local)) {
237 log_error("Local image name '%s' is not valid.", local);
238 return -EINVAL;
239 }
240
0d6e763b
LP
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;
8620a9a3 248 }
0d6e763b 249 }
90199220 250
8620a9a3
LP
251 log_info("Pulling '%s', saving as '%s'.", url, local);
252 } else
253 log_info("Pulling '%s'.", url);
90199220
LP
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
aceac2f0 263 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
90199220
LP
264 if (r < 0)
265 return log_error_errno(r, "Failed to allocate importer: %m");
266
8f695058 267 r = raw_import_pull(import, url, local, arg_force, arg_verify);
90199220
LP
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
5a3b1abd 277 return r;
90199220
LP
278}
279
280static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
72648326
LP
281 sd_event *event = userdata;
282 assert(import);
283
284 if (error == 0)
285 log_info("Operation completed successfully.");
286 else
56ebfaf1 287 log_error_errno(error, "Operation failed: %m");
72648326
LP
288
289 sd_event_exit(event, error);
290}
291
91f4347e
LP
292static int pull_dkr(int argc, char *argv[], void *userdata) {
293 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
72648326
LP
294 _cleanup_event_unref_ sd_event *event = NULL;
295 const char *name, *tag, *local;
296 int r;
297
91f4347e
LP
298 if (!arg_dkr_index_url) {
299 log_error("Please specify an index URL with --dkr-index-url=");
300 return -EINVAL;
301 }
302
8f695058
LP
303 if (arg_verify != IMPORT_VERIFY_NO) {
304 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
305 return -EINVAL;
306 }
307
72648326
LP
308 tag = strchr(argv[1], ':');
309 if (tag) {
310 name = strndupa(argv[1], tag - argv[1]);
311 tag++;
312 } else {
313 name = argv[1];
314 tag = "latest";
315 }
316
8620a9a3
LP
317 if (!dkr_name_is_valid(name)) {
318 log_error("Remote name '%s' is not valid.", name);
319 return -EINVAL;
320 }
321
322 if (!dkr_tag_is_valid(tag)) {
323 log_error("Tag name '%s' is not valid.", tag);
324 return -EINVAL;
325 }
326
72648326
LP
327 if (argc >= 3)
328 local = argv[2];
329 else {
330 local = strchr(name, '/');
331 if (local)
332 local++;
333 else
334 local = name;
335 }
336
0c7bf33a 337 if (isempty(local) || streq(local, "-"))
72648326
LP
338 local = NULL;
339
72648326
LP
340 if (local) {
341 const char *p;
342
0c7bf33a 343 if (!machine_name_is_valid(local)) {
72648326
LP
344 log_error("Local image name '%s' is not valid.", local);
345 return -EINVAL;
346 }
347
087682d1 348 p = strappenda(arg_image_root, "/", local);
72648326
LP
349 if (laccess(p, F_OK) >= 0) {
350 if (!arg_force) {
351 log_info("Image '%s' already exists.", local);
352 return 0;
353 }
354 } else if (errno != ENOENT)
355 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
356
357 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
358 } else
359 log_info("Pulling '%s' with tag '%s'.", name, tag);
360
361 r = sd_event_default(&event);
362 if (r < 0)
363 return log_error_errno(r, "Failed to allocate event loop: %m");
364
365 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
366 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
367 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
368
087682d1 369 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
72648326
LP
370 if (r < 0)
371 return log_error_errno(r, "Failed to allocate importer: %m");
372
ea1ae8c3 373 r = dkr_import_pull(import, name, tag, local, arg_force);
72648326
LP
374 if (r < 0)
375 return log_error_errno(r, "Failed to pull image: %m");
376
377 r = sd_event_loop(event);
378 if (r < 0)
379 return log_error_errno(r, "Failed to run event loop: %m");
380
381 log_info("Exiting.");
382
383 return 0;
384}
385
386static int help(int argc, char *argv[], void *userdata) {
387
388 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
389 "Import container or virtual machine image.\n\n"
390 " -h --help Show this help\n"
391 " --version Show package version\n"
91f4347e 392 " --force Force creation of image\n"
8f695058
LP
393 " --verify= Verify downloaded image, one of: 'no', 'sum'\n"
394 " 'signature'.\n"
087682d1 395 " --image-root= Image root directory\n"
91f4347e 396 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
72648326 397 "Commands:\n"
0d6e763b 398 " pull-tar URL [NAME] Download a TAR image\n"
56ebfaf1
LP
399 " pull-raw URL [NAME] Download a RAW image\n"
400 " pull-dkr REMOTE [NAME] Download a DKR image\n",
72648326
LP
401 program_invocation_short_name);
402
403 return 0;
404}
405
406static int parse_argv(int argc, char *argv[]) {
407
408 enum {
409 ARG_VERSION = 0x100,
410 ARG_FORCE,
91f4347e 411 ARG_DKR_INDEX_URL,
087682d1 412 ARG_IMAGE_ROOT,
8f695058 413 ARG_VERIFY,
72648326
LP
414 };
415
416 static const struct option options[] = {
417 { "help", no_argument, NULL, 'h' },
418 { "version", no_argument, NULL, ARG_VERSION },
419 { "force", no_argument, NULL, ARG_FORCE },
91f4347e 420 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
087682d1 421 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
8f695058 422 { "verify", required_argument, NULL, ARG_VERIFY },
72648326
LP
423 {}
424 };
425
426 int c;
427
428 assert(argc >= 0);
429 assert(argv);
430
431 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
432
433 switch (c) {
434
435 case 'h':
7eeeb28e 436 return help(0, NULL, NULL);
72648326
LP
437
438 case ARG_VERSION:
439 puts(PACKAGE_STRING);
440 puts(SYSTEMD_FEATURES);
441 return 0;
442
443 case ARG_FORCE:
444 arg_force = true;
445 break;
446
91f4347e
LP
447 case ARG_DKR_INDEX_URL:
448 if (!dkr_url_is_valid(optarg)) {
449 log_error("Index URL is not valid: %s", optarg);
450 return -EINVAL;
451 }
452
453 arg_dkr_index_url = optarg;
454 break;
455
087682d1
LP
456 case ARG_IMAGE_ROOT:
457 arg_image_root = optarg;
458 break;
459
8f695058
LP
460 case ARG_VERIFY:
461 arg_verify = import_verify_from_string(optarg);
462 if (arg_verify < 0) {
463 log_error("Invalid verification setting '%s'", optarg);
464 return -EINVAL;
465 }
466
467 break;
468
72648326
LP
469 case '?':
470 return -EINVAL;
471
472 default:
473 assert_not_reached("Unhandled option");
474 }
475
476 return 1;
477}
478
479static int import_main(int argc, char *argv[]) {
480
7eeeb28e 481 static const Verb verbs[] = {
72648326 482 { "help", VERB_ANY, VERB_ANY, 0, help },
56ebfaf1 483 { "pull-tar", 2, 3, 0, pull_tar },
aceac2f0 484 { "pull-raw", 2, 3, 0, pull_raw },
56ebfaf1 485 { "pull-dkr", 2, 3, 0, pull_dkr },
72648326
LP
486 {}
487 };
488
489 return dispatch_verb(argc, argv, verbs, NULL);
490}
491
492int main(int argc, char *argv[]) {
493 int r;
494
495 setlocale(LC_ALL, "");
496 log_parse_environment();
497 log_open();
498
499 r = parse_argv(argc, argv);
500 if (r <= 0)
501 goto finish;
502
503 r = import_main(argc, argv);
504
505finish:
506 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
507}