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