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 "hostname-util.h"
30 #include "machine-image.h"
31 #include "import-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
;
41 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
42 log_notice("Transfer aborted.");
43 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
47 static void on_tar_finished(TarPull
*pull
, int error
, void *userdata
) {
48 sd_event
*event
= userdata
;
52 log_info("Operation completed successfully.");
54 sd_event_exit(event
, abs(error
));
57 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
58 _cleanup_(tar_pull_unrefp
) TarPull
*pull
= NULL
;
59 _cleanup_event_unref_ sd_event
*event
= NULL
;
60 const char *url
, *local
;
61 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
65 if (!http_url_is_valid(url
)) {
66 log_error("URL '%s' is not valid.", url
);
73 r
= import_url_last_component(url
, &l
);
75 return log_error_errno(r
, "Failed get final component of URL: %m");
80 if (isempty(local
) || streq(local
, "-"))
84 r
= tar_strip_suffixes(local
, &ll
);
90 if (!machine_name_is_valid(local
)) {
91 log_error("Local image name '%s' is not valid.", local
);
96 r
= image_find(local
, NULL
);
98 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
100 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
105 log_info("Pulling '%s', saving as '%s'.", url
, local
);
107 log_info("Pulling '%s'.", url
);
109 r
= sd_event_default(&event
);
111 return log_error_errno(r
, "Failed to allocate event loop: %m");
113 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
114 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
115 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
117 r
= tar_pull_new(&pull
, event
, arg_image_root
, on_tar_finished
, event
);
119 return log_error_errno(r
, "Failed to allocate puller: %m");
121 r
= tar_pull_start(pull
, url
, local
, arg_force
, arg_verify
);
123 return log_error_errno(r
, "Failed to pull image: %m");
125 r
= sd_event_loop(event
);
127 return log_error_errno(r
, "Failed to run event loop: %m");
129 log_info("Exiting.");
133 static void on_raw_finished(RawPull
*pull
, int error
, void *userdata
) {
134 sd_event
*event
= userdata
;
138 log_info("Operation completed successfully.");
140 sd_event_exit(event
, abs(error
));
143 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
144 _cleanup_(raw_pull_unrefp
) RawPull
*pull
= NULL
;
145 _cleanup_event_unref_ sd_event
*event
= NULL
;
146 const char *url
, *local
;
147 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
151 if (!http_url_is_valid(url
)) {
152 log_error("URL '%s' is not valid.", url
);
159 r
= import_url_last_component(url
, &l
);
161 return log_error_errno(r
, "Failed get final component of URL: %m");
166 if (isempty(local
) || streq(local
, "-"))
170 r
= raw_strip_suffixes(local
, &ll
);
176 if (!machine_name_is_valid(local
)) {
177 log_error("Local image name '%s' is not valid.", local
);
182 r
= image_find(local
, NULL
);
184 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
186 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
191 log_info("Pulling '%s', saving as '%s'.", url
, local
);
193 log_info("Pulling '%s'.", url
);
195 r
= sd_event_default(&event
);
197 return log_error_errno(r
, "Failed to allocate event loop: %m");
199 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
200 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
201 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
203 r
= raw_pull_new(&pull
, event
, arg_image_root
, on_raw_finished
, event
);
205 return log_error_errno(r
, "Failed to allocate puller: %m");
207 r
= raw_pull_start(pull
, url
, local
, arg_force
, arg_verify
);
209 return log_error_errno(r
, "Failed to pull image: %m");
211 r
= sd_event_loop(event
);
213 return log_error_errno(r
, "Failed to run event loop: %m");
215 log_info("Exiting.");
219 static void on_dkr_finished(DkrPull
*pull
, int error
, void *userdata
) {
220 sd_event
*event
= userdata
;
224 log_info("Operation completed successfully.");
226 sd_event_exit(event
, abs(error
));
229 static int pull_dkr(int argc
, char *argv
[], void *userdata
) {
230 _cleanup_(dkr_pull_unrefp
) DkrPull
*pull
= NULL
;
231 _cleanup_event_unref_ sd_event
*event
= NULL
;
232 const char *name
, *reference
, *local
, *digest
;
235 if (!arg_dkr_index_url
) {
236 log_error("Please specify an index URL with --dkr-index-url=");
240 if (arg_verify
!= IMPORT_VERIFY_NO
) {
241 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
245 digest
= strchr(argv
[1], '@');
247 reference
= digest
+ 1;
248 name
= strndupa(argv
[1], digest
- argv
[1]);
250 reference
= strchr(argv
[1], ':');
252 name
= strndupa(argv
[1], reference
- argv
[1]);
256 reference
= "latest";
260 if (!dkr_name_is_valid(name
)) {
261 log_error("Remote name '%s' is not valid.", name
);
265 if (!dkr_ref_is_valid(reference
)) {
266 log_error("Tag name '%s' is not valid.", reference
);
273 local
= strchr(name
, '/');
280 if (isempty(local
) || streq(local
, "-"))
284 if (!machine_name_is_valid(local
)) {
285 log_error("Local image name '%s' is not valid.", local
);
290 r
= image_find(local
, NULL
);
292 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
294 log_error_errno(EEXIST
, "Image '%s' already exists.", local
);
299 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name
, reference
, local
);
301 log_info("Pulling '%s' with reference '%s'.", name
, reference
);
303 r
= sd_event_default(&event
);
305 return log_error_errno(r
, "Failed to allocate event loop: %m");
307 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
308 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
309 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
311 r
= dkr_pull_new(&pull
, event
, arg_dkr_index_url
, arg_image_root
, on_dkr_finished
, event
);
313 return log_error_errno(r
, "Failed to allocate puller: %m");
315 r
= dkr_pull_start(pull
, name
, reference
, local
, arg_force
, DKR_PULL_V2
);
317 return log_error_errno(r
, "Failed to pull image: %m");
319 r
= sd_event_loop(event
);
321 return log_error_errno(r
, "Failed to run event loop: %m");
323 log_info("Exiting.");
327 static int help(int argc
, char *argv
[], void *userdata
) {
329 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
330 "Download container or virtual machine images.\n\n"
331 " -h --help Show this help\n"
332 " --version Show package version\n"
333 " --force Force creation of image\n"
334 " --verify= Verify downloaded image, one of: 'no',\n"
335 " 'checksum', 'signature'.\n"
336 " --image-root=PATH Image root directory\n"
337 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
339 " tar URL [NAME] Download a TAR image\n"
340 " raw URL [NAME] Download a RAW image\n"
341 " dkr REMOTE [NAME] Download a DKR image\n",
342 program_invocation_short_name
);
347 static int parse_argv(int argc
, char *argv
[]) {
357 static const struct option options
[] = {
358 { "help", no_argument
, NULL
, 'h' },
359 { "version", no_argument
, NULL
, ARG_VERSION
},
360 { "force", no_argument
, NULL
, ARG_FORCE
},
361 { "dkr-index-url", required_argument
, NULL
, ARG_DKR_INDEX_URL
},
362 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
363 { "verify", required_argument
, NULL
, ARG_VERIFY
},
372 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
377 return help(0, NULL
, NULL
);
380 puts(PACKAGE_STRING
);
381 puts(SYSTEMD_FEATURES
);
388 case ARG_DKR_INDEX_URL
:
389 if (!http_url_is_valid(optarg
)) {
390 log_error("Index URL is not valid: %s", optarg
);
394 arg_dkr_index_url
= optarg
;
398 arg_image_root
= optarg
;
402 arg_verify
= import_verify_from_string(optarg
);
403 if (arg_verify
< 0) {
404 log_error("Invalid verification setting '%s'", optarg
);
414 assert_not_reached("Unhandled option");
420 static int pull_main(int argc
, char *argv
[]) {
422 static const Verb verbs
[] = {
423 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
424 { "tar", 2, 3, 0, pull_tar
},
425 { "raw", 2, 3, 0, pull_raw
},
426 { "dkr", 2, 3, 0, pull_dkr
},
430 return dispatch_verb(argc
, argv
, verbs
, NULL
);
433 int main(int argc
, char *argv
[]) {
436 setlocale(LC_ALL
, "");
437 log_parse_environment();
440 r
= parse_argv(argc
, argv
);
444 (void) ignore_signals(SIGPIPE
, -1);
446 r
= pull_main(argc
, argv
);
449 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;