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 "import-common.h"
36 #include "curl-util.h"
38 #include "pull-common.h"
41 typedef enum TarProgress {
55 PullJob *checksum_job;
56 PullJob *signature_job;
58 TarPullFinished on_finished;
63 bool grow_machine_directory;
73 TarPull* tar_pull_unref(TarPull *i) {
78 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
79 (void) wait_for_terminate(i->tar_pid, NULL);
82 pull_job_unref(i->tar_job);
83 pull_job_unref(i->checksum_job);
84 pull_job_unref(i->signature_job);
86 curl_glue_unref(i->glue);
87 sd_event_unref(i->event);
90 (void) btrfs_subvol_remove(i->temp_path);
91 (void) rm_rf_dangerous(i->temp_path, false, true, false);
106 const char *image_root,
107 TarPullFinished on_finished,
110 _cleanup_(tar_pull_unrefp) TarPull *i = NULL;
116 i = new0(TarPull, 1);
120 i->on_finished = on_finished;
121 i->userdata = userdata;
123 i->image_root = strdup(image_root ?: "/var/lib/machines");
127 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
130 i->event = sd_event_ref(event);
132 r = sd_event_default(&i->event);
137 r = curl_glue_new(&i->glue, i->event);
141 i->glue->on_finished = pull_job_curl_on_finished;
142 i->glue->userdata = i;
150 static void tar_pull_report_progress(TarPull *i, TarProgress p) {
157 case TAR_DOWNLOADING: {
158 unsigned remain = 85;
162 if (i->checksum_job) {
163 percent += i->checksum_job->progress_percent * 5 / 100;
167 if (i->signature_job) {
168 percent += i->signature_job->progress_percent * 5 / 100;
173 percent += i->tar_job->progress_percent * remain / 100;
190 assert_not_reached("Unknown progress state");
193 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
194 log_debug("Combined progress %u%%", percent);
197 static int tar_pull_make_local_copy(TarPull *i) {
206 if (!i->final_path) {
207 r = pull_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
212 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
219 static bool tar_pull_is_done(TarPull *i) {
223 if (i->tar_job->state != PULL_JOB_DONE)
225 if (i->checksum_job && i->checksum_job->state != PULL_JOB_DONE)
227 if (i->signature_job && i->signature_job->state != PULL_JOB_DONE)
233 static void tar_pull_job_on_finished(PullJob *j) {
242 if (j == i->checksum_job)
243 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
244 else if (j == i->signature_job)
245 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
247 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
253 /* This is invoked if either the download completed
254 * successfully, or the download was skipped because we
255 * already have the etag. */
257 if (!tar_pull_is_done(i))
260 j->disk_fd = safe_close(i->tar_job->disk_fd);
262 if (i->tar_pid > 0) {
263 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
269 if (!i->tar_job->etag_exists) {
270 /* This is a new download, verify it, and move it into place */
272 tar_pull_report_progress(i, TAR_VERIFYING);
274 r = pull_verify(i->tar_job, i->checksum_job, i->signature_job);
278 tar_pull_report_progress(i, TAR_FINALIZING);
280 r = import_make_read_only(i->temp_path);
284 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
286 log_error_errno(r, "Failed to rename to final image name: %m");
294 tar_pull_report_progress(i, TAR_COPYING);
296 r = tar_pull_make_local_copy(i);
304 i->on_finished(i, r, i->userdata);
306 sd_event_exit(i->event, r);
309 static int tar_pull_job_on_open_disk(PullJob *j) {
317 assert(i->tar_job == j);
318 assert(!i->final_path);
319 assert(!i->temp_path);
320 assert(i->tar_pid <= 0);
322 r = pull_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
326 r = tempfn_random(i->final_path, &i->temp_path);
330 mkdir_parents_label(i->temp_path, 0700);
332 r = btrfs_subvol_make(i->temp_path);
334 if (mkdir(i->temp_path, 0755) < 0)
335 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
337 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
339 j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
346 static void tar_pull_job_on_progress(PullJob *j) {
354 tar_pull_report_progress(i, TAR_DOWNLOADING);
357 int tar_pull_start(TarPull *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
362 if (!http_url_is_valid(url))
365 if (local && !machine_name_is_valid(local))
371 r = free_and_strdup(&i->local, local);
374 i->force_local = force_local;
377 r = pull_job_new(&i->tar_job, url, i->glue, i);
381 i->tar_job->on_finished = tar_pull_job_on_finished;
382 i->tar_job->on_open_disk = tar_pull_job_on_open_disk;
383 i->tar_job->on_progress = tar_pull_job_on_progress;
384 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
385 i->tar_job->grow_machine_directory = i->grow_machine_directory;
387 r = pull_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
391 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_pull_job_on_finished, i);
395 r = pull_job_begin(i->tar_job);
399 if (i->checksum_job) {
400 i->checksum_job->on_progress = tar_pull_job_on_progress;
402 r = pull_job_begin(i->checksum_job);
407 if (i->signature_job) {
408 i->signature_job->on_progress = tar_pull_job_on_progress;
410 r = pull_job_begin(i->signature_job);