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