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[] =
174 _cleanup_free_ char *q = NULL;
182 bool changed = false;
184 NULSTR_FOREACH(sfx, suffixes) {
187 e = endswith(q, sfx);
204 static int pull_raw(int argc, char *argv[], void *userdata) {
205 _cleanup_(raw_import_unrefp) RawImport *import = NULL;
206 _cleanup_event_unref_ sd_event *event = NULL;
207 const char *url, *local;
208 _cleanup_free_ char *l = NULL, *ll = NULL;
212 if (!http_url_is_valid(url)) {
213 log_error("URL '%s' is not valid.", url);
220 r = import_url_last_component(url, &l);
222 return log_error_errno(r, "Failed get final component of URL: %m");
227 if (isempty(local) || streq(local, "-"))
231 r = strip_raw_suffixes(local, &ll);
237 if (!machine_name_is_valid(local)) {
238 log_error("Local image name '%s' is not valid.", local);
243 r = image_find(local, NULL);
245 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
247 log_error_errno(EEXIST, "Image '%s' already exists.", local);
252 log_info("Pulling '%s', saving as '%s'.", url, local);
254 log_info("Pulling '%s'.", url);
256 r = sd_event_default(&event);
258 return log_error_errno(r, "Failed to allocate event loop: %m");
260 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
261 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
262 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
264 r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
266 return log_error_errno(r, "Failed to allocate importer: %m");
268 r = raw_import_pull(import, url, local, arg_force, arg_verify);
270 return log_error_errno(r, "Failed to pull image: %m");
272 r = sd_event_loop(event);
274 return log_error_errno(r, "Failed to run event loop: %m");
276 log_info("Exiting.");
281 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
282 sd_event *event = userdata;
286 log_info("Operation completed successfully.");
288 log_error_errno(error, "Operation failed: %m");
290 sd_event_exit(event, error);
293 static int pull_dkr(int argc, char *argv[], void *userdata) {
294 _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
295 _cleanup_event_unref_ sd_event *event = NULL;
296 const char *name, *tag, *local;
299 if (!arg_dkr_index_url) {
300 log_error("Please specify an index URL with --dkr-index-url=");
304 if (arg_verify != IMPORT_VERIFY_NO) {
305 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
309 tag = strchr(argv[1], ':');
311 name = strndupa(argv[1], tag - argv[1]);
318 if (!dkr_name_is_valid(name)) {
319 log_error("Remote name '%s' is not valid.", name);
323 if (!dkr_tag_is_valid(tag)) {
324 log_error("Tag name '%s' is not valid.", tag);
331 local = strchr(name, '/');
338 if (isempty(local) || streq(local, "-"))
344 if (!machine_name_is_valid(local)) {
345 log_error("Local image name '%s' is not valid.", local);
349 p = strappenda(arg_image_root, "/", local);
350 if (laccess(p, F_OK) >= 0) {
352 log_info("Image '%s' already exists.", local);
355 } else if (errno != ENOENT)
356 return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
358 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
360 log_info("Pulling '%s' with tag '%s'.", name, tag);
362 r = sd_event_default(&event);
364 return log_error_errno(r, "Failed to allocate event loop: %m");
366 assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
367 sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL);
368 sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
370 r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
372 return log_error_errno(r, "Failed to allocate importer: %m");
374 r = dkr_import_pull(import, name, tag, local, arg_force);
376 return log_error_errno(r, "Failed to pull image: %m");
378 r = sd_event_loop(event);
380 return log_error_errno(r, "Failed to run event loop: %m");
382 log_info("Exiting.");
387 static int help(int argc, char *argv[], void *userdata) {
389 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
390 "Import container or virtual machine image.\n\n"
391 " -h --help Show this help\n"
392 " --version Show package version\n"
393 " --force Force creation of image\n"
394 " --verify= Verify downloaded image, one of: 'no', 'sum'\n"
396 " --image-root= Image root directory\n"
397 " --dkr-index-url=URL Specify index URL to use for downloads\n\n"
399 " pull-tar URL [NAME] Download a TAR image\n"
400 " pull-raw URL [NAME] Download a RAW image\n"
401 " pull-dkr REMOTE [NAME] Download a DKR image\n",
402 program_invocation_short_name);
407 static int parse_argv(int argc, char *argv[]) {
417 static const struct option options[] = {
418 { "help", no_argument, NULL, 'h' },
419 { "version", no_argument, NULL, ARG_VERSION },
420 { "force", no_argument, NULL, ARG_FORCE },
421 { "dkr-index-url", required_argument, NULL, ARG_DKR_INDEX_URL },
422 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
423 { "verify", required_argument, NULL, ARG_VERIFY },
432 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
437 return help(0, NULL, NULL);
440 puts(PACKAGE_STRING);
441 puts(SYSTEMD_FEATURES);
448 case ARG_DKR_INDEX_URL:
449 if (!dkr_url_is_valid(optarg)) {
450 log_error("Index URL is not valid: %s", optarg);
454 arg_dkr_index_url = optarg;
458 arg_image_root = optarg;
462 arg_verify = import_verify_from_string(optarg);
463 if (arg_verify < 0) {
464 log_error("Invalid verification setting '%s'", optarg);
474 assert_not_reached("Unhandled option");
480 static int import_main(int argc, char *argv[]) {
482 static const Verb verbs[] = {
483 { "help", VERB_ANY, VERB_ANY, 0, help },
484 { "pull-tar", 2, 3, 0, pull_tar },
485 { "pull-raw", 2, 3, 0, pull_raw },
486 { "pull-dkr", 2, 3, 0, pull_dkr },
490 return dispatch_verb(argc, argv, verbs, NULL);
493 int main(int argc, char *argv[]) {
496 setlocale(LC_ALL, "");
497 log_parse_environment();
500 r = parse_argv(argc, argv);
504 r = import_main(argc, argv);
507 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;