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 "machine-image.h"
29 #include "import-util.h"
34 static bool arg_force = false;
35 static const char *arg_image_root = "/var/lib/machines";
36 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
37 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
39 static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
40 log_notice("Transfer aborted.");
41 sd_event_exit(sd_event_source_get_event(s), EINTR);
45 static void on_tar_finished(TarPull *pull, int error, void *userdata) {
46 sd_event *event = userdata;
50 log_info("Operation completed successfully.");
52 sd_event_exit(event, abs(error));
55 static int pull_tar(int argc, char *argv[], void *userdata) {
56 _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
57 _cleanup_event_unref_ sd_event *event = NULL;
58 const char *url, *local;
59 _cleanup_free_ char *l = NULL, *ll = NULL;
63 if (!http_url_is_valid(url)) {
64 log_error("URL '%s' is not valid.", url);
71 r = import_url_last_component(url, &l);
73 return log_error_errno(r, "Failed get final component of URL: %m");
78 if (isempty(local) || streq(local, "-"))
82 r = tar_strip_suffixes(local, &ll);
88 if (!machine_name_is_valid(local)) {
89 log_error("Local image name '%s' is not valid.", local);
94 r = image_find(local, NULL);
96 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
98 log_error_errno(EEXIST, "Image '%s' already exists.", local);
103 log_info("Pulling '%s', saving as '%s'.", url, local);
105 log_info("Pulling '%s'.", url);
107 r = sd_event_default(&event);
109 return log_error_errno(r, "Failed to allocate event loop: %m");
111 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
112 sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
113 sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
115 r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
117 return log_error_errno(r, "Failed to allocate puller: %m");
119 r = tar_pull_start(pull, url, local, arg_force, arg_verify);
121 return log_error_errno(r, "Failed to pull image: %m");
123 r = sd_event_loop(event);
125 return log_error_errno(r, "Failed to run event loop: %m");
127 log_info("Exiting.");
131 static void on_raw_finished(RawPull *pull, int error, void *userdata) {
132 sd_event *event = userdata;
136 log_info("Operation completed successfully.");
138 sd_event_exit(event, abs(error));
141 static int pull_raw(int argc, char *argv[], void *userdata) {
142 _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
143 _cleanup_event_unref_ sd_event *event = NULL;
144 const char *url, *local;
145 _cleanup_free_ char *l = NULL, *ll = NULL;
149 if (!http_url_is_valid(url)) {
150 log_error("URL '%s' is not valid.", url);
157 r = import_url_last_component(url, &l);
159 return log_error_errno(r, "Failed get final component of URL: %m");
164 if (isempty(local) || streq(local, "-"))
168 r = raw_strip_suffixes(local, &ll);
174 if (!machine_name_is_valid(local)) {
175 log_error("Local image name '%s' is not valid.", local);
180 r = image_find(local, NULL);
182 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
184 log_error_errno(EEXIST, "Image '%s' already exists.", local);
189 log_info("Pulling '%s', saving as '%s'.", url, local);
191 log_info("Pulling '%s'.", url);
193 r = sd_event_default(&event);
195 return log_error_errno(r, "Failed to allocate event loop: %m");
197 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
198 sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
199 sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
201 r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
203 return log_error_errno(r, "Failed to allocate puller: %m");
205 r = raw_pull_start(pull, url, local, arg_force, arg_verify);
207 return log_error_errno(r, "Failed to pull image: %m");
209 r = sd_event_loop(event);
211 return log_error_errno(r, "Failed to run event loop: %m");
213 log_info("Exiting.");
217 static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
218 sd_event *event = userdata;
222 log_info("Operation completed successfully.");
224 sd_event_exit(event, abs(error));
227 static int pull_dkr(int argc, char *argv[], void *userdata) {
228 _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
229 _cleanup_event_unref_ sd_event *event = NULL;
230 const char *name, *tag, *local;
233 if (!arg_dkr_index_url) {
234 log_error("Please specify an index URL with --dkr-index-url=");
238 if (arg_verify != IMPORT_VERIFY_NO) {
239 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
243 tag = strchr(argv[1], ':');
245 name = strndupa(argv[1], tag - argv[1]);
252 if (!dkr_name_is_valid(name)) {
253 log_error("Remote name '%s' is not valid.", name);
257 if (!dkr_tag_is_valid(tag)) {
258 log_error("Tag name '%s' is not valid.", tag);
265 local = strchr(name, '/');
272 if (isempty(local) || streq(local, "-"))
276 if (!machine_name_is_valid(local)) {
277 log_error("Local image name '%s' is not valid.", local);
282 r = image_find(local, NULL);
284 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
286 log_error_errno(EEXIST, "Image '%s' already exists.", local);
291 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
293 log_info("Pulling '%s' with tag '%s'.", name, tag);
295 r = sd_event_default(&event);
297 return log_error_errno(r, "Failed to allocate event loop: %m");
299 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
300 sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
301 sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
303 r = dkr_pull_new(&pull, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
305 return log_error_errno(r, "Failed to allocate puller: %m");
307 r = dkr_pull_start(pull, name, tag, local, arg_force);
309 return log_error_errno(r, "Failed to pull image: %m");
311 r = sd_event_loop(event);
313 return log_error_errno(r, "Failed to run event loop: %m");
315 log_info("Exiting.");
319 static int help(int argc, char *argv[], void *userdata) {
321 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
322 "Download container or virtual machine images.\n\n"
323 " -h --help Show this help\n"
324 " --version Show package version\n"
325 " --force Force creation of image\n"
326 " --verify= Verify downloaded image, one of: 'no',\n"
327 " 'checksum', 'signature'.\n"
328 " --image-root=PATH Image root directory\n"
329 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
331 " tar URL [NAME] Download a TAR image\n"
332 " raw URL [NAME] Download a RAW image\n"
333 " dkr REMOTE [NAME] Download a DKR image\n",
334 program_invocation_short_name);
339 static int parse_argv(int argc, char *argv[]) {
349 static const struct option options[] = {
350 { "help", no_argument, NULL, 'h' },
351 { "version", no_argument, NULL, ARG_VERSION },
352 { "force", no_argument, NULL, ARG_FORCE },
353 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
354 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
355 { "verify", required_argument, NULL, ARG_VERIFY },
364 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
369 return help(0, NULL, NULL);
372 puts(PACKAGE_STRING);
373 puts(SYSTEMD_FEATURES);
380 case ARG_DKR_INDEX_URL:
381 if (!http_url_is_valid(optarg)) {
382 log_error("Index URL is not valid: %s", optarg);
386 arg_dkr_index_url = optarg;
390 arg_image_root = optarg;
394 arg_verify = import_verify_from_string(optarg);
395 if (arg_verify < 0) {
396 log_error("Invalid verification setting '%s'", optarg);
406 assert_not_reached("Unhandled option");
412 static int pull_main(int argc, char *argv[]) {
414 static const Verb verbs[] = {
415 { "help", VERB_ANY, VERB_ANY, 0, help },
416 { "tar", 2, 3, 0, pull_tar },
417 { "raw", 2, 3, 0, pull_raw },
418 { "dkr", 2, 3, 0, pull_dkr },
422 return dispatch_verb(argc, argv, verbs, NULL);
425 int main(int argc, char *argv[]) {
428 setlocale(LC_ALL, "");
429 log_parse_environment();
432 r = parse_argv(argc, argv);
436 ignore_signals(SIGPIPE, -1);
438 r = pull_main(argc, argv);
441 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;