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"
36 static bool arg_force
= false;
37 static const char *arg_image_root
= "/var/lib/machines";
38 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
39 static const char* arg_dkr_index_url
= DEFAULT_DKR_INDEX_URL
;
40 static bool arg_settings
= true;
42 static 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
);
48 static void on_tar_finished(TarPull
*pull
, int error
, void *userdata
) {
49 sd_event
*event
= userdata
;
53 log_info("Operation completed successfully.");
55 sd_event_exit(event
, abs(error
));
58 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
59 _cleanup_(tar_pull_unrefp
) TarPull
*pull
= NULL
;
60 _cleanup_event_unref_ sd_event
*event
= NULL
;
61 const char *url
, *local
;
62 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
66 if (!http_url_is_valid(url
)) {
67 log_error("URL '%s' is not valid.", url
);
74 r
= import_url_last_component(url
, &l
);
76 return log_error_errno(r
, "Failed get final component of URL: %m");
81 if (isempty(local
) || streq(local
, "-"))
85 r
= tar_strip_suffixes(local
, &ll
);
91 if (!machine_name_is_valid(local
)) {
92 log_error("Local image name '%s' is not valid.", local
);
97 r
= image_find(local
, NULL
);
99 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
101 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
106 log_info("Pulling '%s', saving as '%s'.", url
, local
);
108 log_info("Pulling '%s'.", url
);
110 r
= sd_event_default(&event
);
112 return log_error_errno(r
, "Failed to allocate event loop: %m");
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
);
118 r
= tar_pull_new(&pull
, event
, arg_image_root
, on_tar_finished
, event
);
120 return log_error_errno(r
, "Failed to allocate puller: %m");
122 r
= tar_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
124 return log_error_errno(r
, "Failed to pull image: %m");
126 r
= sd_event_loop(event
);
128 return log_error_errno(r
, "Failed to run event loop: %m");
130 log_info("Exiting.");
134 static void on_raw_finished(RawPull
*pull
, int error
, void *userdata
) {
135 sd_event
*event
= userdata
;
139 log_info("Operation completed successfully.");
141 sd_event_exit(event
, abs(error
));
144 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
145 _cleanup_(raw_pull_unrefp
) RawPull
*pull
= NULL
;
146 _cleanup_event_unref_ sd_event
*event
= NULL
;
147 const char *url
, *local
;
148 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
152 if (!http_url_is_valid(url
)) {
153 log_error("URL '%s' is not valid.", url
);
160 r
= import_url_last_component(url
, &l
);
162 return log_error_errno(r
, "Failed get final component of URL: %m");
167 if (isempty(local
) || streq(local
, "-"))
171 r
= raw_strip_suffixes(local
, &ll
);
177 if (!machine_name_is_valid(local
)) {
178 log_error("Local image name '%s' is not valid.", local
);
183 r
= image_find(local
, NULL
);
185 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
187 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
192 log_info("Pulling '%s', saving as '%s'.", url
, local
);
194 log_info("Pulling '%s'.", url
);
196 r
= sd_event_default(&event
);
198 return log_error_errno(r
, "Failed to allocate event loop: %m");
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
);
204 r
= raw_pull_new(&pull
, event
, arg_image_root
, on_raw_finished
, event
);
206 return log_error_errno(r
, "Failed to allocate puller: %m");
208 r
= raw_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
210 return log_error_errno(r
, "Failed to pull image: %m");
212 r
= sd_event_loop(event
);
214 return log_error_errno(r
, "Failed to run event loop: %m");
216 log_info("Exiting.");
220 static void on_dkr_finished(DkrPull
*pull
, int error
, void *userdata
) {
221 sd_event
*event
= userdata
;
225 log_info("Operation completed successfully.");
227 sd_event_exit(event
, abs(error
));
230 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
231 _cleanup_(dkr_pull_unrefp
) DkrPull
*pull
= NULL
;
232 _cleanup_event_unref_ sd_event
*event
= NULL
;
233 const char *name
, *reference
, *local
, *digest
;
236 if (!arg_dkr_index_url
) {
237 log_error("Please specify an index URL with --dkr-index-url=");
241 if (arg_verify
!= IMPORT_VERIFY_NO
) {
242 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
246 digest
= strchr(argv
[1], '@');
248 reference
= digest
+ 1;
249 name
= strndupa(argv
[1], digest
- argv
[1]);
251 reference
= strchr(argv
[1], ':');
253 name
= strndupa(argv
[1], reference
- argv
[1]);
257 reference
= "latest";
261 if (!dkr_name_is_valid(name
)) {
262 log_error("Remote name '%s' is not valid.", name
);
266 if (!dkr_ref_is_valid(reference
)) {
267 log_error("Tag name '%s' is not valid.", reference
);
274 local
= strchr(name
, '/');
281 if (isempty(local
) || streq(local
, "-"))
285 if (!machine_name_is_valid(local
)) {
286 log_error("Local image name '%s' is not valid.", local
);
291 r
= image_find(local
, NULL
);
293 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
295 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
300 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name
, reference
, local
);
302 log_info("Pulling '%s' with reference '%s'.", name
, reference
);
304 r
= sd_event_default(&event
);
306 return log_error_errno(r
, "Failed to allocate event loop: %m");
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
);
312 r
= dkr_pull_new(&pull
, event
, arg_dkr_index_url
, arg_image_root
, on_dkr_finished
, event
);
314 return log_error_errno(r
, "Failed to allocate puller: %m");
316 r
= dkr_pull_start(pull
, name
, reference
, local
, arg_force
, DKR_PULL_V2
);
318 return log_error_errno(r
, "Failed to pull image: %m");
320 r
= sd_event_loop(event
);
322 return log_error_errno(r
, "Failed to run event loop: %m");
324 log_info("Exiting.");
328 static int help(int argc
, char *argv
[], void *userdata
) {
330 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
331 "Download container or virtual machine images.\n\n"
332 " -h --help Show this help\n"
333 " --version Show package version\n"
334 " --force Force creation of image\n"
335 " --verify=MODE Verify downloaded image, one of: 'no',\n"
336 " 'checksum', 'signature'\n"
337 " --settings=BOOL Download settings file with image\n"
338 " --image-root=PATH Image root directory\n"
339 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
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",
344 program_invocation_short_name
);
349 static int parse_argv(int argc
, char *argv
[]) {
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
},
364 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
365 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
366 { "verify", required_argument
, NULL
, ARG_VERIFY
},
367 { "settings", required_argument
, NULL
, ARG_SETTINGS
},
376 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
381 return help(0, NULL
, NULL
);
390 case ARG_DKR_INDEX_URL
:
391 if (!http_url_is_valid(optarg
)) {
392 log_error("Index URL is not valid: %s", optarg
);
396 arg_dkr_index_url
= optarg
;
400 arg_image_root
= optarg
;
404 arg_verify
= import_verify_from_string(optarg
);
405 if (arg_verify
< 0) {
406 log_error("Invalid verification setting '%s'", optarg
);
413 r
= parse_boolean(optarg
);
415 return log_error_errno(r
, "Failed to parse --settings= parameter '%s'", optarg
);
424 assert_not_reached("Unhandled option");
430 static int pull_main(int argc
, char *argv
[]) {
432 static const Verb verbs
[] = {
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
},
440 return dispatch_verb(argc
, argv
, verbs
, NULL
);
443 int main(int argc
, char *argv
[]) {
446 setlocale(LC_ALL
, "");
447 log_parse_environment();
450 r
= parse_argv(argc
, argv
);
454 (void) ignore_signals(SIGPIPE
, -1);
456 r
= pull_main(argc
, argv
);
459 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;