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/>.
25 #include "event-util.h"
28 #include "signal-util.h"
29 #include "machine-image.h"
30 #include "import-util.h"
35 static bool arg_force
= false;
36 static const char *arg_image_root
= "/var/lib/machines";
37 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
38 static const char* arg_dkr_index_url
= DEFAULT_DKR_INDEX_URL
;
40 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
41 log_notice("Transfer aborted.");
42 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
46 static void on_tar_finished(TarPull
*pull
, int error
, void *userdata
) {
47 sd_event
*event
= userdata
;
51 log_info("Operation completed successfully.");
53 sd_event_exit(event
, abs(error
));
56 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
57 _cleanup_(tar_pull_unrefp
) TarPull
*pull
= NULL
;
58 _cleanup_event_unref_ sd_event
*event
= NULL
;
59 const char *url
, *local
;
60 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
64 if (!http_url_is_valid(url
)) {
65 log_error("URL '%s' is not valid.", url
);
72 r
= import_url_last_component(url
, &l
);
74 return log_error_errno(r
, "Failed get final component of URL: %m");
79 if (isempty(local
) || streq(local
, "-"))
83 r
= tar_strip_suffixes(local
, &ll
);
89 if (!machine_name_is_valid(local
)) {
90 log_error("Local image name '%s' is not valid.", local
);
95 r
= image_find(local
, NULL
);
97 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
99 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
104 log_info("Pulling '%s', saving as '%s'.", url
, local
);
106 log_info("Pulling '%s'.", url
);
108 r
= sd_event_default(&event
);
110 return log_error_errno(r
, "Failed to allocate event loop: %m");
112 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
113 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
114 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
116 r
= tar_pull_new(&pull
, event
, arg_image_root
, on_tar_finished
, event
);
118 return log_error_errno(r
, "Failed to allocate puller: %m");
120 r
= tar_pull_start(pull
, url
, local
, arg_force
, arg_verify
);
122 return log_error_errno(r
, "Failed to pull image: %m");
124 r
= sd_event_loop(event
);
126 return log_error_errno(r
, "Failed to run event loop: %m");
128 log_info("Exiting.");
132 static void on_raw_finished(RawPull
*pull
, int error
, void *userdata
) {
133 sd_event
*event
= userdata
;
137 log_info("Operation completed successfully.");
139 sd_event_exit(event
, abs(error
));
142 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
143 _cleanup_(raw_pull_unrefp
) RawPull
*pull
= NULL
;
144 _cleanup_event_unref_ sd_event
*event
= NULL
;
145 const char *url
, *local
;
146 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
150 if (!http_url_is_valid(url
)) {
151 log_error("URL '%s' is not valid.", url
);
158 r
= import_url_last_component(url
, &l
);
160 return log_error_errno(r
, "Failed get final component of URL: %m");
165 if (isempty(local
) || streq(local
, "-"))
169 r
= raw_strip_suffixes(local
, &ll
);
175 if (!machine_name_is_valid(local
)) {
176 log_error("Local image name '%s' is not valid.", local
);
181 r
= image_find(local
, NULL
);
183 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
185 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
190 log_info("Pulling '%s', saving as '%s'.", url
, local
);
192 log_info("Pulling '%s'.", url
);
194 r
= sd_event_default(&event
);
196 return log_error_errno(r
, "Failed to allocate event loop: %m");
198 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
199 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
200 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
202 r
= raw_pull_new(&pull
, event
, arg_image_root
, on_raw_finished
, event
);
204 return log_error_errno(r
, "Failed to allocate puller: %m");
206 r
= raw_pull_start(pull
, url
, local
, arg_force
, arg_verify
);
208 return log_error_errno(r
, "Failed to pull image: %m");
210 r
= sd_event_loop(event
);
212 return log_error_errno(r
, "Failed to run event loop: %m");
214 log_info("Exiting.");
218 static void on_dkr_finished(DkrPull
*pull
, int error
, void *userdata
) {
219 sd_event
*event
= userdata
;
223 log_info("Operation completed successfully.");
225 sd_event_exit(event
, abs(error
));
228 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
229 _cleanup_(dkr_pull_unrefp
) DkrPull
*pull
= NULL
;
230 _cleanup_event_unref_ sd_event
*event
= NULL
;
231 const char *name
, *reference
, *local
, *digest
;
234 if (!arg_dkr_index_url
) {
235 log_error("Please specify an index URL with --dkr-index-url=");
239 if (arg_verify
!= IMPORT_VERIFY_NO
) {
240 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
244 digest
= strchr(argv
[1], '@');
246 reference
= digest
+ 1;
247 name
= strndupa(argv
[1], digest
- argv
[1]);
249 reference
= strchr(argv
[1], ':');
251 name
= strndupa(argv
[1], reference
- argv
[1]);
255 reference
= "latest";
259 if (!dkr_name_is_valid(name
)) {
260 log_error("Remote name '%s' is not valid.", name
);
264 if (!dkr_ref_is_valid(reference
)) {
265 log_error("Tag name '%s' is not valid.", reference
);
272 local
= strchr(name
, '/');
279 if (isempty(local
) || streq(local
, "-"))
283 if (!machine_name_is_valid(local
)) {
284 log_error("Local image name '%s' is not valid.", local
);
289 r
= image_find(local
, NULL
);
291 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
293 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
298 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name
, reference
, local
);
300 log_info("Pulling '%s' with reference '%s'.", name
, reference
);
302 r
= sd_event_default(&event
);
304 return log_error_errno(r
, "Failed to allocate event loop: %m");
306 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
307 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
308 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
310 r
= dkr_pull_new(&pull
, event
, arg_dkr_index_url
, arg_image_root
, on_dkr_finished
, event
);
312 return log_error_errno(r
, "Failed to allocate puller: %m");
314 r
= dkr_pull_start(pull
, name
, reference
, local
, arg_force
, DKR_PULL_V2
);
316 return log_error_errno(r
, "Failed to pull image: %m");
318 r
= sd_event_loop(event
);
320 return log_error_errno(r
, "Failed to run event loop: %m");
322 log_info("Exiting.");
326 static int help(int argc
, char *argv
[], void *userdata
) {
328 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
329 "Download container or virtual machine images.\n\n"
330 " -h --help Show this help\n"
331 " --version Show package version\n"
332 " --force Force creation of image\n"
333 " --verify= Verify downloaded image, one of: 'no',\n"
334 " 'checksum', 'signature'.\n"
335 " --image-root=PATH Image root directory\n"
336 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
338 " tar URL [NAME] Download a TAR image\n"
339 " raw URL [NAME] Download a RAW image\n"
340 " dkr REMOTE [NAME] Download a DKR image\n",
341 program_invocation_short_name
);
346 static int parse_argv(int argc
, char *argv
[]) {
356 static const struct option options
[] = {
357 { "help", no_argument
, NULL
, 'h' },
358 { "version", no_argument
, NULL
, ARG_VERSION
},
359 { "force", no_argument
, NULL
, ARG_FORCE
},
360 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
361 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
362 { "verify", required_argument
, NULL
, ARG_VERIFY
},
371 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
376 return help(0, NULL
, NULL
);
379 puts(PACKAGE_STRING
);
380 puts(SYSTEMD_FEATURES
);
387 case ARG_DKR_INDEX_URL
:
388 if (!http_url_is_valid(optarg
)) {
389 log_error("Index URL is not valid: %s", optarg
);
393 arg_dkr_index_url
= optarg
;
397 arg_image_root
= optarg
;
401 arg_verify
= import_verify_from_string(optarg
);
402 if (arg_verify
< 0) {
403 log_error("Invalid verification setting '%s'", optarg
);
413 assert_not_reached("Unhandled option");
419 static int pull_main(int argc
, char *argv
[]) {
421 static const Verb verbs
[] = {
422 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
423 { "tar", 2, 3, 0, pull_tar
},
424 { "raw", 2, 3, 0, pull_raw
},
425 { "dkr", 2, 3, 0, pull_dkr
},
429 return dispatch_verb(argc
, argv
, verbs
, NULL
);
432 int main(int argc
, char *argv
[]) {
435 setlocale(LC_ALL
, "");
436 log_parse_environment();
439 r
= parse_argv(argc
, argv
);
443 (void) ignore_signals(SIGPIPE
, -1);
445 r
= pull_main(argc
, argv
);
448 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;