]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / import / pull.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"
3f6fd1ba 25
72648326 26#include "event-util.h"
25300b5a 27#include "hostname-util.h"
85dbc41d 28#include "import-util.h"
3f6fd1ba 29#include "machine-image.h"
dc2c282b 30#include "pull-dkr.h"
3f6fd1ba
LP
31#include "pull-raw.h"
32#include "pull-tar.h"
33#include "signal-util.h"
07630cea 34#include "string-util.h"
3f6fd1ba 35#include "verbs.h"
72648326
LP
36
37static bool arg_force = false;
5f129649 38static const char *arg_image_root = "/var/lib/machines";
8f695058 39static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
91f4347e 40static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
9854730b 41static bool arg_settings = true;
91f4347e 42
3d7415f4
LP
43static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
44 log_notice("Transfer aborted.");
45 sd_event_exit(sd_event_source_get_event(s), EINTR);
46 return 0;
47}
48
dc2c282b 49static void on_tar_finished(TarPull *pull, int error, void *userdata) {
56ebfaf1 50 sd_event *event = userdata;
dc2c282b 51 assert(pull);
56ebfaf1
LP
52
53 if (error == 0)
54 log_info("Operation completed successfully.");
56ebfaf1 55
3d7415f4 56 sd_event_exit(event, abs(error));
56ebfaf1
LP
57}
58
59static int pull_tar(int argc, char *argv[], void *userdata) {
dc2c282b 60 _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
56ebfaf1
LP
61 _cleanup_event_unref_ sd_event *event = NULL;
62 const char *url, *local;
63 _cleanup_free_ char *l = NULL, *ll = NULL;
64 int r;
65
66 url = argv[1];
67 if (!http_url_is_valid(url)) {
68 log_error("URL '%s' is not valid.", url);
69 return -EINVAL;
70 }
71
72 if (argc >= 3)
73 local = argv[2];
74 else {
85dbc41d 75 r = import_url_last_component(url, &l);
56ebfaf1
LP
76 if (r < 0)
77 return log_error_errno(r, "Failed get final component of URL: %m");
78
79 local = l;
80 }
81
82 if (isempty(local) || streq(local, "-"))
83 local = NULL;
84
85 if (local) {
3d7415f4 86 r = tar_strip_suffixes(local, &ll);
56ebfaf1
LP
87 if (r < 0)
88 return log_oom();
89
90 local = ll;
91
92 if (!machine_name_is_valid(local)) {
93 log_error("Local image name '%s' is not valid.", local);
94 return -EINVAL;
95 }
96
97 if (!arg_force) {
98 r = image_find(local, NULL);
99 if (r < 0)
100 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
101 else if (r > 0) {
102 log_error_errno(EEXIST, "Image '%s' already exists.", local);
103 return -EEXIST;
104 }
105 }
106
107 log_info("Pulling '%s', saving as '%s'.", url, local);
108 } else
109 log_info("Pulling '%s'.", url);
110
111 r = sd_event_default(&event);
112 if (r < 0)
113 return log_error_errno(r, "Failed to allocate event loop: %m");
114
72c0a2c2
LP
115 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
116 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
117 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
56ebfaf1 118
dc2c282b 119 r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
56ebfaf1 120 if (r < 0)
dc2c282b 121 return log_error_errno(r, "Failed to allocate puller: %m");
56ebfaf1 122
9854730b 123 r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
56ebfaf1
LP
124 if (r < 0)
125 return log_error_errno(r, "Failed to pull image: %m");
126
127 r = sd_event_loop(event);
128 if (r < 0)
129 return log_error_errno(r, "Failed to run event loop: %m");
130
131 log_info("Exiting.");
3d7415f4 132 return -r;
56ebfaf1
LP
133}
134
dc2c282b 135static void on_raw_finished(RawPull *pull, int error, void *userdata) {
90199220 136 sd_event *event = userdata;
dc2c282b 137 assert(pull);
90199220
LP
138
139 if (error == 0)
140 log_info("Operation completed successfully.");
90199220 141
3d7415f4 142 sd_event_exit(event, abs(error));
edce2aed
LP
143}
144
aceac2f0 145static int pull_raw(int argc, char *argv[], void *userdata) {
dc2c282b 146 _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
90199220 147 _cleanup_event_unref_ sd_event *event = NULL;
edce2aed 148 const char *url, *local;
0d6e763b 149 _cleanup_free_ char *l = NULL, *ll = NULL;
90199220
LP
150 int r;
151
152 url = argv[1];
a2e03378 153 if (!http_url_is_valid(url)) {
90199220
LP
154 log_error("URL '%s' is not valid.", url);
155 return -EINVAL;
156 }
157
8620a9a3
LP
158 if (argc >= 3)
159 local = argv[2];
160 else {
85dbc41d 161 r = import_url_last_component(url, &l);
0d6e763b
LP
162 if (r < 0)
163 return log_error_errno(r, "Failed get final component of URL: %m");
90199220 164
0d6e763b 165 local = l;
90199220
LP
166 }
167
8620a9a3
LP
168 if (isempty(local) || streq(local, "-"))
169 local = NULL;
90199220 170
8620a9a3 171 if (local) {
3d7415f4 172 r = raw_strip_suffixes(local, &ll);
edce2aed
LP
173 if (r < 0)
174 return log_oom();
175
0d6e763b 176 local = ll;
8620a9a3
LP
177
178 if (!machine_name_is_valid(local)) {
179 log_error("Local image name '%s' is not valid.", local);
180 return -EINVAL;
181 }
182
0d6e763b
LP
183 if (!arg_force) {
184 r = image_find(local, NULL);
185 if (r < 0)
186 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
187 else if (r > 0) {
188 log_error_errno(EEXIST, "Image '%s' already exists.", local);
189 return -EEXIST;
8620a9a3 190 }
0d6e763b 191 }
90199220 192
8620a9a3
LP
193 log_info("Pulling '%s', saving as '%s'.", url, local);
194 } else
195 log_info("Pulling '%s'.", url);
90199220
LP
196
197 r = sd_event_default(&event);
198 if (r < 0)
199 return log_error_errno(r, "Failed to allocate event loop: %m");
200
72c0a2c2
LP
201 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
202 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
203 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
90199220 204
dc2c282b 205 r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
90199220 206 if (r < 0)
dc2c282b 207 return log_error_errno(r, "Failed to allocate puller: %m");
90199220 208
9854730b 209 r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
90199220
LP
210 if (r < 0)
211 return log_error_errno(r, "Failed to pull image: %m");
212
213 r = sd_event_loop(event);
214 if (r < 0)
215 return log_error_errno(r, "Failed to run event loop: %m");
216
217 log_info("Exiting.");
3d7415f4 218 return -r;
90199220
LP
219}
220
dc2c282b 221static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
72648326 222 sd_event *event = userdata;
dc2c282b 223 assert(pull);
72648326
LP
224
225 if (error == 0)
226 log_info("Operation completed successfully.");
72648326 227
3d7415f4 228 sd_event_exit(event, abs(error));
72648326
LP
229}
230
91f4347e 231static int pull_dkr(int argc, char *argv[], void *userdata) {
dc2c282b 232 _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
72648326 233 _cleanup_event_unref_ sd_event *event = NULL;
7037d506 234 const char *name, *reference, *local, *digest;
72648326
LP
235 int r;
236
91f4347e
LP
237 if (!arg_dkr_index_url) {
238 log_error("Please specify an index URL with --dkr-index-url=");
239 return -EINVAL;
240 }
241
8f695058 242 if (arg_verify != IMPORT_VERIFY_NO) {
dc2c282b 243 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
8f695058
LP
244 return -EINVAL;
245 }
246
7037d506
PO
247 digest = strchr(argv[1], '@');
248 if (digest) {
249 reference = digest + 1;
250 name = strndupa(argv[1], digest - argv[1]);
72648326 251 } else {
4764a544
PO
252 reference = strchr(argv[1], ':');
253 if (reference) {
254 name = strndupa(argv[1], reference - argv[1]);
255 reference++;
256 } else {
257 name = argv[1];
258 reference = "latest";
259 }
72648326
LP
260 }
261
8620a9a3
LP
262 if (!dkr_name_is_valid(name)) {
263 log_error("Remote name '%s' is not valid.", name);
264 return -EINVAL;
265 }
266
7037d506
PO
267 if (!dkr_ref_is_valid(reference)) {
268 log_error("Tag name '%s' is not valid.", reference);
8620a9a3
LP
269 return -EINVAL;
270 }
271
72648326
LP
272 if (argc >= 3)
273 local = argv[2];
274 else {
275 local = strchr(name, '/');
276 if (local)
277 local++;
278 else
279 local = name;
280 }
281
0c7bf33a 282 if (isempty(local) || streq(local, "-"))
72648326
LP
283 local = NULL;
284
72648326 285 if (local) {
0c7bf33a 286 if (!machine_name_is_valid(local)) {
72648326
LP
287 log_error("Local image name '%s' is not valid.", local);
288 return -EINVAL;
289 }
290
ff2670ad
LP
291 if (!arg_force) {
292 r = image_find(local, NULL);
293 if (r < 0)
294 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
295 else if (r > 0) {
296 log_error_errno(EEXIST, "Image '%s' already exists.", local);
297 return -EEXIST;
72648326 298 }
ff2670ad 299 }
72648326 300
7037d506 301 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name, reference, local);
72648326 302 } else
7037d506 303 log_info("Pulling '%s' with reference '%s'.", name, reference);
72648326
LP
304
305 r = sd_event_default(&event);
306 if (r < 0)
307 return log_error_errno(r, "Failed to allocate event loop: %m");
308
72c0a2c2
LP
309 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
310 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
311 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
72648326 312
dc2c282b 313 r = dkr_pull_new(&pull, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
72648326 314 if (r < 0)
dc2c282b 315 return log_error_errno(r, "Failed to allocate puller: %m");
72648326 316
7037d506 317 r = dkr_pull_start(pull, name, reference, local, arg_force, DKR_PULL_V2);
72648326
LP
318 if (r < 0)
319 return log_error_errno(r, "Failed to pull image: %m");
320
321 r = sd_event_loop(event);
322 if (r < 0)
323 return log_error_errno(r, "Failed to run event loop: %m");
324
325 log_info("Exiting.");
3d7415f4 326 return -r;
72648326
LP
327}
328
329static int help(int argc, char *argv[], void *userdata) {
330
331 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
587fec42 332 "Download container or virtual machine images.\n\n"
72648326
LP
333 " -h --help Show this help\n"
334 " --version Show package version\n"
91f4347e 335 " --force Force creation of image\n"
9854730b
LP
336 " --verify=MODE Verify downloaded image, one of: 'no',\n"
337 " 'checksum', 'signature'\n"
338 " --settings=BOOL Download settings file with image\n"
776a9726 339 " --image-root=PATH Image root directory\n"
91f4347e 340 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
72648326 341 "Commands:\n"
aa9bd499
LP
342 " tar URL [NAME] Download a TAR image\n"
343 " raw URL [NAME] Download a RAW image\n"
344 " dkr REMOTE [NAME] Download a DKR image\n",
72648326
LP
345 program_invocation_short_name);
346
347 return 0;
348}
349
350static int parse_argv(int argc, char *argv[]) {
351
352 enum {
353 ARG_VERSION = 0x100,
354 ARG_FORCE,
91f4347e 355 ARG_DKR_INDEX_URL,
087682d1 356 ARG_IMAGE_ROOT,
8f695058 357 ARG_VERIFY,
9854730b 358 ARG_SETTINGS,
72648326
LP
359 };
360
361 static const struct option options[] = {
362 { "help", no_argument, NULL, 'h' },
363 { "version", no_argument, NULL, ARG_VERSION },
364 { "force", no_argument, NULL, ARG_FORCE },
91f4347e 365 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
087682d1 366 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
8f695058 367 { "verify", required_argument, NULL, ARG_VERIFY },
9854730b 368 { "settings", required_argument, NULL, ARG_SETTINGS },
72648326
LP
369 {}
370 };
371
9854730b 372 int c, r;
72648326
LP
373
374 assert(argc >= 0);
375 assert(argv);
376
377 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
378
379 switch (c) {
380
381 case 'h':
7eeeb28e 382 return help(0, NULL, NULL);
72648326
LP
383
384 case ARG_VERSION:
3f6fd1ba 385 return version();
72648326
LP
386
387 case ARG_FORCE:
388 arg_force = true;
389 break;
390
91f4347e 391 case ARG_DKR_INDEX_URL:
ff2670ad 392 if (!http_url_is_valid(optarg)) {
91f4347e
LP
393 log_error("Index URL is not valid: %s", optarg);
394 return -EINVAL;
395 }
396
397 arg_dkr_index_url = optarg;
398 break;
399
087682d1
LP
400 case ARG_IMAGE_ROOT:
401 arg_image_root = optarg;
402 break;
403
8f695058
LP
404 case ARG_VERIFY:
405 arg_verify = import_verify_from_string(optarg);
406 if (arg_verify < 0) {
407 log_error("Invalid verification setting '%s'", optarg);
408 return -EINVAL;
409 }
410
411 break;
412
9854730b
LP
413 case ARG_SETTINGS:
414 r = parse_boolean(optarg);
415 if (r < 0)
416 return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg);
417
418 arg_settings = r;
419 break;
420
72648326
LP
421 case '?':
422 return -EINVAL;
423
424 default:
425 assert_not_reached("Unhandled option");
426 }
427
428 return 1;
429}
430
dc2c282b 431static int pull_main(int argc, char *argv[]) {
72648326 432
7eeeb28e 433 static const Verb verbs[] = {
aa9bd499
LP
434 { "help", VERB_ANY, VERB_ANY, 0, help },
435 { "tar", 2, 3, 0, pull_tar },
436 { "raw", 2, 3, 0, pull_raw },
437 { "dkr", 2, 3, 0, pull_dkr },
72648326
LP
438 {}
439 };
440
441 return dispatch_verb(argc, argv, verbs, NULL);
442}
443
444int main(int argc, char *argv[]) {
445 int r;
446
447 setlocale(LC_ALL, "");
448 log_parse_environment();
449 log_open();
450
451 r = parse_argv(argc, argv);
452 if (r <= 0)
453 goto finish;
454
ce30c8dc 455 (void) ignore_signals(SIGPIPE, -1);
b6e676ce 456
dc2c282b 457 r = pull_main(argc, argv);
72648326
LP
458
459finish:
460 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
461}