]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/pull.c
Merge pull request #85 from keszybz/selinux-context
[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 "machine-image.h"
30 #include "import-util.h"
31 #include "pull-tar.h"
32 #include "pull-raw.h"
33 #include "pull-dkr.h"
34
35 static bool arg_force = false;
36 static const char *arg_image_root = "/var/lib/machines";
37 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
38 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
39
40 static 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
46 static void on_tar_finished(TarPull *pull, int error, void *userdata) {
47 sd_event *event = userdata;
48 assert(pull);
49
50 if (error == 0)
51 log_info("Operation completed successfully.");
52
53 sd_event_exit(event, abs(error));
54 }
55
56 static int pull_tar(int argc, char *argv[], void *userdata) {
57 _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
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 {
72 r = import_url_last_component(url, &l);
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) {
83 r = tar_strip_suffixes(local, &ll);
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
112 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
113 sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
114 sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
115
116 r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
117 if (r < 0)
118 return log_error_errno(r, "Failed to allocate puller: %m");
119
120 r = tar_pull_start(pull, url, local, arg_force, arg_verify);
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.");
129 return -r;
130 }
131
132 static void on_raw_finished(RawPull *pull, int error, void *userdata) {
133 sd_event *event = userdata;
134 assert(pull);
135
136 if (error == 0)
137 log_info("Operation completed successfully.");
138
139 sd_event_exit(event, abs(error));
140 }
141
142 static int pull_raw(int argc, char *argv[], void *userdata) {
143 _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
144 _cleanup_event_unref_ sd_event *event = NULL;
145 const char *url, *local;
146 _cleanup_free_ char *l = NULL, *ll = NULL;
147 int r;
148
149 url = argv[1];
150 if (!http_url_is_valid(url)) {
151 log_error("URL '%s' is not valid.", url);
152 return -EINVAL;
153 }
154
155 if (argc >= 3)
156 local = argv[2];
157 else {
158 r = import_url_last_component(url, &l);
159 if (r < 0)
160 return log_error_errno(r, "Failed get final component of URL: %m");
161
162 local = l;
163 }
164
165 if (isempty(local) || streq(local, "-"))
166 local = NULL;
167
168 if (local) {
169 r = raw_strip_suffixes(local, &ll);
170 if (r < 0)
171 return log_oom();
172
173 local = ll;
174
175 if (!machine_name_is_valid(local)) {
176 log_error("Local image name '%s' is not valid.", local);
177 return -EINVAL;
178 }
179
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;
187 }
188 }
189
190 log_info("Pulling '%s', saving as '%s'.", url, local);
191 } else
192 log_info("Pulling '%s'.", url);
193
194 r = sd_event_default(&event);
195 if (r < 0)
196 return log_error_errno(r, "Failed to allocate event loop: %m");
197
198 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
199 sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
200 sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
201
202 r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
203 if (r < 0)
204 return log_error_errno(r, "Failed to allocate puller: %m");
205
206 r = raw_pull_start(pull, url, local, arg_force, arg_verify);
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.");
215 return -r;
216 }
217
218 static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
219 sd_event *event = userdata;
220 assert(pull);
221
222 if (error == 0)
223 log_info("Operation completed successfully.");
224
225 sd_event_exit(event, abs(error));
226 }
227
228 static int pull_dkr(int argc, char *argv[], void *userdata) {
229 _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
230 _cleanup_event_unref_ sd_event *event = NULL;
231 const char *name, *reference, *local, *digest;
232 int r;
233
234 if (!arg_dkr_index_url) {
235 log_error("Please specify an index URL with --dkr-index-url=");
236 return -EINVAL;
237 }
238
239 if (arg_verify != IMPORT_VERIFY_NO) {
240 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
241 return -EINVAL;
242 }
243
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++;
254 } else {
255 name = argv[1];
256 reference = "latest";
257 }
258
259 if (!dkr_name_is_valid(name)) {
260 log_error("Remote name '%s' is not valid.", name);
261 return -EINVAL;
262 }
263
264 if (!dkr_ref_is_valid(reference)) {
265 log_error("Tag name '%s' is not valid.", reference);
266 return -EINVAL;
267 }
268
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
279 if (isempty(local) || streq(local, "-"))
280 local = NULL;
281
282 if (local) {
283 if (!machine_name_is_valid(local)) {
284 log_error("Local image name '%s' is not valid.", local);
285 return -EINVAL;
286 }
287
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;
295 }
296 }
297
298 log_info("Pulling '%s' with reference '%s', saving as '%s'.", name, reference, local);
299 } else
300 log_info("Pulling '%s' with reference '%s'.", name, reference);
301
302 r = sd_event_default(&event);
303 if (r < 0)
304 return log_error_errno(r, "Failed to allocate event loop: %m");
305
306 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
307 sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
308 sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
309
310 r = dkr_pull_new(&pull, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
311 if (r < 0)
312 return log_error_errno(r, "Failed to allocate puller: %m");
313
314 r = dkr_pull_start(pull, name, reference, local, arg_force, DKR_PULL_V2);
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.");
323 return -r;
324 }
325
326 static int help(int argc, char *argv[], void *userdata) {
327
328 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
329 "Download container or virtual machine images.\n\n"
330 " -h --help Show this help\n"
331 " --version Show package version\n"
332 " --force Force creation of image\n"
333 " --verify= Verify downloaded image, one of: 'no',\n"
334 " 'checksum', 'signature'.\n"
335 " --image-root=PATH Image root directory\n"
336 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
337 "Commands:\n"
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",
341 program_invocation_short_name);
342
343 return 0;
344 }
345
346 static int parse_argv(int argc, char *argv[]) {
347
348 enum {
349 ARG_VERSION = 0x100,
350 ARG_FORCE,
351 ARG_DKR_INDEX_URL,
352 ARG_IMAGE_ROOT,
353 ARG_VERIFY,
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 },
360 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
361 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
362 { "verify", required_argument, NULL, ARG_VERIFY },
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':
376 return help(0, NULL, NULL);
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
387 case ARG_DKR_INDEX_URL:
388 if (!http_url_is_valid(optarg)) {
389 log_error("Index URL is not valid: %s", optarg);
390 return -EINVAL;
391 }
392
393 arg_dkr_index_url = optarg;
394 break;
395
396 case ARG_IMAGE_ROOT:
397 arg_image_root = optarg;
398 break;
399
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
409 case '?':
410 return -EINVAL;
411
412 default:
413 assert_not_reached("Unhandled option");
414 }
415
416 return 1;
417 }
418
419 static int pull_main(int argc, char *argv[]) {
420
421 static const Verb verbs[] = {
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 },
426 {}
427 };
428
429 return dispatch_verb(argc, argv, verbs, NULL);
430 }
431
432 int 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
443 (void) ignore_signals(SIGPIPE, -1);
444
445 r = pull_main(argc, argv);
446
447 finish:
448 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
449 }