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"
33 #include "hostname-util.h"
34 #include "import-common.h"
35 #include "import-util.h"
38 #include "path-util.h"
39 #include "process-util.h"
40 #include "pull-common.h"
44 #include "string-util.h"
49 typedef enum TarProgress
{
63 PullJob
*settings_job
;
64 PullJob
*checksum_job
;
65 PullJob
*signature_job
;
67 TarPullFinished on_finished
;
72 bool grow_machine_directory
;
81 char *settings_temp_path
;
86 TarPull
* tar_pull_unref(TarPull
*i
) {
91 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
92 (void) wait_for_terminate(i
->tar_pid
, NULL
);
95 pull_job_unref(i
->tar_job
);
96 pull_job_unref(i
->settings_job
);
97 pull_job_unref(i
->checksum_job
);
98 pull_job_unref(i
->signature_job
);
100 curl_glue_unref(i
->glue
);
101 sd_event_unref(i
->event
);
104 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
108 if (i
->settings_temp_path
) {
109 (void) unlink(i
->settings_temp_path
);
110 free(i
->settings_temp_path
);
114 free(i
->settings_path
);
125 const char *image_root
,
126 TarPullFinished on_finished
,
129 _cleanup_(tar_pull_unrefp
) TarPull
*i
= NULL
;
134 i
= new0(TarPull
, 1);
138 i
->on_finished
= on_finished
;
139 i
->userdata
= userdata
;
141 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
145 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
148 i
->event
= sd_event_ref(event
);
150 r
= sd_event_default(&i
->event
);
155 r
= curl_glue_new(&i
->glue
, i
->event
);
159 i
->glue
->on_finished
= pull_job_curl_on_finished
;
160 i
->glue
->userdata
= i
;
168 static void tar_pull_report_progress(TarPull
*i
, TarProgress p
) {
175 case TAR_DOWNLOADING
: {
176 unsigned remain
= 85;
180 if (i
->settings_job
) {
181 percent
+= i
->settings_job
->progress_percent
* 5 / 100;
185 if (i
->checksum_job
) {
186 percent
+= i
->checksum_job
->progress_percent
* 5 / 100;
190 if (i
->signature_job
) {
191 percent
+= i
->signature_job
->progress_percent
* 5 / 100;
196 percent
+= i
->tar_job
->progress_percent
* remain
/ 100;
213 assert_not_reached("Unknown progress state");
216 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
217 log_debug("Combined progress %u%%", percent
);
220 static int tar_pull_make_local_copy(TarPull
*i
) {
229 if (!i
->final_path
) {
230 r
= pull_make_path(i
->tar_job
->url
, i
->tar_job
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
235 r
= pull_make_local_copy(i
->final_path
, i
->image_root
, i
->local
, i
->force_local
);
240 const char *local_settings
;
241 assert(i
->settings_job
);
243 if (!i
->settings_path
) {
244 r
= pull_make_path(i
->settings_job
->url
, i
->settings_job
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
249 local_settings
= strjoina(i
->image_root
, "/", i
->local
, ".nspawn");
251 r
= copy_file_atomic(i
->settings_path
, local_settings
, 0664, i
->force_local
, 0);
253 log_warning_errno(r
, "Settings file %s already exists, not replacing.", local_settings
);
254 else if (r
< 0 && r
!= -ENOENT
)
255 log_warning_errno(r
, "Failed to copy settings files %s, ignoring: %m", local_settings
);
257 log_info("Created new settings file '%s.nspawn'", i
->local
);
263 static bool tar_pull_is_done(TarPull
*i
) {
267 if (!PULL_JOB_IS_COMPLETE(i
->tar_job
))
269 if (i
->settings_job
&& !PULL_JOB_IS_COMPLETE(i
->settings_job
))
271 if (i
->checksum_job
&& !PULL_JOB_IS_COMPLETE(i
->checksum_job
))
273 if (i
->signature_job
&& !PULL_JOB_IS_COMPLETE(i
->signature_job
))
279 static void tar_pull_job_on_finished(PullJob
*j
) {
288 if (j
== i
->settings_job
) {
290 log_info_errno(j
->error
, "Settings file could not be retrieved, proceeding without.");
291 } else if (j
->error
!= 0) {
292 if (j
== i
->checksum_job
)
293 log_error_errno(j
->error
, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
294 else if (j
== i
->signature_job
)
295 log_error_errno(j
->error
, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
297 log_error_errno(j
->error
, "Failed to retrieve image file. (Wrong URL?)");
303 /* This is invoked if either the download completed
304 * successfully, or the download was skipped because we
305 * already have the etag. */
307 if (!tar_pull_is_done(i
))
310 i
->tar_job
->disk_fd
= safe_close(i
->tar_job
->disk_fd
);
312 i
->settings_job
->disk_fd
= safe_close(i
->settings_job
->disk_fd
);
314 if (i
->tar_pid
> 0) {
315 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
325 if (!i
->tar_job
->etag_exists
) {
326 /* This is a new download, verify it, and move it into place */
328 tar_pull_report_progress(i
, TAR_VERIFYING
);
330 r
= pull_verify(i
->tar_job
, i
->settings_job
, i
->checksum_job
, i
->signature_job
);
334 tar_pull_report_progress(i
, TAR_FINALIZING
);
336 r
= import_make_read_only(i
->temp_path
);
340 r
= rename_noreplace(AT_FDCWD
, i
->temp_path
, AT_FDCWD
, i
->final_path
);
342 log_error_errno(r
, "Failed to rename to final image name: %m");
346 i
->temp_path
= mfree(i
->temp_path
);
348 if (i
->settings_job
&&
349 i
->settings_job
->error
== 0 &&
350 !i
->settings_job
->etag_exists
) {
352 assert(i
->settings_temp_path
);
353 assert(i
->settings_path
);
355 /* Also move the settings file into place, if
356 * it exist. Note that we do so only if we
357 * also moved the tar file in place, to keep
358 * things strictly in sync. */
360 r
= import_make_read_only(i
->settings_temp_path
);
364 r
= rename_noreplace(AT_FDCWD
, i
->settings_temp_path
, AT_FDCWD
, i
->settings_path
);
366 log_error_errno(r
, "Failed to rename settings file: %m");
370 i
->settings_temp_path
= mfree(i
->settings_temp_path
);
374 tar_pull_report_progress(i
, TAR_COPYING
);
376 r
= tar_pull_make_local_copy(i
);
384 i
->on_finished(i
, r
, i
->userdata
);
386 sd_event_exit(i
->event
, r
);
389 static int tar_pull_job_on_open_disk_tar(PullJob
*j
) {
397 assert(i
->tar_job
== j
);
398 assert(!i
->final_path
);
399 assert(!i
->temp_path
);
400 assert(i
->tar_pid
<= 0);
402 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".tar-", NULL
, &i
->final_path
);
406 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
410 mkdir_parents_label(i
->temp_path
, 0700);
412 r
= btrfs_subvol_make(i
->temp_path
);
414 if (mkdir(i
->temp_path
, 0755) < 0)
415 return log_error_errno(errno
, "Failed to create directory %s: %m", i
->temp_path
);
417 return log_error_errno(errno
, "Failed to create subvolume %s: %m", i
->temp_path
);
419 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
421 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
428 static int tar_pull_job_on_open_disk_settings(PullJob
*j
) {
436 assert(i
->settings_job
== j
);
437 assert(!i
->settings_path
);
438 assert(!i
->settings_temp_path
);
440 r
= pull_make_path(j
->url
, j
->etag
, i
->image_root
, ".settings-", NULL
, &i
->settings_path
);
444 r
= tempfn_random(i
->settings_path
, NULL
, &i
->settings_temp_path
);
448 mkdir_parents_label(i
->settings_temp_path
, 0700);
450 j
->disk_fd
= open(i
->settings_temp_path
, O_RDWR
|O_CREAT
|O_EXCL
|O_NOCTTY
|O_CLOEXEC
, 0664);
452 return log_error_errno(errno
, "Failed to create %s: %m", i
->settings_temp_path
);
457 static void tar_pull_job_on_progress(PullJob
*j
) {
465 tar_pull_report_progress(i
, TAR_DOWNLOADING
);
479 assert(verify
< _IMPORT_VERIFY_MAX
);
482 if (!http_url_is_valid(url
))
485 if (local
&& !machine_name_is_valid(local
))
491 r
= free_and_strdup(&i
->local
, local
);
495 i
->force_local
= force_local
;
497 i
->settings
= settings
;
499 /* Set up download job for TAR file */
500 r
= pull_job_new(&i
->tar_job
, url
, i
->glue
, i
);
504 i
->tar_job
->on_finished
= tar_pull_job_on_finished
;
505 i
->tar_job
->on_open_disk
= tar_pull_job_on_open_disk_tar
;
506 i
->tar_job
->on_progress
= tar_pull_job_on_progress
;
507 i
->tar_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
508 i
->tar_job
->grow_machine_directory
= i
->grow_machine_directory
;
510 r
= pull_find_old_etags(url
, i
->image_root
, DT_DIR
, ".tar-", NULL
, &i
->tar_job
->old_etags
);
514 /* Set up download job for the settings file (.nspawn) */
516 r
= pull_make_settings_job(&i
->settings_job
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
520 i
->settings_job
->on_open_disk
= tar_pull_job_on_open_disk_settings
;
521 i
->settings_job
->on_progress
= tar_pull_job_on_progress
;
522 i
->settings_job
->calc_checksum
= verify
!= IMPORT_VERIFY_NO
;
524 r
= pull_find_old_etags(i
->settings_job
->url
, i
->image_root
, DT_REG
, ".settings-", NULL
, &i
->settings_job
->old_etags
);
529 /* Set up download of checksum/signature files */
530 r
= pull_make_verification_jobs(&i
->checksum_job
, &i
->signature_job
, verify
, url
, i
->glue
, tar_pull_job_on_finished
, i
);
534 r
= pull_job_begin(i
->tar_job
);
538 if (i
->settings_job
) {
539 r
= pull_job_begin(i
->settings_job
);
544 if (i
->checksum_job
) {
545 i
->checksum_job
->on_progress
= tar_pull_job_on_progress
;
547 r
= pull_job_begin(i
->checksum_job
);
552 if (i
->signature_job
) {
553 i
->signature_job
->on_progress
= tar_pull_job_on_progress
;
555 r
= pull_job_begin(i
->signature_job
);