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.gz");
57 e = endswith(name, ".tar.xz");
59 e = endswith(name, ".tgz");
66 s = strndup(name, e - name);
74 static int pull_tar(int argc, char *argv[], void *userdata) {
75 _cleanup_(tar_import_unrefp) TarImport *import = NULL;
76 _cleanup_event_unref_ sd_event *event = NULL;
77 const char *url, *local;
78 _cleanup_free_ char *l = NULL, *ll = NULL;
82 if (!http_url_is_valid(url)) {
83 log_error("URL '%s' is not valid.", url);
90 r = import_url_last_component(url, &l);
92 return log_error_errno(r, "Failed get final component of URL: %m");
97 if (isempty(local) || streq(local, "-"))
101 r = strip_tar_suffixes(local, &ll);
107 if (!machine_name_is_valid(local)) {
108 log_error("Local image name '%s' is not valid.", local);
113 r = image_find(local, NULL);
115 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
117 log_error_errno(EEXIST, "Image '%s' already exists.", local);
122 log_info("Pulling '%s', saving as '%s'.", url, local);
124 log_info("Pulling '%s'.", url);
126 r = sd_event_default(&event);
128 return log_error_errno(r, "Failed to allocate event loop: %m");
130 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
131 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
132 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
134 r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
136 return log_error_errno(r, "Failed to allocate importer: %m");
138 r = tar_import_pull(import, url, local, arg_force, arg_verify);
140 return log_error_errno(r, "Failed to pull image: %m");
142 r = sd_event_loop(event);
144 return log_error_errno(r, "Failed to run event loop: %m");
146 log_info("Exiting.");
151 static void on_raw_finished(RawImport *import, int error, void *userdata) {
152 sd_event *event = userdata;
156 log_info("Operation completed successfully.");
158 sd_event_exit(event, EXIT_FAILURE);
161 static int strip_raw_suffixes(const char *p, char **ret) {
162 static const char suffixes[] =
170 _cleanup_free_ char *q = NULL;
178 bool changed = false;
180 NULSTR_FOREACH(sfx, suffixes) {
183 e = endswith(q, sfx);
200 static int pull_raw(int argc, char *argv[], void *userdata) {
201 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
202 _cleanup_event_unref_ sd_event *event = NULL;
203 const char *url, *local;
204 _cleanup_free_ char *l = NULL, *ll = NULL;
208 if (!http_url_is_valid(url)) {
209 log_error("URL '%s' is not valid.", url);
216 r = import_url_last_component(url, &l);
218 return log_error_errno(r, "Failed get final component of URL: %m");
223 if (isempty(local) || streq(local, "-"))
227 r = strip_raw_suffixes(local, &ll);
233 if (!machine_name_is_valid(local)) {
234 log_error("Local image name '%s' is not valid.", local);
239 r = image_find(local, NULL);
241 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
243 log_error_errno(EEXIST, "Image '%s' already exists.", local);
248 log_info("Pulling '%s', saving as '%s'.", url, local);
250 log_info("Pulling '%s'.", url);
252 r = sd_event_default(&event);
254 return log_error_errno(r, "Failed to allocate event loop: %m");
256 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
257 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
258 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
260 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
262 return log_error_errno(r, "Failed to allocate importer: %m");
264 r = raw_import_pull(import, url, local, arg_force, arg_verify);
266 return log_error_errno(r, "Failed to pull image: %m");
268 r = sd_event_loop(event);
270 return log_error_errno(r, "Failed to run event loop: %m");
272 log_info("Exiting.");
277 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
278 sd_event *event = userdata;
282 log_info("Operation completed successfully.");
284 log_error_errno(error, "Operation failed: %m");
286 sd_event_exit(event, error);
289 static int pull_dkr(int argc, char *argv[], void *userdata) {
290 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
291 _cleanup_event_unref_ sd_event *event = NULL;
292 const char *name, *tag, *local;
295 if (!arg_dkr_index_url) {
296 log_error("Please specify an index URL with --dkr-index-url=");
300 if (arg_verify != IMPORT_VERIFY_NO) {
301 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
305 tag = strchr(argv[1], ':');
307 name = strndupa(argv[1], tag - argv[1]);
314 if (!dkr_name_is_valid(name)) {
315 log_error("Remote name '%s' is not valid.", name);
319 if (!dkr_tag_is_valid(tag)) {
320 log_error("Tag name '%s' is not valid.", tag);
327 local = strchr(name, '/');
334 if (isempty(local) || streq(local, "-"))
340 if (!machine_name_is_valid(local)) {
341 log_error("Local image name '%s' is not valid.", local);
345 p = strappenda(arg_image_root, "/", local);
346 if (laccess(p, F_OK) >= 0) {
348 log_info("Image '%s' already exists.", local);
351 } else if (errno != ENOENT)
352 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
354 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
356 log_info("Pulling '%s' with tag '%s'.", name, tag);
358 r = sd_event_default(&event);
360 return log_error_errno(r, "Failed to allocate event loop: %m");
362 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
363 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
364 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
366 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
368 return log_error_errno(r, "Failed to allocate importer: %m");
370 r = dkr_import_pull(import, name, tag, local, arg_force);
372 return log_error_errno(r, "Failed to pull image: %m");
374 r = sd_event_loop(event);
376 return log_error_errno(r, "Failed to run event loop: %m");
378 log_info("Exiting.");
383 static int help(int argc, char *argv[], void *userdata) {
385 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
386 "Import container or virtual machine image.\n\n"
387 " -h --help Show this help\n"
388 " --version Show package version\n"
389 " --force Force creation of image\n"
390 " --verify= Verify downloaded image, one of: 'no', 'sum'\n"
392 " --image-root= Image root directory\n"
393 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
395 " pull-tar URL [NAME] Download a TAR image\n"
396 " pull-raw URL [NAME] Download a RAW image\n"
397 " pull-dkr REMOTE [NAME] Download a DKR image\n",
398 program_invocation_short_name);
403 static int parse_argv(int argc, char *argv[]) {
413 static const struct option options[] = {
414 { "help", no_argument, NULL, 'h' },
415 { "version", no_argument, NULL, ARG_VERSION },
416 { "force", no_argument, NULL, ARG_FORCE },
417 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
418 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
419 { "verify", required_argument, NULL, ARG_VERIFY },
428 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
433 return help(0, NULL, NULL);
436 puts(PACKAGE_STRING);
437 puts(SYSTEMD_FEATURES);
444 case ARG_DKR_INDEX_URL:
445 if (!dkr_url_is_valid(optarg)) {
446 log_error("Index URL is not valid: %s", optarg);
450 arg_dkr_index_url = optarg;
454 arg_image_root = optarg;
458 arg_verify = import_verify_from_string(optarg);
459 if (arg_verify < 0) {
460 log_error("Invalid verification setting '%s'", optarg);
470 assert_not_reached("Unhandled option");
476 static int import_main(int argc, char *argv[]) {
478 static const Verb verbs[] = {
479 { "help", VERB_ANY, VERB_ANY, 0, help },
480 { "pull-tar", 2, 3, 0, pull_tar },
481 { "pull-raw", 2, 3, 0, pull_raw },
482 { "pull-dkr", 2, 3, 0, pull_dkr },
486 return dispatch_verb(argc, argv, verbs, NULL);
489 int main(int argc, char *argv[]) {
492 setlocale(LC_ALL, "");
493 log_parse_environment();
496 r = parse_argv(argc, argv);
500 r = import_main(argc, argv);
503 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;