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