]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-tar.c
pull: improve --help text
[thirdparty/systemd.git] / src / import / pull-tar.c
CommitLineData
56ebfaf1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 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 <sys/prctl.h>
23#include <curl/curl.h>
24
7079cfef 25#include "sd-daemon.h"
56ebfaf1
LP
26#include "utf8.h"
27#include "strv.h"
28#include "copy.h"
29#include "btrfs-util.h"
30#include "util.h"
31#include "macro.h"
32#include "mkdir.h"
26166c88 33#include "path-util.h"
3d7415f4 34#include "import-util.h"
56ebfaf1 35#include "curl-util.h"
dc2c282b
LP
36#include "pull-job.h"
37#include "pull-common.h"
38#include "pull-tar.h"
56ebfaf1 39
7079cfef
LP
40typedef enum TarProgress {
41 TAR_DOWNLOADING,
42 TAR_VERIFYING,
43 TAR_FINALIZING,
44 TAR_COPYING,
45} TarProgress;
46
dc2c282b 47struct TarPull {
56ebfaf1
LP
48 sd_event *event;
49 CurlGlue *glue;
50
51 char *image_root;
52
dc2c282b
LP
53 PullJob *tar_job;
54 PullJob *checksum_job;
55 PullJob *signature_job;
56ebfaf1 56
dc2c282b 57 TarPullFinished on_finished;
56ebfaf1
LP
58 void *userdata;
59
56ebfaf1
LP
60 char *local;
61 bool force_local;
26166c88 62 bool grow_machine_directory;
56ebfaf1
LP
63
64 pid_t tar_pid;
65
66 char *temp_path;
67 char *final_path;
0100b6e1
LP
68
69 ImportVerify verify;
56ebfaf1
LP
70};
71
dc2c282b 72TarPull* tar_pull_unref(TarPull *i) {
56ebfaf1
LP
73 if (!i)
74 return NULL;
75
0100b6e1 76 if (i->tar_pid > 1) {
8b71fce8 77 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
0100b6e1 78 (void) wait_for_terminate(i->tar_pid, NULL);
56ebfaf1
LP
79 }
80
dc2c282b
LP
81 pull_job_unref(i->tar_job);
82 pull_job_unref(i->checksum_job);
83 pull_job_unref(i->signature_job);
56ebfaf1
LP
84
85 curl_glue_unref(i->glue);
86 sd_event_unref(i->event);
87
88 if (i->temp_path) {
89 (void) btrfs_subvol_remove(i->temp_path);
90 (void) rm_rf_dangerous(i->temp_path, false, true, false);
0d6e763b 91 free(i->temp_path);
56ebfaf1
LP
92 }
93
94 free(i->final_path);
95 free(i->image_root);
96 free(i->local);
56ebfaf1
LP
97 free(i);
98
99 return NULL;
100}
101
dc2c282b
LP
102int tar_pull_new(
103 TarPull **ret,
8b71fce8
LP
104 sd_event *event,
105 const char *image_root,
dc2c282b 106 TarPullFinished on_finished,
8b71fce8
LP
107 void *userdata) {
108
dc2c282b 109 _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
56ebfaf1
LP
110 int r;
111
112 assert(ret);
113 assert(event);
114
dc2c282b 115 i = new0(TarPull, 1);
56ebfaf1
LP
116 if (!i)
117 return -ENOMEM;
118
119 i->on_finished = on_finished;
120 i->userdata = userdata;
121
122 i->image_root = strdup(image_root ?: "/var/lib/machines");
123 if (!i->image_root)
124 return -ENOMEM;
125
26166c88
LP
126 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
127
56ebfaf1
LP
128 if (event)
129 i->event = sd_event_ref(event);
130 else {
131 r = sd_event_default(&i->event);
132 if (r < 0)
133 return r;
134 }
135
136 r = curl_glue_new(&i->glue, i->event);
137 if (r < 0)
138 return r;
139
dc2c282b 140 i->glue->on_finished = pull_job_curl_on_finished;
56ebfaf1
LP
141 i->glue->userdata = i;
142
143 *ret = i;
144 i = NULL;
145
146 return 0;
147}
148
dc2c282b 149static void tar_pull_report_progress(TarPull *i, TarProgress p) {
7079cfef
LP
150 unsigned percent;
151
152 assert(i);
153
154 switch (p) {
155
156 case TAR_DOWNLOADING: {
157 unsigned remain = 85;
158
159 percent = 0;
160
161 if (i->checksum_job) {
162 percent += i->checksum_job->progress_percent * 5 / 100;
163 remain -= 5;
164 }
165
166 if (i->signature_job) {
167 percent += i->signature_job->progress_percent * 5 / 100;
168 remain -= 5;
169 }
170
171 if (i->tar_job)
172 percent += i->tar_job->progress_percent * remain / 100;
173 break;
174 }
175
176 case TAR_VERIFYING:
177 percent = 85;
178 break;
179
180 case TAR_FINALIZING:
181 percent = 90;
182 break;
183
184 case TAR_COPYING:
185 percent = 95;
186 break;
187
188 default:
189 assert_not_reached("Unknown progress state");
190 }
191
192 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
193 log_debug("Combined progress %u%%", percent);
194}
195
dc2c282b 196static int tar_pull_make_local_copy(TarPull *i) {
0d6e763b
LP
197 int r;
198
199 assert(i);
200 assert(i->tar_job);
201
202 if (!i->local)
203 return 0;
204
205 if (!i->final_path) {
dc2c282b 206 r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
0d6e763b
LP
207 if (r < 0)
208 return log_oom();
0d6e763b
LP
209 }
210
dc2c282b 211 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
0100b6e1
LP
212 if (r < 0)
213 return r;
214
0d6e763b
LP
215 return 0;
216}
217
dc2c282b 218static bool tar_pull_is_done(TarPull *i) {
8b71fce8
LP
219 assert(i);
220 assert(i->tar_job);
221
dc2c282b 222 if (i->tar_job->state != PULL_JOB_DONE)
8b71fce8 223 return false;
dc2c282b 224 if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
8b71fce8 225 return false;
dc2c282b 226 if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
8b71fce8
LP
227 return false;
228
229 return true;
230}
231
dc2c282b
LP
232static void tar_pull_job_on_finished(PullJob *j) {
233 TarPull *i;
56ebfaf1
LP
234 int r;
235
236 assert(j);
237 assert(j->userdata);
238
239 i = j->userdata;
56ebfaf1 240 if (j->error != 0) {
0100b6e1
LP
241 if (j == i->checksum_job)
242 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
243 else if (j == i->signature_job)
244 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
245 else
246 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
247
56ebfaf1
LP
248 r = j->error;
249 goto finish;
250 }
251
252 /* This is invoked if either the download completed
253 * successfully, or the download was skipped because we
254 * already have the etag. */
255
dc2c282b 256 if (!tar_pull_is_done(i))
0100b6e1
LP
257 return;
258
259 j->disk_fd = safe_close(i->tar_job->disk_fd);
56ebfaf1
LP
260
261 if (i->tar_pid > 0) {
262 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
263 i->tar_pid = 0;
264 if (r < 0)
265 goto finish;
266 }
267
0100b6e1
LP
268 if (!i->tar_job->etag_exists) {
269 /* This is a new download, verify it, and move it into place */
270
dc2c282b 271 tar_pull_report_progress(i, TAR_VERIFYING);
7079cfef 272
dc2c282b 273 r = pull_verify(i->tar_job, i->checksum_job, i->signature_job);
0100b6e1
LP
274 if (r < 0)
275 goto finish;
276
dc2c282b 277 tar_pull_report_progress(i, TAR_FINALIZING);
7079cfef 278
dc2c282b 279 r = pull_make_read_only(i->temp_path);
56ebfaf1
LP
280 if (r < 0)
281 goto finish;
282
283 if (rename(i->temp_path, i->final_path) < 0) {
284 r = log_error_errno(errno, "Failed to rename to final image name: %m");
285 goto finish;
286 }
56ebfaf1 287
0d6e763b
LP
288 free(i->temp_path);
289 i->temp_path = NULL;
56ebfaf1
LP
290 }
291
dc2c282b 292 tar_pull_report_progress(i, TAR_COPYING);
7079cfef 293
dc2c282b 294 r = tar_pull_make_local_copy(i);
0d6e763b
LP
295 if (r < 0)
296 goto finish;
297
56ebfaf1
LP
298 r = 0;
299
300finish:
56ebfaf1
LP
301 if (i->on_finished)
302 i->on_finished(i, r, i->userdata);
303 else
304 sd_event_exit(i->event, r);
305}
306
dc2c282b
LP
307static int tar_pull_job_on_open_disk(PullJob *j) {
308 TarPull *i;
56ebfaf1
LP
309 int r;
310
311 assert(j);
312 assert(j->userdata);
313
314 i = j->userdata;
8b71fce8
LP
315 assert(i->tar_job == j);
316 assert(!i->final_path);
317 assert(!i->temp_path);
318 assert(i->tar_pid <= 0);
56ebfaf1 319
dc2c282b 320 r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
56ebfaf1
LP
321 if (r < 0)
322 return log_oom();
323
324 r = tempfn_random(i->final_path, &i->temp_path);
325 if (r < 0)
326 return log_oom();
327
328 mkdir_parents_label(i->temp_path, 0700);
329
330 r = btrfs_subvol_make(i->temp_path);
331 if (r == -ENOTTY) {
332 if (mkdir(i->temp_path, 0755) < 0)
333 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
334 } else if (r < 0)
335 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
336
dc2c282b 337 j->disk_fd = pull_fork_tar(i->temp_path, &i->tar_pid);
2c140ded
LP
338 if (j->disk_fd < 0)
339 return j->disk_fd;
56ebfaf1
LP
340
341 return 0;
342}
343
dc2c282b
LP
344static void tar_pull_job_on_progress(PullJob *j) {
345 TarPull *i;
7079cfef
LP
346
347 assert(j);
348 assert(j->userdata);
349
350 i = j->userdata;
351
dc2c282b 352 tar_pull_report_progress(i, TAR_DOWNLOADING);
7079cfef
LP
353}
354
dc2c282b 355int tar_pull_start(TarPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
56ebfaf1
LP
356 int r;
357
358 assert(i);
359
56ebfaf1
LP
360 if (!http_url_is_valid(url))
361 return -EINVAL;
362
363 if (local && !machine_name_is_valid(local))
364 return -EINVAL;
365
8b71fce8
LP
366 if (i->tar_job)
367 return -EBUSY;
368
56ebfaf1
LP
369 r = free_and_strdup(&i->local, local);
370 if (r < 0)
371 return r;
56ebfaf1 372 i->force_local = force_local;
0100b6e1 373 i->verify = verify;
56ebfaf1 374
dc2c282b 375 r = pull_job_new(&i->tar_job, url, i->glue, i);
56ebfaf1
LP
376 if (r < 0)
377 return r;
378
dc2c282b
LP
379 i->tar_job->on_finished = tar_pull_job_on_finished;
380 i->tar_job->on_open_disk = tar_pull_job_on_open_disk;
381 i->tar_job->on_progress = tar_pull_job_on_progress;
0100b6e1 382 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
26166c88 383 i->tar_job->grow_machine_directory = i->grow_machine_directory;
56ebfaf1 384
dc2c282b 385 r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
56ebfaf1
LP
386 if (r < 0)
387 return r;
388
dc2c282b 389 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
0100b6e1
LP
390 if (r < 0)
391 return r;
392
dc2c282b 393 r = pull_job_begin(i->tar_job);
0100b6e1
LP
394 if (r < 0)
395 return r;
396
397 if (i->checksum_job) {
dc2c282b 398 i->checksum_job->on_progress = tar_pull_job_on_progress;
7079cfef 399
dc2c282b 400 r = pull_job_begin(i->checksum_job);
0100b6e1
LP
401 if (r < 0)
402 return r;
403 }
404
405 if (i->signature_job) {
dc2c282b 406 i->signature_job->on_progress = tar_pull_job_on_progress;
7079cfef 407
dc2c282b 408 r = pull_job_begin(i->signature_job);
0100b6e1
LP
409 if (r < 0)
410 return r;
411 }
412
413 return 0;
56ebfaf1 414}