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