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