chiark / gitweb /
journal: Fix syslog forwarding without CAP_SYS_ADMIN
[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 "machine-image.h"
29 #include "import-tar.h"
30 #include "import-raw.h"
31 #include "import-dkr.h"
32 #include "import-util.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 void on_tar_finished(TarImport *import, int error, void *userdata) {
40         sd_event *event = userdata;
41         assert(import);
42
43         if (error == 0)
44                 log_info("Operation completed successfully.");
45
46         sd_event_exit(event, EXIT_FAILURE);
47 }
48
49 static int strip_tar_suffixes(const char *name, char **ret) {
50         const char *e;
51         char *s;
52
53         e = endswith(name, ".tar");
54         if (!e)
55                 e = endswith(name, ".tar.gz");
56         if (!e)
57                 e = endswith(name, ".tar.xz");
58         if (!e)
59                 e = endswith(name, ".tgz");
60         if (!e)
61                 e = strchr(name, 0);
62
63         if (e <= name)
64                 return -EINVAL;
65
66         s = strndup(name, e - name);
67         if (!s)
68                 return -ENOMEM;
69
70         *ret = s;
71         return 0;
72 }
73
74 static int pull_tar(int argc, char *argv[], void *userdata) {
75         _cleanup_(tar_import_unrefp) TarImport *import = NULL;
76         _cleanup_event_unref_ sd_event *event = NULL;
77         const char *url, *local;
78         _cleanup_free_ char *l = NULL, *ll = NULL;
79         int r;
80
81         url = argv[1];
82         if (!http_url_is_valid(url)) {
83                 log_error("URL '%s' is not valid.", url);
84                 return -EINVAL;
85         }
86
87         if (argc >= 3)
88                 local = argv[2];
89         else {
90                 r = import_url_last_component(url, &l);
91                 if (r < 0)
92                         return log_error_errno(r, "Failed get final component of URL: %m");
93
94                 local = l;
95         }
96
97         if (isempty(local) || streq(local, "-"))
98                 local = NULL;
99
100         if (local) {
101                 r = strip_tar_suffixes(local, &ll);
102                 if (r < 0)
103                         return log_oom();
104
105                 local = ll;
106
107                 if (!machine_name_is_valid(local)) {
108                         log_error("Local image name '%s' is not valid.", local);
109                         return -EINVAL;
110                 }
111
112                 if (!arg_force) {
113                         r = image_find(local, NULL);
114                         if (r < 0)
115                                 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
116                         else if (r > 0) {
117                                 log_error_errno(EEXIST, "Image '%s' already exists.", local);
118                                 return -EEXIST;
119                         }
120                 }
121
122                 log_info("Pulling '%s', saving as '%s'.", url, local);
123         } else
124                 log_info("Pulling '%s'.", url);
125
126         r = sd_event_default(&event);
127         if (r < 0)
128                 return log_error_errno(r, "Failed to allocate event loop: %m");
129
130         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
131         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
132         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
133
134         r = tar_import_new(&import, event, arg_image_root, on_tar_finished, event);
135         if (r < 0)
136                 return log_error_errno(r, "Failed to allocate importer: %m");
137
138         r = tar_import_pull(import, url, local, arg_force, arg_verify);
139         if (r < 0)
140                 return log_error_errno(r, "Failed to pull image: %m");
141
142         r = sd_event_loop(event);
143         if (r < 0)
144                 return log_error_errno(r, "Failed to run event loop: %m");
145
146         log_info("Exiting.");
147
148         return r;
149 }
150
151 static void on_raw_finished(RawImport *import, int error, void *userdata) {
152         sd_event *event = userdata;
153         assert(import);
154
155         if (error == 0)
156                 log_info("Operation completed successfully.");
157
158         sd_event_exit(event, EXIT_FAILURE);
159 }
160
161 static int strip_raw_suffixes(const char *p, char **ret) {
162         static const char suffixes[] =
163                 ".xz\0"
164                 ".gz\0"
165                 ".raw\0"
166                 ".qcow2\0"
167                 ".img\0"
168                 ".bin\0";
169
170         _cleanup_free_ char *q = NULL;
171
172         q = strdup(p);
173         if (!q)
174                 return -ENOMEM;
175
176         for (;;) {
177                 const char *sfx;
178                 bool changed = false;
179
180                 NULSTR_FOREACH(sfx, suffixes) {
181                         char *e;
182
183                         e = endswith(q, sfx);
184                         if (e) {
185                                 *e = 0;
186                                 changed = true;
187                         }
188                 }
189
190                 if (!changed)
191                         break;
192         }
193
194         *ret = q;
195         q = NULL;
196
197         return 0;
198 }
199
200 static int pull_raw(int argc, char *argv[], void *userdata) {
201         _cleanup_(raw_import_unrefp) RawImport *import = NULL;
202         _cleanup_event_unref_ sd_event *event = NULL;
203         const char *url, *local;
204         _cleanup_free_ char *l = NULL, *ll = NULL;
205         int r;
206
207         url = argv[1];
208         if (!http_url_is_valid(url)) {
209                 log_error("URL '%s' is not valid.", url);
210                 return -EINVAL;
211         }
212
213         if (argc >= 3)
214                 local = argv[2];
215         else {
216                 r = import_url_last_component(url, &l);
217                 if (r < 0)
218                         return log_error_errno(r, "Failed get final component of URL: %m");
219
220                 local = l;
221         }
222
223         if (isempty(local) || streq(local, "-"))
224                 local = NULL;
225
226         if (local) {
227                 r = strip_raw_suffixes(local, &ll);
228                 if (r < 0)
229                         return log_oom();
230
231                 local = ll;
232
233                 if (!machine_name_is_valid(local)) {
234                         log_error("Local image name '%s' is not valid.", local);
235                         return -EINVAL;
236                 }
237
238                 if (!arg_force) {
239                         r = image_find(local, NULL);
240                         if (r < 0)
241                                 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
242                         else if (r > 0) {
243                                 log_error_errno(EEXIST, "Image '%s' already exists.", local);
244                                 return -EEXIST;
245                         }
246                 }
247
248                 log_info("Pulling '%s', saving as '%s'.", url, local);
249         } else
250                 log_info("Pulling '%s'.", url);
251
252         r = sd_event_default(&event);
253         if (r < 0)
254                 return log_error_errno(r, "Failed to allocate event loop: %m");
255
256         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
257         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
258         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
259
260         r = raw_import_new(&import, event, arg_image_root, on_raw_finished, event);
261         if (r < 0)
262                 return log_error_errno(r, "Failed to allocate importer: %m");
263
264         r = raw_import_pull(import, url, local, arg_force, arg_verify);
265         if (r < 0)
266                 return log_error_errno(r, "Failed to pull image: %m");
267
268         r = sd_event_loop(event);
269         if (r < 0)
270                 return log_error_errno(r, "Failed to run event loop: %m");
271
272         log_info("Exiting.");
273
274         return r;
275 }
276
277 static void on_dkr_finished(DkrImport *import, int error, void *userdata) {
278         sd_event *event = userdata;
279         assert(import);
280
281         if (error == 0)
282                 log_info("Operation completed successfully.");
283         else
284                 log_error_errno(error, "Operation failed: %m");
285
286         sd_event_exit(event, error);
287 }
288
289 static int pull_dkr(int argc, char *argv[], void *userdata) {
290         _cleanup_(dkr_import_unrefp) DkrImport *import = NULL;
291         _cleanup_event_unref_ sd_event *event = NULL;
292         const char *name, *tag, *local;
293         int r;
294
295         if (!arg_dkr_index_url) {
296                 log_error("Please specify an index URL with --dkr-index-url=");
297                 return -EINVAL;
298         }
299
300         if (arg_verify != IMPORT_VERIFY_NO) {
301                 log_error("Imports from dkr do not support image verification, please pass --verify=no.");
302                 return -EINVAL;
303         }
304
305         tag = strchr(argv[1], ':');
306         if (tag) {
307                 name = strndupa(argv[1], tag - argv[1]);
308                 tag++;
309         } else {
310                 name = argv[1];
311                 tag = "latest";
312         }
313
314         if (!dkr_name_is_valid(name)) {
315                 log_error("Remote name '%s' is not valid.", name);
316                 return -EINVAL;
317         }
318
319         if (!dkr_tag_is_valid(tag)) {
320                 log_error("Tag name '%s' is not valid.", tag);
321                 return -EINVAL;
322         }
323
324         if (argc >= 3)
325                 local = argv[2];
326         else {
327                 local = strchr(name, '/');
328                 if (local)
329                         local++;
330                 else
331                         local = name;
332         }
333
334         if (isempty(local) || streq(local, "-"))
335                 local = NULL;
336
337         if (local) {
338                 const char *p;
339
340                 if (!machine_name_is_valid(local)) {
341                         log_error("Local image name '%s' is not valid.", local);
342                         return -EINVAL;
343                 }
344
345                 p = strappenda(arg_image_root, "/", local);
346                 if (laccess(p, F_OK) >= 0) {
347                         if (!arg_force) {
348                                 log_info("Image '%s' already exists.", local);
349                                 return 0;
350                         }
351                 } else if (errno != ENOENT)
352                         return log_error_errno(errno, "Can't check if image '%s' already exists: %m", local);
353
354                 log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
355         } else
356                 log_info("Pulling '%s' with tag '%s'.", name, tag);
357
358         r = sd_event_default(&event);
359         if (r < 0)
360                 return log_error_errno(r, "Failed to allocate event loop: %m");
361
362         assert_se(sigprocmask_many(SIG_BLOCK, SIGTERM, SIGINT, -1) == 0);
363         sd_event_add_signal(event, NULL, SIGTERM, NULL,  NULL);
364         sd_event_add_signal(event, NULL, SIGINT, NULL, NULL);
365
366         r = dkr_import_new(&import, event, arg_dkr_index_url, arg_image_root, on_dkr_finished, event);
367         if (r < 0)
368                 return log_error_errno(r, "Failed to allocate importer: %m");
369
370         r = dkr_import_pull(import, name, tag, local, arg_force);
371         if (r < 0)
372                 return log_error_errno(r, "Failed to pull image: %m");
373
374         r = sd_event_loop(event);
375         if (r < 0)
376                 return log_error_errno(r, "Failed to run event loop: %m");
377
378         log_info("Exiting.");
379
380         return 0;
381 }
382
383 static int help(int argc, char *argv[], void *userdata) {
384
385         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
386                "Import container or virtual machine image.\n\n"
387                "  -h --help                   Show this help\n"
388                "     --version                Show package version\n"
389                "     --force                  Force creation of image\n"
390                "     --verify=                Verify downloaded image, one of: 'no', 'sum'\n"
391                "                              'signature'.\n"
392                "     --image-root=            Image root directory\n"
393                "     --dkr-index-url=URL      Specify index URL to use for downloads\n\n"
394                "Commands:\n"
395                "  pull-tar URL [NAME]         Download a TAR image\n"
396                "  pull-raw URL [NAME]         Download a RAW image\n"
397                "  pull-dkr REMOTE [NAME]      Download a DKR image\n",
398                program_invocation_short_name);
399
400         return 0;
401 }
402
403 static int parse_argv(int argc, char *argv[]) {
404
405         enum {
406                 ARG_VERSION = 0x100,
407                 ARG_FORCE,
408                 ARG_DKR_INDEX_URL,
409                 ARG_IMAGE_ROOT,
410                 ARG_VERIFY,
411         };
412
413         static const struct option options[] = {
414                 { "help",            no_argument,       NULL, 'h'                 },
415                 { "version",         no_argument,       NULL, ARG_VERSION         },
416                 { "force",           no_argument,       NULL, ARG_FORCE           },
417                 { "dkr-index-url",   required_argument, NULL, ARG_DKR_INDEX_URL   },
418                 { "image-root",      required_argument, NULL, ARG_IMAGE_ROOT      },
419                 { "verify",          required_argument, NULL, ARG_VERIFY          },
420                 {}
421         };
422
423         int c;
424
425         assert(argc >= 0);
426         assert(argv);
427
428         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
429
430                 switch (c) {
431
432                 case 'h':
433                         return help(0, NULL, NULL);
434
435                 case ARG_VERSION:
436                         puts(PACKAGE_STRING);
437                         puts(SYSTEMD_FEATURES);
438                         return 0;
439
440                 case ARG_FORCE:
441                         arg_force = true;
442                         break;
443
444                 case ARG_DKR_INDEX_URL:
445                         if (!dkr_url_is_valid(optarg)) {
446                                 log_error("Index URL is not valid: %s", optarg);
447                                 return -EINVAL;
448                         }
449
450                         arg_dkr_index_url = optarg;
451                         break;
452
453                 case ARG_IMAGE_ROOT:
454                         arg_image_root = optarg;
455                         break;
456
457                 case ARG_VERIFY:
458                         arg_verify = import_verify_from_string(optarg);
459                         if (arg_verify < 0) {
460                                 log_error("Invalid verification setting '%s'", optarg);
461                                 return -EINVAL;
462                         }
463
464                         break;
465
466                 case '?':
467                         return -EINVAL;
468
469                 default:
470                         assert_not_reached("Unhandled option");
471                 }
472
473         return 1;
474 }
475
476 static int import_main(int argc, char *argv[]) {
477
478         static const Verb verbs[] = {
479                 { "help",     VERB_ANY, VERB_ANY, 0, help     },
480                 { "pull-tar", 2,        3,        0, pull_tar },
481                 { "pull-raw", 2,        3,        0, pull_raw },
482                 { "pull-dkr", 2,        3,        0, pull_dkr },
483                 {}
484         };
485
486         return dispatch_verb(argc, argv, verbs, NULL);
487 }
488
489 int main(int argc, char *argv[]) {
490         int r;
491
492         setlocale(LC_ALL, "");
493         log_parse_environment();
494         log_open();
495
496         r = parse_argv(argc, argv);
497         if (r <= 0)
498                 goto finish;
499
500         r = import_main(argc, argv);
501
502 finish:
503         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
504 }