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 "alloc-util.h"
27 #include "event-util.h"
28 #include "hostname-util.h"
29 #include "import-util.h"
30 #include "machine-image.h"
31 #include "parse-util.h"
35 #include "signal-util.h"
36 #include "string-util.h"
40 static bool arg_force
= false;
41 static const char *arg_image_root
= "/var/lib/machines";
42 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
43 static const char* arg_dkr_index_url
= DEFAULT_DKR_INDEX_URL
;
44 static bool arg_settings
= true;
46 static 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
);
52 static void on_tar_finished(TarPull
*pull
, int error
, void *userdata
) {
53 sd_event
*event
= userdata
;
57 log_info("Operation completed successfully.");
59 sd_event_exit(event
, abs(error
));
62 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
63 _cleanup_(tar_pull_unrefp
) TarPull
*pull
= NULL
;
64 _cleanup_event_unref_ sd_event
*event
= NULL
;
65 const char *url
, *local
;
66 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
70 if (!http_url_is_valid(url
)) {
71 log_error("URL '%s' is not valid.", url
);
78 r
= import_url_last_component(url
, &l
);
80 return log_error_errno(r
, "Failed get final component of URL: %m");
85 if (isempty(local
) || streq(local
, "-"))
89 r
= tar_strip_suffixes(local
, &ll
);
95 if (!machine_name_is_valid(local
)) {
96 log_error("Local image name '%s' is not valid.", local
);
101 r
= image_find(local
, NULL
);
103 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
105 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
110 log_info("Pulling '%s', saving as '%s'.", url
, local
);
112 log_info("Pulling '%s'.", url
);
114 r
= sd_event_default(&event
);
116 return log_error_errno(r
, "Failed to allocate event loop: %m");
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
);
122 r
= tar_pull_new(&pull
, event
, arg_image_root
, on_tar_finished
, event
);
124 return log_error_errno(r
, "Failed to allocate puller: %m");
126 r
= tar_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
128 return log_error_errno(r
, "Failed to pull image: %m");
130 r
= sd_event_loop(event
);
132 return log_error_errno(r
, "Failed to run event loop: %m");
134 log_info("Exiting.");
138 static void on_raw_finished(RawPull
*pull
, int error
, void *userdata
) {
139 sd_event
*event
= userdata
;
143 log_info("Operation completed successfully.");
145 sd_event_exit(event
, abs(error
));
148 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
149 _cleanup_(raw_pull_unrefp
) RawPull
*pull
= NULL
;
150 _cleanup_event_unref_ sd_event
*event
= NULL
;
151 const char *url
, *local
;
152 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
156 if (!http_url_is_valid(url
)) {
157 log_error("URL '%s' is not valid.", url
);
164 r
= import_url_last_component(url
, &l
);
166 return log_error_errno(r
, "Failed get final component of URL: %m");
171 if (isempty(local
) || streq(local
, "-"))
175 r
= raw_strip_suffixes(local
, &ll
);
181 if (!machine_name_is_valid(local
)) {
182 log_error("Local image name '%s' is not valid.", local
);
187 r
= image_find(local
, NULL
);
189 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
191 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
196 log_info("Pulling '%s', saving as '%s'.", url
, local
);
198 log_info("Pulling '%s'.", url
);
200 r
= sd_event_default(&event
);
202 return log_error_errno(r
, "Failed to allocate event loop: %m");
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
);
208 r
= raw_pull_new(&pull
, event
, arg_image_root
, on_raw_finished
, event
);
210 return log_error_errno(r
, "Failed to allocate puller: %m");
212 r
= raw_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
214 return log_error_errno(r
, "Failed to pull image: %m");
216 r
= sd_event_loop(event
);
218 return log_error_errno(r
, "Failed to run event loop: %m");
220 log_info("Exiting.");
224 static void on_dkr_finished(DkrPull
*pull
, int error
, void *userdata
) {
225 sd_event
*event
= userdata
;
229 log_info("Operation completed successfully.");
231 sd_event_exit(event
, abs(error
));
234 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
235 _cleanup_(dkr_pull_unrefp
) DkrPull
*pull
= NULL
;
236 _cleanup_event_unref_ sd_event
*event
= NULL
;
237 const char *name
, *reference
, *local
, *digest
;
240 if (!arg_dkr_index_url
) {
241 log_error("Please specify an index URL with --dkr-index-url=");
245 if (arg_verify
!= IMPORT_VERIFY_NO
) {
246 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
250 digest
= strchr(argv
[1], '@');
252 reference
= digest
+ 1;
253 name
= strndupa(argv
[1], digest
- argv
[1]);
255 reference
= strchr(argv
[1], ':');
257 name
= strndupa(argv
[1], reference
- argv
[1]);
261 reference
= "latest";
265 if (!dkr_name_is_valid(name
)) {
266 log_error("Remote name '%s' is not valid.", name
);
270 if (!dkr_ref_is_valid(reference
)) {
271 log_error("Tag name '%s' is not valid.", reference
);
278 local
= strchr(name
, '/');
285 if (isempty(local
) || streq(local
, "-"))
289 if (!machine_name_is_valid(local
)) {
290 log_error("Local image name '%s' is not valid.", local
);
295 r
= image_find(local
, NULL
);
297 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
299 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
304 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name
, reference
, local
);
306 log_info("Pulling '%s' with reference '%s'.", name
, reference
);
308 r
= sd_event_default(&event
);
310 return log_error_errno(r
, "Failed to allocate event loop: %m");
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
);
316 r
= dkr_pull_new(&pull
, event
, arg_dkr_index_url
, arg_image_root
, on_dkr_finished
, event
);
318 return log_error_errno(r
, "Failed to allocate puller: %m");
320 r
= dkr_pull_start(pull
, name
, reference
, local
, arg_force
, DKR_PULL_V2
);
322 return log_error_errno(r
, "Failed to pull image: %m");
324 r
= sd_event_loop(event
);
326 return log_error_errno(r
, "Failed to run event loop: %m");
328 log_info("Exiting.");
332 static int help(int argc
, char *argv
[], void *userdata
) {
334 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
335 "Download container or virtual machine images.\n\n"
336 " -h --help Show this help\n"
337 " --version Show package version\n"
338 " --force Force creation of image\n"
339 " --verify=MODE Verify downloaded image, one of: 'no',\n"
340 " 'checksum', 'signature'\n"
341 " --settings=BOOL Download settings file with image\n"
342 " --image-root=PATH Image root directory\n"
343 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
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",
348 program_invocation_short_name
);
353 static int parse_argv(int argc
, char *argv
[]) {
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
},
368 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
369 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
370 { "verify", required_argument
, NULL
, ARG_VERIFY
},
371 { "settings", required_argument
, NULL
, ARG_SETTINGS
},
380 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
385 return help(0, NULL
, NULL
);
394 case ARG_DKR_INDEX_URL
:
395 if (!http_url_is_valid(optarg
)) {
396 log_error("Index URL is not valid: %s", optarg
);
400 arg_dkr_index_url
= optarg
;
404 arg_image_root
= optarg
;
408 arg_verify
= import_verify_from_string(optarg
);
409 if (arg_verify
< 0) {
410 log_error("Invalid verification setting '%s'", optarg
);
417 r
= parse_boolean(optarg
);
419 return log_error_errno(r
, "Failed to parse --settings= parameter '%s'", optarg
);
428 assert_not_reached("Unhandled option");
434 static int pull_main(int argc
, char *argv
[]) {
436 static const Verb verbs
[] = {
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
},
444 return dispatch_verb(argc
, argv
, verbs
, NULL
);
447 int main(int argc
, char *argv
[]) {
450 setlocale(LC_ALL
, "");
451 log_parse_environment();
454 r
= parse_argv(argc
, argv
);
458 (void) ignore_signals(SIGPIPE
, -1);
460 r
= pull_main(argc
, argv
);
463 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;