chiark / gitweb /
import: rename download code from "import" to "pull"
[elogind.git] / src / import / pull.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 "machine-image.h"
29 #include "import-util.h"
30 #include "pull-tar.h"
31 #include "pull-raw.h"
32 #include "pull-dkr.h"
33
34 static bool arg_force = false;
35 static const char *arg_image_root = "/var/lib/machines";
36 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
37 static const char* arg_dkr_index_url = DEFAULT_DKR_INDEX_URL;
38
39 static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
40         log_notice("Transfer aborted.");
41         sd_event_exit(sd_event_source_get_event(s), EINTR);
42         return 0;
43 }
44
45 static void on_tar_finished(TarPull *pull, int error, void *userdata) {
46         sd_event *event = userdata;
47         assert(pull);
48
49         if (error == 0)
50                 log_info("Operation completed successfully.");
51
52         sd_event_exit(event, abs(error));
53 }
54
55 static int pull_tar(int argc, char *argv[], void *userdata) {
56         _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
57         _cleanup_event_unref_ sd_event *event = NULL;
58         const char *url, *local;
59         _cleanup_free_ char *l = NULL, *ll = NULL;
60         int r;
61
62         url = argv[1];
63         if (!http_url_is_valid(url)) {
64                 log_error("URL '%s' is not valid.", url);
65                 return -EINVAL;
66         }
67
68         if (argc >= 3)
69                 local = argv[2];
70         else {
71                 r = import_url_last_component(url, &l);
72                 if (r < 0)
73                         return log_error_errno(r, "Failed get final component of URL: %m");
74
75                 local = l;
76         }
77
78         if (isempty(local) || streq(local, "-"))
79                 local = NULL;
80
81         if (local) {
82                 r = tar_strip_suffixes(local, &ll);
83                 if (r < 0)
84                         return log_oom();
85
86                 local = ll;
87
88                 if (!machine_name_is_valid(local)) {
89                         log_error("Local image name '%s' is not valid.", local);
90                         return -EINVAL;
91                 }
92
93                 if (!arg_force) {
94                         r = image_find(local, NULL);
95                         if (r < 0)
96                                 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
97                         else if (r > 0) {
98                                 log_error_errno(EEXIST, "Image '%s' already exists.", local);
99                                 return -EEXIST;
100                         }
101                 }
102
103                 log_info("Pulling '%s', saving as '%s'.", url, local);
104         } else
105                 log_info("Pulling '%s'.", url);
106
107         r = sd_event_default(&event);
108         if (r < 0)
109                 return log_error_errno(r, "Failed to allocate event loop: %m");
110
111         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
112         sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
113         sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
114
115         r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
116         if (r < 0)
117                 return log_error_errno(r, "Failed to allocate puller: %m");
118
119         r = tar_pull_start(pull, url, local, arg_force, arg_verify);
120         if (r < 0)
121                 return log_error_errno(r, "Failed to pull image: %m");
122
123         r = sd_event_loop(event);
124         if (r < 0)
125                 return log_error_errno(r, "Failed to run event loop: %m");
126
127         log_info("Exiting.");
128         return -r;
129 }
130
131 static void on_raw_finished(RawPull *pull, int error, void *userdata) {
132         sd_event *event = userdata;
133         assert(pull);
134
135         if (error == 0)
136                 log_info("Operation completed successfully.");
137
138         sd_event_exit(event, abs(error));
139 }
140
141 static int pull_raw(int argc, char *argv[], void *userdata) {
142         _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
143         _cleanup_event_unref_ sd_event *event = NULL;
144         const char *url, *local;
145         _cleanup_free_ char *l = NULL, *ll = NULL;
146         int r;
147
148         url = argv[1];
149         if (!http_url_is_valid(url)) {
150                 log_error("URL '%s' is not valid.", url);
151                 return -EINVAL;
152         }
153
154         if (argc >= 3)
155                 local = argv[2];
156         else {
157                 r = import_url_last_component(url, &l);
158                 if (r < 0)
159                         return log_error_errno(r, "Failed get final component of URL: %m");
160
161                 local = l;
162         }
163
164         if (isempty(local) || streq(local, "-"))
165                 local = NULL;
166
167         if (local) {
168                 r = raw_strip_suffixes(local, &ll);
169                 if (r < 0)
170                         return log_oom();
171
172                 local = ll;
173
174                 if (!machine_name_is_valid(local)) {
175                         log_error("Local image name '%s' is not valid.", local);
176                         return -EINVAL;
177                 }
178
179                 if (!arg_force) {
180                         r = image_find(local, NULL);
181                         if (r < 0)
182                                 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
183                         else if (r > 0) {
184                                 log_error_errno(EEXIST, "Image '%s' already exists.", local);
185                                 return -EEXIST;
186                         }
187                 }
188
189                 log_info("Pulling '%s', saving as '%s'.", url, local);
190         } else
191                 log_info("Pulling '%s'.", url);
192
193         r = sd_event_default(&event);
194         if (r < 0)
195                 return log_error_errno(r, "Failed to allocate event loop: %m");
196
197         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
198         sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
199         sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
200
201         r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
202         if (r < 0)
203                 return log_error_errno(r, "Failed to allocate puller: %m");
204
205         r = raw_pull_start(pull, url, local, arg_force, arg_verify);
206         if (r < 0)
207                 return log_error_errno(r, "Failed to pull image: %m");
208
209         r = sd_event_loop(event);
210         if (r < 0)
211                 return log_error_errno(r, "Failed to run event loop: %m");
212
213         log_info("Exiting.");
214         return -r;
215 }
216
217 static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
218         sd_event *event = userdata;
219         assert(pull);
220
221         if (error == 0)
222                 log_info("Operation completed successfully.");
223
224         sd_event_exit(event, abs(error));
225 }
226
227 static int pull_dkr(int argc, char *argv[], void *userdata) {
228         _cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
229         _cleanup_event_unref_ sd_event *event = NULL;
230         const char *name, *tag, *local;
231         int r;
232
233         if (!arg_dkr_index_url) {
234                 log_error("Please specify an index URL with --dkr-index-url=");
235                 return -EINVAL;
236         }
237
238         if (arg_verify != IMPORT_VERIFY_NO) {
239                 log_error("Pulls from dkr do not support image verification, please pass --verify=no.");
240                 return -EINVAL;
241         }
242
243         tag = strchr(argv[1], ':');
244         if (tag) {
245                 name = strndupa(argv[1], tag - argv[1]);
246                 tag++;
247         } else {
248                 name = argv[1];
249                 tag = "latest";
250         }
251
252         if (!dkr_name_is_valid(name)) {
253                 log_error("Remote name '%s' is not valid.", name);
254                 return -EINVAL;
255         }
256
257         if (!dkr_tag_is_valid(tag)) {
258                 log_error("Tag name '%s' is not valid.", tag);
259                 return -EINVAL;
260         }
261
262         if (argc >= 3)
263                 local = argv[2];
264         else {
265                 local = strchr(name, '/');
266                 if (local)
267                         local++;
268                 else
269                         local = name;
270         }
271
272         if (isempty(local) || streq(local, "-"))
273                 local = NULL;
274
275         if (local) {
276                 if (!machine_name_is_valid(local)) {
277                         log_error("Local image name '%s' is not valid.", local);
278                         return -EINVAL;
279                 }
280
281                 if (!arg_force) {
282                         r = image_find(local, NULL);
283                         if (r < 0)
284                                 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
285                         else if (r > 0) {
286                                 log_error_errno(EEXIST, "Image '%s' already exists.", local);
287                                 return -EEXIST;
288                         }
289                 }
290
291                 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
292         } else
293                 log_info("Pulling '%s' with tag '%s'.", name, tag);
294
295         r = sd_event_default(&event);
296         if (r < 0)
297                 return log_error_errno(r, "Failed to allocate event loop: %m");
298
299         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
300         sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler,  NULL);
301         sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
302
303         r = dkr_pull_new(&pull, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
304         if (r < 0)
305                 return log_error_errno(r, "Failed to allocate puller: %m");
306
307         r = dkr_pull_start(pull, name, tag, local, arg_force);
308         if (r < 0)
309                 return log_error_errno(r, "Failed to pull image: %m");
310
311         r = sd_event_loop(event);
312         if (r < 0)
313                 return log_error_errno(r, "Failed to run event loop: %m");
314
315         log_info("Exiting.");
316         return -r;
317 }
318
319 static int help(int argc, char *argv[], void *userdata) {
320
321         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
322                "Download container or virtual machine image.\n\n"
323                "  -h --help                   Show this help\n"
324                "     --version                Show package version\n"
325                "     --force                  Force creation of image\n"
326                "     --verify=                Verify downloaded image, one of: 'no',\n"
327                "                              'checksum', 'signature'.\n"
328                "     --image-root=            Image root directory\n"
329                "     --dkr-index-url=URL      Specify index URL to use for downloads\n\n"
330                "Commands:\n"
331                "  tar URL [NAME]              Download a TAR image\n"
332                "  raw URL [NAME]              Download a RAW image\n"
333                "  dkr REMOTE [NAME]           Download a DKR image\n",
334                program_invocation_short_name);
335
336         return 0;
337 }
338
339 static int parse_argv(int argc, char *argv[]) {
340
341         enum {
342                 ARG_VERSION = 0x100,
343                 ARG_FORCE,
344                 ARG_DKR_INDEX_URL,
345                 ARG_IMAGE_ROOT,
346                 ARG_VERIFY,
347         };
348
349         static const struct option options[] = {
350                 { "help",            no_argument,       NULL, 'h'                 },
351                 { "version",         no_argument,       NULL, ARG_VERSION         },
352                 { "force",           no_argument,       NULL, ARG_FORCE           },
353                 { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
354                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
355                 { "verify",          required_argument, NULL, ARG_VERIFY          },
356                 {}
357         };
358
359         int c;
360
361         assert(argc >= 0);
362         assert(argv);
363
364         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
365
366                 switch (c) {
367
368                 case 'h':
369                         return help(0, NULL, NULL);
370
371                 case ARG_VERSION:
372                         puts(PACKAGE_STRING);
373                         puts(SYSTEMD_FEATURES);
374                         return 0;
375
376                 case ARG_FORCE:
377                         arg_force = true;
378                         break;
379
380                 case ARG_DKR_INDEX_URL:
381                         if (!http_url_is_valid(optarg)) {
382                                 log_error("Index URL is not valid: %s", optarg);
383                                 return -EINVAL;
384                         }
385
386                         arg_dkr_index_url = optarg;
387                         break;
388
389                 case ARG_IMAGE_ROOT:
390                         arg_image_root = optarg;
391                         break;
392
393                 case ARG_VERIFY:
394                         arg_verify = import_verify_from_string(optarg);
395                         if (arg_verify < 0) {
396                                 log_error("Invalid verification setting '%s'", optarg);
397                                 return -EINVAL;
398                         }
399
400                         break;
401
402                 case '?':
403                         return -EINVAL;
404
405                 default:
406                         assert_not_reached("Unhandled option");
407                 }
408
409         return 1;
410 }
411
412 static int pull_main(int argc, char *argv[]) {
413
414         static const Verb verbs[] = {
415                 { "help", VERB_ANY, VERB_ANY, 0, help     },
416                 { "tar",  2,        3,        0, pull_tar },
417                 { "raw",  2,        3,        0, pull_raw },
418                 { "dkr",  2,        3,        0, pull_dkr },
419                 {}
420         };
421
422         return dispatch_verb(argc, argv, verbs, NULL);
423 }
424
425 int main(int argc, char *argv[]) {
426         int r;
427
428         setlocale(LC_ALL, "");
429         log_parse_environment();
430         log_open();
431
432         r = parse_argv(argc, argv);
433         if (r <= 0)
434                 goto finish;
435
436         r = pull_main(argc, argv);
437
438 finish:
439         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
440 }