]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/import/pull.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / import / pull.c
... / ...
CommitLineData
1/* SPDX-License-Identifier: LGPL-2.1+ */
2/***
3 This file is part of systemd.
4
5 Copyright 2014 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <getopt.h>
22
23#include "sd-event.h"
24
25#include "alloc-util.h"
26#include "hostname-util.h"
27#include "import-util.h"
28#include "machine-image.h"
29#include "parse-util.h"
30#include "pull-raw.h"
31#include "pull-tar.h"
32#include "signal-util.h"
33#include "string-util.h"
34#include "verbs.h"
35#include "web-util.h"
36
37static bool arg_force = false;
38static const char *arg_image_root = "/var/lib/machines";
39static ImportVerify arg_verify = IMPORT_VERIFY_SIGNATURE;
40static bool arg_settings = true;
41static bool arg_roothash = true;
42
43static 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
49static 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
59static 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("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
135static 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
145static 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("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, arg_roothash);
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
221static 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 " --roothash=BOOL Download root hash file with image\n"
232 " --image-root=PATH Image root directory\n\n"
233 "Commands:\n"
234 " tar URL [NAME] Download a TAR image\n"
235 " raw URL [NAME] Download a RAW image\n",
236 program_invocation_short_name);
237
238 return 0;
239}
240
241static int parse_argv(int argc, char *argv[]) {
242
243 enum {
244 ARG_VERSION = 0x100,
245 ARG_FORCE,
246 ARG_IMAGE_ROOT,
247 ARG_VERIFY,
248 ARG_SETTINGS,
249 ARG_ROOTHASH,
250 };
251
252 static const struct option options[] = {
253 { "help", no_argument, NULL, 'h' },
254 { "version", no_argument, NULL, ARG_VERSION },
255 { "force", no_argument, NULL, ARG_FORCE },
256 { "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
257 { "verify", required_argument, NULL, ARG_VERIFY },
258 { "settings", required_argument, NULL, ARG_SETTINGS },
259 { "roothash", required_argument, NULL, ARG_ROOTHASH },
260 {}
261 };
262
263 int c, r;
264
265 assert(argc >= 0);
266 assert(argv);
267
268 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
269
270 switch (c) {
271
272 case 'h':
273 return help(0, NULL, NULL);
274
275 case ARG_VERSION:
276 return version();
277
278 case ARG_FORCE:
279 arg_force = true;
280 break;
281
282 case ARG_IMAGE_ROOT:
283 arg_image_root = optarg;
284 break;
285
286 case ARG_VERIFY:
287 arg_verify = import_verify_from_string(optarg);
288 if (arg_verify < 0) {
289 log_error("Invalid verification setting '%s'", optarg);
290 return -EINVAL;
291 }
292
293 break;
294
295 case ARG_SETTINGS:
296 r = parse_boolean(optarg);
297 if (r < 0)
298 return log_error_errno(r, "Failed to parse --settings= parameter '%s'", optarg);
299
300 arg_settings = r;
301 break;
302
303 case ARG_ROOTHASH:
304 r = parse_boolean(optarg);
305 if (r < 0)
306 return log_error_errno(r, "Failed to parse --roothash= parameter '%s'", optarg);
307
308 arg_roothash = r;
309 break;
310
311 case '?':
312 return -EINVAL;
313
314 default:
315 assert_not_reached("Unhandled option");
316 }
317
318 return 1;
319}
320
321static int pull_main(int argc, char *argv[]) {
322
323 static const Verb verbs[] = {
324 { "help", VERB_ANY, VERB_ANY, 0, help },
325 { "tar", 2, 3, 0, pull_tar },
326 { "raw", 2, 3, 0, pull_raw },
327 {}
328 };
329
330 return dispatch_verb(argc, argv, verbs, NULL);
331}
332
333int main(int argc, char *argv[]) {
334 int r;
335
336 setlocale(LC_ALL, "");
337 log_parse_environment();
338 log_open();
339
340 r = parse_argv(argc, argv);
341 if (r <= 0)
342 goto finish;
343
344 (void) ignore_signals(SIGPIPE, -1);
345
346 r = pull_main(argc, argv);
347
348finish:
349 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
350}