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"
27 #include "alloc-util.h"
28 #include "btrfs-util.h"
30 #include "curl-util.h"
34 #include "hostname-util.h"
35 #include "import-common.h"
36 #include "import-util.h"
39 #include "path-util.h"
40 #include "process-util.h"
41 #include "pull-common.h"
45 #include "string-util.h"
51 typedef enum TarProgress
{
65 PullJob
*settings_job
;
66 PullJob
*checksum_job
;
67 PullJob
*signature_job
;
69 TarPullFinished on_finished
;
74 bool grow_machine_directory
;
83 char *settings_temp_path
;
88 TarPull
* tar_pull_unref(TarPull
*i
) {
93 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
94 (void) wait_for_terminate(i
->tar_pid
, NULL
);
97 pull_job_unref(i
->tar_job
);
98 pull_job_unref(i
->settings_job
);
99 pull_job_unref(i
->checksum_job
);
100 pull_job_unref(i
->signature_job
);
102 curl_glue_unref(i
->glue
);
103 sd_event_unref(i
->event
);
106 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
110 if (i
->settings_temp_path
) {
111 (void) unlink(i
->settings_temp_path
);
112 free(i
->settings_temp_path
);
116 free(i
->settings_path
);
127 const char *image_root
,
128 TarPullFinished on_finished
,
131 _cleanup_(tar_pull_unrefp
) TarPull
*i
= NULL
;
136 i
= new0(TarPull
, 1);
140 i
->on_finished
= on_finished
;
141 i
->userdata
= userdata
;
143 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
147 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
150 i
->event
= sd_event_ref(event
);
152 r
= sd_event_default(&i
->event
);
157 r
= curl_glue_new(&i
->glue
, i
->event
);
161 i
->glue
->on_finished
= pull_job_curl_on_finished
;
162 i
->glue
->userdata
= i
;
170 static void tar_pull_report_progress(TarPull
*i
, TarProgress p
) {
177 case TAR_DOWNLOADING
: {
178 unsigned remain
= 85;
182 if (i
->settings_job
) {
183 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
187 if (i
->checksum_job
) {
188 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
192 if (i
->signature_job
) {
193 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
198 percent
+= i
->tar_job
->progress_percent
* remain
/ 100;
215 assert_not_reached("Unknown progress state");
218 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
219 log_debug("Combined progress %u%%", percent
);
222 static int tar_pull_make_local_copy(TarPull
*i
) {
231 if (!i
->final_path
) {
232 r
= pull_make_path(i
->tar_job
->url
, i
->tar_job
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
237 r
= pull_make_local_copy(i
->final_path
, i
->image_root
, i
->local
, i
->force_local
);
242 const char *local_settings
;
243 assert(i
->settings_job
);
245 if (!i
->settings_path
) {
246 r
= pull_make_path(i
->settings_job
->url
, i
->settings_job
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
251 local_settings
= strjoina(i
->image_root
, "/", i
->local
, ".nspawn");
253 r
= copy_file_atomic(i
->settings_path
, local_settings
, 0664, i
->force_local
, 0);
255 log_warning_errno(r
, "Settings file %s already exists, not replacing.", local_settings
);
256 else if (r
< 0 && r
!= -ENOENT
)
257 log_warning_errno(r
, "Failed to copy settings files %s, ignoring: %m", local_settings
);
259 log_info("Created new settings file '%s.nspawn'", i
->local
);
265 static bool tar_pull_is_done(TarPull
*i
) {
269 if (!PULL_JOB_IS_COMPLETE(i
->tar_job
))
271 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
273 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
275 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
281 static void tar_pull_job_on_finished(PullJob
*j
) {
290 if (j
== i
->settings_job
) {
292 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
293 } else if (j
->error
!= 0) {
294 if (j
== i
->checksum_job
)
295 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
296 else if (j
== i
->signature_job
)
297 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
299 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
305 /* This is invoked if either the download completed
306 * successfully, or the download was skipped because we
307 * already have the etag. */
309 if (!tar_pull_is_done(i
))
312 i
->tar_job
->disk_fd
= safe_close(i
->tar_job
->disk_fd
);
314 i
->settings_job
->disk_fd
= safe_close(i
->settings_job
->disk_fd
);
316 if (i
->tar_pid
> 0) {
317 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
327 if (!i
->tar_job
->etag_exists
) {
328 /* This is a new download, verify it, and move it into place */
330 tar_pull_report_progress(i
, TAR_VERIFYING
);
332 r
= pull_verify(i
->tar_job
, i
->settings_job
, i
->checksum_job
, i
->signature_job
);
336 tar_pull_report_progress(i
, TAR_FINALIZING
);
338 r
= import_make_read_only(i
->temp_path
);
342 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
344 log_error_errno(r
, "Failed to rename to final image name: %m");
348 i
->temp_path
= mfree(i
->temp_path
);
350 if (i
->settings_job
&&
351 i
->settings_job
->error
== 0 &&
352 !i
->settings_job
->etag_exists
) {
354 assert(i
->settings_temp_path
);
355 assert(i
->settings_path
);
357 /* Also move the settings file into place, if
358 * it exist. Note that we do so only if we
359 * also moved the tar file in place, to keep
360 * things strictly in sync. */
362 r
= import_make_read_only(i
->settings_temp_path
);
366 r
= rename_noreplace(AT_FDCWD
, i
->settings_temp_path
, AT_FDCWD
, i
->settings_path
);
368 log_error_errno(r
, "Failed to rename settings file: %m");
372 i
->settings_temp_path
= mfree(i
->settings_temp_path
);
376 tar_pull_report_progress(i
, TAR_COPYING
);
378 r
= tar_pull_make_local_copy(i
);
386 i
->on_finished(i
, r
, i
->userdata
);
388 sd_event_exit(i
->event
, r
);
391 static int tar_pull_job_on_open_disk_tar(PullJob
*j
) {
399 assert(i
->tar_job
== j
);
400 assert(!i
->final_path
);
401 assert(!i
->temp_path
);
402 assert(i
->tar_pid
<= 0);
404 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
408 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
412 mkdir_parents_label(i
->temp_path
, 0700);
414 r
= btrfs_subvol_make(i
->temp_path
);
416 if (mkdir(i
->temp_path
, 0755) < 0)
417 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
419 return log_error_errno(r
, "Failed to create subvolume %s: %m", i
->temp_path
);
421 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
423 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
430 static int tar_pull_job_on_open_disk_settings(PullJob
*j
) {
438 assert(i
->settings_job
== j
);
439 assert(!i
->settings_path
);
440 assert(!i
->settings_temp_path
);
442 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
446 r
= tempfn_random(i
->settings_path
, NULL
, &i
->settings_temp_path
);
450 mkdir_parents_label(i
->settings_temp_path
, 0700);
452 j
->disk_fd
= open(i
->settings_temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
454 return log_error_errno(errno
, "Failed to create %s: %m", i
->settings_temp_path
);
459 static void tar_pull_job_on_progress(PullJob
*j
) {
467 tar_pull_report_progress(i
, TAR_DOWNLOADING
);
481 assert(verify
< _IMPORT_VERIFY_MAX
);
484 if (!http_url_is_valid(url
))
487 if (local
&& !machine_name_is_valid(local
))
493 r
= free_and_strdup(&i
->local
, local
);
497 i
->force_local
= force_local
;
499 i
->settings
= settings
;
501 /* Set up download job for TAR file */
502 r
= pull_job_new(&i
->tar_job
, url
, i
->glue
, i
);
506 i
->tar_job
->on_finished
= tar_pull_job_on_finished
;
507 i
->tar_job
->on_open_disk
= tar_pull_job_on_open_disk_tar
;
508 i
->tar_job
->on_progress
= tar_pull_job_on_progress
;
509 i
->tar_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
510 i
->tar_job
->grow_machine_directory
= i
->grow_machine_directory
;
512 r
= pull_find_old_etags(url
, i
->image_root
, DT_DIR
, ".tar-", NULL
, &i
->tar_job
->old_etags
);
516 /* Set up download job for the settings file (.nspawn) */
518 r
= pull_make_settings_job(&i
->settings_job
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
522 i
->settings_job
->on_open_disk
= tar_pull_job_on_open_disk_settings
;
523 i
->settings_job
->on_progress
= tar_pull_job_on_progress
;
524 i
->settings_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
526 r
= pull_find_old_etags(i
->settings_job
->url
, i
->image_root
, DT_REG
, ".settings-", NULL
, &i
->settings_job
->old_etags
);
531 /* Set up download of checksum/signature files */
532 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
536 r
= pull_job_begin(i
->tar_job
);
540 if (i
->settings_job
) {
541 r
= pull_job_begin(i
->settings_job
);
546 if (i
->checksum_job
) {
547 i
->checksum_job
->on_progress
= tar_pull_job_on_progress
;
549 r
= pull_job_begin(i
->checksum_job
);
554 if (i
->signature_job
) {
555 i
->signature_job
->on_progress
= tar_pull_job_on_progress
;
557 r
= pull_job_begin(i
->signature_job
);