Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <getopt.h>
4 : #include <locale.h>
5 :
6 : #include "sd-event.h"
7 : #include "sd-id128.h"
8 :
9 : #include "alloc-util.h"
10 : #include "hostname-util.h"
11 : #include "import-util.h"
12 : #include "machine-image.h"
13 : #include "main-func.h"
14 : #include "parse-util.h"
15 : #include "pull-raw.h"
16 : #include "pull-tar.h"
17 : #include "signal-util.h"
18 : #include "string-util.h"
19 : #include "verbs.h"
20 : #include "web-util.h"
21 :
22 : static bool arg_force = false;
23 : static const char *arg_image_root = "/var/lib/machines";
24 : static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
25 : static bool arg_settings = true;
26 : static bool arg_roothash = true;
27 :
28 0 : static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
29 0 : log_notice("Transfer aborted.");
30 0 : sd_event_exit(sd_event_source_get_event(s), EINTR);
31 0 : return 0;
32 : }
33 :
34 0 : static void on_tar_finished(TarPull *pull, int error, void *userdata) {
35 0 : sd_event *event = userdata;
36 0 : assert(pull);
37 :
38 0 : if (error == 0)
39 0 : log_info("Operation completed successfully.");
40 :
41 0 : sd_event_exit(event, abs(error));
42 0 : }
43 :
44 0 : static int pull_tar(int argc, char *argv[], void *userdata) {
45 0 : _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
46 0 : _cleanup_(sd_event_unrefp) sd_event *event = NULL;
47 : const char *url, *local;
48 0 : _cleanup_free_ char *l = NULL, *ll = NULL;
49 : int r;
50 :
51 0 : url = argv[1];
52 0 : if (!http_url_is_valid(url)) {
53 0 : log_error("URL '%s' is not valid.", url);
54 0 : return -EINVAL;
55 : }
56 :
57 0 : if (argc >= 3)
58 0 : local = argv[2];
59 : else {
60 0 : r = import_url_last_component(url, &l);
61 0 : if (r < 0)
62 0 : return log_error_errno(r, "Failed get final component of URL: %m");
63 :
64 0 : local = l;
65 : }
66 :
67 0 : local = empty_or_dash_to_null(local);
68 :
69 0 : if (local) {
70 0 : r = tar_strip_suffixes(local, &ll);
71 0 : if (r < 0)
72 0 : return log_oom();
73 :
74 0 : local = ll;
75 :
76 0 : if (!machine_name_is_valid(local)) {
77 0 : log_error("Local image name '%s' is not valid.", local);
78 0 : return -EINVAL;
79 : }
80 :
81 0 : if (!arg_force) {
82 0 : r = image_find(IMAGE_MACHINE, local, NULL);
83 0 : if (r < 0) {
84 0 : if (r != -ENOENT)
85 0 : return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
86 : } else {
87 0 : log_error("Image '%s' already exists.", local);
88 0 : return -EEXIST;
89 : }
90 : }
91 :
92 0 : log_info("Pulling '%s', saving as '%s'.", url, local);
93 : } else
94 0 : log_info("Pulling '%s'.", url);
95 :
96 0 : r = sd_event_default(&event);
97 0 : if (r < 0)
98 0 : return log_error_errno(r, "Failed to allocate event loop: %m");
99 :
100 0 : assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
101 0 : (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
102 0 : (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
103 :
104 0 : r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
105 0 : if (r < 0)
106 0 : return log_error_errno(r, "Failed to allocate puller: %m");
107 :
108 0 : r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
109 0 : if (r < 0)
110 0 : return log_error_errno(r, "Failed to pull image: %m");
111 :
112 0 : r = sd_event_loop(event);
113 0 : if (r < 0)
114 0 : return log_error_errno(r, "Failed to run event loop: %m");
115 :
116 0 : log_info("Exiting.");
117 0 : return -r;
118 : }
119 :
120 0 : static void on_raw_finished(RawPull *pull, int error, void *userdata) {
121 0 : sd_event *event = userdata;
122 0 : assert(pull);
123 :
124 0 : if (error == 0)
125 0 : log_info("Operation completed successfully.");
126 :
127 0 : sd_event_exit(event, abs(error));
128 0 : }
129 :
130 0 : static int pull_raw(int argc, char *argv[], void *userdata) {
131 0 : _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
132 0 : _cleanup_(sd_event_unrefp) sd_event *event = NULL;
133 : const char *url, *local;
134 0 : _cleanup_free_ char *l = NULL, *ll = NULL;
135 : int r;
136 :
137 0 : url = argv[1];
138 0 : if (!http_url_is_valid(url)) {
139 0 : log_error("URL '%s' is not valid.", url);
140 0 : return -EINVAL;
141 : }
142 :
143 0 : if (argc >= 3)
144 0 : local = argv[2];
145 : else {
146 0 : r = import_url_last_component(url, &l);
147 0 : if (r < 0)
148 0 : return log_error_errno(r, "Failed get final component of URL: %m");
149 :
150 0 : local = l;
151 : }
152 :
153 0 : local = empty_or_dash_to_null(local);
154 :
155 0 : if (local) {
156 0 : r = raw_strip_suffixes(local, &ll);
157 0 : if (r < 0)
158 0 : return log_oom();
159 :
160 0 : local = ll;
161 :
162 0 : if (!machine_name_is_valid(local)) {
163 0 : log_error("Local image name '%s' is not valid.", local);
164 0 : return -EINVAL;
165 : }
166 :
167 0 : if (!arg_force) {
168 0 : r = image_find(IMAGE_MACHINE, local, NULL);
169 0 : if (r < 0) {
170 0 : if (r != -ENOENT)
171 0 : return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
172 : } else {
173 0 : log_error("Image '%s' already exists.", local);
174 0 : return -EEXIST;
175 : }
176 : }
177 :
178 0 : log_info("Pulling '%s', saving as '%s'.", url, local);
179 : } else
180 0 : log_info("Pulling '%s'.", url);
181 :
182 0 : r = sd_event_default(&event);
183 0 : if (r < 0)
184 0 : return log_error_errno(r, "Failed to allocate event loop: %m");
185 :
186 0 : assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
187 0 : (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
188 0 : (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
189 :
190 0 : r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
191 0 : if (r < 0)
192 0 : return log_error_errno(r, "Failed to allocate puller: %m");
193 :
194 0 : r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings, arg_roothash);
195 0 : if (r < 0)
196 0 : return log_error_errno(r, "Failed to pull image: %m");
197 :
198 0 : r = sd_event_loop(event);
199 0 : if (r < 0)
200 0 : return log_error_errno(r, "Failed to run event loop: %m");
201 :
202 0 : log_info("Exiting.");
203 0 : return -r;
204 : }
205 :
206 3 : static int help(int argc, char *argv[], void *userdata) {
207 :
208 3 : printf("%s [OPTIONS...] {COMMAND} ...\n\n"
209 : "Download container or virtual machine images.\n\n"
210 : " -h --help Show this help\n"
211 : " --version Show package version\n"
212 : " --force Force creation of image\n"
213 : " --verify=MODE Verify downloaded image, one of: 'no',\n"
214 : " 'checksum', 'signature'\n"
215 : " --settings=BOOL Download settings file with image\n"
216 : " --roothash=BOOL Download root hash file with image\n"
217 : " --image-root=PATH Image root directory\n\n"
218 : "Commands:\n"
219 : " tar URL [NAME] Download a TAR image\n"
220 : " raw URL [NAME] Download a RAW image\n",
221 : program_invocation_short_name);
222 :
223 3 : return 0;
224 : }
225 :
226 4 : static int parse_argv(int argc, char *argv[]) {
227 :
228 : enum {
229 : ARG_VERSION = 0x100,
230 : ARG_FORCE,
231 : ARG_IMAGE_ROOT,
232 : ARG_VERIFY,
233 : ARG_SETTINGS,
234 : ARG_ROOTHASH,
235 : };
236 :
237 : static const struct option options[] = {
238 : { "help", no_argument, NULL, 'h' },
239 : { "version", no_argument, NULL, ARG_VERSION },
240 : { "force", no_argument, NULL, ARG_FORCE },
241 : { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
242 : { "verify", required_argument, NULL, ARG_VERIFY },
243 : { "settings", required_argument, NULL, ARG_SETTINGS },
244 : { "roothash", required_argument, NULL, ARG_ROOTHASH },
245 : {}
246 : };
247 :
248 : int c, r;
249 :
250 4 : assert(argc >= 0);
251 4 : assert(argv);
252 :
253 4 : while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
254 :
255 4 : switch (c) {
256 :
257 3 : case 'h':
258 3 : return help(0, NULL, NULL);
259 :
260 0 : case ARG_VERSION:
261 0 : return version();
262 :
263 0 : case ARG_FORCE:
264 0 : arg_force = true;
265 0 : break;
266 :
267 0 : case ARG_IMAGE_ROOT:
268 0 : arg_image_root = optarg;
269 0 : break;
270 :
271 0 : case ARG_VERIFY:
272 0 : arg_verify = import_verify_from_string(optarg);
273 0 : if (arg_verify < 0)
274 0 : return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
275 : "Invalid verification setting '%s'", optarg);
276 :
277 0 : break;
278 :
279 0 : case ARG_SETTINGS:
280 0 : r = parse_boolean(optarg);
281 0 : if (r < 0)
282 0 : return log_error_errno(r, "Failed to parse --settings= parameter '%s': %m", optarg);
283 :
284 0 : arg_settings = r;
285 0 : break;
286 :
287 0 : case ARG_ROOTHASH:
288 0 : r = parse_boolean(optarg);
289 0 : if (r < 0)
290 0 : return log_error_errno(r, "Failed to parse --roothash= parameter '%s': %m", optarg);
291 :
292 0 : arg_roothash = r;
293 0 : break;
294 :
295 1 : case '?':
296 1 : return -EINVAL;
297 :
298 0 : default:
299 0 : assert_not_reached("Unhandled option");
300 : }
301 :
302 0 : return 1;
303 : }
304 :
305 0 : static int pull_main(int argc, char *argv[]) {
306 : static const Verb verbs[] = {
307 : { "help", VERB_ANY, VERB_ANY, 0, help },
308 : { "tar", 2, 3, 0, pull_tar },
309 : { "raw", 2, 3, 0, pull_raw },
310 : {}
311 : };
312 :
313 0 : return dispatch_verb(argc, argv, verbs, NULL);
314 : }
315 :
316 4 : static int run(int argc, char *argv[]) {
317 : int r;
318 :
319 4 : setlocale(LC_ALL, "");
320 4 : log_parse_environment();
321 4 : log_open();
322 :
323 4 : r = parse_argv(argc, argv);
324 4 : if (r <= 0)
325 4 : return r;
326 :
327 0 : (void) ignore_signals(SIGPIPE, -1);
328 :
329 0 : return pull_main(argc, argv);
330 : }
331 :
332 4 : DEFINE_MAIN_FUNCTION(run);
|