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