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 "btrfs-util.h"
29 #include "curl-util.h"
31 #include "hostname-util.h"
32 #include "import-common.h"
33 #include "import-util.h"
36 #include "path-util.h"
37 #include "process-util.h"
38 #include "pull-common.h"
42 #include "string-util.h"
47 typedef enum TarProgress
{
61 PullJob
*settings_job
;
62 PullJob
*checksum_job
;
63 PullJob
*signature_job
;
65 TarPullFinished on_finished
;
70 bool grow_machine_directory
;
79 char *settings_temp_path
;
84 TarPull
* tar_pull_unref(TarPull
*i
) {
89 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
90 (void) wait_for_terminate(i
->tar_pid
, NULL
);
93 pull_job_unref(i
->tar_job
);
94 pull_job_unref(i
->settings_job
);
95 pull_job_unref(i
->checksum_job
);
96 pull_job_unref(i
->signature_job
);
98 curl_glue_unref(i
->glue
);
99 sd_event_unref(i
->event
);
102 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
106 if (i
->settings_temp_path
) {
107 (void) unlink(i
->settings_temp_path
);
108 free(i
->settings_temp_path
);
112 free(i
->settings_path
);
123 const char *image_root
,
124 TarPullFinished on_finished
,
127 _cleanup_(tar_pull_unrefp
) TarPull
*i
= NULL
;
132 i
= new0(TarPull
, 1);
136 i
->on_finished
= on_finished
;
137 i
->userdata
= userdata
;
139 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
143 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
146 i
->event
= sd_event_ref(event
);
148 r
= sd_event_default(&i
->event
);
153 r
= curl_glue_new(&i
->glue
, i
->event
);
157 i
->glue
->on_finished
= pull_job_curl_on_finished
;
158 i
->glue
->userdata
= i
;
166 static void tar_pull_report_progress(TarPull
*i
, TarProgress p
) {
173 case TAR_DOWNLOADING
: {
174 unsigned remain
= 85;
178 if (i
->settings_job
) {
179 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
183 if (i
->checksum_job
) {
184 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
188 if (i
->signature_job
) {
189 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
194 percent
+= i
->tar_job
->progress_percent
* remain
/ 100;
211 assert_not_reached("Unknown progress state");
214 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
215 log_debug("Combined progress %u%%", percent
);
218 static int tar_pull_make_local_copy(TarPull
*i
) {
227 if (!i
->final_path
) {
228 r
= pull_make_path(i
->tar_job
->url
, i
->tar_job
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
233 r
= pull_make_local_copy(i
->final_path
, i
->image_root
, i
->local
, i
->force_local
);
238 const char *local_settings
;
239 assert(i
->settings_job
);
241 if (!i
->settings_path
) {
242 r
= pull_make_path(i
->settings_job
->url
, i
->settings_job
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
247 local_settings
= strjoina(i
->image_root
, "/", i
->local
, ".nspawn");
249 r
= copy_file_atomic(i
->settings_path
, local_settings
, 0664, i
->force_local
, 0);
251 log_warning_errno(r
, "Settings file %s already exists, not replacing.", local_settings
);
252 else if (r
< 0 && r
!= -ENOENT
)
253 log_warning_errno(r
, "Failed to copy settings files %s, ignoring: %m", local_settings
);
255 log_info("Created new settings file '%s.nspawn'", i
->local
);
261 static bool tar_pull_is_done(TarPull
*i
) {
265 if (!PULL_JOB_IS_COMPLETE(i
->tar_job
))
267 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
269 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
271 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
277 static void tar_pull_job_on_finished(PullJob
*j
) {
286 if (j
== i
->settings_job
) {
288 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
289 } else if (j
->error
!= 0) {
290 if (j
== i
->checksum_job
)
291 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
292 else if (j
== i
->signature_job
)
293 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
295 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
301 /* This is invoked if either the download completed
302 * successfully, or the download was skipped because we
303 * already have the etag. */
305 if (!tar_pull_is_done(i
))
308 i
->tar_job
->disk_fd
= safe_close(i
->tar_job
->disk_fd
);
310 i
->settings_job
->disk_fd
= safe_close(i
->settings_job
->disk_fd
);
312 if (i
->tar_pid
> 0) {
313 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
323 if (!i
->tar_job
->etag_exists
) {
324 /* This is a new download, verify it, and move it into place */
326 tar_pull_report_progress(i
, TAR_VERIFYING
);
328 r
= pull_verify(i
->tar_job
, i
->settings_job
, i
->checksum_job
, i
->signature_job
);
332 tar_pull_report_progress(i
, TAR_FINALIZING
);
334 r
= import_make_read_only(i
->temp_path
);
338 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
340 log_error_errno(r
, "Failed to rename to final image name: %m");
344 i
->temp_path
= mfree(i
->temp_path
);
346 if (i
->settings_job
&&
347 i
->settings_job
->error
== 0 &&
348 !i
->settings_job
->etag_exists
) {
350 assert(i
->settings_temp_path
);
351 assert(i
->settings_path
);
353 /* Also move the settings file into place, if
354 * it exist. Note that we do so only if we
355 * also moved the tar file in place, to keep
356 * things strictly in sync. */
358 r
= import_make_read_only(i
->settings_temp_path
);
362 r
= rename_noreplace(AT_FDCWD
, i
->settings_temp_path
, AT_FDCWD
, i
->settings_path
);
364 log_error_errno(r
, "Failed to rename settings file: %m");
368 i
->settings_temp_path
= mfree(i
->settings_temp_path
);
372 tar_pull_report_progress(i
, TAR_COPYING
);
374 r
= tar_pull_make_local_copy(i
);
382 i
->on_finished(i
, r
, i
->userdata
);
384 sd_event_exit(i
->event
, r
);
387 static int tar_pull_job_on_open_disk_tar(PullJob
*j
) {
395 assert(i
->tar_job
== j
);
396 assert(!i
->final_path
);
397 assert(!i
->temp_path
);
398 assert(i
->tar_pid
<= 0);
400 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
404 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
408 mkdir_parents_label(i
->temp_path
, 0700);
410 r
= btrfs_subvol_make(i
->temp_path
);
412 if (mkdir(i
->temp_path
, 0755) < 0)
413 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
415 return log_error_errno(errno
, "Failed to create subvolume %s: %m", i
->temp_path
);
417 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
419 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
426 static int tar_pull_job_on_open_disk_settings(PullJob
*j
) {
434 assert(i
->settings_job
== j
);
435 assert(!i
->settings_path
);
436 assert(!i
->settings_temp_path
);
438 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
442 r
= tempfn_random(i
->settings_path
, NULL
, &i
->settings_temp_path
);
446 mkdir_parents_label(i
->settings_temp_path
, 0700);
448 j
->disk_fd
= open(i
->settings_temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
450 return log_error_errno(errno
, "Failed to create %s: %m", i
->settings_temp_path
);
455 static void tar_pull_job_on_progress(PullJob
*j
) {
463 tar_pull_report_progress(i
, TAR_DOWNLOADING
);
477 assert(verify
< _IMPORT_VERIFY_MAX
);
480 if (!http_url_is_valid(url
))
483 if (local
&& !machine_name_is_valid(local
))
489 r
= free_and_strdup(&i
->local
, local
);
493 i
->force_local
= force_local
;
495 i
->settings
= settings
;
497 /* Set up download job for TAR file */
498 r
= pull_job_new(&i
->tar_job
, url
, i
->glue
, i
);
502 i
->tar_job
->on_finished
= tar_pull_job_on_finished
;
503 i
->tar_job
->on_open_disk
= tar_pull_job_on_open_disk_tar
;
504 i
->tar_job
->on_progress
= tar_pull_job_on_progress
;
505 i
->tar_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
506 i
->tar_job
->grow_machine_directory
= i
->grow_machine_directory
;
508 r
= pull_find_old_etags(url
, i
->image_root
, DT_DIR
, ".tar-", NULL
, &i
->tar_job
->old_etags
);
512 /* Set up download job for the settings file (.nspawn) */
514 r
= pull_make_settings_job(&i
->settings_job
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
518 i
->settings_job
->on_open_disk
= tar_pull_job_on_open_disk_settings
;
519 i
->settings_job
->on_progress
= tar_pull_job_on_progress
;
520 i
->settings_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
522 r
= pull_find_old_etags(i
->settings_job
->url
, i
->image_root
, DT_REG
, ".settings-", NULL
, &i
->settings_job
->old_etags
);
527 /* Set up download of checksum/signature files */
528 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
532 r
= pull_job_begin(i
->tar_job
);
536 if (i
->settings_job
) {
537 r
= pull_job_begin(i
->settings_job
);
542 if (i
->checksum_job
) {
543 i
->checksum_job
->on_progress
= tar_pull_job_on_progress
;
545 r
= pull_job_begin(i
->checksum_job
);
550 if (i
->signature_job
) {
551 i
->signature_job
->on_progress
= tar_pull_job_on_progress
;
553 r
= pull_job_begin(i
->signature_job
);