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
++;
599 log_info("Pulling layer %s...", layer
);
601 i
->final_path
= path
;
604 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/blobs/", layer
);
605 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
607 return log_error_errno(r
, "Failed to allocate layer job: %m");
609 r
= dkr_pull_add_bearer_token(i
, i
->layer_job
);
613 i
->layer_job
->on_finished
= dkr_pull_job_on_finished_v2
;
614 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
615 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
616 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
618 r
= pull_job_begin(i
->layer_job
);
620 return log_error_errno(r
, "Failed to start layer job: %m");
625 static int dkr_pull_pull_layer(DkrPull
*i
) {
626 _cleanup_free_
char *path
= NULL
;
627 const char *url
, *layer
= NULL
;
631 assert(!i
->layer_job
);
632 assert(!i
->temp_path
);
633 assert(!i
->final_path
);
636 layer
= dkr_pull_current_layer(i
);
638 return 0; /* no more layers */
640 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
644 if (laccess(path
, F_OK
) < 0) {
648 return log_error_errno(errno
, "Failed to check for container: %m");
651 log_info("Layer %s already exists, skipping.", layer
);
653 i
->current_ancestry
++;
659 log_info("Pulling layer %s...", layer
);
661 i
->final_path
= path
;
664 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", layer
, "/layer");
665 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
667 return log_error_errno(r
, "Failed to allocate layer job: %m");
669 r
= dkr_pull_add_token(i
, i
->layer_job
);
673 i
->layer_job
->on_finished
= dkr_pull_job_on_finished
;
674 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
675 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
676 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
678 r
= pull_job_begin(i
->layer_job
);
680 return log_error_errno(r
, "Failed to start layer job: %m");
685 static int dkr_pull_job_on_header(PullJob
*j
, const char *header
, size_t sz
) {
686 _cleanup_free_
char *registry
= NULL
;
687 char *token
, *digest
;
695 r
= curl_header_strdup(header
, sz
, HEADER_TOKEN
, &token
);
699 free(i
->response_token
);
700 i
->response_token
= token
;
704 r
= curl_header_strdup(header
, sz
, HEADER_DIGEST
, &digest
);
708 free(i
->response_digest
);
709 i
->response_digest
= digest
;
713 r
= curl_header_strdup(header
, sz
, HEADER_REGISTRY
, ®istry
);
719 l
= strv_split(registry
, ",");
724 if (!hostname_is_valid(*k
)) {
725 log_error("Registry hostname is not valid.");
731 strv_free(i
->response_registries
);
732 i
->response_registries
= l
;
738 static void dkr_pull_job_on_finished_v2(PullJob
*j
) {
747 if (j
== i
->images_job
)
748 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
749 else if (j
== i
->ancestry_job
)
750 log_error_errno(j
->error
, "Failed to retrieve manifest.");
751 else if (j
== i
->json_job
)
752 log_error_errno(j
->error
, "Failed to retrieve json data.");
754 log_error_errno(j
->error
, "Failed to retrieve layer data.");
760 if (i
->images_job
== j
) {
763 assert(!i
->tags_job
);
764 assert(!i
->ancestry_job
);
765 assert(!i
->json_job
);
766 assert(!i
->layer_job
);
768 if (strv_isempty(i
->response_registries
)) {
770 log_error("Didn't get registry information.");
774 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
775 dkr_pull_report_progress(i
, DKR_RESOLVING
);
777 url
= strjoina(i
->index_protocol
, "auth.", i
->index_address
, "/v2/token/?scope=repository:",
778 i
->name
, ":pull&service=registry.", i
->index_address
);
779 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
781 log_error_errno(r
, "Failed to allocate tags job: %m");
785 i
->tags_job
->on_finished
= dkr_pull_job_on_finished_v2
;
786 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
788 r
= pull_job_begin(i
->tags_job
);
790 log_error_errno(r
, "Failed to start tags job: %m");
794 } else if (i
->tags_job
== j
) {
796 _cleanup_free_
const char *buf
;
797 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
;
798 JsonVariant
*e
= NULL
;
800 assert(!i
->ancestry_job
);
801 assert(!i
->json_job
);
802 assert(!i
->layer_job
);
804 buf
= strndup((const char *)j
->payload
, j
->payload_size
);
811 r
= json_parse(buf
, &doc
);
813 log_error("Unable to parse bearer token\n%s", j
->payload
);
817 e
= json_variant_value(doc
, "token");
818 if (!e
|| e
->type
!= JSON_VARIANT_STRING
) {
820 log_error("Invalid JSON format for Bearer token");
824 r
= free_and_strdup(&i
->response_token
, json_variant_string(e
));
830 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/manifests/", i
->reference
);
831 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
833 log_error_errno(r
, "Failed to allocate ancestry job: %m");
837 r
= dkr_pull_add_bearer_token(i
, i
->ancestry_job
);
841 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished_v2
;
842 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
843 i
->ancestry_job
->on_header
= dkr_pull_job_on_header
;
846 r
= pull_job_begin(i
->ancestry_job
);
848 log_error_errno(r
, "Failed to start ancestry job: %m");
852 } else if (i
->ancestry_job
== j
) {
854 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
, *compat
= NULL
;
855 JsonVariant
*e
= NULL
;
856 _cleanup_strv_free_
char **ancestry
= NULL
;
857 size_t allocated
= 0, size
= 0;
858 char *path
= NULL
, **k
= NULL
;
860 r
= json_parse((const char *)j
->payload
, &doc
);
862 log_error("Invalid JSON Manifest");
866 e
= json_variant_value(doc
, "fsLayers");
867 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
|| e
->size
== 0) {
872 log_info("JSON manifest with schema v%"PRIi64
" for %s parsed!",
873 json_variant_integer(json_variant_value(doc
, "schemaVersion")),
874 json_variant_string(json_variant_value(doc
, "name")));
876 for (unsigned z
= 0; z
< e
->size
; z
++) {
877 JsonVariant
*f
= json_variant_element(e
, z
), *g
= NULL
;
879 if (f
->type
!= JSON_VARIANT_OBJECT
) {
884 g
= json_variant_value(f
, "blobSum");
886 layer
= json_variant_string(g
);
887 if (!dkr_digest_is_valid(layer
)) {
892 if (!GREEDY_REALLOC(ancestry
, allocated
, size
+ 2)) {
898 ancestry
[size
] = strdup(layer
);
899 if (!ancestry
[size
]) {
905 ancestry
[size
+1] = NULL
;
909 e
= json_variant_value(doc
, "history");
910 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
) {
915 e
= json_variant_element(e
, 0);
916 e
= json_variant_value(e
, "v1Compatibility");
917 r
= json_parse(json_variant_string(e
), &compat
);
919 log_error("Invalid v1Compatibility JSON");
923 e
= json_variant_value(compat
, "id");
925 strv_free(i
->ancestry
);
926 i
->ancestry
= strv_reverse(strv_uniq(ancestry
));
927 i
->n_ancestry
= strv_length(i
->ancestry
);
928 i
->current_ancestry
= 0;
929 i
->id
= strdup(i
->ancestry
[i
->n_ancestry
- 1]);
935 path
= strjoin(i
->image_root
, "/.dkr-", json_variant_string(e
), NULL
);
942 i
->image_root
= path
;
945 log_info("Required layers:\n");
946 STRV_FOREACH(k
, i
->ancestry
)
947 log_info("\t%s", *k
);
948 log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e
), i
->response_digest
);
950 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
952 r
= dkr_pull_pull_layer_v2(i
);
956 } else if (i
->layer_job
== j
) {
957 assert(i
->temp_path
);
958 assert(i
->final_path
);
960 j
->disk_fd
= safe_close(j
->disk_fd
);
962 if (i
->tar_pid
> 0) {
963 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
969 r
= aufs_resolve(i
->temp_path
);
971 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
975 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
977 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
981 if (rename(i
->temp_path
, i
->final_path
) < 0) {
982 log_error_errno(errno
, "Failed to rename snaphsot: %m");
986 log_info("Completed writing to layer %s.", i
->final_path
);
988 i
->layer_job
= pull_job_unref(i
->layer_job
);
992 i
->final_path
= NULL
;
994 i
->current_ancestry
++;
995 r
= dkr_pull_pull_layer_v2(i
);
999 } else if (i
->json_job
!= j
)
1000 assert_not_reached("Got finished event for unknown curl object");
1002 if (!dkr_pull_is_done(i
))
1005 dkr_pull_report_progress(i
, DKR_COPYING
);
1007 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V2
);
1015 i
->on_finished(i
, r
, i
->userdata
);
1017 sd_event_exit(i
->event
, r
);
1021 static void dkr_pull_job_on_finished(PullJob
*j
) {
1026 assert(j
->userdata
);
1029 if (j
->error
!= 0) {
1030 if (j
== i
->images_job
)
1031 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
1032 else if (j
== i
->tags_job
)
1033 log_error_errno(j
->error
, "Failed to retrieve tags list.");
1034 else if (j
== i
->ancestry_job
)
1035 log_error_errno(j
->error
, "Failed to retrieve ancestry list.");
1036 else if (j
== i
->json_job
)
1037 log_error_errno(j
->error
, "Failed to retrieve json data.");
1039 log_error_errno(j
->error
, "Failed to retrieve layer data.");
1045 if (i
->images_job
== j
) {
1048 assert(!i
->tags_job
);
1049 assert(!i
->ancestry_job
);
1050 assert(!i
->json_job
);
1051 assert(!i
->layer_job
);
1053 if (strv_isempty(i
->response_registries
)) {
1055 log_error("Didn't get registry information.");
1059 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
1060 dkr_pull_report_progress(i
, DKR_RESOLVING
);
1062 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/repositories/", i
->name
, "/tags/", i
->reference
);
1063 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
1065 log_error_errno(r
, "Failed to allocate tags job: %m");
1069 r
= dkr_pull_add_token(i
, i
->tags_job
);
1075 i
->tags_job
->on_finished
= dkr_pull_job_on_finished
;
1076 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
1078 r
= pull_job_begin(i
->tags_job
);
1080 log_error_errno(r
, "Failed to start tags job: %m");
1084 } else if (i
->tags_job
== j
) {
1088 assert(!i
->ancestry_job
);
1089 assert(!i
->json_job
);
1090 assert(!i
->layer_job
);
1092 r
= parse_id(j
->payload
, j
->payload_size
, &id
);
1094 log_error_errno(r
, "Failed to parse JSON id.");
1101 log_info("Tag lookup succeeded, resolved to layer %s.", i
->id
);
1102 dkr_pull_report_progress(i
, DKR_METADATA
);
1104 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/ancestry");
1105 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
1107 log_error_errno(r
, "Failed to allocate ancestry job: %m");
1111 r
= dkr_pull_add_token(i
, i
->ancestry_job
);
1117 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished
;
1118 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
1120 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/json");
1121 r
= pull_job_new(&i
->json_job
, url
, i
->glue
, i
);
1123 log_error_errno(r
, "Failed to allocate json job: %m");
1127 r
= dkr_pull_add_token(i
, i
->json_job
);
1133 i
->json_job
->on_finished
= dkr_pull_job_on_finished
;
1134 i
->json_job
->on_progress
= dkr_pull_job_on_progress
;
1136 r
= pull_job_begin(i
->ancestry_job
);
1138 log_error_errno(r
, "Failed to start ancestry job: %m");
1142 r
= pull_job_begin(i
->json_job
);
1144 log_error_errno(r
, "Failed to start json job: %m");
1148 } else if (i
->ancestry_job
== j
) {
1149 char **ancestry
= NULL
, **k
;
1152 assert(!i
->layer_job
);
1154 r
= parse_ancestry(j
->payload
, j
->payload_size
, &ancestry
);
1156 log_error_errno(r
, "Failed to parse JSON id.");
1160 n
= strv_length(ancestry
);
1161 if (n
<= 0 || !streq(ancestry
[n
-1], i
->id
)) {
1162 log_error("Ancestry doesn't end in main layer.");
1163 strv_free(ancestry
);
1168 log_info("Ancestor lookup succeeded, requires layers:\n");
1169 STRV_FOREACH(k
, ancestry
)
1170 log_info("\t%s", *k
);
1172 strv_free(i
->ancestry
);
1173 i
->ancestry
= ancestry
;
1175 i
->current_ancestry
= 0;
1177 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
1179 r
= dkr_pull_pull_layer(i
);
1183 } else if (i
->layer_job
== j
) {
1184 assert(i
->temp_path
);
1185 assert(i
->final_path
);
1187 j
->disk_fd
= safe_close(j
->disk_fd
);
1189 if (i
->tar_pid
> 0) {
1190 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
1196 r
= aufs_resolve(i
->temp_path
);
1198 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
1202 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
1204 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
1208 if (rename(i
->temp_path
, i
->final_path
) < 0) {
1209 log_error_errno(errno
, "Failed to rename snaphsot: %m");
1213 log_info("Completed writing to layer %s.", i
->final_path
);
1215 i
->layer_job
= pull_job_unref(i
->layer_job
);
1217 i
->temp_path
= NULL
;
1218 free(i
->final_path
);
1219 i
->final_path
= NULL
;
1221 i
->current_ancestry
++;
1222 r
= dkr_pull_pull_layer(i
);
1226 } else if (i
->json_job
!= j
)
1227 assert_not_reached("Got finished event for unknown curl object");
1229 if (!dkr_pull_is_done(i
))
1232 dkr_pull_report_progress(i
, DKR_COPYING
);
1234 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V1
);
1241 i
->on_finished(i
, r
, i
->userdata
);
1243 sd_event_exit(i
->event
, r
);
1246 static int get_protocol_address(char **protocol
, char **address
, const char *url
) {
1247 const char *sep
, *dot
;
1248 _cleanup_free_
char *a
= NULL
, *p
= NULL
;
1250 sep
= strstr(url
, "://");
1254 dot
= strrchr(url
, '.');
1259 p
= strndup(url
, (sep
- url
) + 3);
1263 while (dot
> (sep
+ 3) && *dot
!= '.')
1266 a
= strdup(dot
+ 1);
1277 int dkr_pull_start(DkrPull
*i
, const char *name
, const char *reference
, const char *local
, bool force_local
, DkrPullVersion version
) {
1283 if (!dkr_name_is_valid(name
))
1286 if (reference
&& !dkr_ref_is_valid(reference
))
1289 if (local
&& !machine_name_is_valid(local
))
1296 reference
= "latest";
1298 free(i
->index_protocol
);
1299 free(i
->index_address
);
1300 r
= get_protocol_address(&i
->index_protocol
, &i
->index_address
, i
->index_url
);
1304 r
= free_and_strdup(&i
->local
, local
);
1307 i
->force_local
= force_local
;
1309 r
= free_and_strdup(&i
->name
, name
);
1312 r
= free_and_strdup(&i
->reference
, reference
);
1316 url
= strjoina(i
->index_url
, "/v1/repositories/", name
, "/images");
1318 r
= pull_job_new(&i
->images_job
, url
, i
->glue
, i
);
1322 r
= dkr_pull_add_token(i
, i
->images_job
);
1326 if (version
== DKR_PULL_V1
)
1327 i
->images_job
->on_finished
= dkr_pull_job_on_finished
;
1329 i
->images_job
->on_finished
= dkr_pull_job_on_finished_v2
;
1331 i
->images_job
->on_header
= dkr_pull_job_on_header
;
1332 i
->images_job
->on_progress
= dkr_pull_job_on_progress
;
1334 return pull_job_begin(i
->images_job
);