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