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