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