]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import.c
impot: minor cleanups
[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.");
72648326 286
ff2670ad 287 sd_event_exit(event, EXIT_FAILURE);
72648326
LP
288}
289
91f4347e
LP
290static int pull_dkr(int argc, char *argv[], void *userdata) {
291 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
72648326
LP
292 _cleanup_event_unref_ sd_event *event = NULL;
293 const char *name, *tag, *local;
294 int r;
295
91f4347e
LP
296 if (!arg_dkr_index_url) {
297 log_error("Please specify an index URL with --dkr-index-url=");
298 return -EINVAL;
299 }
300
8f695058
LP
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
72648326
LP
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
8620a9a3
LP
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
72648326
LP
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
0c7bf33a 335 if (isempty(local) || streq(local, "-"))
72648326
LP
336 local = NULL;
337
72648326 338 if (local) {
0c7bf33a 339 if (!machine_name_is_valid(local)) {
72648326
LP
340 log_error("Local image name '%s' is not valid.", local);
341 return -EINVAL;
342 }
343
ff2670ad
LP
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;
72648326 351 }
ff2670ad 352 }
72648326
LP
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 444 case ARG_DKR_INDEX_URL:
ff2670ad 445 if (!http_url_is_valid(optarg)) {
91f4347e
LP
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}