1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2015 Lennart Poettering
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.
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.
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/>.
22 #include <sys/prctl.h>
23 #include <curl/curl.h>
25 #include "sd-daemon.h"
29 #include "btrfs-util.h"
33 #include "path-util.h"
34 #include "import-util.h"
35 #include "curl-util.h"
37 #include "pull-common.h"
40 typedef enum TarProgress {
54 PullJob *checksum_job;
55 PullJob *signature_job;
57 TarPullFinished on_finished;
62 bool grow_machine_directory;
72 TarPull* tar_pull_unref(TarPull *i) {
77 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
78 (void) wait_for_terminate(i->tar_pid, NULL);
81 pull_job_unref(i->tar_job);
82 pull_job_unref(i->checksum_job);
83 pull_job_unref(i->signature_job);
85 curl_glue_unref(i->glue);
86 sd_event_unref(i->event);
89 (void) btrfs_subvol_remove(i->temp_path);
90 (void) rm_rf_dangerous(i->temp_path, false, true, false);
105 const char *image_root,
106 TarPullFinished on_finished,
109 _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
115 i = new0(TarPull, 1);
119 i->on_finished = on_finished;
120 i->userdata = userdata;
122 i->image_root = strdup(image_root ?: "/var/lib/machines");
126 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
129 i->event = sd_event_ref(event);
131 r = sd_event_default(&i->event);
136 r = curl_glue_new(&i->glue, i->event);
140 i->glue->on_finished = pull_job_curl_on_finished;
141 i->glue->userdata = i;
149 static void tar_pull_report_progress(TarPull *i, TarProgress p) {
156 case TAR_DOWNLOADING: {
157 unsigned remain = 85;
161 if (i->checksum_job) {
162 percent += i->checksum_job->progress_percent * 5 / 100;
166 if (i->signature_job) {
167 percent += i->signature_job->progress_percent * 5 / 100;
172 percent += i->tar_job->progress_percent * remain / 100;
189 assert_not_reached("Unknown progress state");
192 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
193 log_debug("Combined progress %u%%", percent);
196 static int tar_pull_make_local_copy(TarPull *i) {
205 if (!i->final_path) {
206 r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
211 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
218 static bool tar_pull_is_done(TarPull *i) {
222 if (i->tar_job->state != PULL_JOB_DONE)
224 if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
226 if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
232 static void tar_pull_job_on_finished(PullJob *j) {
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?)");
246 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
252 /* This is invoked if either the download completed
253 * successfully, or the download was skipped because we
254 * already have the etag. */
256 if (!tar_pull_is_done(i))
259 j->disk_fd = safe_close(i->tar_job->disk_fd);
261 if (i->tar_pid > 0) {
262 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
268 if (!i->tar_job->etag_exists) {
269 /* This is a new download, verify it, and move it into place */
271 tar_pull_report_progress(i, TAR_VERIFYING);
273 r = pull_verify(i->tar_job, i->checksum_job, i->signature_job);
277 tar_pull_report_progress(i, TAR_FINALIZING);
279 r = pull_make_read_only(i->temp_path);
283 if (rename(i->temp_path, i->final_path) < 0) {
284 r = log_error_errno(errno, "Failed to rename to final image name: %m");
292 tar_pull_report_progress(i, TAR_COPYING);
294 r = tar_pull_make_local_copy(i);
302 i->on_finished(i, r, i->userdata);
304 sd_event_exit(i->event, r);
307 static int tar_pull_job_on_open_disk(PullJob *j) {
315 assert(i->tar_job == j);
316 assert(!i->final_path);
317 assert(!i->temp_path);
318 assert(i->tar_pid <= 0);
320 r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
324 r = tempfn_random(i->final_path, &i->temp_path);
328 mkdir_parents_label(i->temp_path, 0700);
330 r = btrfs_subvol_make(i->temp_path);
332 if (mkdir(i->temp_path, 0755) < 0)
333 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
335 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
337 j->disk_fd = pull_fork_tar(i->temp_path, &i->tar_pid);
344 static void tar_pull_job_on_progress(PullJob *j) {
352 tar_pull_report_progress(i, TAR_DOWNLOADING);
355 int tar_pull_start(TarPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
360 if (!http_url_is_valid(url))
363 if (local && !machine_name_is_valid(local))
369 r = free_and_strdup(&i->local, local);
372 i->force_local = force_local;
375 r = pull_job_new(&i->tar_job, url, i->glue, i);
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;
382 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
383 i->tar_job->grow_machine_directory = i->grow_machine_directory;
385 r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
389 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
393 r = pull_job_begin(i->tar_job);
397 if (i->checksum_job) {
398 i->checksum_job->on_progress = tar_pull_job_on_progress;
400 r = pull_job_begin(i->checksum_job);
405 if (i->signature_job) {
406 i->signature_job->on_progress = tar_pull_job_on_progress;
408 r = pull_job_begin(i->signature_job);