1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
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.
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.
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/>.
25 #include "event-util.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"
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;
39 static void on_tar_finished(TarImport *import, int error, void *userdata) {
40 sd_event *event = userdata;
44 log_info("Operation completed successfully.");
46 sd_event_exit(event, EXIT_FAILURE);
49 static int strip_tar_suffixes(const char *name, char **ret) {
53 e = endswith(name, ".tar");
55 e = endswith(name, ".tar.xz");
57 e = endswith(name, ".tar.gz");
59 e = endswith(name, ".tar.bz2");
61 e = endswith(name, ".tgz");
68 s = strndup(name, e - name);
76 static int pull_tar(int argc, char *argv[], void *userdata) {
77 _cleanup_(tar_import_unrefp) TarImport *import = NULL;
78 _cleanup_event_unref_ sd_event *event = NULL;
79 const char *url, *local;
80 _cleanup_free_ char *l = NULL, *ll = NULL;
84 if (!http_url_is_valid(url)) {
85 log_error("URL '%s' is not valid.", url);
92 r = import_url_last_component(url, &l);
94 return log_error_errno(r, "Failed get final component of URL: %m");
99 if (isempty(local) || streq(local, "-"))
103 r = strip_tar_suffixes(local, &ll);
109 if (!machine_name_is_valid(local)) {
110 log_error("Local image name '%s' is not valid.", local);
115 r = image_find(local, NULL);
117 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
119 log_error_errno(EEXIST, "Image '%s' already exists.", local);
124 log_info("Pulling '%s', saving as '%s'.", url, local);
126 log_info("Pulling '%s'.", url);
128 r = sd_event_default(&event);
130 return log_error_errno(r, "Failed to allocate event loop: %m");
132 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
133 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
134 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
136 r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
138 return log_error_errno(r, "Failed to allocate importer: %m");
140 r = tar_import_pull(import, url, local, arg_force, arg_verify);
142 return log_error_errno(r, "Failed to pull image: %m");
144 r = sd_event_loop(event);
146 return log_error_errno(r, "Failed to run event loop: %m");
148 log_info("Exiting.");
153 static void on_raw_finished(RawImport *import, int error, void *userdata) {
154 sd_event *event = userdata;
158 log_info("Operation completed successfully.");
160 sd_event_exit(event, EXIT_FAILURE);
163 static int strip_raw_suffixes(const char *p, char **ret) {
164 static const char suffixes[] =
173 _cleanup_free_ char *q = NULL;
181 bool changed = false;
183 NULSTR_FOREACH(sfx, suffixes) {
186 e = endswith(q, sfx);
203 static int pull_raw(int argc, char *argv[], void *userdata) {
204 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
205 _cleanup_event_unref_ sd_event *event = NULL;
206 const char *url, *local;
207 _cleanup_free_ char *l = NULL, *ll = NULL;
211 if (!http_url_is_valid(url)) {
212 log_error("URL '%s' is not valid.", url);
219 r = import_url_last_component(url, &l);
221 return log_error_errno(r, "Failed get final component of URL: %m");
226 if (isempty(local) || streq(local, "-"))
230 r = strip_raw_suffixes(local, &ll);
236 if (!machine_name_is_valid(local)) {
237 log_error("Local image name '%s' is not valid.", local);
242 r = image_find(local, NULL);
244 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
246 log_error_errno(EEXIST, "Image '%s' already exists.", local);
251 log_info("Pulling '%s', saving as '%s'.", url, local);
253 log_info("Pulling '%s'.", url);
255 r = sd_event_default(&event);
257 return log_error_errno(r, "Failed to allocate event loop: %m");
259 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
260 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
261 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
263 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
265 return log_error_errno(r, "Failed to allocate importer: %m");
267 r = raw_import_pull(import, url, local, arg_force, arg_verify);
269 return log_error_errno(r, "Failed to pull image: %m");
271 r = sd_event_loop(event);
273 return log_error_errno(r, "Failed to run event loop: %m");
275 log_info("Exiting.");
280 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
281 sd_event *event = userdata;
285 log_info("Operation completed successfully.");
287 log_error_errno(error, "Operation failed: %m");
289 sd_event_exit(event, error);
292 static int pull_dkr(int argc, char *argv[], void *userdata) {
293 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
294 _cleanup_event_unref_ sd_event *event = NULL;
295 const char *name, *tag, *local;
298 if (!arg_dkr_index_url) {
299 log_error("Please specify an index URL with --dkr-index-url=");
303 if (arg_verify != IMPORT_VERIFY_NO) {
304 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
308 tag = strchr(argv[1], ':');
310 name = strndupa(argv[1], tag - argv[1]);
317 if (!dkr_name_is_valid(name)) {
318 log_error("Remote name '%s' is not valid.", name);
322 if (!dkr_tag_is_valid(tag)) {
323 log_error("Tag name '%s' is not valid.", tag);
330 local = strchr(name, '/');
337 if (isempty(local) || streq(local, "-"))
343 if (!machine_name_is_valid(local)) {
344 log_error("Local image name '%s' is not valid.", local);
348 p = strappenda(arg_image_root, "/", local);
349 if (laccess(p, F_OK) >= 0) {
351 log_info("Image '%s' already exists.", local);
354 } else if (errno != ENOENT)
355 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
357 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
359 log_info("Pulling '%s' with tag '%s'.", name, tag);
361 r = sd_event_default(&event);
363 return log_error_errno(r, "Failed to allocate event loop: %m");
365 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
366 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
367 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
369 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
371 return log_error_errno(r, "Failed to allocate importer: %m");
373 r = dkr_import_pull(import, name, tag, local, arg_force);
375 return log_error_errno(r, "Failed to pull image: %m");
377 r = sd_event_loop(event);
379 return log_error_errno(r, "Failed to run event loop: %m");
381 log_info("Exiting.");
386 static int help(int argc, char *argv[], void *userdata) {
388 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
389 "Import container or virtual machine image.\n\n"
390 " -h --help Show this help\n"
391 " --version Show package version\n"
392 " --force Force creation of image\n"
393 " --verify= Verify downloaded image, one of: 'no', 'sum'\n"
395 " --image-root= Image root directory\n"
396 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
398 " pull-tar URL [NAME] Download a TAR image\n"
399 " pull-raw URL [NAME] Download a RAW image\n"
400 " pull-dkr REMOTE [NAME] Download a DKR image\n",
401 program_invocation_short_name);
406 static int parse_argv(int argc, char *argv[]) {
416 static const struct option options[] = {
417 { "help", no_argument, NULL, 'h' },
418 { "version", no_argument, NULL, ARG_VERSION },
419 { "force", no_argument, NULL, ARG_FORCE },
420 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
421 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
422 { "verify", required_argument, NULL, ARG_VERIFY },
431 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
436 return help(0, NULL, NULL);
439 puts(PACKAGE_STRING);
440 puts(SYSTEMD_FEATURES);
447 case ARG_DKR_INDEX_URL:
448 if (!dkr_url_is_valid(optarg)) {
449 log_error("Index URL is not valid: %s", optarg);
453 arg_dkr_index_url = optarg;
457 arg_image_root = optarg;
461 arg_verify = import_verify_from_string(optarg);
462 if (arg_verify < 0) {
463 log_error("Invalid verification setting '%s'", optarg);
473 assert_not_reached("Unhandled option");
479 static int import_main(int argc, char *argv[]) {
481 static const Verb verbs[] = {
482 { "help", VERB_ANY, VERB_ANY, 0, help },
483 { "pull-tar", 2, 3, 0, pull_tar },
484 { "pull-raw", 2, 3, 0, pull_raw },
485 { "pull-dkr", 2, 3, 0, pull_dkr },
489 return dispatch_verb(argc, argv, verbs, NULL);
492 int main(int argc, char *argv[]) {
495 setlocale(LC_ALL, "");
496 log_parse_environment();
499 r = parse_argv(argc, argv);
503 r = import_main(argc, argv);
506 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;