]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import.c
sd-dhcp6-client: Remove unnecessary debug printout
[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";
72648326 36
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.");
45 else
46 log_error_errno(error, "Operation failed: %m");
47
48 sd_event_exit(event, error);
49}
50
56ebfaf1
LP
51static 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
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
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
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.");
159 else
56ebfaf1 160 log_error_errno(error, "Operation failed: %m");
90199220
LP
161
162 sd_event_exit(event, error);
163}
164
edce2aed
LP
165static 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
aceac2f0
LP
202static int pull_raw(int argc, char *argv[], void *userdata) {
203 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
90199220 204 _cleanup_event_unref_ sd_event *event = NULL;
edce2aed 205 const char *url, *local;
0d6e763b 206 _cleanup_free_ char *l = NULL, *ll = NULL;
90199220
LP
207 int r;
208
209 url = argv[1];
a2e03378 210 if (!http_url_is_valid(url)) {
90199220
LP
211 log_error("URL '%s' is not valid.", url);
212 return -EINVAL;
213 }
214
8620a9a3
LP
215 if (argc >= 3)
216 local = argv[2];
217 else {
85dbc41d 218 r = import_url_last_component(url, &l);
0d6e763b
LP
219 if (r < 0)
220 return log_error_errno(r, "Failed get final component of URL: %m");
90199220 221
0d6e763b 222 local = l;
90199220
LP
223 }
224
8620a9a3
LP
225 if (isempty(local) || streq(local, "-"))
226 local = NULL;
90199220 227
8620a9a3 228 if (local) {
0d6e763b 229 r = strip_raw_suffixes(local, &ll);
edce2aed
LP
230 if (r < 0)
231 return log_oom();
232
0d6e763b 233 local = ll;
8620a9a3
LP
234
235 if (!machine_name_is_valid(local)) {
236 log_error("Local image name '%s' is not valid.", local);
237 return -EINVAL;
238 }
239
0d6e763b
LP
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;
8620a9a3 247 }
0d6e763b 248 }
90199220 249
8620a9a3
LP
250 log_info("Pulling '%s', saving as '%s'.", url, local);
251 } else
252 log_info("Pulling '%s'.", url);
90199220
LP
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
aceac2f0 262 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
90199220
LP
263 if (r < 0)
264 return log_error_errno(r, "Failed to allocate importer: %m");
265
aceac2f0 266 r = raw_import_pull(import, url, local, arg_force);
90199220
LP
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
279static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
72648326
LP
280 sd_event *event = userdata;
281 assert(import);
282
283 if (error == 0)
284 log_info("Operation completed successfully.");
285 else
56ebfaf1 286 log_error_errno(error, "Operation failed: %m");
72648326
LP
287
288 sd_event_exit(event, error);
289}
290
91f4347e
LP
291static int pull_dkr(int argc, char *argv[], void *userdata) {
292 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
72648326
LP
293 _cleanup_event_unref_ sd_event *event = NULL;
294 const char *name, *tag, *local;
295 int r;
296
91f4347e
LP
297 if (!arg_dkr_index_url) {
298 log_error("Please specify an index URL with --dkr-index-url=");
299 return -EINVAL;
300 }
301
72648326
LP
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
8620a9a3
LP
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
72648326
LP
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
0c7bf33a 331 if (isempty(local) || streq(local, "-"))
72648326
LP
332 local = NULL;
333
72648326
LP
334 if (local) {
335 const char *p;
336
0c7bf33a 337 if (!machine_name_is_valid(local)) {
72648326
LP
338 log_error("Local image name '%s' is not valid.", local);
339 return -EINVAL;
340 }
341
087682d1 342 p = strappenda(arg_image_root, "/", local);
72648326
LP
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
087682d1 363 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
72648326
LP
364 if (r < 0)
365 return log_error_errno(r, "Failed to allocate importer: %m");
366
ea1ae8c3 367 r = dkr_import_pull(import, name, tag, local, arg_force);
72648326
LP
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
380static 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"
91f4347e 386 " --force Force creation of image\n"
087682d1 387 " --image-root= Image root directory\n"
91f4347e 388 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
72648326 389 "Commands:\n"
0d6e763b 390 " pull-tar URL [NAME] Download a TAR image\n"
56ebfaf1
LP
391 " pull-raw URL [NAME] Download a RAW image\n"
392 " pull-dkr REMOTE [NAME] Download a DKR image\n",
72648326
LP
393 program_invocation_short_name);
394
395 return 0;
396}
397
398static int parse_argv(int argc, char *argv[]) {
399
400 enum {
401 ARG_VERSION = 0x100,
402 ARG_FORCE,
91f4347e 403 ARG_DKR_INDEX_URL,
087682d1 404 ARG_IMAGE_ROOT,
72648326
LP
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 },
91f4347e 411 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
087682d1 412 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
72648326
LP
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':
7eeeb28e 426 return help(0, NULL, NULL);
72648326
LP
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
91f4347e
LP
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
087682d1
LP
446 case ARG_IMAGE_ROOT:
447 arg_image_root = optarg;
448 break;
449
72648326
LP
450 case '?':
451 return -EINVAL;
452
453 default:
454 assert_not_reached("Unhandled option");
455 }
456
457 return 1;
458}
459
460static int import_main(int argc, char *argv[]) {
461
7eeeb28e 462 static const Verb verbs[] = {
72648326 463 { "help", VERB_ANY, VERB_ANY, 0, help },
56ebfaf1 464 { "pull-tar", 2, 3, 0, pull_tar },
aceac2f0 465 { "pull-raw", 2, 3, 0, pull_raw },
56ebfaf1 466 { "pull-dkr", 2, 3, 0, pull_dkr },
72648326
LP
467 {}
468 };
469
470 return dispatch_verb(argc, argv, verbs, NULL);
471}
472
473int 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
486finish:
487 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
488}