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