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"
33 static bool arg_force = false;
34 static const char *arg_image_root = "/var/lib/machines";
36 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
38 static void on_tar_finished(TarImport *import, int error, void *userdata) {
39 sd_event *event = userdata;
43 log_info("Operation completed successfully.");
45 log_error_errno(error, "Operation failed: %m");
47 sd_event_exit(event, error);
50 static int url_final_component(const char *url, char **ret) {
54 e = strchrnul(url, '?');
56 while (e > url && e[-1] == '/')
60 while (p > url && p[-1] != '/')
66 s = strndup(p, e - p);
74 static int strip_tar_suffixes(const char *name, char **ret) {
78 e = endswith(name, ".tar");
80 e = endswith(name, ".tar.gz");
82 e = endswith(name, ".tar.xz");
84 e = endswith(name, ".tgz");
91 s = strndup(name, e - name);
99 static int pull_tar(int argc, char *argv[], void *userdata) {
100 _cleanup_(tar_import_unrefp) TarImport *import = NULL;
101 _cleanup_event_unref_ sd_event *event = NULL;
102 const char *url, *local;
103 _cleanup_free_ char *l = NULL, *ll = NULL;
107 if (!http_url_is_valid(url)) {
108 log_error("URL '%s' is not valid.", url);
115 r = url_final_component(url, &l);
117 return log_error_errno(r, "Failed get final component of URL: %m");
122 if (isempty(local) || streq(local, "-"))
126 r = strip_tar_suffixes(local, &ll);
132 if (!machine_name_is_valid(local)) {
133 log_error("Local image name '%s' is not valid.", local);
138 r = image_find(local, NULL);
140 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
142 log_error_errno(EEXIST, "Image '%s' already exists.", local);
147 log_info("Pulling '%s', saving as '%s'.", url, local);
149 log_info("Pulling '%s'.", url);
151 r = sd_event_default(&event);
153 return log_error_errno(r, "Failed to allocate event loop: %m");
155 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
156 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
157 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
159 r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
161 return log_error_errno(r, "Failed to allocate importer: %m");
163 r = tar_import_pull(import, url, local, arg_force);
165 return log_error_errno(r, "Failed to pull image: %m");
167 r = sd_event_loop(event);
169 return log_error_errno(r, "Failed to run event loop: %m");
171 log_info("Exiting.");
176 static void on_raw_finished(RawImport *import, int error, void *userdata) {
177 sd_event *event = userdata;
181 log_info("Operation completed successfully.");
183 log_error_errno(error, "Operation failed: %m");
185 sd_event_exit(event, error);
188 static int strip_raw_suffixes(const char *p, char **ret) {
189 static const char suffixes[] =
195 _cleanup_free_ char *q = NULL;
203 bool changed = false;
205 NULSTR_FOREACH(sfx, suffixes) {
208 e = endswith(q, sfx);
225 static int pull_raw(int argc, char *argv[], void *userdata) {
226 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
227 _cleanup_event_unref_ sd_event *event = NULL;
228 const char *url, *local;
229 _cleanup_free_ char *l = NULL;
233 if (!http_url_is_valid(url)) {
234 log_error("URL '%s' is not valid.", url);
243 e = url + strlen(url);
244 while (e > url && e[-1] == '/')
248 while (p > url && p[-1] != '/')
251 local = strndupa(p, e - p);
254 if (isempty(local) || streq(local, "-"))
260 r = strip_raw_suffixes(local, &l);
266 if (!machine_name_is_valid(local)) {
267 log_error("Local image name '%s' is not valid.", local);
271 p = strappenda(arg_image_root, "/", local, ".raw");
272 if (laccess(p, F_OK) >= 0) {
274 log_info("Image '%s' already exists.", local);
277 } else if (errno != ENOENT)
278 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
280 log_info("Pulling '%s', saving as '%s'.", url, local);
282 log_info("Pulling '%s'.", url);
284 r = sd_event_default(&event);
286 return log_error_errno(r, "Failed to allocate event loop: %m");
288 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
289 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
290 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
292 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
294 return log_error_errno(r, "Failed to allocate importer: %m");
296 r = raw_import_pull(import, url, local, arg_force);
298 return log_error_errno(r, "Failed to pull image: %m");
300 r = sd_event_loop(event);
302 return log_error_errno(r, "Failed to run event loop: %m");
304 log_info("Exiting.");
309 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
310 sd_event *event = userdata;
314 log_info("Operation completed successfully.");
316 log_error_errno(error, "Operation failed: %m");
318 sd_event_exit(event, error);
321 static int pull_dkr(int argc, char *argv[], void *userdata) {
322 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
323 _cleanup_event_unref_ sd_event *event = NULL;
324 const char *name, *tag, *local;
327 if (!arg_dkr_index_url) {
328 log_error("Please specify an index URL with --dkr-index-url=");
332 tag = strchr(argv[1], ':');
334 name = strndupa(argv[1], tag - argv[1]);
341 if (!dkr_name_is_valid(name)) {
342 log_error("Remote name '%s' is not valid.", name);
346 if (!dkr_tag_is_valid(tag)) {
347 log_error("Tag name '%s' is not valid.", tag);
354 local = strchr(name, '/');
361 if (isempty(local) || streq(local, "-"))
367 if (!machine_name_is_valid(local)) {
368 log_error("Local image name '%s' is not valid.", local);
372 p = strappenda(arg_image_root, "/", local);
373 if (laccess(p, F_OK) >= 0) {
375 log_info("Image '%s' already exists.", local);
378 } else if (errno != ENOENT)
379 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
381 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
383 log_info("Pulling '%s' with tag '%s'.", name, tag);
385 r = sd_event_default(&event);
387 return log_error_errno(r, "Failed to allocate event loop: %m");
389 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
390 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
391 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
393 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
395 return log_error_errno(r, "Failed to allocate importer: %m");
397 r = dkr_import_pull(import, name, tag, local, arg_force);
399 return log_error_errno(r, "Failed to pull image: %m");
401 r = sd_event_loop(event);
403 return log_error_errno(r, "Failed to run event loop: %m");
405 log_info("Exiting.");
410 static int help(int argc, char *argv[], void *userdata) {
412 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
413 "Import container or virtual machine image.\n\n"
414 " -h --help Show this help\n"
415 " --version Show package version\n"
416 " --force Force creation of image\n"
417 " --image-root= Image root directory\n"
418 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
420 " pull-tar URL Download a TAR image\n"
421 " pull-raw URL [NAME] Download a RAW image\n"
422 " pull-dkr REMOTE [NAME] Download a DKR image\n",
423 program_invocation_short_name);
428 static int parse_argv(int argc, char *argv[]) {
437 static const struct option options[] = {
438 { "help", no_argument, NULL, 'h' },
439 { "version", no_argument, NULL, ARG_VERSION },
440 { "force", no_argument, NULL, ARG_FORCE },
441 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
442 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
451 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
456 return help(0, NULL, NULL);
459 puts(PACKAGE_STRING);
460 puts(SYSTEMD_FEATURES);
467 case ARG_DKR_INDEX_URL:
468 if (!dkr_url_is_valid(optarg)) {
469 log_error("Index URL is not valid: %s", optarg);
473 arg_dkr_index_url = optarg;
477 arg_image_root = optarg;
484 assert_not_reached("Unhandled option");
490 static int import_main(int argc, char *argv[]) {
492 static const Verb verbs[] = {
493 { "help", VERB_ANY, VERB_ANY, 0, help },
494 { "pull-tar", 2, 3, 0, pull_tar },
495 { "pull-raw", 2, 3, 0, pull_raw },
496 { "pull-dkr", 2, 3, 0, pull_dkr },
500 return dispatch_verb(argc, argv, verbs, NULL);
503 int main(int argc, char *argv[]) {
506 setlocale(LC_ALL, "");
507 log_parse_environment();
510 r = parse_argv(argc, argv);
514 r = import_main(argc, argv);
517 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;