1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 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 <curl/curl.h>
23 #include <sys/prctl.h>
25 #include "sd-daemon.h"
28 #include "btrfs-util.h"
32 #include "path-util.h"
33 #include "import-util.h"
34 #include "curl-util.h"
35 #include "aufs-util.h"
37 #include "pull-common.h"
38 #include "import-common.h"
40 #include "process-util.h"
41 #include "hostname-util.h"
43 typedef enum DkrProgress
{
63 PullJob
*ancestry_job
;
71 char *response_digest
;
73 char **response_registries
;
77 unsigned current_ancestry
;
79 DkrPullFinished on_finished
;
84 bool grow_machine_directory
;
92 #define PROTOCOL_PREFIX "https://"
94 #define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
95 #define HEADER_REGISTRY "X-Do" /* the HTTP header for the registry */ "cker-Endpoints:"
96 #define HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:"
97 #define LAYERS_MAX 127
99 static void dkr_pull_job_on_finished(PullJob
*j
);
101 DkrPull
* dkr_pull_unref(DkrPull
*i
) {
105 if (i
->tar_pid
> 1) {
106 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
107 (void) wait_for_terminate(i
->tar_pid
, NULL
);
110 pull_job_unref(i
->images_job
);
111 pull_job_unref(i
->tags_job
);
112 pull_job_unref(i
->ancestry_job
);
113 pull_job_unref(i
->json_job
);
114 pull_job_unref(i
->layer_job
);
116 curl_glue_unref(i
->glue
);
117 sd_event_unref(i
->event
);
120 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
127 free(i
->response_token
);
128 strv_free(i
->ancestry
);
130 free(i
->index_address
);
131 free(i
->index_protocol
);
143 const char *index_url
,
144 const char *image_root
,
145 DkrPullFinished on_finished
,
148 _cleanup_(dkr_pull_unrefp
) DkrPull
*i
= NULL
;
155 if (!http_url_is_valid(index_url
))
158 i
= new0(DkrPull
, 1);
162 i
->on_finished
= on_finished
;
163 i
->userdata
= userdata
;
165 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
169 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
171 i
->index_url
= strdup(index_url
);
175 e
= endswith(i
->index_url
, "/");
180 i
->event
= sd_event_ref(event
);
182 r
= sd_event_default(&i
->event
);
187 r
= curl_glue_new(&i
->glue
, i
->event
);
191 i
->glue
->on_finished
= pull_job_curl_on_finished
;
192 i
->glue
->userdata
= i
;
200 static void dkr_pull_report_progress(DkrPull
*i
, DkrProgress p
) {
210 percent
+= i
->images_job
->progress_percent
* 5 / 100;
216 percent
+= i
->tags_job
->progress_percent
* 5 / 100;
222 percent
+= i
->ancestry_job
->progress_percent
* 5 / 100;
224 percent
+= i
->json_job
->progress_percent
* 5 / 100;
227 case DKR_DOWNLOADING
:
229 percent
+= 75 * i
->current_ancestry
/ MAX(1U, i
->n_ancestry
);
231 percent
+= i
->layer_job
->progress_percent
* 75 / MAX(1U, i
->n_ancestry
) / 100;
240 assert_not_reached("Unknown progress state");
243 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
244 log_debug("Combined progress %u%%", percent
);
247 static int parse_id(const void *payload
, size_t size
, char **ret
) {
248 _cleanup_free_
char *buf
= NULL
, *id
= NULL
, *other
= NULL
;
249 union json_value v
= {};
250 void *json_state
= NULL
;
260 if (memchr(payload
, 0, size
))
263 buf
= strndup(payload
, size
);
268 t
= json_tokenize(&p
, &id
, &v
, &json_state
, NULL
);
271 if (t
!= JSON_STRING
)
274 t
= json_tokenize(&p
, &other
, &v
, &json_state
, NULL
);
280 if (!dkr_id_is_valid(id
))
289 static int parse_ancestry(const void *payload
, size_t size
, char ***ret
) {
290 _cleanup_free_
char *buf
= NULL
;
291 void *json_state
= NULL
;
298 } state
= STATE_BEGIN
;
299 _cleanup_strv_free_
char **l
= NULL
;
300 size_t n
= 0, allocated
= 0;
305 if (memchr(payload
, 0, size
))
308 buf
= strndup(payload
, size
);
314 _cleanup_free_
char *str
;
315 union json_value v
= {};
318 t
= json_tokenize(&p
, &str
, &v
, &json_state
, NULL
);
325 if (t
== JSON_ARRAY_OPEN
)
333 if (t
== JSON_STRING
) {
334 if (!dkr_id_is_valid(str
))
337 if (n
+1 > LAYERS_MAX
)
340 if (!GREEDY_REALLOC(l
, allocated
, n
+ 2))
349 } else if (t
== JSON_ARRAY_CLOSE
)
359 else if (t
== JSON_ARRAY_CLOSE
)
371 if (!strv_is_uniq(l
))
386 static const char *dkr_pull_current_layer(DkrPull
*i
) {
389 if (strv_isempty(i
->ancestry
))
392 return i
->ancestry
[i
->current_ancestry
];
395 static const char *dkr_pull_current_base_layer(DkrPull
*i
) {
398 if (strv_isempty(i
->ancestry
))
401 if (i
->current_ancestry
<= 0)
404 return i
->ancestry
[i
->current_ancestry
-1];
407 static int dkr_pull_add_token(DkrPull
*i
, PullJob
*j
) {
413 if (i
->response_token
)
414 t
= strjoina("Authorization: Token ", i
->response_token
);
416 t
= HEADER_TOKEN
" true";
418 j
->request_header
= curl_slist_new("Accept: application/json", t
, NULL
);
419 if (!j
->request_header
)
425 static int dkr_pull_add_bearer_token(DkrPull
*i
, PullJob
*j
) {
426 const char *t
= NULL
;
431 if (i
->response_token
)
432 t
= strjoina("Authorization: Bearer ", i
->response_token
);
436 j
->request_header
= curl_slist_new("Accept: application/json", t
, NULL
);
437 if (!j
->request_header
)
443 static bool dkr_pull_is_done(DkrPull
*i
) {
445 assert(i
->images_job
);
446 if (i
->images_job
->state
!= PULL_JOB_DONE
)
449 if (!i
->tags_job
|| i
->tags_job
->state
!= PULL_JOB_DONE
)
452 if (!i
->ancestry_job
|| i
->ancestry_job
->state
!= PULL_JOB_DONE
)
455 if (i
->json_job
&& i
->json_job
->state
!= PULL_JOB_DONE
)
458 if (i
->layer_job
&& i
->layer_job
->state
!= PULL_JOB_DONE
)
461 if (dkr_pull_current_layer(i
))
467 static int dkr_pull_make_local_copy(DkrPull
*i
, DkrPullVersion version
) {
469 _cleanup_free_
char *p
= NULL
;
476 if (!i
->final_path
) {
477 i
->final_path
= strjoin(i
->image_root
, "/.dkr-", i
->id
, NULL
);
482 if (version
== DKR_PULL_V2
) {
483 r
= path_get_parent(i
->image_root
, &p
);
488 r
= pull_make_local_copy(i
->final_path
, p
?: i
->image_root
, i
->local
, i
->force_local
);
492 if (version
== DKR_PULL_V2
) {
494 STRV_FOREACH(k
, i
->ancestry
) {
495 _cleanup_free_
char *d
= strjoin(i
->image_root
, "/.dkr-", *k
, NULL
);
496 r
= btrfs_subvol_remove(d
, false);
501 r
= rmdir(i
->image_root
);
509 static int dkr_pull_job_on_open_disk(PullJob
*j
) {
518 assert(i
->layer_job
== j
);
519 assert(i
->final_path
);
520 assert(!i
->temp_path
);
521 assert(i
->tar_pid
<= 0);
523 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
527 mkdir_parents_label(i
->temp_path
, 0700);
529 base
= dkr_pull_current_base_layer(i
);
531 const char *base_path
;
533 base_path
= strjoina(i
->image_root
, "/.dkr-", base
);
534 r
= btrfs_subvol_snapshot(base_path
, i
->temp_path
, BTRFS_SNAPSHOT_FALLBACK_COPY
);
536 r
= btrfs_subvol_make(i
->temp_path
);
538 return log_error_errno(r
, "Failed to make btrfs subvolume %s: %m", i
->temp_path
);
540 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
547 static void dkr_pull_job_on_progress(PullJob
*j
) {
555 dkr_pull_report_progress(
557 j
== i
->images_job
? DKR_SEARCHING
:
558 j
== i
->tags_job
? DKR_RESOLVING
:
559 j
== i
->ancestry_job
|| j
== i
->json_job
? DKR_METADATA
:
563 static void dkr_pull_job_on_finished_v2(PullJob
*j
);
565 static int dkr_pull_pull_layer_v2(DkrPull
*i
) {
566 _cleanup_free_
char *path
= NULL
;
567 const char *url
, *layer
= NULL
;
571 assert(!i
->layer_job
);
572 assert(!i
->temp_path
);
573 assert(!i
->final_path
);
576 layer
= dkr_pull_current_layer(i
);
578 return 0; /* no more layers */
580 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
584 if (laccess(path
, F_OK
) < 0) {
588 return log_error_errno(errno
, "Failed to check for container: %m");
591 log_info("Layer %s already exists, skipping.", layer
);
593 i
->current_ancestry
++;
598 log_info("Pulling layer %s...", layer
);
600 i
->final_path
= path
;
603 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/blobs/", layer
);
604 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
606 return log_error_errno(r
, "Failed to allocate layer job: %m");
608 r
= dkr_pull_add_bearer_token(i
, i
->layer_job
);
612 i
->layer_job
->on_finished
= dkr_pull_job_on_finished_v2
;
613 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
614 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
615 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
617 r
= pull_job_begin(i
->layer_job
);
619 return log_error_errno(r
, "Failed to start layer job: %m");
624 static int dkr_pull_pull_layer(DkrPull
*i
) {
625 _cleanup_free_
char *path
= NULL
;
626 const char *url
, *layer
= NULL
;
630 assert(!i
->layer_job
);
631 assert(!i
->temp_path
);
632 assert(!i
->final_path
);
635 layer
= dkr_pull_current_layer(i
);
637 return 0; /* no more layers */
639 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
643 if (laccess(path
, F_OK
) < 0) {
647 return log_error_errno(errno
, "Failed to check for container: %m");
650 log_info("Layer %s already exists, skipping.", layer
);
652 i
->current_ancestry
++;
657 log_info("Pulling layer %s...", layer
);
659 i
->final_path
= path
;
662 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", layer
, "/layer");
663 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
665 return log_error_errno(r
, "Failed to allocate layer job: %m");
667 r
= dkr_pull_add_token(i
, i
->layer_job
);
671 i
->layer_job
->on_finished
= dkr_pull_job_on_finished
;
672 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
673 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
674 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
676 r
= pull_job_begin(i
->layer_job
);
678 return log_error_errno(r
, "Failed to start layer job: %m");
683 static int dkr_pull_job_on_header(PullJob
*j
, const char *header
, size_t sz
) {
684 _cleanup_free_
char *registry
= NULL
;
685 char *token
, *digest
;
693 r
= curl_header_strdup(header
, sz
, HEADER_TOKEN
, &token
);
697 free(i
->response_token
);
698 i
->response_token
= token
;
702 r
= curl_header_strdup(header
, sz
, HEADER_DIGEST
, &digest
);
706 free(i
->response_digest
);
707 i
->response_digest
= digest
;
711 r
= curl_header_strdup(header
, sz
, HEADER_REGISTRY
, ®istry
);
717 l
= strv_split(registry
, ",");
722 if (!hostname_is_valid(*k
, false)) {
723 log_error("Registry hostname is not valid.");
729 strv_free(i
->response_registries
);
730 i
->response_registries
= l
;
736 static void dkr_pull_job_on_finished_v2(PullJob
*j
) {
745 if (j
== i
->images_job
)
746 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
747 else if (j
== i
->ancestry_job
)
748 log_error_errno(j
->error
, "Failed to retrieve manifest.");
749 else if (j
== i
->json_job
)
750 log_error_errno(j
->error
, "Failed to retrieve json data.");
752 log_error_errno(j
->error
, "Failed to retrieve layer data.");
758 if (i
->images_job
== j
) {
761 assert(!i
->tags_job
);
762 assert(!i
->ancestry_job
);
763 assert(!i
->json_job
);
764 assert(!i
->layer_job
);
766 if (strv_isempty(i
->response_registries
)) {
768 log_error("Didn't get registry information.");
772 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
773 dkr_pull_report_progress(i
, DKR_RESOLVING
);
775 url
= strjoina(i
->index_protocol
, "auth.", i
->index_address
, "/v2/token/?scope=repository:",
776 i
->name
, ":pull&service=registry.", i
->index_address
);
777 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
779 log_error_errno(r
, "Failed to allocate tags job: %m");
783 i
->tags_job
->on_finished
= dkr_pull_job_on_finished_v2
;
784 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
786 r
= pull_job_begin(i
->tags_job
);
788 log_error_errno(r
, "Failed to start tags job: %m");
792 } else if (i
->tags_job
== j
) {
794 _cleanup_free_
char *buf
;
795 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
;
796 JsonVariant
*e
= NULL
;
798 assert(!i
->ancestry_job
);
799 assert(!i
->json_job
);
800 assert(!i
->layer_job
);
802 buf
= strndup((const char *)j
->payload
, j
->payload_size
);
809 r
= json_parse(buf
, &doc
);
811 log_error("Unable to parse bearer token\n%s", j
->payload
);
815 e
= json_variant_value(doc
, "token");
816 if (!e
|| e
->type
!= JSON_VARIANT_STRING
) {
818 log_error("Invalid JSON format for Bearer token");
822 r
= free_and_strdup(&i
->response_token
, json_variant_string(e
));
828 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/manifests/", i
->reference
);
829 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
831 log_error_errno(r
, "Failed to allocate ancestry job: %m");
835 r
= dkr_pull_add_bearer_token(i
, i
->ancestry_job
);
839 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished_v2
;
840 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
841 i
->ancestry_job
->on_header
= dkr_pull_job_on_header
;
844 r
= pull_job_begin(i
->ancestry_job
);
846 log_error_errno(r
, "Failed to start ancestry job: %m");
850 } else if (i
->ancestry_job
== j
) {
852 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
, *compat
= NULL
;
853 JsonVariant
*e
= NULL
;
854 _cleanup_strv_free_
char **ancestry
= NULL
;
855 size_t allocated
= 0, size
= 0;
856 char *path
= NULL
, **k
= NULL
;
858 r
= json_parse((const char *)j
->payload
, &doc
);
860 log_error("Invalid JSON Manifest");
864 e
= json_variant_value(doc
, "fsLayers");
865 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
|| e
->size
== 0) {
870 log_info("JSON manifest with schema v%"PRIi64
" for %s parsed!",
871 json_variant_integer(json_variant_value(doc
, "schemaVersion")),
872 json_variant_string(json_variant_value(doc
, "name")));
874 for (unsigned z
= 0; z
< e
->size
; z
++) {
875 JsonVariant
*f
= json_variant_element(e
, z
), *g
= NULL
;
877 if (f
->type
!= JSON_VARIANT_OBJECT
) {
882 g
= json_variant_value(f
, "blobSum");
884 layer
= json_variant_string(g
);
885 if (!dkr_digest_is_valid(layer
)) {
890 if (!GREEDY_REALLOC(ancestry
, allocated
, size
+ 2)) {
896 ancestry
[size
] = strdup(layer
);
897 if (!ancestry
[size
]) {
903 ancestry
[size
+1] = NULL
;
907 e
= json_variant_value(doc
, "history");
908 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
) {
913 e
= json_variant_element(e
, 0);
914 e
= json_variant_value(e
, "v1Compatibility");
915 r
= json_parse(json_variant_string(e
), &compat
);
917 log_error("Invalid v1Compatibility JSON");
921 e
= json_variant_value(compat
, "id");
923 strv_free(i
->ancestry
);
924 i
->ancestry
= strv_reverse(strv_uniq(ancestry
));
925 i
->n_ancestry
= strv_length(i
->ancestry
);
926 i
->current_ancestry
= 0;
927 i
->id
= strdup(i
->ancestry
[i
->n_ancestry
- 1]);
933 path
= strjoin(i
->image_root
, "/.dkr-", json_variant_string(e
), NULL
);
940 i
->image_root
= path
;
943 log_info("Required layers:\n");
944 STRV_FOREACH(k
, i
->ancestry
)
945 log_info("\t%s", *k
);
946 log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e
), i
->response_digest
);
948 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
950 r
= dkr_pull_pull_layer_v2(i
);
954 } else if (i
->layer_job
== j
) {
955 assert(i
->temp_path
);
956 assert(i
->final_path
);
958 j
->disk_fd
= safe_close(j
->disk_fd
);
960 if (i
->tar_pid
> 0) {
961 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
967 r
= aufs_resolve(i
->temp_path
);
969 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
973 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
975 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
979 if (rename(i
->temp_path
, i
->final_path
) < 0) {
980 log_error_errno(errno
, "Failed to rename snaphsot: %m");
984 log_info("Completed writing to layer %s.", i
->final_path
);
986 i
->layer_job
= pull_job_unref(i
->layer_job
);
990 i
->final_path
= NULL
;
992 i
->current_ancestry
++;
993 r
= dkr_pull_pull_layer_v2(i
);
997 } else if (i
->json_job
!= j
)
998 assert_not_reached("Got finished event for unknown curl object");
1000 if (!dkr_pull_is_done(i
))
1003 dkr_pull_report_progress(i
, DKR_COPYING
);
1005 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V2
);
1013 i
->on_finished(i
, r
, i
->userdata
);
1015 sd_event_exit(i
->event
, r
);
1019 static void dkr_pull_job_on_finished(PullJob
*j
) {
1024 assert(j
->userdata
);
1027 if (j
->error
!= 0) {
1028 if (j
== i
->images_job
)
1029 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
1030 else if (j
== i
->tags_job
)
1031 log_error_errno(j
->error
, "Failed to retrieve tags list.");
1032 else if (j
== i
->ancestry_job
)
1033 log_error_errno(j
->error
, "Failed to retrieve ancestry list.");
1034 else if (j
== i
->json_job
)
1035 log_error_errno(j
->error
, "Failed to retrieve json data.");
1037 log_error_errno(j
->error
, "Failed to retrieve layer data.");
1043 if (i
->images_job
== j
) {
1046 assert(!i
->tags_job
);
1047 assert(!i
->ancestry_job
);
1048 assert(!i
->json_job
);
1049 assert(!i
->layer_job
);
1051 if (strv_isempty(i
->response_registries
)) {
1053 log_error("Didn't get registry information.");
1057 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
1058 dkr_pull_report_progress(i
, DKR_RESOLVING
);
1060 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/repositories/", i
->name
, "/tags/", i
->reference
);
1061 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
1063 log_error_errno(r
, "Failed to allocate tags job: %m");
1067 r
= dkr_pull_add_token(i
, i
->tags_job
);
1073 i
->tags_job
->on_finished
= dkr_pull_job_on_finished
;
1074 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
1076 r
= pull_job_begin(i
->tags_job
);
1078 log_error_errno(r
, "Failed to start tags job: %m");
1082 } else if (i
->tags_job
== j
) {
1086 assert(!i
->ancestry_job
);
1087 assert(!i
->json_job
);
1088 assert(!i
->layer_job
);
1090 r
= parse_id(j
->payload
, j
->payload_size
, &id
);
1092 log_error_errno(r
, "Failed to parse JSON id.");
1099 log_info("Tag lookup succeeded, resolved to layer %s.", i
->id
);
1100 dkr_pull_report_progress(i
, DKR_METADATA
);
1102 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/ancestry");
1103 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
1105 log_error_errno(r
, "Failed to allocate ancestry job: %m");
1109 r
= dkr_pull_add_token(i
, i
->ancestry_job
);
1115 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished
;
1116 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
1118 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/json");
1119 r
= pull_job_new(&i
->json_job
, url
, i
->glue
, i
);
1121 log_error_errno(r
, "Failed to allocate json job: %m");
1125 r
= dkr_pull_add_token(i
, i
->json_job
);
1131 i
->json_job
->on_finished
= dkr_pull_job_on_finished
;
1132 i
->json_job
->on_progress
= dkr_pull_job_on_progress
;
1134 r
= pull_job_begin(i
->ancestry_job
);
1136 log_error_errno(r
, "Failed to start ancestry job: %m");
1140 r
= pull_job_begin(i
->json_job
);
1142 log_error_errno(r
, "Failed to start json job: %m");
1146 } else if (i
->ancestry_job
== j
) {
1147 char **ancestry
= NULL
, **k
;
1150 assert(!i
->layer_job
);
1152 r
= parse_ancestry(j
->payload
, j
->payload_size
, &ancestry
);
1154 log_error_errno(r
, "Failed to parse JSON id.");
1158 n
= strv_length(ancestry
);
1159 if (n
<= 0 || !streq(ancestry
[n
-1], i
->id
)) {
1160 log_error("Ancestry doesn't end in main layer.");
1161 strv_free(ancestry
);
1166 log_info("Ancestor lookup succeeded, requires layers:\n");
1167 STRV_FOREACH(k
, ancestry
)
1168 log_info("\t%s", *k
);
1170 strv_free(i
->ancestry
);
1171 i
->ancestry
= ancestry
;
1173 i
->current_ancestry
= 0;
1175 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
1177 r
= dkr_pull_pull_layer(i
);
1181 } else if (i
->layer_job
== j
) {
1182 assert(i
->temp_path
);
1183 assert(i
->final_path
);
1185 j
->disk_fd
= safe_close(j
->disk_fd
);
1187 if (i
->tar_pid
> 0) {
1188 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
1194 r
= aufs_resolve(i
->temp_path
);
1196 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
1200 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
1202 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
1206 if (rename(i
->temp_path
, i
->final_path
) < 0) {
1207 log_error_errno(errno
, "Failed to rename snaphsot: %m");
1211 log_info("Completed writing to layer %s.", i
->final_path
);
1213 i
->layer_job
= pull_job_unref(i
->layer_job
);
1215 i
->temp_path
= NULL
;
1216 free(i
->final_path
);
1217 i
->final_path
= NULL
;
1219 i
->current_ancestry
++;
1220 r
= dkr_pull_pull_layer(i
);
1224 } else if (i
->json_job
!= j
)
1225 assert_not_reached("Got finished event for unknown curl object");
1227 if (!dkr_pull_is_done(i
))
1230 dkr_pull_report_progress(i
, DKR_COPYING
);
1232 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V1
);
1239 i
->on_finished(i
, r
, i
->userdata
);
1241 sd_event_exit(i
->event
, r
);
1244 static int get_protocol_address(char **protocol
, char **address
, const char *url
) {
1245 const char *sep
, *dot
;
1246 _cleanup_free_
char *a
= NULL
, *p
= NULL
;
1248 sep
= strstr(url
, "://");
1252 dot
= strrchr(url
, '.');
1257 p
= strndup(url
, (sep
- url
) + 3);
1261 while (dot
> (sep
+ 3) && *dot
!= '.')
1264 a
= strdup(dot
+ 1);
1275 int dkr_pull_start(DkrPull
*i
, const char *name
, const char *reference
, const char *local
, bool force_local
, DkrPullVersion version
) {
1281 if (!dkr_name_is_valid(name
))
1284 if (reference
&& !dkr_ref_is_valid(reference
))
1287 if (local
&& !machine_name_is_valid(local
))
1294 reference
= "latest";
1296 free(i
->index_protocol
);
1297 free(i
->index_address
);
1298 r
= get_protocol_address(&i
->index_protocol
, &i
->index_address
, i
->index_url
);
1302 r
= free_and_strdup(&i
->local
, local
);
1305 i
->force_local
= force_local
;
1307 r
= free_and_strdup(&i
->name
, name
);
1310 r
= free_and_strdup(&i
->reference
, reference
);
1314 url
= strjoina(i
->index_url
, "/v1/repositories/", name
, "/images");
1316 r
= pull_job_new(&i
->images_job
, url
, i
->glue
, i
);
1320 r
= dkr_pull_add_token(i
, i
->images_job
);
1324 if (version
== DKR_PULL_V1
)
1325 i
->images_job
->on_finished
= dkr_pull_job_on_finished
;
1327 i
->images_job
->on_finished
= dkr_pull_job_on_finished_v2
;
1329 i
->images_job
->on_header
= dkr_pull_job_on_header
;
1330 i
->images_job
->on_progress
= dkr_pull_job_on_progress
;
1332 return pull_job_begin(i
->images_job
);