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