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