Branch data 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 : 12 : static int help(int argc, char *argv[], void *userdata) {
207 : :
208 : 12 : 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 : 12 : return 0;
224 : : }
225 : :
226 : 16 : 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 [ - + ]: 16 : assert(argc >= 0);
251 [ - + ]: 16 : assert(argv);
252 : :
253 [ + - ]: 16 : while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
254 : :
255 [ + - - - : 16 : switch (c) {
- - - +
- ]
256 : :
257 : 12 : case 'h':
258 : 12 : 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 : 4 : case '?':
296 : 4 : 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 : 16 : static int run(int argc, char *argv[]) {
317 : : int r;
318 : :
319 : 16 : setlocale(LC_ALL, "");
320 : 16 : log_parse_environment();
321 : 16 : log_open();
322 : :
323 : 16 : r = parse_argv(argc, argv);
324 [ + - ]: 16 : if (r <= 0)
325 : 16 : return r;
326 : :
327 : 0 : (void) ignore_signals(SIGPIPE, -1);
328 : :
329 : 0 : return pull_main(argc, argv);
330 : : }
331 : :
332 : 16 : DEFINE_MAIN_FUNCTION(run);
|