1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2014 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
26 #include "alloc-util.h"
27 #include "hostname-util.h"
28 #include "import-util.h"
29 #include "machine-image.h"
30 #include "parse-util.h"
33 #include "signal-util.h"
34 #include "string-util.h"
38 static bool arg_force
= false;
39 static const char *arg_image_root
= "/var/lib/machines";
40 static ImportVerify arg_verify
= IMPORT_VERIFY_SIGNATURE
;
41 static bool arg_settings
= true;
42 static bool arg_roothash
= true;
44 static int interrupt_signal_handler(sd_event_source
*s
, const struct signalfd_siginfo
*si
, void *userdata
) {
45 log_notice("Transfer aborted.");
46 sd_event_exit(sd_event_source_get_event(s
), EINTR
);
50 static void on_tar_finished(TarPull
*pull
, int error
, void *userdata
) {
51 sd_event
*event
= userdata
;
55 log_info("Operation completed successfully.");
57 sd_event_exit(event
, abs(error
));
60 static int pull_tar(int argc
, char *argv
[], void *userdata
) {
61 _cleanup_(tar_pull_unrefp
) TarPull
*pull
= NULL
;
62 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
63 const char *url
, *local
;
64 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
68 if (!http_url_is_valid(url
)) {
69 log_error("URL '%s' is not valid.", url
);
76 r
= import_url_last_component(url
, &l
);
78 return log_error_errno(r
, "Failed get final component of URL: %m");
83 if (isempty(local
) || streq(local
, "-"))
87 r
= tar_strip_suffixes(local
, &ll
);
93 if (!machine_name_is_valid(local
)) {
94 log_error("Local image name '%s' is not valid.", local
);
99 r
= image_find(local
, NULL
);
101 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
103 log_error("Image '%s' already exists.", local
);
108 log_info("Pulling '%s', saving as '%s'.", url
, local
);
110 log_info("Pulling '%s'.", url
);
112 r
= sd_event_default(&event
);
114 return log_error_errno(r
, "Failed to allocate event loop: %m");
116 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
117 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
118 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
120 r
= tar_pull_new(&pull
, event
, arg_image_root
, on_tar_finished
, event
);
122 return log_error_errno(r
, "Failed to allocate puller: %m");
124 r
= tar_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
);
126 return log_error_errno(r
, "Failed to pull image: %m");
128 r
= sd_event_loop(event
);
130 return log_error_errno(r
, "Failed to run event loop: %m");
132 log_info("Exiting.");
136 static void on_raw_finished(RawPull
*pull
, int error
, void *userdata
) {
137 sd_event
*event
= userdata
;
141 log_info("Operation completed successfully.");
143 sd_event_exit(event
, abs(error
));
146 static int pull_raw(int argc
, char *argv
[], void *userdata
) {
147 _cleanup_(raw_pull_unrefp
) RawPull
*pull
= NULL
;
148 _cleanup_(sd_event_unrefp
) sd_event
*event
= NULL
;
149 const char *url
, *local
;
150 _cleanup_free_
char *l
= NULL
, *ll
= NULL
;
154 if (!http_url_is_valid(url
)) {
155 log_error("URL '%s' is not valid.", url
);
162 r
= import_url_last_component(url
, &l
);
164 return log_error_errno(r
, "Failed get final component of URL: %m");
169 if (isempty(local
) || streq(local
, "-"))
173 r
= raw_strip_suffixes(local
, &ll
);
179 if (!machine_name_is_valid(local
)) {
180 log_error("Local image name '%s' is not valid.", local
);
185 r
= image_find(local
, NULL
);
187 return log_error_errno(r
, "Failed to check whether image '%s' exists: %m", local
);
189 log_error("Image '%s' already exists.", local
);
194 log_info("Pulling '%s', saving as '%s'.", url
, local
);
196 log_info("Pulling '%s'.", url
);
198 r
= sd_event_default(&event
);
200 return log_error_errno(r
, "Failed to allocate event loop: %m");
202 assert_se(sigprocmask_many(SIG_BLOCK
, NULL
, SIGTERM
, SIGINT
, -1) >= 0);
203 (void) sd_event_add_signal(event
, NULL
, SIGTERM
, interrupt_signal_handler
, NULL
);
204 (void) sd_event_add_signal(event
, NULL
, SIGINT
, interrupt_signal_handler
, NULL
);
206 r
= raw_pull_new(&pull
, event
, arg_image_root
, on_raw_finished
, event
);
208 return log_error_errno(r
, "Failed to allocate puller: %m");
210 r
= raw_pull_start(pull
, url
, local
, arg_force
, arg_verify
, arg_settings
, arg_roothash
);
212 return log_error_errno(r
, "Failed to pull image: %m");
214 r
= sd_event_loop(event
);
216 return log_error_errno(r
, "Failed to run event loop: %m");
218 log_info("Exiting.");
222 static int help(int argc
, char *argv
[], void *userdata
) {
224 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
225 "Download container or virtual machine images.\n\n"
226 " -h --help Show this help\n"
227 " --version Show package version\n"
228 " --force Force creation of image\n"
229 " --verify=MODE Verify downloaded image, one of: 'no',\n"
230 " 'checksum', 'signature'\n"
231 " --settings=BOOL Download settings file with image\n"
232 " --roothash=BOOL Download root hash file with image\n"
233 " --image-root=PATH Image root directory\n\n"
235 " tar URL [NAME] Download a TAR image\n"
236 " raw URL [NAME] Download a RAW image\n",
237 program_invocation_short_name
);
242 static int parse_argv(int argc
, char *argv
[]) {
253 static const struct option options
[] = {
254 { "help", no_argument
, NULL
, 'h' },
255 { "version", no_argument
, NULL
, ARG_VERSION
},
256 { "force", no_argument
, NULL
, ARG_FORCE
},
257 { "image-root", required_argument
, NULL
, ARG_IMAGE_ROOT
},
258 { "verify", required_argument
, NULL
, ARG_VERIFY
},
259 { "settings", required_argument
, NULL
, ARG_SETTINGS
},
260 { "roothash", required_argument
, NULL
, ARG_ROOTHASH
},
269 while ((c
= getopt_long(argc
, argv
, "h", options
, NULL
)) >= 0)
274 return help(0, NULL
, NULL
);
284 arg_image_root
= optarg
;
288 arg_verify
= import_verify_from_string(optarg
);
289 if (arg_verify
< 0) {
290 log_error("Invalid verification setting '%s'", optarg
);
297 r
= parse_boolean(optarg
);
299 return log_error_errno(r
, "Failed to parse --settings= parameter '%s'", optarg
);
305 r
= parse_boolean(optarg
);
307 return log_error_errno(r
, "Failed to parse --roothash= parameter '%s'", optarg
);
316 assert_not_reached("Unhandled option");
322 static int pull_main(int argc
, char *argv
[]) {
324 static const Verb verbs
[] = {
325 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
326 { "tar", 2, 3, 0, pull_tar
},
327 { "raw", 2, 3, 0, pull_raw
},
331 return dispatch_verb(argc
, argv
, verbs
, NULL
);
334 int main(int argc
, char *argv
[]) {
337 setlocale(LC_ALL
, "");
338 log_parse_environment();
341 r
= parse_argv(argc
, argv
);
345 (void) ignore_signals(SIGPIPE
, -1);
347 r
= pull_main(argc
, argv
);
350 return r
< 0 ? EXIT_FAILURE
: EXIT_SUCCESS
;