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