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"
34 #include "path-util.h"
35 #include "import-util.h"
36 #include "import-common.h"
37 #include "curl-util.h"
39 #include "pull-common.h"
41 #include "process-util.h"
43 typedef enum TarProgress
{
57 PullJob
*checksum_job
;
58 PullJob
*signature_job
;
60 TarPullFinished on_finished
;
65 bool grow_machine_directory
;
75 TarPull
* tar_pull_unref(TarPull
*i
) {
80 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
81 (void) wait_for_terminate(i
->tar_pid
, NULL
);
84 pull_job_unref(i
->tar_job
);
85 pull_job_unref(i
->checksum_job
);
86 pull_job_unref(i
->signature_job
);
88 curl_glue_unref(i
->glue
);
89 sd_event_unref(i
->event
);
92 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
107 const char *image_root
,
108 TarPullFinished on_finished
,
111 _cleanup_(tar_pull_unrefp
) TarPull
*i
= NULL
;
117 i
= new0(TarPull
, 1);
121 i
->on_finished
= on_finished
;
122 i
->userdata
= userdata
;
124 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
128 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
130 i
->event
= sd_event_ref(event
);
132 r
= curl_glue_new(&i
->glue
, i
->event
);
136 i
->glue
->on_finished
= pull_job_curl_on_finished
;
137 i
->glue
->userdata
= i
;
145 static void tar_pull_report_progress(TarPull
*i
, TarProgress p
) {
152 case TAR_DOWNLOADING
: {
153 unsigned remain
= 85;
157 if (i
->checksum_job
) {
158 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
162 if (i
->signature_job
) {
163 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
168 percent
+= i
->tar_job
->progress_percent
* remain
/ 100;
185 assert_not_reached("Unknown progress state");
188 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
189 log_debug("Combined progress %u%%", percent
);
192 static int tar_pull_make_local_copy(TarPull
*i
) {
201 if (!i
->final_path
) {
202 r
= pull_make_path(i
->tar_job
->url
, i
->tar_job
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
207 r
= pull_make_local_copy(i
->final_path
, i
->image_root
, i
->local
, i
->force_local
);
214 static bool tar_pull_is_done(TarPull
*i
) {
218 if (i
->tar_job
->state
!= PULL_JOB_DONE
)
220 if (i
->checksum_job
&& i
->checksum_job
->state
!= PULL_JOB_DONE
)
222 if (i
->signature_job
&& i
->signature_job
->state
!= PULL_JOB_DONE
)
228 static void tar_pull_job_on_finished(PullJob
*j
) {
237 if (j
== i
->checksum_job
)
238 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
239 else if (j
== i
->signature_job
)
240 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
242 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
248 /* This is invoked if either the download completed
249 * successfully, or the download was skipped because we
250 * already have the etag. */
252 if (!tar_pull_is_done(i
))
255 j
->disk_fd
= safe_close(i
->tar_job
->disk_fd
);
257 if (i
->tar_pid
> 0) {
258 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
264 if (!i
->tar_job
->etag_exists
) {
265 /* This is a new download, verify it, and move it into place */
267 tar_pull_report_progress(i
, TAR_VERIFYING
);
269 r
= pull_verify(i
->tar_job
, i
->checksum_job
, i
->signature_job
);
273 tar_pull_report_progress(i
, TAR_FINALIZING
);
275 r
= import_make_read_only(i
->temp_path
);
279 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
281 log_error_errno(r
, "Failed to rename to final image name: %m");
289 tar_pull_report_progress(i
, TAR_COPYING
);
291 r
= tar_pull_make_local_copy(i
);
299 i
->on_finished(i
, r
, i
->userdata
);
301 sd_event_exit(i
->event
, r
);
304 static int tar_pull_job_on_open_disk(PullJob
*j
) {
312 assert(i
->tar_job
== j
);
313 assert(!i
->final_path
);
314 assert(!i
->temp_path
);
315 assert(i
->tar_pid
<= 0);
317 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
321 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
325 mkdir_parents_label(i
->temp_path
, 0700);
327 r
= btrfs_subvol_make(i
->temp_path
);
329 if (mkdir(i
->temp_path
, 0755) < 0)
330 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
332 return log_error_errno(errno
, "Failed to create subvolume %s: %m", i
->temp_path
);
334 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
341 static void tar_pull_job_on_progress(PullJob
*j
) {
349 tar_pull_report_progress(i
, TAR_DOWNLOADING
);
352 int tar_pull_start(TarPull
*i
, const char *url
, const char *local
, bool force_local
, ImportVerify verify
) {
357 if (!http_url_is_valid(url
))
360 if (local
&& !machine_name_is_valid(local
))
366 r
= free_and_strdup(&i
->local
, local
);
369 i
->force_local
= force_local
;
372 r
= pull_job_new(&i
->tar_job
, url
, i
->glue
, i
);
376 i
->tar_job
->on_finished
= tar_pull_job_on_finished
;
377 i
->tar_job
->on_open_disk
= tar_pull_job_on_open_disk
;
378 i
->tar_job
->on_progress
= tar_pull_job_on_progress
;
379 i
->tar_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
380 i
->tar_job
->grow_machine_directory
= i
->grow_machine_directory
;
382 r
= pull_find_old_etags(url
, i
->image_root
, DT_DIR
, ".tar-", NULL
, &i
->tar_job
->old_etags
);
386 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
390 r
= pull_job_begin(i
->tar_job
);
394 if (i
->checksum_job
) {
395 i
->checksum_job
->on_progress
= tar_pull_job_on_progress
;
397 r
= pull_job_begin(i
->checksum_job
);
402 if (i
->signature_job
) {
403 i
->signature_job
->on_progress
= tar_pull_job_on_progress
;
405 r
= pull_job_begin(i
->signature_job
);