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 log_error_errno(error, "Operation failed: %m");
48 sd_event_exit(event, error);
51 static int strip_tar_suffixes(const char *name, char **ret) {
55 e = endswith(name, ".tar");
57 e = endswith(name, ".tar.gz");
59 e = endswith(name, ".tar.xz");
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);
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 log_error_errno(error, "Operation failed: %m");
162 sd_event_exit(event, error);
165 static int strip_raw_suffixes(const char *p, char **ret) {
166 static const char suffixes[] =
172 _cleanup_free_ char *q = NULL;
180 bool changed = false;
182 NULSTR_FOREACH(sfx, suffixes) {
185 e = endswith(q, sfx);
202 static int pull_raw(int argc, char *argv[], void *userdata) {
203 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
204 _cleanup_event_unref_ sd_event *event = NULL;
205 const char *url, *local;
206 _cleanup_free_ char *l = NULL, *ll = NULL;
210 if (!http_url_is_valid(url)) {
211 log_error("URL '%s' is not valid.", url);
218 r = import_url_last_component(url, &l);
220 return log_error_errno(r, "Failed get final component of URL: %m");
225 if (isempty(local) || streq(local, "-"))
229 r = strip_raw_suffixes(local, &ll);
235 if (!machine_name_is_valid(local)) {
236 log_error("Local image name '%s' is not valid.", local);
241 r = image_find(local, NULL);
243 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
245 log_error_errno(EEXIST, "Image '%s' already exists.", local);
250 log_info("Pulling '%s', saving as '%s'.", url, local);
252 log_info("Pulling '%s'.", url);
254 r = sd_event_default(&event);
256 return log_error_errno(r, "Failed to allocate event loop: %m");
258 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
259 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
260 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
262 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
264 return log_error_errno(r, "Failed to allocate importer: %m");
266 r = raw_import_pull(import, url, local, arg_force, arg_verify);
268 return log_error_errno(r, "Failed to pull image: %m");
270 r = sd_event_loop(event);
272 return log_error_errno(r, "Failed to run event loop: %m");
274 log_info("Exiting.");
279 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
280 sd_event *event = userdata;
284 log_info("Operation completed successfully.");
286 log_error_errno(error, "Operation failed: %m");
288 sd_event_exit(event, error);
291 static int pull_dkr(int argc, char *argv[], void *userdata) {
292 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
293 _cleanup_event_unref_ sd_event *event = NULL;
294 const char *name, *tag, *local;
297 if (!arg_dkr_index_url) {
298 log_error("Please specify an index URL with --dkr-index-url=");
302 if (arg_verify != IMPORT_VERIFY_NO) {
303 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
307 tag = strchr(argv[1], ':');
309 name = strndupa(argv[1], tag - argv[1]);
316 if (!dkr_name_is_valid(name)) {
317 log_error("Remote name '%s' is not valid.", name);
321 if (!dkr_tag_is_valid(tag)) {
322 log_error("Tag name '%s' is not valid.", tag);
329 local = strchr(name, '/');
336 if (isempty(local) || streq(local, "-"))
342 if (!machine_name_is_valid(local)) {
343 log_error("Local image name '%s' is not valid.", local);
347 p = strappenda(arg_image_root, "/", local);
348 if (laccess(p, F_OK) >= 0) {
350 log_info("Image '%s' already exists.", local);
353 } else if (errno != ENOENT)
354 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
356 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
358 log_info("Pulling '%s' with tag '%s'.", name, tag);
360 r = sd_event_default(&event);
362 return log_error_errno(r, "Failed to allocate event loop: %m");
364 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
365 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
366 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
368 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
370 return log_error_errno(r, "Failed to allocate importer: %m");
372 r = dkr_import_pull(import, name, tag, local, arg_force);
374 return log_error_errno(r, "Failed to pull image: %m");
376 r = sd_event_loop(event);
378 return log_error_errno(r, "Failed to run event loop: %m");
380 log_info("Exiting.");
385 static int help(int argc, char *argv[], void *userdata) {
387 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
388 "Import container or virtual machine image.\n\n"
389 " -h --help Show this help\n"
390 " --version Show package version\n"
391 " --force Force creation of image\n"
392 " --verify= Verify downloaded image, one of: 'no', 'sum'\n"
394 " --image-root= Image root directory\n"
395 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
397 " pull-tar URL [NAME] Download a TAR image\n"
398 " pull-raw URL [NAME] Download a RAW image\n"
399 " pull-dkr REMOTE [NAME] Download a DKR image\n",
400 program_invocation_short_name);
405 static int parse_argv(int argc, char *argv[]) {
415 static const struct option options[] = {
416 { "help", no_argument, NULL, 'h' },
417 { "version", no_argument, NULL, ARG_VERSION },
418 { "force", no_argument, NULL, ARG_FORCE },
419 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
420 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
421 { "verify", required_argument, NULL, ARG_VERIFY },
430 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
435 return help(0, NULL, NULL);
438 puts(PACKAGE_STRING);
439 puts(SYSTEMD_FEATURES);
446 case ARG_DKR_INDEX_URL:
447 if (!dkr_url_is_valid(optarg)) {
448 log_error("Index URL is not valid: %s", optarg);
452 arg_dkr_index_url = optarg;
456 arg_image_root = optarg;
460 arg_verify = import_verify_from_string(optarg);
461 if (arg_verify < 0) {
462 log_error("Invalid verification setting '%s'", optarg);
472 assert_not_reached("Unhandled option");
478 static int import_main(int argc, char *argv[]) {
480 static const Verb verbs[] = {
481 { "help", VERB_ANY, VERB_ANY, 0, help },
482 { "pull-tar", 2, 3, 0, pull_tar },
483 { "pull-raw", 2, 3, 0, pull_raw },
484 { "pull-dkr", 2, 3, 0, pull_dkr },
488 return dispatch_verb(argc, argv, verbs, NULL);
491 int main(int argc, char *argv[]) {
494 setlocale(LC_ALL, "");
495 log_parse_environment();
498 r = parse_argv(argc, argv);
502 r = import_main(argc, argv);
505 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;