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 "aufs-util.h"
28 #include "btrfs-util.h"
29 #include "curl-util.h"
32 #include "hostname-util.h"
33 #include "import-common.h"
34 #include "import-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "pull-common.h"
43 #include "string-util.h"
47 typedef enum DkrProgress
{
67 PullJob
*ancestry_job
;
75 char *response_digest
;
77 char **response_registries
;
81 unsigned current_ancestry
;
83 DkrPullFinished on_finished
;
88 bool grow_machine_directory
;
96 #define PROTOCOL_PREFIX "https://"
98 #define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
99 #define HEADER_REGISTRY "X-Do" /* the HTTP header for the registry */ "cker-Endpoints:"
100 #define HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:"
101 #define LAYERS_MAX 127
103 static void dkr_pull_job_on_finished(PullJob
*j
);
105 DkrPull
* dkr_pull_unref(DkrPull
*i
) {
109 if (i
->tar_pid
> 1) {
110 (void) kill_and_sigcont(i
->tar_pid
, SIGKILL
);
111 (void) wait_for_terminate(i
->tar_pid
, NULL
);
114 pull_job_unref(i
->images_job
);
115 pull_job_unref(i
->tags_job
);
116 pull_job_unref(i
->ancestry_job
);
117 pull_job_unref(i
->json_job
);
118 pull_job_unref(i
->layer_job
);
120 curl_glue_unref(i
->glue
);
121 sd_event_unref(i
->event
);
124 (void) rm_rf(i
->temp_path
, REMOVE_ROOT
|REMOVE_PHYSICAL
|REMOVE_SUBVOLUME
);
131 free(i
->response_token
);
132 strv_free(i
->ancestry
);
134 free(i
->index_address
);
135 free(i
->index_protocol
);
147 const char *index_url
,
148 const char *image_root
,
149 DkrPullFinished on_finished
,
152 _cleanup_(dkr_pull_unrefp
) DkrPull
*i
= NULL
;
159 if (!http_url_is_valid(index_url
))
162 i
= new0(DkrPull
, 1);
166 i
->on_finished
= on_finished
;
167 i
->userdata
= userdata
;
169 i
->image_root
= strdup(image_root
?: "/var/lib/machines");
173 i
->grow_machine_directory
= path_startswith(i
->image_root
, "/var/lib/machines");
175 i
->index_url
= strdup(index_url
);
179 e
= endswith(i
->index_url
, "/");
184 i
->event
= sd_event_ref(event
);
186 r
= sd_event_default(&i
->event
);
191 r
= curl_glue_new(&i
->glue
, i
->event
);
195 i
->glue
->on_finished
= pull_job_curl_on_finished
;
196 i
->glue
->userdata
= i
;
204 static void dkr_pull_report_progress(DkrPull
*i
, DkrProgress p
) {
214 percent
+= i
->images_job
->progress_percent
* 5 / 100;
220 percent
+= i
->tags_job
->progress_percent
* 5 / 100;
226 percent
+= i
->ancestry_job
->progress_percent
* 5 / 100;
228 percent
+= i
->json_job
->progress_percent
* 5 / 100;
231 case DKR_DOWNLOADING
:
233 percent
+= 75 * i
->current_ancestry
/ MAX(1U, i
->n_ancestry
);
235 percent
+= i
->layer_job
->progress_percent
* 75 / MAX(1U, i
->n_ancestry
) / 100;
244 assert_not_reached("Unknown progress state");
247 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent
);
248 log_debug("Combined progress %u%%", percent
);
251 static int parse_id(const void *payload
, size_t size
, char **ret
) {
252 _cleanup_free_
char *buf
= NULL
, *id
= NULL
, *other
= NULL
;
253 union json_value v
= {};
254 void *json_state
= NULL
;
264 if (memchr(payload
, 0, size
))
267 buf
= strndup(payload
, size
);
272 t
= json_tokenize(&p
, &id
, &v
, &json_state
, NULL
);
275 if (t
!= JSON_STRING
)
278 t
= json_tokenize(&p
, &other
, &v
, &json_state
, NULL
);
284 if (!dkr_id_is_valid(id
))
293 static int parse_ancestry(const void *payload
, size_t size
, char ***ret
) {
294 _cleanup_free_
char *buf
= NULL
;
295 void *json_state
= NULL
;
302 } state
= STATE_BEGIN
;
303 _cleanup_strv_free_
char **l
= NULL
;
304 size_t n
= 0, allocated
= 0;
309 if (memchr(payload
, 0, size
))
312 buf
= strndup(payload
, size
);
318 _cleanup_free_
char *str
;
319 union json_value v
= {};
322 t
= json_tokenize(&p
, &str
, &v
, &json_state
, NULL
);
329 if (t
== JSON_ARRAY_OPEN
)
337 if (t
== JSON_STRING
) {
338 if (!dkr_id_is_valid(str
))
341 if (n
+1 > LAYERS_MAX
)
344 if (!GREEDY_REALLOC(l
, allocated
, n
+ 2))
353 } else if (t
== JSON_ARRAY_CLOSE
)
363 else if (t
== JSON_ARRAY_CLOSE
)
375 if (!strv_is_uniq(l
))
390 static const char *dkr_pull_current_layer(DkrPull
*i
) {
393 if (strv_isempty(i
->ancestry
))
396 return i
->ancestry
[i
->current_ancestry
];
399 static const char *dkr_pull_current_base_layer(DkrPull
*i
) {
402 if (strv_isempty(i
->ancestry
))
405 if (i
->current_ancestry
<= 0)
408 return i
->ancestry
[i
->current_ancestry
-1];
411 static int dkr_pull_add_token(DkrPull
*i
, PullJob
*j
) {
417 if (i
->response_token
)
418 t
= strjoina("Authorization: Token ", i
->response_token
);
420 t
= HEADER_TOKEN
" true";
422 j
->request_header
= curl_slist_new("Accept: application/json", t
, NULL
);
423 if (!j
->request_header
)
429 static int dkr_pull_add_bearer_token(DkrPull
*i
, PullJob
*j
) {
430 const char *t
= NULL
;
435 if (i
->response_token
)
436 t
= strjoina("Authorization: Bearer ", i
->response_token
);
440 j
->request_header
= curl_slist_new("Accept: application/json", t
, NULL
);
441 if (!j
->request_header
)
447 static bool dkr_pull_is_done(DkrPull
*i
) {
449 assert(i
->images_job
);
450 if (i
->images_job
->state
!= PULL_JOB_DONE
)
453 if (!i
->tags_job
|| i
->tags_job
->state
!= PULL_JOB_DONE
)
456 if (!i
->ancestry_job
|| i
->ancestry_job
->state
!= PULL_JOB_DONE
)
459 if (i
->json_job
&& i
->json_job
->state
!= PULL_JOB_DONE
)
462 if (i
->layer_job
&& i
->layer_job
->state
!= PULL_JOB_DONE
)
465 if (dkr_pull_current_layer(i
))
471 static int dkr_pull_make_local_copy(DkrPull
*i
, DkrPullVersion version
) {
473 _cleanup_free_
char *p
= NULL
;
480 if (!i
->final_path
) {
481 i
->final_path
= strjoin(i
->image_root
, "/.dkr-", i
->id
, NULL
);
486 if (version
== DKR_PULL_V2
) {
487 p
= dirname_malloc(i
->image_root
);
492 r
= pull_make_local_copy(i
->final_path
, p
?: i
->image_root
, i
->local
, i
->force_local
);
496 if (version
== DKR_PULL_V2
) {
499 STRV_FOREACH(k
, i
->ancestry
) {
500 _cleanup_free_
char *d
;
502 d
= strjoin(i
->image_root
, "/.dkr-", *k
, NULL
);
506 r
= btrfs_subvol_remove(d
, BTRFS_REMOVE_QUOTA
);
511 r
= rmdir(i
->image_root
);
519 static int dkr_pull_job_on_open_disk(PullJob
*j
) {
528 assert(i
->layer_job
== j
);
529 assert(i
->final_path
);
530 assert(!i
->temp_path
);
531 assert(i
->tar_pid
<= 0);
533 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
537 mkdir_parents_label(i
->temp_path
, 0700);
539 base
= dkr_pull_current_base_layer(i
);
541 const char *base_path
;
543 base_path
= strjoina(i
->image_root
, "/.dkr-", base
);
544 r
= btrfs_subvol_snapshot(base_path
, i
->temp_path
, BTRFS_SNAPSHOT_FALLBACK_COPY
|BTRFS_SNAPSHOT_QUOTA
);
546 r
= btrfs_subvol_make(i
->temp_path
);
548 return log_error_errno(r
, "Failed to make btrfs subvolume %s: %m", i
->temp_path
);
550 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
552 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
559 static void dkr_pull_job_on_progress(PullJob
*j
) {
567 dkr_pull_report_progress(
569 j
== i
->images_job
? DKR_SEARCHING
:
570 j
== i
->tags_job
? DKR_RESOLVING
:
571 j
== i
->ancestry_job
|| j
== i
->json_job
? DKR_METADATA
:
575 static void dkr_pull_job_on_finished_v2(PullJob
*j
);
577 static int dkr_pull_pull_layer_v2(DkrPull
*i
) {
578 _cleanup_free_
char *path
= NULL
;
579 const char *url
, *layer
= NULL
;
583 assert(!i
->layer_job
);
584 assert(!i
->temp_path
);
585 assert(!i
->final_path
);
588 layer
= dkr_pull_current_layer(i
);
590 return 0; /* no more layers */
592 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
596 if (laccess(path
, F_OK
) < 0) {
600 return log_error_errno(errno
, "Failed to check for container: %m");
603 log_info("Layer %s already exists, skipping.", layer
);
605 i
->current_ancestry
++;
610 log_info("Pulling layer %s...", layer
);
612 i
->final_path
= path
;
615 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/blobs/", layer
);
616 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
618 return log_error_errno(r
, "Failed to allocate layer job: %m");
620 r
= dkr_pull_add_bearer_token(i
, i
->layer_job
);
624 i
->layer_job
->on_finished
= dkr_pull_job_on_finished_v2
;
625 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
626 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
627 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
629 r
= pull_job_begin(i
->layer_job
);
631 return log_error_errno(r
, "Failed to start layer job: %m");
636 static int dkr_pull_pull_layer(DkrPull
*i
) {
637 _cleanup_free_
char *path
= NULL
;
638 const char *url
, *layer
= NULL
;
642 assert(!i
->layer_job
);
643 assert(!i
->temp_path
);
644 assert(!i
->final_path
);
647 layer
= dkr_pull_current_layer(i
);
649 return 0; /* no more layers */
651 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
655 if (laccess(path
, F_OK
) < 0) {
659 return log_error_errno(errno
, "Failed to check for container: %m");
662 log_info("Layer %s already exists, skipping.", layer
);
664 i
->current_ancestry
++;
669 log_info("Pulling layer %s...", layer
);
671 i
->final_path
= path
;
674 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", layer
, "/layer");
675 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
677 return log_error_errno(r
, "Failed to allocate layer job: %m");
679 r
= dkr_pull_add_token(i
, i
->layer_job
);
683 i
->layer_job
->on_finished
= dkr_pull_job_on_finished
;
684 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
685 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
686 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
688 r
= pull_job_begin(i
->layer_job
);
690 return log_error_errno(r
, "Failed to start layer job: %m");
695 static int dkr_pull_job_on_header(PullJob
*j
, const char *header
, size_t sz
) {
696 _cleanup_free_
char *registry
= NULL
;
697 char *token
, *digest
;
705 r
= curl_header_strdup(header
, sz
, HEADER_TOKEN
, &token
);
709 free(i
->response_token
);
710 i
->response_token
= token
;
714 r
= curl_header_strdup(header
, sz
, HEADER_DIGEST
, &digest
);
718 free(i
->response_digest
);
719 i
->response_digest
= digest
;
723 r
= curl_header_strdup(header
, sz
, HEADER_REGISTRY
, ®istry
);
729 l
= strv_split(registry
, ",");
734 if (!hostname_is_valid(*k
, false)) {
735 log_error("Registry hostname is not valid.");
741 strv_free(i
->response_registries
);
742 i
->response_registries
= l
;
748 static void dkr_pull_job_on_finished_v2(PullJob
*j
) {
757 if (j
== i
->images_job
)
758 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
759 else if (j
== i
->ancestry_job
)
760 log_error_errno(j
->error
, "Failed to retrieve manifest.");
761 else if (j
== i
->json_job
)
762 log_error_errno(j
->error
, "Failed to retrieve json data.");
764 log_error_errno(j
->error
, "Failed to retrieve layer data.");
770 if (i
->images_job
== j
) {
773 assert(!i
->tags_job
);
774 assert(!i
->ancestry_job
);
775 assert(!i
->json_job
);
776 assert(!i
->layer_job
);
778 if (strv_isempty(i
->response_registries
)) {
780 log_error("Didn't get registry information.");
784 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
785 dkr_pull_report_progress(i
, DKR_RESOLVING
);
787 url
= strjoina(i
->index_protocol
, "auth.", i
->index_address
, "/v2/token/?scope=repository:",
788 i
->name
, ":pull&service=registry.", i
->index_address
);
789 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
791 log_error_errno(r
, "Failed to allocate tags job: %m");
795 i
->tags_job
->on_finished
= dkr_pull_job_on_finished_v2
;
796 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
798 r
= pull_job_begin(i
->tags_job
);
800 log_error_errno(r
, "Failed to start tags job: %m");
804 } else if (i
->tags_job
== j
) {
806 _cleanup_free_
char *buf
;
807 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
;
808 JsonVariant
*e
= NULL
;
810 assert(!i
->ancestry_job
);
811 assert(!i
->json_job
);
812 assert(!i
->layer_job
);
814 buf
= strndup((const char *)j
->payload
, j
->payload_size
);
821 r
= json_parse(buf
, &doc
);
823 log_error("Unable to parse bearer token\n%s", j
->payload
);
827 e
= json_variant_value(doc
, "token");
828 if (!e
|| e
->type
!= JSON_VARIANT_STRING
) {
830 log_error("Invalid JSON format for Bearer token");
834 r
= free_and_strdup(&i
->response_token
, json_variant_string(e
));
840 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/manifests/", i
->reference
);
841 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
843 log_error_errno(r
, "Failed to allocate ancestry job: %m");
847 r
= dkr_pull_add_bearer_token(i
, i
->ancestry_job
);
851 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished_v2
;
852 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
853 i
->ancestry_job
->on_header
= dkr_pull_job_on_header
;
856 r
= pull_job_begin(i
->ancestry_job
);
858 log_error_errno(r
, "Failed to start ancestry job: %m");
862 } else if (i
->ancestry_job
== j
) {
864 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
, *compat
= NULL
;
865 JsonVariant
*e
= NULL
;
866 _cleanup_strv_free_
char **ancestry
= NULL
;
867 size_t allocated
= 0, size
= 0;
868 char *path
= NULL
, **k
= NULL
;
870 r
= json_parse((const char *)j
->payload
, &doc
);
872 log_error("Invalid JSON Manifest");
876 e
= json_variant_value(doc
, "fsLayers");
877 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
|| e
->size
== 0) {
882 log_info("JSON manifest with schema v%"PRIi64
" for %s parsed!",
883 json_variant_integer(json_variant_value(doc
, "schemaVersion")),
884 json_variant_string(json_variant_value(doc
, "name")));
886 for (unsigned z
= 0; z
< e
->size
; z
++) {
887 JsonVariant
*f
= json_variant_element(e
, z
), *g
= NULL
;
889 if (f
->type
!= JSON_VARIANT_OBJECT
) {
894 g
= json_variant_value(f
, "blobSum");
896 layer
= json_variant_string(g
);
897 if (!dkr_digest_is_valid(layer
)) {
902 if (!GREEDY_REALLOC(ancestry
, allocated
, size
+ 2)) {
908 ancestry
[size
] = strdup(layer
);
909 if (!ancestry
[size
]) {
915 ancestry
[size
+1] = NULL
;
919 e
= json_variant_value(doc
, "history");
920 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
) {
925 e
= json_variant_element(e
, 0);
926 e
= json_variant_value(e
, "v1Compatibility");
927 r
= json_parse(json_variant_string(e
), &compat
);
929 log_error("Invalid v1Compatibility JSON");
933 e
= json_variant_value(compat
, "id");
935 strv_free(i
->ancestry
);
936 i
->ancestry
= strv_reverse(strv_uniq(ancestry
));
937 i
->n_ancestry
= strv_length(i
->ancestry
);
938 i
->current_ancestry
= 0;
939 i
->id
= strdup(i
->ancestry
[i
->n_ancestry
- 1]);
945 path
= strjoin(i
->image_root
, "/.dkr-", json_variant_string(e
), NULL
);
952 i
->image_root
= path
;
955 log_info("Required layers:\n");
956 STRV_FOREACH(k
, i
->ancestry
)
957 log_info("\t%s", *k
);
958 log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e
), i
->response_digest
);
960 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
962 r
= dkr_pull_pull_layer_v2(i
);
966 } else if (i
->layer_job
== j
) {
967 assert(i
->temp_path
);
968 assert(i
->final_path
);
970 j
->disk_fd
= safe_close(j
->disk_fd
);
972 if (i
->tar_pid
> 0) {
973 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
979 r
= aufs_resolve(i
->temp_path
);
981 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
985 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
987 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
991 if (rename(i
->temp_path
, i
->final_path
) < 0) {
992 log_error_errno(errno
, "Failed to rename snaphsot: %m");
996 log_info("Completed writing to layer %s.", i
->final_path
);
998 i
->layer_job
= pull_job_unref(i
->layer_job
);
1000 i
->temp_path
= NULL
;
1001 free(i
->final_path
);
1002 i
->final_path
= NULL
;
1004 i
->current_ancestry
++;
1005 r
= dkr_pull_pull_layer_v2(i
);
1009 } else if (i
->json_job
!= j
)
1010 assert_not_reached("Got finished event for unknown curl object");
1012 if (!dkr_pull_is_done(i
))
1015 dkr_pull_report_progress(i
, DKR_COPYING
);
1017 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V2
);
1025 i
->on_finished(i
, r
, i
->userdata
);
1027 sd_event_exit(i
->event
, r
);
1031 static void dkr_pull_job_on_finished(PullJob
*j
) {
1036 assert(j
->userdata
);
1039 if (j
->error
!= 0) {
1040 if (j
== i
->images_job
)
1041 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
1042 else if (j
== i
->tags_job
)
1043 log_error_errno(j
->error
, "Failed to retrieve tags list.");
1044 else if (j
== i
->ancestry_job
)
1045 log_error_errno(j
->error
, "Failed to retrieve ancestry list.");
1046 else if (j
== i
->json_job
)
1047 log_error_errno(j
->error
, "Failed to retrieve json data.");
1049 log_error_errno(j
->error
, "Failed to retrieve layer data.");
1055 if (i
->images_job
== j
) {
1058 assert(!i
->tags_job
);
1059 assert(!i
->ancestry_job
);
1060 assert(!i
->json_job
);
1061 assert(!i
->layer_job
);
1063 if (strv_isempty(i
->response_registries
)) {
1065 log_error("Didn't get registry information.");
1069 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
1070 dkr_pull_report_progress(i
, DKR_RESOLVING
);
1072 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/repositories/", i
->name
, "/tags/", i
->reference
);
1073 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
1075 log_error_errno(r
, "Failed to allocate tags job: %m");
1079 r
= dkr_pull_add_token(i
, i
->tags_job
);
1085 i
->tags_job
->on_finished
= dkr_pull_job_on_finished
;
1086 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
1088 r
= pull_job_begin(i
->tags_job
);
1090 log_error_errno(r
, "Failed to start tags job: %m");
1094 } else if (i
->tags_job
== j
) {
1098 assert(!i
->ancestry_job
);
1099 assert(!i
->json_job
);
1100 assert(!i
->layer_job
);
1102 r
= parse_id(j
->payload
, j
->payload_size
, &id
);
1104 log_error_errno(r
, "Failed to parse JSON id.");
1111 log_info("Tag lookup succeeded, resolved to layer %s.", i
->id
);
1112 dkr_pull_report_progress(i
, DKR_METADATA
);
1114 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/ancestry");
1115 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
1117 log_error_errno(r
, "Failed to allocate ancestry job: %m");
1121 r
= dkr_pull_add_token(i
, i
->ancestry_job
);
1127 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished
;
1128 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
1130 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/json");
1131 r
= pull_job_new(&i
->json_job
, url
, i
->glue
, i
);
1133 log_error_errno(r
, "Failed to allocate json job: %m");
1137 r
= dkr_pull_add_token(i
, i
->json_job
);
1143 i
->json_job
->on_finished
= dkr_pull_job_on_finished
;
1144 i
->json_job
->on_progress
= dkr_pull_job_on_progress
;
1146 r
= pull_job_begin(i
->ancestry_job
);
1148 log_error_errno(r
, "Failed to start ancestry job: %m");
1152 r
= pull_job_begin(i
->json_job
);
1154 log_error_errno(r
, "Failed to start json job: %m");
1158 } else if (i
->ancestry_job
== j
) {
1159 char **ancestry
= NULL
, **k
;
1162 assert(!i
->layer_job
);
1164 r
= parse_ancestry(j
->payload
, j
->payload_size
, &ancestry
);
1166 log_error_errno(r
, "Failed to parse JSON id.");
1170 n
= strv_length(ancestry
);
1171 if (n
<= 0 || !streq(ancestry
[n
-1], i
->id
)) {
1172 log_error("Ancestry doesn't end in main layer.");
1173 strv_free(ancestry
);
1178 log_info("Ancestor lookup succeeded, requires layers:\n");
1179 STRV_FOREACH(k
, ancestry
)
1180 log_info("\t%s", *k
);
1182 strv_free(i
->ancestry
);
1183 i
->ancestry
= ancestry
;
1185 i
->current_ancestry
= 0;
1187 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
1189 r
= dkr_pull_pull_layer(i
);
1193 } else if (i
->layer_job
== j
) {
1194 assert(i
->temp_path
);
1195 assert(i
->final_path
);
1197 j
->disk_fd
= safe_close(j
->disk_fd
);
1199 if (i
->tar_pid
> 0) {
1200 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
1206 r
= aufs_resolve(i
->temp_path
);
1208 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
1212 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
1214 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
1218 if (rename(i
->temp_path
, i
->final_path
) < 0) {
1219 log_error_errno(errno
, "Failed to rename snaphsot: %m");
1223 log_info("Completed writing to layer %s.", i
->final_path
);
1225 i
->layer_job
= pull_job_unref(i
->layer_job
);
1226 i
->temp_path
= mfree(i
->temp_path
);
1227 i
->final_path
= mfree(i
->final_path
);
1229 i
->current_ancestry
++;
1230 r
= dkr_pull_pull_layer(i
);
1234 } else if (i
->json_job
!= j
)
1235 assert_not_reached("Got finished event for unknown curl object");
1237 if (!dkr_pull_is_done(i
))
1240 dkr_pull_report_progress(i
, DKR_COPYING
);
1242 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V1
);
1249 i
->on_finished(i
, r
, i
->userdata
);
1251 sd_event_exit(i
->event
, r
);
1254 static int get_protocol_address(char **protocol
, char **address
, const char *url
) {
1255 const char *sep
, *dot
;
1256 _cleanup_free_
char *a
= NULL
, *p
= NULL
;
1258 sep
= strstr(url
, "://");
1262 dot
= strrchr(url
, '.');
1267 p
= strndup(url
, (sep
- url
) + 3);
1271 while (dot
> (sep
+ 3) && *dot
!= '.')
1274 a
= strdup(dot
+ 1);
1285 int dkr_pull_start(DkrPull
*i
, const char *name
, const char *reference
, const char *local
, bool force_local
, DkrPullVersion version
) {
1291 if (!dkr_name_is_valid(name
))
1294 if (reference
&& !dkr_ref_is_valid(reference
))
1297 if (local
&& !machine_name_is_valid(local
))
1304 reference
= "latest";
1306 free(i
->index_protocol
);
1307 free(i
->index_address
);
1308 r
= get_protocol_address(&i
->index_protocol
, &i
->index_address
, i
->index_url
);
1312 r
= free_and_strdup(&i
->local
, local
);
1315 i
->force_local
= force_local
;
1317 r
= free_and_strdup(&i
->name
, name
);
1320 r
= free_and_strdup(&i
->reference
, reference
);
1324 url
= strjoina(i
->index_url
, "/v1/repositories/", name
, "/images");
1326 r
= pull_job_new(&i
->images_job
, url
, i
->glue
, i
);
1330 r
= dkr_pull_add_token(i
, i
->images_job
);
1334 if (version
== DKR_PULL_V1
)
1335 i
->images_job
->on_finished
= dkr_pull_job_on_finished
;
1337 i
->images_job
->on_finished
= dkr_pull_job_on_finished_v2
;
1339 i
->images_job
->on_header
= dkr_pull_job_on_header
;
1340 i
->images_job
->on_progress
= dkr_pull_job_on_progress
;
1342 return pull_job_begin(i
->images_job
);