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