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"
33 #include "signal-util.h"
34 #include "string-util.h"
37 static bool arg_force
= false;
38 static const char *arg_image_root
= "/var/lib/machines";
39 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
40 static const char* arg_dkr_index_url
= DEFAULT_DKR_INDEX_URL
;
41 static bool arg_settings
= true;
43 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
44 log_notice("Transfer aborted.");
45 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
49 static void on_tar_finished(TarPull
*pull
, int error
, void *userdata
) {
50 sd_event
*event
= userdata
;
54 log_info("Operation completed successfully.");
56 sd_event_exit(event
, abs(error
));
59 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
60 _cleanup_(tar_pull_unrefp
) TarPull
*pull
= NULL
;
61 _cleanup_event_unref_ sd_event
*event
= NULL
;
62 const char *url
, *local
;
63 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
67 if (!http_url_is_valid(url
)) {
68 log_error("URL '%s' is not valid.", url
);
75 r
= import_url_last_component(url
, &l
);
77 return log_error_errno(r
, "Failed get final component of URL: %m");
82 if (isempty(local
) || streq(local
, "-"))
86 r
= tar_strip_suffixes(local
, &ll
);
92 if (!machine_name_is_valid(local
)) {
93 log_error("Local image name '%s' is not valid.", local
);
98 r
= image_find(local
, NULL
);
100 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
102 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
107 log_info("Pulling '%s', saving as '%s'.", url
, local
);
109 log_info("Pulling '%s'.", url
);
111 r
= sd_event_default(&event
);
113 return log_error_errno(r
, "Failed to allocate event loop: %m");
115 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
116 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
117 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
119 r
= tar_pull_new(&pull
, event
, arg_image_root
, on_tar_finished
, event
);
121 return log_error_errno(r
, "Failed to allocate puller: %m");
123 r
= tar_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
125 return log_error_errno(r
, "Failed to pull image: %m");
127 r
= sd_event_loop(event
);
129 return log_error_errno(r
, "Failed to run event loop: %m");
131 log_info("Exiting.");
135 static void on_raw_finished(RawPull
*pull
, int error
, void *userdata
) {
136 sd_event
*event
= userdata
;
140 log_info("Operation completed successfully.");
142 sd_event_exit(event
, abs(error
));
145 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
146 _cleanup_(raw_pull_unrefp
) RawPull
*pull
= NULL
;
147 _cleanup_event_unref_ sd_event
*event
= NULL
;
148 const char *url
, *local
;
149 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
153 if (!http_url_is_valid(url
)) {
154 log_error("URL '%s' is not valid.", url
);
161 r
= import_url_last_component(url
, &l
);
163 return log_error_errno(r
, "Failed get final component of URL: %m");
168 if (isempty(local
) || streq(local
, "-"))
172 r
= raw_strip_suffixes(local
, &ll
);
178 if (!machine_name_is_valid(local
)) {
179 log_error("Local image name '%s' is not valid.", local
);
184 r
= image_find(local
, NULL
);
186 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
188 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
193 log_info("Pulling '%s', saving as '%s'.", url
, local
);
195 log_info("Pulling '%s'.", url
);
197 r
= sd_event_default(&event
);
199 return log_error_errno(r
, "Failed to allocate event loop: %m");
201 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
202 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
203 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
205 r
= raw_pull_new(&pull
, event
, arg_image_root
, on_raw_finished
, event
);
207 return log_error_errno(r
, "Failed to allocate puller: %m");
209 r
= raw_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
211 return log_error_errno(r
, "Failed to pull image: %m");
213 r
= sd_event_loop(event
);
215 return log_error_errno(r
, "Failed to run event loop: %m");
217 log_info("Exiting.");
221 static void on_dkr_finished(DkrPull
*pull
, int error
, void *userdata
) {
222 sd_event
*event
= userdata
;
226 log_info("Operation completed successfully.");
228 sd_event_exit(event
, abs(error
));
231 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
232 _cleanup_(dkr_pull_unrefp
) DkrPull
*pull
= NULL
;
233 _cleanup_event_unref_ sd_event
*event
= NULL
;
234 const char *name
, *reference
, *local
, *digest
;
237 if (!arg_dkr_index_url
) {
238 log_error("Please specify an index URL with --dkr-index-url=");
242 if (arg_verify
!= IMPORT_VERIFY_NO
) {
243 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
247 digest
= strchr(argv
[1], '@');
249 reference
= digest
+ 1;
250 name
= strndupa(argv
[1], digest
- argv
[1]);
252 reference
= strchr(argv
[1], ':');
254 name
= strndupa(argv
[1], reference
- argv
[1]);
258 reference
= "latest";
262 if (!dkr_name_is_valid(name
)) {
263 log_error("Remote name '%s' is not valid.", name
);
267 if (!dkr_ref_is_valid(reference
)) {
268 log_error("Tag name '%s' is not valid.", reference
);
275 local
= strchr(name
, '/');
282 if (isempty(local
) || streq(local
, "-"))
286 if (!machine_name_is_valid(local
)) {
287 log_error("Local image name '%s' is not valid.", local
);
292 r
= image_find(local
, NULL
);
294 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
296 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
301 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name
, reference
, local
);
303 log_info("Pulling '%s' with reference '%s'.", name
, reference
);
305 r
= sd_event_default(&event
);
307 return log_error_errno(r
, "Failed to allocate event loop: %m");
309 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
310 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
311 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
313 r
= dkr_pull_new(&pull
, event
, arg_dkr_index_url
, arg_image_root
, on_dkr_finished
, event
);
315 return log_error_errno(r
, "Failed to allocate puller: %m");
317 r
= dkr_pull_start(pull
, name
, reference
, local
, arg_force
, DKR_PULL_V2
);
319 return log_error_errno(r
, "Failed to pull image: %m");
321 r
= sd_event_loop(event
);
323 return log_error_errno(r
, "Failed to run event loop: %m");
325 log_info("Exiting.");
329 static int help(int argc
, char *argv
[], void *userdata
) {
331 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
332 "Download container or virtual machine images.\n\n"
333 " -h --help Show this help\n"
334 " --version Show package version\n"
335 " --force Force creation of image\n"
336 " --verify=MODE Verify downloaded image, one of: 'no',\n"
337 " 'checksum', 'signature'\n"
338 " --settings=BOOL Download settings file with image\n"
339 " --image-root=PATH Image root directory\n"
340 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
342 " tar URL [NAME] Download a TAR image\n"
343 " raw URL [NAME] Download a RAW image\n"
344 " dkr REMOTE [NAME] Download a DKR image\n",
345 program_invocation_short_name
);
350 static int parse_argv(int argc
, char *argv
[]) {
361 static const struct option options
[] = {
362 { "help", no_argument
, NULL
, 'h' },
363 { "version", no_argument
, NULL
, ARG_VERSION
},
364 { "force", no_argument
, NULL
, ARG_FORCE
},
365 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
366 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
367 { "verify", required_argument
, NULL
, ARG_VERIFY
},
368 { "settings", required_argument
, NULL
, ARG_SETTINGS
},
377 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
382 return help(0, NULL
, NULL
);
391 case ARG_DKR_INDEX_URL
:
392 if (!http_url_is_valid(optarg
)) {
393 log_error("Index URL is not valid: %s", optarg
);
397 arg_dkr_index_url
= optarg
;
401 arg_image_root
= optarg
;
405 arg_verify
= import_verify_from_string(optarg
);
406 if (arg_verify
< 0) {
407 log_error("Invalid verification setting '%s'", optarg
);
414 r
= parse_boolean(optarg
);
416 return log_error_errno(r
, "Failed to parse --settings= parameter '%s'", optarg
);
425 assert_not_reached("Unhandled option");
431 static int pull_main(int argc
, char *argv
[]) {
433 static const Verb verbs
[] = {
434 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
435 { "tar", 2, 3, 0, pull_tar
},
436 { "raw", 2, 3, 0, pull_raw
},
437 { "dkr", 2, 3, 0, pull_dkr
},
441 return dispatch_verb(argc
, argv
, verbs
, NULL
);
444 int main(int argc
, char *argv
[]) {
447 setlocale(LC_ALL
, "");
448 log_parse_environment();
451 r
= parse_argv(argc
, argv
);
455 (void) ignore_signals(SIGPIPE
, -1);
457 r
= pull_main(argc
, argv
);
460 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;