]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/pull.c
Merge pull request #1654 from poettering/util-lib
[thirdparty/systemd.git] / src / import / pull.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <getopt.h>
23
24 #include "sd-event.h"
25
26 #include "event-util.h"
27 #include "hostname-util.h"
28 #include "import-util.h"
29 #include "machine-image.h"
30 #include "pull-dkr.h"
31 #include "pull-raw.h"
32 #include "pull-tar.h"
33 #include "signal-util.h"
34 #include "string-util.h"
35 #include "verbs.h"
36
37 static bool arg_force = false;
38 static const char *arg_image_root = "/var/lib/machines";
39 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
40 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
41 static bool arg_settings = true;
42
43 static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
44 log_notice("Transfer aborted.");
45 sd_event_exit(sd_event_source_get_event(s), EINTR);
46 return 0;
47 }
48
49 static void on_tar_finished(TarPull *pull, int error, void *userdata) {
50 sd_event *event = userdata;
51 assert(pull);
52
53 if (error == 0)
54 log_info("Operation completed successfully.");
55
56 sd_event_exit(event, abs(error));
57 }
58
59 static int pull_tar(int argc, char *argv[], void *userdata) {
60 _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
61 _cleanup_event_unref_ sd_event *event = NULL;
62 const char *url, *local;
63 _cleanup_free_ char *l = NULL, *ll = NULL;
64 int r;
65
66 url = argv[1];
67 if (!http_url_is_valid(url)) {
68 log_error("URL '%s' is not valid.", url);
69 return -EINVAL;
70 }
71
72 if (argc >= 3)
73 local = argv[2];
74 else {
75 r = import_url_last_component(url, &l);
76 if (r < 0)
77 return log_error_errno(r, "Failed get final component of URL: %m");
78
79 local = l;
80 }
81
82 if (isempty(local) || streq(local, "-"))
83 local = NULL;
84
85 if (local) {
86 r = tar_strip_suffixes(local, &ll);
87 if (r < 0)
88 return log_oom();
89
90 local = ll;
91
92 if (!machine_name_is_valid(local)) {
93 log_error("Local image name '%s' is not valid.", local);
94 return -EINVAL;
95 }
96
97 if (!arg_force) {
98 r = image_find(local, NULL);
99 if (r < 0)
100 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
101 else if (r > 0) {
102 log_error_errno(EEXIST, "Image '%s' already exists.", local);
103 return -EEXIST;
104 }
105 }
106
107 log_info("Pulling '%s', saving as '%s'.", url, local);
108 } else
109 log_info("Pulling '%s'.", url);
110
111 r = sd_event_default(&event);
112 if (r < 0)
113 return log_error_errno(r, "Failed to allocate event loop: %m");
114
115 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
116 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
117 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
118
119 r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
120 if (r < 0)
121 return log_error_errno(r, "Failed to allocate puller: %m");
122
123 r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
124 if (r < 0)
125 return log_error_errno(r, "Failed to pull image: %m");
126
127 r = sd_event_loop(event);
128 if (r < 0)
129 return log_error_errno(r, "Failed to run event loop: %m");
130
131 log_info("Exiting.");
132 return -r;
133 }
134
135 static void on_raw_finished(RawPull *pull, int error, void *userdata) {
136 sd_event *event = userdata;
137 assert(pull);
138
139 if (error == 0)
140 log_info("Operation completed successfully.");
141
142 sd_event_exit(event, abs(error));
143 }
144
145 static int pull_raw(int argc, char *argv[], void *userdata) {
146 _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
147 _cleanup_event_unref_ sd_event *event = NULL;
148 const char *url, *local;
149 _cleanup_free_ char *l = NULL, *ll = NULL;
150 int r;
151
152 url = argv[1];
153 if (!http_url_is_valid(url)) {
154 log_error("URL '%s' is not valid.", url);
155 return -EINVAL;
156 }
157
158 if (argc >= 3)
159 local = argv[2];
160 else {
161 r = import_url_last_component(url, &l);
162 if (r < 0)
163 return log_error_errno(r, "Failed get final component of URL: %m");
164
165 local = l;
166 }
167
168 if (isempty(local) || streq(local, "-"))
169 local = NULL;
170
171 if (local) {
172 r = raw_strip_suffixes(local, &ll);
173 if (r < 0)
174 return log_oom();
175
176 local = ll;
177
178 if (!machine_name_is_valid(local)) {
179 log_error("Local image name '%s' is not valid.", local);
180 return -EINVAL;
181 }
182
183 if (!arg_force) {
184 r = image_find(local, NULL);
185 if (r < 0)
186 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
187 else if (r > 0) {
188 log_error_errno(EEXIST, "Image '%s' already exists.", local);
189 return -EEXIST;
190 }
191 }
192
193 log_info("Pulling '%s', saving as '%s'.", url, local);
194 } else
195 log_info("Pulling '%s'.", url);
196
197 r = sd_event_default(&event);
198 if (r < 0)
199 return log_error_errno(r, "Failed to allocate event loop: %m");
200
201 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
202 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
203 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
204
205 r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
206 if (r < 0)
207 return log_error_errno(r, "Failed to allocate puller: %m");
208
209 r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
210 if (r < 0)
211 return log_error_errno(r, "Failed to pull image: %m");
212
213 r = sd_event_loop(event);
214 if (r < 0)
215 return log_error_errno(r, "Failed to run event loop: %m");
216
217 log_info("Exiting.");
218 return -r;
219 }
220
221 static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
222 sd_event *event = userdata;
223 assert(pull);
224
225 if (error == 0)
226 log_info("Operation completed successfully.");
227
228 sd_event_exit(event, abs(error));
229 }
230
231 static int pull_dkr(int argc, char *argv[], void *userdata) {
232 _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
233 _cleanup_event_unref_ sd_event *event = NULL;
234 const char *name, *reference, *local, *digest;
235 int r;
236
237 if (!arg_dkr_index_url) {
238 log_error("Please specify an index URL with --dkr-index-url=");
239 return -EINVAL;
240 }
241
242 if (arg_verify != IMPORT_VERIFY_NO) {
243 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
244 return -EINVAL;
245 }
246
247 digest = strchr(argv[1], '@');
248 if (digest) {
249 reference = digest + 1;
250 name = strndupa(argv[1], digest - argv[1]);
251 } else {
252 reference = strchr(argv[1], ':');
253 if (reference) {
254 name = strndupa(argv[1], reference - argv[1]);
255 reference++;
256 } else {
257 name = argv[1];
258 reference = "latest";
259 }
260 }
261
262 if (!dkr_name_is_valid(name)) {
263 log_error("Remote name '%s' is not valid.", name);
264 return -EINVAL;
265 }
266
267 if (!dkr_ref_is_valid(reference)) {
268 log_error("Tag name '%s' is not valid.", reference);
269 return -EINVAL;
270 }
271
272 if (argc >= 3)
273 local = argv[2];
274 else {
275 local = strchr(name, '/');
276 if (local)
277 local++;
278 else
279 local = name;
280 }
281
282 if (isempty(local) || streq(local, "-"))
283 local = NULL;
284
285 if (local) {
286 if (!machine_name_is_valid(local)) {
287 log_error("Local image name '%s' is not valid.", local);
288 return -EINVAL;
289 }
290
291 if (!arg_force) {
292 r = image_find(local, NULL);
293 if (r < 0)
294 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
295 else if (r > 0) {
296 log_error_errno(EEXIST, "Image '%s' already exists.", local);
297 return -EEXIST;
298 }
299 }
300
301 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name, reference, local);
302 } else
303 log_info("Pulling '%s' with reference '%s'.", name, reference);
304
305 r = sd_event_default(&event);
306 if (r < 0)
307 return log_error_errno(r, "Failed to allocate event loop: %m");
308
309 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
310 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
311 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
312
313 r = dkr_pull_new(&pull, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
314 if (r < 0)
315 return log_error_errno(r, "Failed to allocate puller: %m");
316
317 r = dkr_pull_start(pull, name, reference, local, arg_force, DKR_PULL_V2);
318 if (r < 0)
319 return log_error_errno(r, "Failed to pull image: %m");
320
321 r = sd_event_loop(event);
322 if (r < 0)
323 return log_error_errno(r, "Failed to run event loop: %m");
324
325 log_info("Exiting.");
326 return -r;
327 }
328
329 static int help(int argc, char *argv[], void *userdata) {
330
331 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
332 "Download container or virtual machine images.\n\n"
333 " -h --help Show this help\n"
334 " --version Show package version\n"
335 " --force Force creation of image\n"
336 " --verify=MODE Verify downloaded image, one of: 'no',\n"
337 " 'checksum', 'signature'\n"
338 " --settings=BOOL Download settings file with image\n"
339 " --image-root=PATH Image root directory\n"
340 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
341 "Commands:\n"
342 " tar URL [NAME] Download a TAR image\n"
343 " raw URL [NAME] Download a RAW image\n"
344 " dkr REMOTE [NAME] Download a DKR image\n",
345 program_invocation_short_name);
346
347 return 0;
348 }
349
350 static int parse_argv(int argc, char *argv[]) {
351
352 enum {
353 ARG_VERSION = 0x100,
354 ARG_FORCE,
355 ARG_DKR_INDEX_URL,
356 ARG_IMAGE_ROOT,
357 ARG_VERIFY,
358 ARG_SETTINGS,
359 };
360
361 static const struct option options[] = {
362 { "help", no_argument, NULL, 'h' },
363 { "version", no_argument, NULL, ARG_VERSION },
364 { "force", no_argument, NULL, ARG_FORCE },
365 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
366 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
367 { "verify", required_argument, NULL, ARG_VERIFY },
368 { "settings", required_argument, NULL, ARG_SETTINGS },
369 {}
370 };
371
372 int c, r;
373
374 assert(argc >= 0);
375 assert(argv);
376
377 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
378
379 switch (c) {
380
381 case 'h':
382 return help(0, NULL, NULL);
383
384 case ARG_VERSION:
385 return version();
386
387 case ARG_FORCE:
388 arg_force = true;
389 break;
390
391 case ARG_DKR_INDEX_URL:
392 if (!http_url_is_valid(optarg)) {
393 log_error("Index URL is not valid: %s", optarg);
394 return -EINVAL;
395 }
396
397 arg_dkr_index_url = optarg;
398 break;
399
400 case ARG_IMAGE_ROOT:
401 arg_image_root = optarg;
402 break;
403
404 case ARG_VERIFY:
405 arg_verify = import_verify_from_string(optarg);
406 if (arg_verify < 0) {
407 log_error("Invalid verification setting '%s'", optarg);
408 return -EINVAL;
409 }
410
411 break;
412
413 case ARG_SETTINGS:
414 r = parse_boolean(optarg);
415 if (r < 0)
416 return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg);
417
418 arg_settings = r;
419 break;
420
421 case '?':
422 return -EINVAL;
423
424 default:
425 assert_not_reached("Unhandled option");
426 }
427
428 return 1;
429 }
430
431 static int pull_main(int argc, char *argv[]) {
432
433 static const Verb verbs[] = {
434 { "help", VERB_ANY, VERB_ANY, 0, help },
435 { "tar", 2, 3, 0, pull_tar },
436 { "raw", 2, 3, 0, pull_raw },
437 { "dkr", 2, 3, 0, pull_dkr },
438 {}
439 };
440
441 return dispatch_verb(argc, argv, verbs, NULL);
442 }
443
444 int main(int argc, char *argv[]) {
445 int r;
446
447 setlocale(LC_ALL, "");
448 log_parse_environment();
449 log_open();
450
451 r = parse_argv(argc, argv);
452 if (r <= 0)
453 goto finish;
454
455 (void) ignore_signals(SIGPIPE, -1);
456
457 r = pull_main(argc, argv);
458
459 finish:
460 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
461 }