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"
30 #include "hostname-util.h"
31 #include "import-common.h"
32 #include "import-util.h"
35 #include "path-util.h"
36 #include "process-util.h"
37 #include "pull-common.h"
40 #include "string-util.h"
46 typedef enum TarProgress
{
60 PullJob
*settings_job
;
61 PullJob
*checksum_job
;
62 PullJob
*signature_job
;
64 TarPullFinished on_finished
;
69 bool grow_machine_directory
;
78 char *settings_temp_path
;
83 TarPull
* tar_pull_unref(TarPull
*i
) {
88 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
89 (void) wait_for_terminate(i
->tar_pid
, NULL
);
92 pull_job_unref(i
->tar_job
);
93 pull_job_unref(i
->settings_job
);
94 pull_job_unref(i
->checksum_job
);
95 pull_job_unref(i
->signature_job
);
97 curl_glue_unref(i
->glue
);
98 sd_event_unref(i
->event
);
101 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
105 if (i
->settings_temp_path
) {
106 (void) unlink(i
->settings_temp_path
);
107 free(i
->settings_temp_path
);
111 free(i
->settings_path
);
122 const char *image_root
,
123 TarPullFinished on_finished
,
126 _cleanup_(tar_pull_unrefp
) TarPull
*i
= NULL
;
131 i
= new0(TarPull
, 1);
135 i
->on_finished
= on_finished
;
136 i
->userdata
= userdata
;
138 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
142 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
145 i
->event
= sd_event_ref(event
);
147 r
= sd_event_default(&i
->event
);
152 r
= curl_glue_new(&i
->glue
, i
->event
);
156 i
->glue
->on_finished
= pull_job_curl_on_finished
;
157 i
->glue
->userdata
= i
;
165 static void tar_pull_report_progress(TarPull
*i
, TarProgress p
) {
172 case TAR_DOWNLOADING
: {
173 unsigned remain
= 85;
177 if (i
->settings_job
) {
178 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
182 if (i
->checksum_job
) {
183 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
187 if (i
->signature_job
) {
188 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
193 percent
+= i
->tar_job
->progress_percent
* remain
/ 100;
210 assert_not_reached("Unknown progress state");
213 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
214 log_debug("Combined progress %u%%", percent
);
217 static int tar_pull_make_local_copy(TarPull
*i
) {
226 if (!i
->final_path
) {
227 r
= pull_make_path(i
->tar_job
->url
, i
->tar_job
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
232 r
= pull_make_local_copy(i
->final_path
, i
->image_root
, i
->local
, i
->force_local
);
237 const char *local_settings
;
238 assert(i
->settings_job
);
240 if (!i
->settings_path
) {
241 r
= pull_make_path(i
->settings_job
->url
, i
->settings_job
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
246 local_settings
= strjoina(i
->image_root
, "/", i
->local
, ".nspawn");
248 r
= copy_file_atomic(i
->settings_path
, local_settings
, 0664, i
->force_local
, 0);
250 log_warning_errno(r
, "Settings file %s already exists, not replacing.", local_settings
);
251 else if (r
< 0 && r
!= -ENOENT
)
252 log_warning_errno(r
, "Failed to copy settings files %s, ignoring: %m", local_settings
);
254 log_info("Created new settings file '%s.nspawn'", i
->local
);
260 static bool tar_pull_is_done(TarPull
*i
) {
264 if (!PULL_JOB_IS_COMPLETE(i
->tar_job
))
266 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
268 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
270 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
276 static void tar_pull_job_on_finished(PullJob
*j
) {
285 if (j
== i
->settings_job
) {
287 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
288 } else if (j
->error
!= 0) {
289 if (j
== i
->checksum_job
)
290 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
291 else if (j
== i
->signature_job
)
292 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
294 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
300 /* This is invoked if either the download completed
301 * successfully, or the download was skipped because we
302 * already have the etag. */
304 if (!tar_pull_is_done(i
))
307 i
->tar_job
->disk_fd
= safe_close(i
->tar_job
->disk_fd
);
309 i
->settings_job
->disk_fd
= safe_close(i
->settings_job
->disk_fd
);
311 if (i
->tar_pid
> 0) {
312 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
322 if (!i
->tar_job
->etag_exists
) {
323 /* This is a new download, verify it, and move it into place */
325 tar_pull_report_progress(i
, TAR_VERIFYING
);
327 r
= pull_verify(i
->tar_job
, i
->settings_job
, i
->checksum_job
, i
->signature_job
);
331 tar_pull_report_progress(i
, TAR_FINALIZING
);
333 r
= import_make_read_only(i
->temp_path
);
337 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
339 log_error_errno(r
, "Failed to rename to final image name: %m");
343 i
->temp_path
= mfree(i
->temp_path
);
345 if (i
->settings_job
&&
346 i
->settings_job
->error
== 0 &&
347 !i
->settings_job
->etag_exists
) {
349 assert(i
->settings_temp_path
);
350 assert(i
->settings_path
);
352 /* Also move the settings file into place, if
353 * it exist. Note that we do so only if we
354 * also moved the tar file in place, to keep
355 * things strictly in sync. */
357 r
= import_make_read_only(i
->settings_temp_path
);
361 r
= rename_noreplace(AT_FDCWD
, i
->settings_temp_path
, AT_FDCWD
, i
->settings_path
);
363 log_error_errno(r
, "Failed to rename settings file: %m");
367 i
->settings_temp_path
= mfree(i
->settings_temp_path
);
371 tar_pull_report_progress(i
, TAR_COPYING
);
373 r
= tar_pull_make_local_copy(i
);
381 i
->on_finished(i
, r
, i
->userdata
);
383 sd_event_exit(i
->event
, r
);
386 static int tar_pull_job_on_open_disk_tar(PullJob
*j
) {
394 assert(i
->tar_job
== j
);
395 assert(!i
->final_path
);
396 assert(!i
->temp_path
);
397 assert(i
->tar_pid
<= 0);
399 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
403 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
407 mkdir_parents_label(i
->temp_path
, 0700);
409 r
= btrfs_subvol_make(i
->temp_path
);
411 if (mkdir(i
->temp_path
, 0755) < 0)
412 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
414 return log_error_errno(errno
, "Failed to create subvolume %s: %m", i
->temp_path
);
416 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
418 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
425 static int tar_pull_job_on_open_disk_settings(PullJob
*j
) {
433 assert(i
->settings_job
== j
);
434 assert(!i
->settings_path
);
435 assert(!i
->settings_temp_path
);
437 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
441 r
= tempfn_random(i
->settings_path
, NULL
, &i
->settings_temp_path
);
445 mkdir_parents_label(i
->settings_temp_path
, 0700);
447 j
->disk_fd
= open(i
->settings_temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
449 return log_error_errno(errno
, "Failed to create %s: %m", i
->settings_temp_path
);
454 static void tar_pull_job_on_progress(PullJob
*j
) {
462 tar_pull_report_progress(i
, TAR_DOWNLOADING
);
476 assert(verify
< _IMPORT_VERIFY_MAX
);
479 if (!http_url_is_valid(url
))
482 if (local
&& !machine_name_is_valid(local
))
488 r
= free_and_strdup(&i
->local
, local
);
492 i
->force_local
= force_local
;
494 i
->settings
= settings
;
496 /* Set up download job for TAR file */
497 r
= pull_job_new(&i
->tar_job
, url
, i
->glue
, i
);
501 i
->tar_job
->on_finished
= tar_pull_job_on_finished
;
502 i
->tar_job
->on_open_disk
= tar_pull_job_on_open_disk_tar
;
503 i
->tar_job
->on_progress
= tar_pull_job_on_progress
;
504 i
->tar_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
505 i
->tar_job
->grow_machine_directory
= i
->grow_machine_directory
;
507 r
= pull_find_old_etags(url
, i
->image_root
, DT_DIR
, ".tar-", NULL
, &i
->tar_job
->old_etags
);
511 /* Set up download job for the settings file (.nspawn) */
513 r
= pull_make_settings_job(&i
->settings_job
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
517 i
->settings_job
->on_open_disk
= tar_pull_job_on_open_disk_settings
;
518 i
->settings_job
->on_progress
= tar_pull_job_on_progress
;
519 i
->settings_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
521 r
= pull_find_old_etags(i
->settings_job
->url
, i
->image_root
, DT_REG
, ".settings-", NULL
, &i
->settings_job
->old_etags
);
526 /* Set up download of checksum/signature files */
527 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
531 r
= pull_job_begin(i
->tar_job
);
535 if (i
->settings_job
) {
536 r
= pull_job_begin(i
->settings_job
);
541 if (i
->checksum_job
) {
542 i
->checksum_job
->on_progress
= tar_pull_job_on_progress
;
544 r
= pull_job_begin(i
->checksum_job
);
549 if (i
->signature_job
) {
550 i
->signature_job
->on_progress
= tar_pull_job_on_progress
;
552 r
= pull_job_begin(i
->signature_job
);