import.c revision ff2670ad11a1a29d483b7a246c95b159ddc1137c
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering/***
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering This file is part of systemd.
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering Copyright 2014 Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering systemd is free software; you can redistribute it and/or modify it
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering under the terms of the GNU Lesser General Public License as published by
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering (at your option) any later version.
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering systemd is distributed in the hope that it will be useful, but
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering Lesser General Public License for more details.
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering You should have received a copy of the GNU Lesser General Public License
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering***/
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog#include <getopt.h>
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering#include "sd-event.h"
4871690d9e32608bbd9b18505b5326c2079c9690Allin Cottrell#include "event-util.h"
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering#include "verbs.h"
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog#include "build.h"
d025f1e4dca8fc1436aff76f9e6185fe3e728daaZbigniew Jędrzejewski-Szmek#include "machine-image.h"
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering#include "import-tar.h"
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering#include "import-raw.h"
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog#include "import-dkr.h"
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog#include "import-util.h"
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskogstatic bool arg_force = false;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskogstatic const char *arg_image_root = "/var/lib/machines";
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskogstatic ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskogstatic const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskogstatic void on_tar_finished(TarImport *import, int error, void *userdata) {
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog sd_event *event = userdata;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog assert(import);
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog if (error == 0)
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog log_info("Operation completed successfully.");
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering sd_event_exit(event, EXIT_FAILURE);
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering}
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poetteringstatic int strip_tar_suffixes(const char *name, char **ret) {
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering const char *e;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering char *s;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog e = endswith(name, ".tar");
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (!e)
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog e = endswith(name, ".tar.xz");
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog if (!e)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering e = endswith(name, ".tar.gz");
fb4729006a7174472e8a435b0887e532cd6217fcZbigniew Jędrzejewski-Szmek if (!e)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering e = endswith(name, ".tar.bz2");
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (!e)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering e = endswith(name, ".tgz");
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (!e)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering e = strchr(name, 0);
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (e <= name)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering return -EINVAL;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog s = strndup(name, e - name);
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog if (!s)
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog return -ENOMEM;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog *ret = s;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog return 0;
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog}
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskogstatic int pull_tar(int argc, char *argv[], void *userdata) {
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering _cleanup_(tar_import_unrefp) TarImport *import = NULL;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering _cleanup_event_unref_ sd_event *event = NULL;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering const char *url, *local;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering _cleanup_free_ char *l = NULL, *ll = NULL;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering int r;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering url = argv[1];
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (!http_url_is_valid(url)) {
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering log_error("URL '%s' is not valid.", url);
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering return -EINVAL;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering }
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (argc >= 3)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering local = argv[2];
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering else {
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering r = import_url_last_component(url, &l);
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (r < 0)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering return log_error_errno(r, "Failed get final component of URL: %m");
ad79565d6b37bcc93cf773a39b975e5b85d122daUmut Tezduyar Lindskog
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering local = l;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering }
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (isempty(local) || streq(local, "-"))
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering local = NULL;
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (local) {
f5f6d0e25574dd63fb605b81fa7767dd71c454dbDaniel Buch r = strip_tar_suffixes(local, &ll);
fb4729006a7174472e8a435b0887e532cd6217fcZbigniew Jędrzejewski-Szmek if (r < 0)
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering return log_oom();
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering local = ll;
f5f6d0e25574dd63fb605b81fa7767dd71c454dbDaniel Buch
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering if (!machine_name_is_valid(local)) {
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering log_error("Local image name '%s' is not valid.", local);
3b7124a8db56ed57525b9ecfd19cfdc8c9facba0Lennart Poettering return -EINVAL;
}
if (!arg_force) {
r = image_find(local, NULL);
if (r < 0)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
else if (r > 0) {
log_error_errno(EEXIST, "Image '%s' already exists.", local);
return -EEXIST;
}
}
log_info("Pulling '%s', saving as '%s'.", url, local);
} else
log_info("Pulling '%s'.", url);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate importer: %m");
r = tar_import_pull(import, url, local, arg_force, arg_verify);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
log_info("Exiting.");
return r;
}
static void on_raw_finished(RawImport *import, int error, void *userdata) {
sd_event *event = userdata;
assert(import);
if (error == 0)
log_info("Operation completed successfully.");
sd_event_exit(event, EXIT_FAILURE);
}
static int strip_raw_suffixes(const char *p, char **ret) {
static const char suffixes[] =
".xz\0"
".gz\0"
".bz2\0"
".raw\0"
".qcow2\0"
".img\0"
".bin\0";
_cleanup_free_ char *q = NULL;
q = strdup(p);
if (!q)
return -ENOMEM;
for (;;) {
const char *sfx;
bool changed = false;
NULSTR_FOREACH(sfx, suffixes) {
char *e;
e = endswith(q, sfx);
if (e) {
*e = 0;
changed = true;
}
}
if (!changed)
break;
}
*ret = q;
q = NULL;
return 0;
}
static int pull_raw(int argc, char *argv[], void *userdata) {
_cleanup_(raw_import_unrefp) RawImport *import = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
const char *url, *local;
_cleanup_free_ char *l = NULL, *ll = NULL;
int r;
url = argv[1];
if (!http_url_is_valid(url)) {
log_error("URL '%s' is not valid.", url);
return -EINVAL;
}
if (argc >= 3)
local = argv[2];
else {
r = import_url_last_component(url, &l);
if (r < 0)
return log_error_errno(r, "Failed get final component of URL: %m");
local = l;
}
if (isempty(local) || streq(local, "-"))
local = NULL;
if (local) {
r = strip_raw_suffixes(local, &ll);
if (r < 0)
return log_oom();
local = ll;
if (!machine_name_is_valid(local)) {
log_error("Local image name '%s' is not valid.", local);
return -EINVAL;
}
if (!arg_force) {
r = image_find(local, NULL);
if (r < 0)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
else if (r > 0) {
log_error_errno(EEXIST, "Image '%s' already exists.", local);
return -EEXIST;
}
}
log_info("Pulling '%s', saving as '%s'.", url, local);
} else
log_info("Pulling '%s'.", url);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate importer: %m");
r = raw_import_pull(import, url, local, arg_force, arg_verify);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
log_info("Exiting.");
return r;
}
static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
sd_event *event = userdata;
assert(import);
if (error == 0)
log_info("Operation completed successfully.");
sd_event_exit(event, EXIT_FAILURE);
}
static int pull_dkr(int argc, char *argv[], void *userdata) {
_cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
const char *name, *tag, *local;
int r;
if (!arg_dkr_index_url) {
log_error("Please specify an index URL with --dkr-index-url=");
return -EINVAL;
}
if (arg_verify != IMPORT_VERIFY_NO) {
log_error("Imports from dkr do not support image verification, please pass --verify=no.");
return -EINVAL;
}
tag = strchr(argv[1], ':');
if (tag) {
name = strndupa(argv[1], tag - argv[1]);
tag++;
} else {
name = argv[1];
tag = "latest";
}
if (!dkr_name_is_valid(name)) {
log_error("Remote name '%s' is not valid.", name);
return -EINVAL;
}
if (!dkr_tag_is_valid(tag)) {
log_error("Tag name '%s' is not valid.", tag);
return -EINVAL;
}
if (argc >= 3)
local = argv[2];
else {
local = strchr(name, '/');
if (local)
local++;
else
local = name;
}
if (isempty(local) || streq(local, "-"))
local = NULL;
if (local) {
if (!machine_name_is_valid(local)) {
log_error("Local image name '%s' is not valid.", local);
return -EINVAL;
}
if (!arg_force) {
r = image_find(local, NULL);
if (r < 0)
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
else if (r > 0) {
log_error_errno(EEXIST, "Image '%s' already exists.", local);
return -EEXIST;
}
}
log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
} else
log_info("Pulling '%s' with tag '%s'.", name, tag);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
if (r < 0)
return log_error_errno(r, "Failed to allocate importer: %m");
r = dkr_import_pull(import, name, tag, local, arg_force);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
r = sd_event_loop(event);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
log_info("Exiting.");
return 0;
}
static int help(int argc, char *argv[], void *userdata) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Import container or virtual machine image.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --force Force creation of image\n"
" --verify= Verify downloaded image, one of: 'no', 'sum'\n"
" 'signature'.\n"
" --image-root= Image root directory\n"
" --dkr-index-url=URL Specify index URL to use for downloads\n\n"
"Commands:\n"
" pull-tar URL [NAME] Download a TAR image\n"
" pull-raw URL [NAME] Download a RAW image\n"
" pull-dkr REMOTE [NAME] Download a DKR image\n",
program_invocation_short_name);
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FORCE,
ARG_DKR_INDEX_URL,
ARG_IMAGE_ROOT,
ARG_VERIFY,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "force", no_argument, NULL, ARG_FORCE },
{ "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
{ "verify", required_argument, NULL, ARG_VERIFY },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) {
case 'h':
return help(0, NULL, NULL);
case ARG_VERSION:
puts(PACKAGE_STRING);
puts(SYSTEMD_FEATURES);
return 0;
case ARG_FORCE:
arg_force = true;
break;
case ARG_DKR_INDEX_URL:
if (!http_url_is_valid(optarg)) {
log_error("Index URL is not valid: %s", optarg);
return -EINVAL;
}
arg_dkr_index_url = optarg;
break;
case ARG_IMAGE_ROOT:
arg_image_root = optarg;
break;
case ARG_VERIFY:
arg_verify = import_verify_from_string(optarg);
if (arg_verify < 0) {
log_error("Invalid verification setting '%s'", optarg);
return -EINVAL;
}
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
return 1;
}
static int import_main(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "help", VERB_ANY, VERB_ANY, 0, help },
{ "pull-tar", 2, 3, 0, pull_tar },
{ "pull-raw", 2, 3, 0, pull_raw },
{ "pull-dkr", 2, 3, 0, pull_dkr },
{}
};
return dispatch_verb(argc, argv, verbs, NULL);
}
int main(int argc, char *argv[]) {
int r;
setlocale(LC_ALL, "");
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
goto finish;
r = import_main(argc, argv);
finish:
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}