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"
27 #include "alloc-util.h"
28 #include "aufs-util.h"
29 #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"
50 typedef enum DkrProgress
{
70 PullJob
*ancestry_job
;
78 char *response_digest
;
80 char **response_registries
;
84 unsigned current_ancestry
;
86 DkrPullFinished on_finished
;
91 bool grow_machine_directory
;
99 #define PROTOCOL_PREFIX "https://"
101 #define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
102 #define HEADER_REGISTRY "X-Do" /* the HTTP header for the registry */ "cker-Endpoints:"
103 #define HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:"
104 #define LAYERS_MAX 127
106 static void dkr_pull_job_on_finished(PullJob
*j
);
108 DkrPull
* dkr_pull_unref(DkrPull
*i
) {
112 if (i
->tar_pid
> 1) {
113 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
114 (void) wait_for_terminate(i
->tar_pid
, NULL
);
117 pull_job_unref(i
->images_job
);
118 pull_job_unref(i
->tags_job
);
119 pull_job_unref(i
->ancestry_job
);
120 pull_job_unref(i
->json_job
);
121 pull_job_unref(i
->layer_job
);
123 curl_glue_unref(i
->glue
);
124 sd_event_unref(i
->event
);
127 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
134 free(i
->response_token
);
135 strv_free(i
->ancestry
);
137 free(i
->index_address
);
138 free(i
->index_protocol
);
150 const char *index_url
,
151 const char *image_root
,
152 DkrPullFinished on_finished
,
155 _cleanup_(dkr_pull_unrefp
) DkrPull
*i
= NULL
;
162 if (!http_url_is_valid(index_url
))
165 i
= new0(DkrPull
, 1);
169 i
->on_finished
= on_finished
;
170 i
->userdata
= userdata
;
172 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
176 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
178 i
->index_url
= strdup(index_url
);
182 e
= endswith(i
->index_url
, "/");
187 i
->event
= sd_event_ref(event
);
189 r
= sd_event_default(&i
->event
);
194 r
= curl_glue_new(&i
->glue
, i
->event
);
198 i
->glue
->on_finished
= pull_job_curl_on_finished
;
199 i
->glue
->userdata
= i
;
207 static void dkr_pull_report_progress(DkrPull
*i
, DkrProgress p
) {
217 percent
+= i
->images_job
->progress_percent
* 5 / 100;
223 percent
+= i
->tags_job
->progress_percent
* 5 / 100;
229 percent
+= i
->ancestry_job
->progress_percent
* 5 / 100;
231 percent
+= i
->json_job
->progress_percent
* 5 / 100;
234 case DKR_DOWNLOADING
:
236 percent
+= 75 * i
->current_ancestry
/ MAX(1U, i
->n_ancestry
);
238 percent
+= i
->layer_job
->progress_percent
* 75 / MAX(1U, i
->n_ancestry
) / 100;
247 assert_not_reached("Unknown progress state");
250 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
251 log_debug("Combined progress %u%%", percent
);
254 static int parse_id(const void *payload
, size_t size
, char **ret
) {
255 _cleanup_free_
char *buf
= NULL
, *id
= NULL
, *other
= NULL
;
256 union json_value v
= {};
257 void *json_state
= NULL
;
267 if (memchr(payload
, 0, size
))
270 buf
= strndup(payload
, size
);
275 t
= json_tokenize(&p
, &id
, &v
, &json_state
, NULL
);
278 if (t
!= JSON_STRING
)
281 t
= json_tokenize(&p
, &other
, &v
, &json_state
, NULL
);
287 if (!dkr_id_is_valid(id
))
296 static int parse_ancestry(const void *payload
, size_t size
, char ***ret
) {
297 _cleanup_free_
char *buf
= NULL
;
298 void *json_state
= NULL
;
305 } state
= STATE_BEGIN
;
306 _cleanup_strv_free_
char **l
= NULL
;
307 size_t n
= 0, allocated
= 0;
312 if (memchr(payload
, 0, size
))
315 buf
= strndup(payload
, size
);
321 _cleanup_free_
char *str
;
322 union json_value v
= {};
325 t
= json_tokenize(&p
, &str
, &v
, &json_state
, NULL
);
332 if (t
== JSON_ARRAY_OPEN
)
340 if (t
== JSON_STRING
) {
341 if (!dkr_id_is_valid(str
))
344 if (n
+1 > LAYERS_MAX
)
347 if (!GREEDY_REALLOC(l
, allocated
, n
+ 2))
356 } else if (t
== JSON_ARRAY_CLOSE
)
366 else if (t
== JSON_ARRAY_CLOSE
)
378 if (!strv_is_uniq(l
))
393 static const char *dkr_pull_current_layer(DkrPull
*i
) {
396 if (strv_isempty(i
->ancestry
))
399 return i
->ancestry
[i
->current_ancestry
];
402 static const char *dkr_pull_current_base_layer(DkrPull
*i
) {
405 if (strv_isempty(i
->ancestry
))
408 if (i
->current_ancestry
<= 0)
411 return i
->ancestry
[i
->current_ancestry
-1];
414 static int dkr_pull_add_token(DkrPull
*i
, PullJob
*j
) {
420 if (i
->response_token
)
421 t
= strjoina("Authorization: Token ", i
->response_token
);
423 t
= HEADER_TOKEN
" true";
425 j
->request_header
= curl_slist_new("Accept: application/json", t
, NULL
);
426 if (!j
->request_header
)
432 static int dkr_pull_add_bearer_token(DkrPull
*i
, PullJob
*j
) {
433 const char *t
= NULL
;
438 if (i
->response_token
)
439 t
= strjoina("Authorization: Bearer ", i
->response_token
);
443 j
->request_header
= curl_slist_new("Accept: application/json", t
, NULL
);
444 if (!j
->request_header
)
450 static bool dkr_pull_is_done(DkrPull
*i
) {
452 assert(i
->images_job
);
453 if (i
->images_job
->state
!= PULL_JOB_DONE
)
456 if (!i
->tags_job
|| i
->tags_job
->state
!= PULL_JOB_DONE
)
459 if (!i
->ancestry_job
|| i
->ancestry_job
->state
!= PULL_JOB_DONE
)
462 if (i
->json_job
&& i
->json_job
->state
!= PULL_JOB_DONE
)
465 if (i
->layer_job
&& i
->layer_job
->state
!= PULL_JOB_DONE
)
468 if (dkr_pull_current_layer(i
))
474 static int dkr_pull_make_local_copy(DkrPull
*i
, DkrPullVersion version
) {
476 _cleanup_free_
char *p
= NULL
;
483 if (!i
->final_path
) {
484 i
->final_path
= strjoin(i
->image_root
, "/.dkr-", i
->id
, NULL
);
489 if (version
== DKR_PULL_V2
) {
490 p
= dirname_malloc(i
->image_root
);
495 r
= pull_make_local_copy(i
->final_path
, p
?: i
->image_root
, i
->local
, i
->force_local
);
499 if (version
== DKR_PULL_V2
) {
502 STRV_FOREACH(k
, i
->ancestry
) {
503 _cleanup_free_
char *d
;
505 d
= strjoin(i
->image_root
, "/.dkr-", *k
, NULL
);
509 r
= btrfs_subvol_remove(d
, BTRFS_REMOVE_QUOTA
);
514 r
= rmdir(i
->image_root
);
522 static int dkr_pull_job_on_open_disk(PullJob
*j
) {
531 assert(i
->layer_job
== j
);
532 assert(i
->final_path
);
533 assert(!i
->temp_path
);
534 assert(i
->tar_pid
<= 0);
536 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
540 mkdir_parents_label(i
->temp_path
, 0700);
542 base
= dkr_pull_current_base_layer(i
);
544 const char *base_path
;
546 base_path
= strjoina(i
->image_root
, "/.dkr-", base
);
547 r
= btrfs_subvol_snapshot(base_path
, i
->temp_path
, BTRFS_SNAPSHOT_FALLBACK_COPY
|BTRFS_SNAPSHOT_QUOTA
);
549 r
= btrfs_subvol_make(i
->temp_path
);
551 return log_error_errno(r
, "Failed to make btrfs subvolume %s: %m", i
->temp_path
);
553 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
555 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
562 static void dkr_pull_job_on_progress(PullJob
*j
) {
570 dkr_pull_report_progress(
572 j
== i
->images_job
? DKR_SEARCHING
:
573 j
== i
->tags_job
? DKR_RESOLVING
:
574 j
== i
->ancestry_job
|| j
== i
->json_job
? DKR_METADATA
:
578 static void dkr_pull_job_on_finished_v2(PullJob
*j
);
580 static int dkr_pull_pull_layer_v2(DkrPull
*i
) {
581 _cleanup_free_
char *path
= NULL
;
582 const char *url
, *layer
= NULL
;
586 assert(!i
->layer_job
);
587 assert(!i
->temp_path
);
588 assert(!i
->final_path
);
591 layer
= dkr_pull_current_layer(i
);
593 return 0; /* no more layers */
595 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
599 if (laccess(path
, F_OK
) < 0) {
603 return log_error_errno(errno
, "Failed to check for container: %m");
606 log_info("Layer %s already exists, skipping.", layer
);
608 i
->current_ancestry
++;
613 log_info("Pulling layer %s...", layer
);
615 i
->final_path
= path
;
618 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/blobs/", layer
);
619 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
621 return log_error_errno(r
, "Failed to allocate layer job: %m");
623 r
= dkr_pull_add_bearer_token(i
, i
->layer_job
);
627 i
->layer_job
->on_finished
= dkr_pull_job_on_finished_v2
;
628 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
629 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
630 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
632 r
= pull_job_begin(i
->layer_job
);
634 return log_error_errno(r
, "Failed to start layer job: %m");
639 static int dkr_pull_pull_layer(DkrPull
*i
) {
640 _cleanup_free_
char *path
= NULL
;
641 const char *url
, *layer
= NULL
;
645 assert(!i
->layer_job
);
646 assert(!i
->temp_path
);
647 assert(!i
->final_path
);
650 layer
= dkr_pull_current_layer(i
);
652 return 0; /* no more layers */
654 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
658 if (laccess(path
, F_OK
) < 0) {
662 return log_error_errno(errno
, "Failed to check for container: %m");
665 log_info("Layer %s already exists, skipping.", layer
);
667 i
->current_ancestry
++;
672 log_info("Pulling layer %s...", layer
);
674 i
->final_path
= path
;
677 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", layer
, "/layer");
678 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
680 return log_error_errno(r
, "Failed to allocate layer job: %m");
682 r
= dkr_pull_add_token(i
, i
->layer_job
);
686 i
->layer_job
->on_finished
= dkr_pull_job_on_finished
;
687 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
688 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
689 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
691 r
= pull_job_begin(i
->layer_job
);
693 return log_error_errno(r
, "Failed to start layer job: %m");
698 static int dkr_pull_job_on_header(PullJob
*j
, const char *header
, size_t sz
) {
699 _cleanup_free_
char *registry
= NULL
;
700 char *token
, *digest
;
708 r
= curl_header_strdup(header
, sz
, HEADER_TOKEN
, &token
);
712 free(i
->response_token
);
713 i
->response_token
= token
;
717 r
= curl_header_strdup(header
, sz
, HEADER_DIGEST
, &digest
);
721 free(i
->response_digest
);
722 i
->response_digest
= digest
;
726 r
= curl_header_strdup(header
, sz
, HEADER_REGISTRY
, ®istry
);
732 l
= strv_split(registry
, ",");
737 if (!hostname_is_valid(*k
, false)) {
738 log_error("Registry hostname is not valid.");
744 strv_free(i
->response_registries
);
745 i
->response_registries
= l
;
751 static void dkr_pull_job_on_finished_v2(PullJob
*j
) {
760 if (j
== i
->images_job
)
761 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
762 else if (j
== i
->ancestry_job
)
763 log_error_errno(j
->error
, "Failed to retrieve manifest.");
764 else if (j
== i
->json_job
)
765 log_error_errno(j
->error
, "Failed to retrieve json data.");
767 log_error_errno(j
->error
, "Failed to retrieve layer data.");
773 if (i
->images_job
== j
) {
776 assert(!i
->tags_job
);
777 assert(!i
->ancestry_job
);
778 assert(!i
->json_job
);
779 assert(!i
->layer_job
);
781 if (strv_isempty(i
->response_registries
)) {
783 log_error("Didn't get registry information.");
787 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
788 dkr_pull_report_progress(i
, DKR_RESOLVING
);
790 url
= strjoina(i
->index_protocol
, "auth.", i
->index_address
, "/v2/token/?scope=repository:",
791 i
->name
, ":pull&service=registry.", i
->index_address
);
792 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
794 log_error_errno(r
, "Failed to allocate tags job: %m");
798 i
->tags_job
->on_finished
= dkr_pull_job_on_finished_v2
;
799 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
801 r
= pull_job_begin(i
->tags_job
);
803 log_error_errno(r
, "Failed to start tags job: %m");
807 } else if (i
->tags_job
== j
) {
809 _cleanup_free_
char *buf
;
810 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
;
811 JsonVariant
*e
= NULL
;
813 assert(!i
->ancestry_job
);
814 assert(!i
->json_job
);
815 assert(!i
->layer_job
);
817 buf
= strndup((const char *)j
->payload
, j
->payload_size
);
824 r
= json_parse(buf
, &doc
);
826 log_error("Unable to parse bearer token\n%s", j
->payload
);
830 e
= json_variant_value(doc
, "token");
831 if (!e
|| e
->type
!= JSON_VARIANT_STRING
) {
833 log_error("Invalid JSON format for Bearer token");
837 r
= free_and_strdup(&i
->response_token
, json_variant_string(e
));
843 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/manifests/", i
->reference
);
844 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
846 log_error_errno(r
, "Failed to allocate ancestry job: %m");
850 r
= dkr_pull_add_bearer_token(i
, i
->ancestry_job
);
854 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished_v2
;
855 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
856 i
->ancestry_job
->on_header
= dkr_pull_job_on_header
;
859 r
= pull_job_begin(i
->ancestry_job
);
861 log_error_errno(r
, "Failed to start ancestry job: %m");
865 } else if (i
->ancestry_job
== j
) {
867 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
, *compat
= NULL
;
868 JsonVariant
*e
= NULL
;
869 _cleanup_strv_free_
char **ancestry
= NULL
;
870 size_t allocated
= 0, size
= 0;
871 char *path
= NULL
, **k
= NULL
;
873 r
= json_parse((const char *)j
->payload
, &doc
);
875 log_error("Invalid JSON Manifest");
879 e
= json_variant_value(doc
, "fsLayers");
880 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
|| e
->size
== 0) {
885 log_info("JSON manifest with schema v%"PRIi64
" for %s parsed!",
886 json_variant_integer(json_variant_value(doc
, "schemaVersion")),
887 json_variant_string(json_variant_value(doc
, "name")));
889 for (unsigned z
= 0; z
< e
->size
; z
++) {
890 JsonVariant
*f
= json_variant_element(e
, z
), *g
= NULL
;
892 if (f
->type
!= JSON_VARIANT_OBJECT
) {
897 g
= json_variant_value(f
, "blobSum");
899 layer
= json_variant_string(g
);
900 if (!dkr_digest_is_valid(layer
)) {
905 if (!GREEDY_REALLOC(ancestry
, allocated
, size
+ 2)) {
911 ancestry
[size
] = strdup(layer
);
912 if (!ancestry
[size
]) {
918 ancestry
[size
+1] = NULL
;
922 e
= json_variant_value(doc
, "history");
923 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
) {
928 e
= json_variant_element(e
, 0);
929 e
= json_variant_value(e
, "v1Compatibility");
930 r
= json_parse(json_variant_string(e
), &compat
);
932 log_error("Invalid v1Compatibility JSON");
936 e
= json_variant_value(compat
, "id");
938 strv_free(i
->ancestry
);
939 i
->ancestry
= strv_reverse(strv_uniq(ancestry
));
940 i
->n_ancestry
= strv_length(i
->ancestry
);
941 i
->current_ancestry
= 0;
942 i
->id
= strdup(i
->ancestry
[i
->n_ancestry
- 1]);
948 path
= strjoin(i
->image_root
, "/.dkr-", json_variant_string(e
), NULL
);
955 i
->image_root
= path
;
958 log_info("Required layers:\n");
959 STRV_FOREACH(k
, i
->ancestry
)
960 log_info("\t%s", *k
);
961 log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e
), i
->response_digest
);
963 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
965 r
= dkr_pull_pull_layer_v2(i
);
969 } else if (i
->layer_job
== j
) {
970 assert(i
->temp_path
);
971 assert(i
->final_path
);
973 j
->disk_fd
= safe_close(j
->disk_fd
);
975 if (i
->tar_pid
> 0) {
976 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
982 r
= aufs_resolve(i
->temp_path
);
984 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
988 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
990 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
994 if (rename(i
->temp_path
, i
->final_path
) < 0) {
995 log_error_errno(errno
, "Failed to rename snaphsot: %m");
999 log_info("Completed writing to layer %s.", i
->final_path
);
1001 i
->layer_job
= pull_job_unref(i
->layer_job
);
1003 i
->temp_path
= NULL
;
1004 free(i
->final_path
);
1005 i
->final_path
= NULL
;
1007 i
->current_ancestry
++;
1008 r
= dkr_pull_pull_layer_v2(i
);
1012 } else if (i
->json_job
!= j
)
1013 assert_not_reached("Got finished event for unknown curl object");
1015 if (!dkr_pull_is_done(i
))
1018 dkr_pull_report_progress(i
, DKR_COPYING
);
1020 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V2
);
1028 i
->on_finished(i
, r
, i
->userdata
);
1030 sd_event_exit(i
->event
, r
);
1034 static void dkr_pull_job_on_finished(PullJob
*j
) {
1039 assert(j
->userdata
);
1042 if (j
->error
!= 0) {
1043 if (j
== i
->images_job
)
1044 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
1045 else if (j
== i
->tags_job
)
1046 log_error_errno(j
->error
, "Failed to retrieve tags list.");
1047 else if (j
== i
->ancestry_job
)
1048 log_error_errno(j
->error
, "Failed to retrieve ancestry list.");
1049 else if (j
== i
->json_job
)
1050 log_error_errno(j
->error
, "Failed to retrieve json data.");
1052 log_error_errno(j
->error
, "Failed to retrieve layer data.");
1058 if (i
->images_job
== j
) {
1061 assert(!i
->tags_job
);
1062 assert(!i
->ancestry_job
);
1063 assert(!i
->json_job
);
1064 assert(!i
->layer_job
);
1066 if (strv_isempty(i
->response_registries
)) {
1068 log_error("Didn't get registry information.");
1072 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
1073 dkr_pull_report_progress(i
, DKR_RESOLVING
);
1075 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/repositories/", i
->name
, "/tags/", i
->reference
);
1076 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
1078 log_error_errno(r
, "Failed to allocate tags job: %m");
1082 r
= dkr_pull_add_token(i
, i
->tags_job
);
1088 i
->tags_job
->on_finished
= dkr_pull_job_on_finished
;
1089 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
1091 r
= pull_job_begin(i
->tags_job
);
1093 log_error_errno(r
, "Failed to start tags job: %m");
1097 } else if (i
->tags_job
== j
) {
1101 assert(!i
->ancestry_job
);
1102 assert(!i
->json_job
);
1103 assert(!i
->layer_job
);
1105 r
= parse_id(j
->payload
, j
->payload_size
, &id
);
1107 log_error_errno(r
, "Failed to parse JSON id.");
1114 log_info("Tag lookup succeeded, resolved to layer %s.", i
->id
);
1115 dkr_pull_report_progress(i
, DKR_METADATA
);
1117 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/ancestry");
1118 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
1120 log_error_errno(r
, "Failed to allocate ancestry job: %m");
1124 r
= dkr_pull_add_token(i
, i
->ancestry_job
);
1130 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished
;
1131 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
1133 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/json");
1134 r
= pull_job_new(&i
->json_job
, url
, i
->glue
, i
);
1136 log_error_errno(r
, "Failed to allocate json job: %m");
1140 r
= dkr_pull_add_token(i
, i
->json_job
);
1146 i
->json_job
->on_finished
= dkr_pull_job_on_finished
;
1147 i
->json_job
->on_progress
= dkr_pull_job_on_progress
;
1149 r
= pull_job_begin(i
->ancestry_job
);
1151 log_error_errno(r
, "Failed to start ancestry job: %m");
1155 r
= pull_job_begin(i
->json_job
);
1157 log_error_errno(r
, "Failed to start json job: %m");
1161 } else if (i
->ancestry_job
== j
) {
1162 char **ancestry
= NULL
, **k
;
1165 assert(!i
->layer_job
);
1167 r
= parse_ancestry(j
->payload
, j
->payload_size
, &ancestry
);
1169 log_error_errno(r
, "Failed to parse JSON id.");
1173 n
= strv_length(ancestry
);
1174 if (n
<= 0 || !streq(ancestry
[n
-1], i
->id
)) {
1175 log_error("Ancestry doesn't end in main layer.");
1176 strv_free(ancestry
);
1181 log_info("Ancestor lookup succeeded, requires layers:\n");
1182 STRV_FOREACH(k
, ancestry
)
1183 log_info("\t%s", *k
);
1185 strv_free(i
->ancestry
);
1186 i
->ancestry
= ancestry
;
1188 i
->current_ancestry
= 0;
1190 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
1192 r
= dkr_pull_pull_layer(i
);
1196 } else if (i
->layer_job
== j
) {
1197 assert(i
->temp_path
);
1198 assert(i
->final_path
);
1200 j
->disk_fd
= safe_close(j
->disk_fd
);
1202 if (i
->tar_pid
> 0) {
1203 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
1209 r
= aufs_resolve(i
->temp_path
);
1211 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
1215 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
1217 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
1221 if (rename(i
->temp_path
, i
->final_path
) < 0) {
1222 log_error_errno(errno
, "Failed to rename snaphsot: %m");
1226 log_info("Completed writing to layer %s.", i
->final_path
);
1228 i
->layer_job
= pull_job_unref(i
->layer_job
);
1229 i
->temp_path
= mfree(i
->temp_path
);
1230 i
->final_path
= mfree(i
->final_path
);
1232 i
->current_ancestry
++;
1233 r
= dkr_pull_pull_layer(i
);
1237 } else if (i
->json_job
!= j
)
1238 assert_not_reached("Got finished event for unknown curl object");
1240 if (!dkr_pull_is_done(i
))
1243 dkr_pull_report_progress(i
, DKR_COPYING
);
1245 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V1
);
1252 i
->on_finished(i
, r
, i
->userdata
);
1254 sd_event_exit(i
->event
, r
);
1257 static int get_protocol_address(char **protocol
, char **address
, const char *url
) {
1258 const char *sep
, *dot
;
1259 _cleanup_free_
char *a
= NULL
, *p
= NULL
;
1261 sep
= strstr(url
, "://");
1265 dot
= strrchr(url
, '.');
1270 p
= strndup(url
, (sep
- url
) + 3);
1274 while (dot
> (sep
+ 3) && *dot
!= '.')
1277 a
= strdup(dot
+ 1);
1288 int dkr_pull_start(DkrPull
*i
, const char *name
, const char *reference
, const char *local
, bool force_local
, DkrPullVersion version
) {
1294 if (!dkr_name_is_valid(name
))
1297 if (reference
&& !dkr_ref_is_valid(reference
))
1300 if (local
&& !machine_name_is_valid(local
))
1307 reference
= "latest";
1309 free(i
->index_protocol
);
1310 free(i
->index_address
);
1311 r
= get_protocol_address(&i
->index_protocol
, &i
->index_address
, i
->index_url
);
1315 r
= free_and_strdup(&i
->local
, local
);
1318 i
->force_local
= force_local
;
1320 r
= free_and_strdup(&i
->name
, name
);
1323 r
= free_and_strdup(&i
->reference
, reference
);
1327 url
= strjoina(i
->index_url
, "/v1/repositories/", name
, "/images");
1329 r
= pull_job_new(&i
->images_job
, url
, i
->glue
, i
);
1333 r
= dkr_pull_add_token(i
, i
->images_job
);
1337 if (version
== DKR_PULL_V1
)
1338 i
->images_job
->on_finished
= dkr_pull_job_on_finished
;
1340 i
->images_job
->on_finished
= dkr_pull_job_on_finished_v2
;
1342 i
->images_job
->on_header
= dkr_pull_job_on_header
;
1343 i
->images_job
->on_progress
= dkr_pull_job_on_progress
;
1345 return pull_job_begin(i
->images_job
);