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