]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/import/pull.c
Merge pull request #1607 from keszybz/lz4-remove-v1
[thirdparty/systemd.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
26 #include "alloc-util.h"
27 #include "hostname-util.h"
28 #include "import-util.h"
29 #include "machine-image.h"
30 #include "parse-util.h"
31 #include "pull-raw.h"
32 #include "pull-tar.h"
33 #include "signal-util.h"
34 #include "string-util.h"
35 #include "verbs.h"
36 #include "web-util.h"
37
38 static bool arg_force = false;
39 static const char *arg_image_root = "/var/lib/machines";
40 static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
41 static bool arg_settings = true;
42
43 static int interrupt_signal_handler(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
44 log_notice("Transfer aborted.");
45 sd_event_exit(sd_event_source_get_event(s), EINTR);
46 return 0;
47 }
48
49 static void on_tar_finished(TarPull *pull, int error, void *userdata) {
50 sd_event *event = userdata;
51 assert(pull);
52
53 if (error == 0)
54 log_info("Operation completed successfully.");
55
56 sd_event_exit(event, abs(error));
57 }
58
59 static int pull_tar(int argc, char *argv[], void *userdata) {
60 _cleanup_(tar_pull_unrefp) TarPull *pull = NULL;
61 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
62 const char *url, *local;
63 _cleanup_free_ char *l = NULL, *ll = NULL;
64 int r;
65
66 url = argv[1];
67 if (!http_url_is_valid(url)) {
68 log_error("URL '%s' is not valid.", url);
69 return -EINVAL;
70 }
71
72 if (argc >= 3)
73 local = argv[2];
74 else {
75 r = import_url_last_component(url, &l);
76 if (r < 0)
77 return log_error_errno(r, "Failed get final component of URL: %m");
78
79 local = l;
80 }
81
82 if (isempty(local) || streq(local, "-"))
83 local = NULL;
84
85 if (local) {
86 r = tar_strip_suffixes(local, &ll);
87 if (r < 0)
88 return log_oom();
89
90 local = ll;
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 if (!arg_force) {
98 r = image_find(local, NULL);
99 if (r < 0)
100 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
101 else if (r > 0) {
102 log_error_errno(EEXIST, "Image '%s' already exists.", local);
103 return -EEXIST;
104 }
105 }
106
107 log_info("Pulling '%s', saving as '%s'.", url, local);
108 } else
109 log_info("Pulling '%s'.", url);
110
111 r = sd_event_default(&event);
112 if (r < 0)
113 return log_error_errno(r, "Failed to allocate event loop: %m");
114
115 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
116 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
117 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
118
119 r = tar_pull_new(&pull, event, arg_image_root, on_tar_finished, event);
120 if (r < 0)
121 return log_error_errno(r, "Failed to allocate puller: %m");
122
123 r = tar_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
124 if (r < 0)
125 return log_error_errno(r, "Failed to pull image: %m");
126
127 r = sd_event_loop(event);
128 if (r < 0)
129 return log_error_errno(r, "Failed to run event loop: %m");
130
131 log_info("Exiting.");
132 return -r;
133 }
134
135 static void on_raw_finished(RawPull *pull, int error, void *userdata) {
136 sd_event *event = userdata;
137 assert(pull);
138
139 if (error == 0)
140 log_info("Operation completed successfully.");
141
142 sd_event_exit(event, abs(error));
143 }
144
145 static int pull_raw(int argc, char *argv[], void *userdata) {
146 _cleanup_(raw_pull_unrefp) RawPull *pull = NULL;
147 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
148 const char *url, *local;
149 _cleanup_free_ char *l = NULL, *ll = NULL;
150 int r;
151
152 url = argv[1];
153 if (!http_url_is_valid(url)) {
154 log_error("URL '%s' is not valid.", url);
155 return -EINVAL;
156 }
157
158 if (argc >= 3)
159 local = argv[2];
160 else {
161 r = import_url_last_component(url, &l);
162 if (r < 0)
163 return log_error_errno(r, "Failed get final component of URL: %m");
164
165 local = l;
166 }
167
168 if (isempty(local) || streq(local, "-"))
169 local = NULL;
170
171 if (local) {
172 r = raw_strip_suffixes(local, &ll);
173 if (r < 0)
174 return log_oom();
175
176 local = ll;
177
178 if (!machine_name_is_valid(local)) {
179 log_error("Local image name '%s' is not valid.", local);
180 return -EINVAL;
181 }
182
183 if (!arg_force) {
184 r = image_find(local, NULL);
185 if (r < 0)
186 return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
187 else if (r > 0) {
188 log_error_errno(EEXIST, "Image '%s' already exists.", local);
189 return -EEXIST;
190 }
191 }
192
193 log_info("Pulling '%s', saving as '%s'.", url, local);
194 } else
195 log_info("Pulling '%s'.", url);
196
197 r = sd_event_default(&event);
198 if (r < 0)
199 return log_error_errno(r, "Failed to allocate event loop: %m");
200
201 assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0);
202 (void) sd_event_add_signal(event, NULL, SIGTERM, interrupt_signal_handler, NULL);
203 (void) sd_event_add_signal(event, NULL, SIGINT, interrupt_signal_handler, NULL);
204
205 r = raw_pull_new(&pull, event, arg_image_root, on_raw_finished, event);
206 if (r < 0)
207 return log_error_errno(r, "Failed to allocate puller: %m");
208
209 r = raw_pull_start(pull, url, local, arg_force, arg_verify, arg_settings);
210 if (r < 0)
211 return log_error_errno(r, "Failed to pull image: %m");
212
213 r = sd_event_loop(event);
214 if (r < 0)
215 return log_error_errno(r, "Failed to run event loop: %m");
216
217 log_info("Exiting.");
218 return -r;
219 }
220
221 static int help(int argc, char *argv[], void *userdata) {
222
223 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
224 "Download container or virtual machine images.\n\n"
225 " -h --help Show this help\n"
226 " --version Show package version\n"
227 " --force Force creation of image\n"
228 " --verify=MODE Verify downloaded image, one of: 'no',\n"
229 " 'checksum', 'signature'\n"
230 " --settings=BOOL Download settings file with image\n"
231 " --image-root=PATH Image root directory\n\n"
232 "Commands:\n"
233 " tar URL [NAME] Download a TAR image\n"
234 " raw URL [NAME] Download a RAW image\n",
235 program_invocation_short_name);
236
237 return 0;
238 }
239
240 static int parse_argv(int argc, char *argv[]) {
241
242 enum {
243 ARG_VERSION = 0x100,
244 ARG_FORCE,
245 ARG_IMAGE_ROOT,
246 ARG_VERIFY,
247 ARG_SETTINGS,
248 };
249
250 static const struct option options[] = {
251 { "help", no_argument, NULL, 'h' },
252 { "version", no_argument, NULL, ARG_VERSION },
253 { "force", no_argument, NULL, ARG_FORCE },
254 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
255 { "verify", required_argument, NULL, ARG_VERIFY },
256 { "settings", required_argument, NULL, ARG_SETTINGS },
257 {}
258 };
259
260 int c, r;
261
262 assert(argc >= 0);
263 assert(argv);
264
265 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
266
267 switch (c) {
268
269 case 'h':
270 return help(0, NULL, NULL);
271
272 case ARG_VERSION:
273 return version();
274
275 case ARG_FORCE:
276 arg_force = true;
277 break;
278
279 case ARG_IMAGE_ROOT:
280 arg_image_root = optarg;
281 break;
282
283 case ARG_VERIFY:
284 arg_verify = import_verify_from_string(optarg);
285 if (arg_verify < 0) {
286 log_error("Invalid verification setting '%s'", optarg);
287 return -EINVAL;
288 }
289
290 break;
291
292 case ARG_SETTINGS:
293 r = parse_boolean(optarg);
294 if (r < 0)
295 return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg);
296
297 arg_settings = r;
298 break;
299
300 case '?':
301 return -EINVAL;
302
303 default:
304 assert_not_reached("Unhandled option");
305 }
306
307 return 1;
308 }
309
310 static int pull_main(int argc, char *argv[]) {
311
312 static const Verb verbs[] = {
313 { "help", VERB_ANY, VERB_ANY, 0, help },
314 { "tar", 2, 3, 0, pull_tar },
315 { "raw", 2, 3, 0, pull_raw },
316 {}
317 };
318
319 return dispatch_verb(argc, argv, verbs, NULL);
320 }
321
322 int main(int argc, char *argv[]) {
323 int r;
324
325 setlocale(LC_ALL, "");
326 log_parse_environment();
327 log_open();
328
329 r = parse_argv(argc, argv);
330 if (r <= 0)
331 goto finish;
332
333 (void) ignore_signals(SIGPIPE, -1);
334
335 r = pull_main(argc, argv);
336
337 finish:
338 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
339 }