chiark / gitweb /
b4d859df1f408b538efaf99c8ae37ea625e59c0b
[elogind.git] / src / import / import.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <getopt.h>
23
24 #include "sd-event.h"
25 #include "event-util.h"
26 #include "verbs.h"
27 #include "build.h"
28 #include "import-raw.h"
29 #include "import-dkr.h"
30
31 static bool arg_force = false;
32 static const char *arg_image_root = "/var/lib/machines";
33
34 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
35
36 static void on_raw_finished(RawImport *import, int error, void *userdata) {
37         sd_event *event = userdata;
38         assert(import);
39
40         if (error == 0)
41                 log_info("Operation completed successfully.");
42         else
43                 log_info_errno(error, "Operation failed: %m");
44
45         sd_event_exit(event, error);
46 }
47
48 static int pull_raw(int argc, char *argv[], void *userdata) {
49         _cleanup_(raw_import_unrefp) RawImport *import = NULL;
50         _cleanup_event_unref_ sd_event *event = NULL;
51         const char *url, *local, *suffix;
52         int r;
53
54         url = argv[1];
55         if (!raw_url_is_valid(url)) {
56                 log_error("URL '%s' is not valid.", url);
57                 return -EINVAL;
58         }
59
60         if (argc >= 3)
61                 local = argv[2];
62         else {
63                 const char *e, *p;
64
65                 e = url + strlen(url);
66                 while (e > url && e[-1] == '/')
67                         e--;
68
69                 p = e;
70                 while (p > url && p[-1] != '/')
71                         p--;
72
73                 local = strndupa(p, e - p);
74         }
75
76         if (isempty(local) || streq(local, "-"))
77                 local = NULL;
78
79         if (local) {
80                 const char *p;
81
82                 suffix = endswith(local, ".raw");
83                 if (suffix)
84                         local = strndupa(local, suffix - local);
85
86                 if (!machine_name_is_valid(local)) {
87                         log_error("Local image name '%s' is not valid.", local);
88                         return -EINVAL;
89                 }
90
91                 p = strappenda(arg_image_root, "/", local, ".raw");
92                 if (laccess(p, F_OK) >= 0) {
93                         if (!arg_force) {
94                                 log_info("Image '%s' already exists.", local);
95                                 return 0;
96                         }
97                 } else if (errno != ENOENT)
98                         return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
99
100                 log_info("Pulling '%s', saving as '%s'.", url, local);
101         } else
102                 log_info("Pulling '%s'.", url);
103
104         r = sd_event_default(&event);
105         if (r < 0)
106                 return log_error_errno(r, "Failed to allocate event loop: %m");
107
108         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
109         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
110         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
111
112         r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
113         if (r < 0)
114                 return log_error_errno(r, "Failed to allocate importer: %m");
115
116         r = raw_import_pull(import, url, local, arg_force);
117         if (r < 0)
118                 return log_error_errno(r, "Failed to pull image: %m");
119
120         r = sd_event_loop(event);
121         if (r < 0)
122                 return log_error_errno(r, "Failed to run event loop: %m");
123
124         log_info("Exiting.");
125
126         return 0;
127 }
128
129 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
130         sd_event *event = userdata;
131         assert(import);
132
133         if (error == 0)
134                 log_info("Operation completed successfully.");
135         else
136                 log_info_errno(error, "Operation failed: %m");
137
138         sd_event_exit(event, error);
139 }
140
141 static int pull_dkr(int argc, char *argv[], void *userdata) {
142         _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
143         _cleanup_event_unref_ sd_event *event = NULL;
144         const char *name, *tag, *local;
145         int r;
146
147         if (!arg_dkr_index_url) {
148                 log_error("Please specify an index URL with --dkr-index-url=");
149                 return -EINVAL;
150         }
151
152         tag = strchr(argv[1], ':');
153         if (tag) {
154                 name = strndupa(argv[1], tag - argv[1]);
155                 tag++;
156         } else {
157                 name = argv[1];
158                 tag = "latest";
159         }
160
161         if (!dkr_name_is_valid(name)) {
162                 log_error("Remote name '%s' is not valid.", name);
163                 return -EINVAL;
164         }
165
166         if (!dkr_tag_is_valid(tag)) {
167                 log_error("Tag name '%s' is not valid.", tag);
168                 return -EINVAL;
169         }
170
171         if (argc >= 3)
172                 local = argv[2];
173         else {
174                 local = strchr(name, '/');
175                 if (local)
176                         local++;
177                 else
178                         local = name;
179         }
180
181         if (isempty(local) || streq(local, "-"))
182                 local = NULL;
183
184         if (local) {
185                 const char *p;
186
187                 if (!machine_name_is_valid(local)) {
188                         log_error("Local image name '%s' is not valid.", local);
189                         return -EINVAL;
190                 }
191
192                 p = strappenda(arg_image_root, "/", local);
193                 if (laccess(p, F_OK) >= 0) {
194                         if (!arg_force) {
195                                 log_info("Image '%s' already exists.", local);
196                                 return 0;
197                         }
198                 } else if (errno != ENOENT)
199                         return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
200
201                 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
202         } else
203                 log_info("Pulling '%s' with tag '%s'.", name, tag);
204
205         r = sd_event_default(&event);
206         if (r < 0)
207                 return log_error_errno(r, "Failed to allocate event loop: %m");
208
209         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
210         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
211         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
212
213         r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
214         if (r < 0)
215                 return log_error_errno(r, "Failed to allocate importer: %m");
216
217         r = dkr_import_pull(import, name, tag, local, arg_force);
218         if (r < 0)
219                 return log_error_errno(r, "Failed to pull image: %m");
220
221         r = sd_event_loop(event);
222         if (r < 0)
223                 return log_error_errno(r, "Failed to run event loop: %m");
224
225         log_info("Exiting.");
226
227         return 0;
228 }
229
230 static int help(int argc, char *argv[], void *userdata) {
231
232         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
233                "Import container or virtual machine image.\n\n"
234                "  -h --help                   Show this help\n"
235                "     --version                Show package version\n"
236                "     --force                  Force creation of image\n"
237                "     --image-root=            Image root directory\n"
238                "     --dkr-index-url=URL      Specify index URL to use for downloads\n\n"
239                "Commands:\n"
240                "  pull-dkr REMOTE [NAME]      Download a DKR image\n"
241                "  pull-raw URL [NAME]         Download a RAW image\n",
242                program_invocation_short_name);
243
244         return 0;
245 }
246
247 static int parse_argv(int argc, char *argv[]) {
248
249         enum {
250                 ARG_VERSION = 0x100,
251                 ARG_FORCE,
252                 ARG_DKR_INDEX_URL,
253                 ARG_IMAGE_ROOT,
254         };
255
256         static const struct option options[] = {
257                 { "help",            no_argument,       NULL, 'h'                 },
258                 { "version",         no_argument,       NULL, ARG_VERSION         },
259                 { "force",           no_argument,       NULL, ARG_FORCE           },
260                 { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
261                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
262                 {}
263         };
264
265         int c;
266
267         assert(argc >= 0);
268         assert(argv);
269
270         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
271
272                 switch (c) {
273
274                 case 'h':
275                         return help(0, NULL, NULL);
276
277                 case ARG_VERSION:
278                         puts(PACKAGE_STRING);
279                         puts(SYSTEMD_FEATURES);
280                         return 0;
281
282                 case ARG_FORCE:
283                         arg_force = true;
284                         break;
285
286                 case ARG_DKR_INDEX_URL:
287                         if (!dkr_url_is_valid(optarg)) {
288                                 log_error("Index URL is not valid: %s", optarg);
289                                 return -EINVAL;
290                         }
291
292                         arg_dkr_index_url = optarg;
293                         break;
294
295                 case ARG_IMAGE_ROOT:
296                         arg_image_root = optarg;
297                         break;
298
299                 case '?':
300                         return -EINVAL;
301
302                 default:
303                         assert_not_reached("Unhandled option");
304                 }
305
306         return 1;
307 }
308
309 static int import_main(int argc, char *argv[]) {
310
311         static const Verb verbs[] = {
312                 { "help",     VERB_ANY, VERB_ANY, 0, help     },
313                 { "pull-dkr", 2,        3,        0, pull_dkr },
314                 { "pull-raw", 2,        3,        0, pull_raw },
315                 {}
316         };
317
318         return dispatch_verb(argc, argv, verbs, NULL);
319 }
320
321 int main(int argc, char *argv[]) {
322         int r;
323
324         setlocale(LC_ALL, "");
325         log_parse_environment();
326         log_open();
327
328         r = parse_argv(argc, argv);
329         if (r <= 0)
330                 goto finish;
331
332         r = import_main(argc, argv);
333
334 finish:
335         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
336 }