1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
26 #include "event-util.h"
27 #include "hostname-util.h"
28 #include "import-util.h"
29 #include "machine-image.h"
30 #include "parse-util.h"
34 #include "signal-util.h"
35 #include "string-util.h"
38 static bool arg_force
= false;
39 static const char *arg_image_root
= "/var/lib/machines";
40 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
41 static const char* arg_dkr_index_url
= DEFAULT_DKR_INDEX_URL
;
42 static bool arg_settings
= true;
44 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
45 log_notice("Transfer aborted.");
46 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
50 static void on_tar_finished(TarPull
*pull
, int error
, void *userdata
) {
51 sd_event
*event
= userdata
;
55 log_info("Operation completed successfully.");
57 sd_event_exit(event
, abs(error
));
60 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
61 _cleanup_(tar_pull_unrefp
) TarPull
*pull
= NULL
;
62 _cleanup_event_unref_ sd_event
*event
= NULL
;
63 const char *url
, *local
;
64 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
68 if (!http_url_is_valid(url
)) {
69 log_error("URL '%s' is not valid.", url
);
76 r
= import_url_last_component(url
, &l
);
78 return log_error_errno(r
, "Failed get final component of URL: %m");
83 if (isempty(local
) || streq(local
, "-"))
87 r
= tar_strip_suffixes(local
, &ll
);
93 if (!machine_name_is_valid(local
)) {
94 log_error("Local image name '%s' is not valid.", local
);
99 r
= image_find(local
, NULL
);
101 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
103 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
108 log_info("Pulling '%s', saving as '%s'.", url
, local
);
110 log_info("Pulling '%s'.", url
);
112 r
= sd_event_default(&event
);
114 return log_error_errno(r
, "Failed to allocate event loop: %m");
116 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
117 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
118 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
120 r
= tar_pull_new(&pull
, event
, arg_image_root
, on_tar_finished
, event
);
122 return log_error_errno(r
, "Failed to allocate puller: %m");
124 r
= tar_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
126 return log_error_errno(r
, "Failed to pull image: %m");
128 r
= sd_event_loop(event
);
130 return log_error_errno(r
, "Failed to run event loop: %m");
132 log_info("Exiting.");
136 static void on_raw_finished(RawPull
*pull
, int error
, void *userdata
) {
137 sd_event
*event
= userdata
;
141 log_info("Operation completed successfully.");
143 sd_event_exit(event
, abs(error
));
146 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
147 _cleanup_(raw_pull_unrefp
) RawPull
*pull
= NULL
;
148 _cleanup_event_unref_ sd_event
*event
= NULL
;
149 const char *url
, *local
;
150 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
154 if (!http_url_is_valid(url
)) {
155 log_error("URL '%s' is not valid.", url
);
162 r
= import_url_last_component(url
, &l
);
164 return log_error_errno(r
, "Failed get final component of URL: %m");
169 if (isempty(local
) || streq(local
, "-"))
173 r
= raw_strip_suffixes(local
, &ll
);
179 if (!machine_name_is_valid(local
)) {
180 log_error("Local image name '%s' is not valid.", local
);
185 r
= image_find(local
, NULL
);
187 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
189 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
194 log_info("Pulling '%s', saving as '%s'.", url
, local
);
196 log_info("Pulling '%s'.", url
);
198 r
= sd_event_default(&event
);
200 return log_error_errno(r
, "Failed to allocate event loop: %m");
202 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
203 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
204 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
206 r
= raw_pull_new(&pull
, event
, arg_image_root
, on_raw_finished
, event
);
208 return log_error_errno(r
, "Failed to allocate puller: %m");
210 r
= raw_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
212 return log_error_errno(r
, "Failed to pull image: %m");
214 r
= sd_event_loop(event
);
216 return log_error_errno(r
, "Failed to run event loop: %m");
218 log_info("Exiting.");
222 static void on_dkr_finished(DkrPull
*pull
, int error
, void *userdata
) {
223 sd_event
*event
= userdata
;
227 log_info("Operation completed successfully.");
229 sd_event_exit(event
, abs(error
));
232 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
233 _cleanup_(dkr_pull_unrefp
) DkrPull
*pull
= NULL
;
234 _cleanup_event_unref_ sd_event
*event
= NULL
;
235 const char *name
, *reference
, *local
, *digest
;
238 if (!arg_dkr_index_url
) {
239 log_error("Please specify an index URL with --dkr-index-url=");
243 if (arg_verify
!= IMPORT_VERIFY_NO
) {
244 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
248 digest
= strchr(argv
[1], '@');
250 reference
= digest
+ 1;
251 name
= strndupa(argv
[1], digest
- argv
[1]);
253 reference
= strchr(argv
[1], ':');
255 name
= strndupa(argv
[1], reference
- argv
[1]);
259 reference
= "latest";
263 if (!dkr_name_is_valid(name
)) {
264 log_error("Remote name '%s' is not valid.", name
);
268 if (!dkr_ref_is_valid(reference
)) {
269 log_error("Tag name '%s' is not valid.", reference
);
276 local
= strchr(name
, '/');
283 if (isempty(local
) || streq(local
, "-"))
287 if (!machine_name_is_valid(local
)) {
288 log_error("Local image name '%s' is not valid.", local
);
293 r
= image_find(local
, NULL
);
295 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
297 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
302 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name
, reference
, local
);
304 log_info("Pulling '%s' with reference '%s'.", name
, reference
);
306 r
= sd_event_default(&event
);
308 return log_error_errno(r
, "Failed to allocate event loop: %m");
310 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
311 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
312 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
314 r
= dkr_pull_new(&pull
, event
, arg_dkr_index_url
, arg_image_root
, on_dkr_finished
, event
);
316 return log_error_errno(r
, "Failed to allocate puller: %m");
318 r
= dkr_pull_start(pull
, name
, reference
, local
, arg_force
, DKR_PULL_V2
);
320 return log_error_errno(r
, "Failed to pull image: %m");
322 r
= sd_event_loop(event
);
324 return log_error_errno(r
, "Failed to run event loop: %m");
326 log_info("Exiting.");
330 static int help(int argc
, char *argv
[], void *userdata
) {
332 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
333 "Download container or virtual machine images.\n\n"
334 " -h --help Show this help\n"
335 " --version Show package version\n"
336 " --force Force creation of image\n"
337 " --verify=MODE Verify downloaded image, one of: 'no',\n"
338 " 'checksum', 'signature'\n"
339 " --settings=BOOL Download settings file with image\n"
340 " --image-root=PATH Image root directory\n"
341 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
343 " tar URL [NAME] Download a TAR image\n"
344 " raw URL [NAME] Download a RAW image\n"
345 " dkr REMOTE [NAME] Download a DKR image\n",
346 program_invocation_short_name
);
351 static int parse_argv(int argc
, char *argv
[]) {
362 static const struct option options
[] = {
363 { "help", no_argument
, NULL
, 'h' },
364 { "version", no_argument
, NULL
, ARG_VERSION
},
365 { "force", no_argument
, NULL
, ARG_FORCE
},
366 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
367 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
368 { "verify", required_argument
, NULL
, ARG_VERIFY
},
369 { "settings", required_argument
, NULL
, ARG_SETTINGS
},
378 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
383 return help(0, NULL
, NULL
);
392 case ARG_DKR_INDEX_URL
:
393 if (!http_url_is_valid(optarg
)) {
394 log_error("Index URL is not valid: %s", optarg
);
398 arg_dkr_index_url
= optarg
;
402 arg_image_root
= optarg
;
406 arg_verify
= import_verify_from_string(optarg
);
407 if (arg_verify
< 0) {
408 log_error("Invalid verification setting '%s'", optarg
);
415 r
= parse_boolean(optarg
);
417 return log_error_errno(r
, "Failed to parse --settings= parameter '%s'", optarg
);
426 assert_not_reached("Unhandled option");
432 static int pull_main(int argc
, char *argv
[]) {
434 static const Verb verbs
[] = {
435 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
436 { "tar", 2, 3, 0, pull_tar
},
437 { "raw", 2, 3, 0, pull_raw
},
438 { "dkr", 2, 3, 0, pull_dkr
},
442 return dispatch_verb(argc
, argv
, verbs
, NULL
);
445 int main(int argc
, char *argv
[]) {
448 setlocale(LC_ALL
, "");
449 log_parse_environment();
452 r
= parse_argv(argc
, argv
);
456 (void) ignore_signals(SIGPIPE
, -1);
458 r
= pull_main(argc
, argv
);
461 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;