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
) {
495 STRV_FOREACH(k
, i
->ancestry
) {
496 _cleanup_free_
char *d
;
498 d
= strjoin(i
->image_root
, "/.dkr-", *k
, NULL
);
502 r
= btrfs_subvol_remove(d
, BTRFS_REMOVE_QUOTA
);
507 r
= rmdir(i
->image_root
);
515 static int dkr_pull_job_on_open_disk(PullJob
*j
) {
524 assert(i
->layer_job
== j
);
525 assert(i
->final_path
);
526 assert(!i
->temp_path
);
527 assert(i
->tar_pid
<= 0);
529 r
= tempfn_random(i
->final_path
, NULL
, &i
->temp_path
);
533 mkdir_parents_label(i
->temp_path
, 0700);
535 base
= dkr_pull_current_base_layer(i
);
537 const char *base_path
;
539 base_path
= strjoina(i
->image_root
, "/.dkr-", base
);
540 r
= btrfs_subvol_snapshot(base_path
, i
->temp_path
, BTRFS_SNAPSHOT_FALLBACK_COPY
|BTRFS_SNAPSHOT_QUOTA
);
542 r
= btrfs_subvol_make(i
->temp_path
);
544 return log_error_errno(r
, "Failed to make btrfs subvolume %s: %m", i
->temp_path
);
546 (void) import_assign_pool_quota_and_warn(i
->temp_path
);
548 j
->disk_fd
= import_fork_tar_x(i
->temp_path
, &i
->tar_pid
);
555 static void dkr_pull_job_on_progress(PullJob
*j
) {
563 dkr_pull_report_progress(
565 j
== i
->images_job
? DKR_SEARCHING
:
566 j
== i
->tags_job
? DKR_RESOLVING
:
567 j
== i
->ancestry_job
|| j
== i
->json_job
? DKR_METADATA
:
571 static void dkr_pull_job_on_finished_v2(PullJob
*j
);
573 static int dkr_pull_pull_layer_v2(DkrPull
*i
) {
574 _cleanup_free_
char *path
= NULL
;
575 const char *url
, *layer
= NULL
;
579 assert(!i
->layer_job
);
580 assert(!i
->temp_path
);
581 assert(!i
->final_path
);
584 layer
= dkr_pull_current_layer(i
);
586 return 0; /* no more layers */
588 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
592 if (laccess(path
, F_OK
) < 0) {
596 return log_error_errno(errno
, "Failed to check for container: %m");
599 log_info("Layer %s already exists, skipping.", layer
);
601 i
->current_ancestry
++;
606 log_info("Pulling layer %s...", layer
);
608 i
->final_path
= path
;
611 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/blobs/", layer
);
612 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
614 return log_error_errno(r
, "Failed to allocate layer job: %m");
616 r
= dkr_pull_add_bearer_token(i
, i
->layer_job
);
620 i
->layer_job
->on_finished
= dkr_pull_job_on_finished_v2
;
621 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
622 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
623 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
625 r
= pull_job_begin(i
->layer_job
);
627 return log_error_errno(r
, "Failed to start layer job: %m");
632 static int dkr_pull_pull_layer(DkrPull
*i
) {
633 _cleanup_free_
char *path
= NULL
;
634 const char *url
, *layer
= NULL
;
638 assert(!i
->layer_job
);
639 assert(!i
->temp_path
);
640 assert(!i
->final_path
);
643 layer
= dkr_pull_current_layer(i
);
645 return 0; /* no more layers */
647 path
= strjoin(i
->image_root
, "/.dkr-", layer
, NULL
);
651 if (laccess(path
, F_OK
) < 0) {
655 return log_error_errno(errno
, "Failed to check for container: %m");
658 log_info("Layer %s already exists, skipping.", layer
);
660 i
->current_ancestry
++;
665 log_info("Pulling layer %s...", layer
);
667 i
->final_path
= path
;
670 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", layer
, "/layer");
671 r
= pull_job_new(&i
->layer_job
, url
, i
->glue
, i
);
673 return log_error_errno(r
, "Failed to allocate layer job: %m");
675 r
= dkr_pull_add_token(i
, i
->layer_job
);
679 i
->layer_job
->on_finished
= dkr_pull_job_on_finished
;
680 i
->layer_job
->on_open_disk
= dkr_pull_job_on_open_disk
;
681 i
->layer_job
->on_progress
= dkr_pull_job_on_progress
;
682 i
->layer_job
->grow_machine_directory
= i
->grow_machine_directory
;
684 r
= pull_job_begin(i
->layer_job
);
686 return log_error_errno(r
, "Failed to start layer job: %m");
691 static int dkr_pull_job_on_header(PullJob
*j
, const char *header
, size_t sz
) {
692 _cleanup_free_
char *registry
= NULL
;
693 char *token
, *digest
;
701 r
= curl_header_strdup(header
, sz
, HEADER_TOKEN
, &token
);
705 free(i
->response_token
);
706 i
->response_token
= token
;
710 r
= curl_header_strdup(header
, sz
, HEADER_DIGEST
, &digest
);
714 free(i
->response_digest
);
715 i
->response_digest
= digest
;
719 r
= curl_header_strdup(header
, sz
, HEADER_REGISTRY
, ®istry
);
725 l
= strv_split(registry
, ",");
730 if (!hostname_is_valid(*k
, false)) {
731 log_error("Registry hostname is not valid.");
737 strv_free(i
->response_registries
);
738 i
->response_registries
= l
;
744 static void dkr_pull_job_on_finished_v2(PullJob
*j
) {
753 if (j
== i
->images_job
)
754 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
755 else if (j
== i
->ancestry_job
)
756 log_error_errno(j
->error
, "Failed to retrieve manifest.");
757 else if (j
== i
->json_job
)
758 log_error_errno(j
->error
, "Failed to retrieve json data.");
760 log_error_errno(j
->error
, "Failed to retrieve layer data.");
766 if (i
->images_job
== j
) {
769 assert(!i
->tags_job
);
770 assert(!i
->ancestry_job
);
771 assert(!i
->json_job
);
772 assert(!i
->layer_job
);
774 if (strv_isempty(i
->response_registries
)) {
776 log_error("Didn't get registry information.");
780 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
781 dkr_pull_report_progress(i
, DKR_RESOLVING
);
783 url
= strjoina(i
->index_protocol
, "auth.", i
->index_address
, "/v2/token/?scope=repository:",
784 i
->name
, ":pull&service=registry.", i
->index_address
);
785 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
787 log_error_errno(r
, "Failed to allocate tags job: %m");
791 i
->tags_job
->on_finished
= dkr_pull_job_on_finished_v2
;
792 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
794 r
= pull_job_begin(i
->tags_job
);
796 log_error_errno(r
, "Failed to start tags job: %m");
800 } else if (i
->tags_job
== j
) {
802 _cleanup_free_
char *buf
;
803 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
;
804 JsonVariant
*e
= NULL
;
806 assert(!i
->ancestry_job
);
807 assert(!i
->json_job
);
808 assert(!i
->layer_job
);
810 buf
= strndup((const char *)j
->payload
, j
->payload_size
);
817 r
= json_parse(buf
, &doc
);
819 log_error("Unable to parse bearer token\n%s", j
->payload
);
823 e
= json_variant_value(doc
, "token");
824 if (!e
|| e
->type
!= JSON_VARIANT_STRING
) {
826 log_error("Invalid JSON format for Bearer token");
830 r
= free_and_strdup(&i
->response_token
, json_variant_string(e
));
836 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v2/", i
->name
, "/manifests/", i
->reference
);
837 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
839 log_error_errno(r
, "Failed to allocate ancestry job: %m");
843 r
= dkr_pull_add_bearer_token(i
, i
->ancestry_job
);
847 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished_v2
;
848 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
849 i
->ancestry_job
->on_header
= dkr_pull_job_on_header
;
852 r
= pull_job_begin(i
->ancestry_job
);
854 log_error_errno(r
, "Failed to start ancestry job: %m");
858 } else if (i
->ancestry_job
== j
) {
860 _cleanup_json_variant_unref_ JsonVariant
*doc
= NULL
, *compat
= NULL
;
861 JsonVariant
*e
= NULL
;
862 _cleanup_strv_free_
char **ancestry
= NULL
;
863 size_t allocated
= 0, size
= 0;
864 char *path
= NULL
, **k
= NULL
;
866 r
= json_parse((const char *)j
->payload
, &doc
);
868 log_error("Invalid JSON Manifest");
872 e
= json_variant_value(doc
, "fsLayers");
873 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
|| e
->size
== 0) {
878 log_info("JSON manifest with schema v%"PRIi64
" for %s parsed!",
879 json_variant_integer(json_variant_value(doc
, "schemaVersion")),
880 json_variant_string(json_variant_value(doc
, "name")));
882 for (unsigned z
= 0; z
< e
->size
; z
++) {
883 JsonVariant
*f
= json_variant_element(e
, z
), *g
= NULL
;
885 if (f
->type
!= JSON_VARIANT_OBJECT
) {
890 g
= json_variant_value(f
, "blobSum");
892 layer
= json_variant_string(g
);
893 if (!dkr_digest_is_valid(layer
)) {
898 if (!GREEDY_REALLOC(ancestry
, allocated
, size
+ 2)) {
904 ancestry
[size
] = strdup(layer
);
905 if (!ancestry
[size
]) {
911 ancestry
[size
+1] = NULL
;
915 e
= json_variant_value(doc
, "history");
916 if (!e
|| e
->type
!= JSON_VARIANT_ARRAY
) {
921 e
= json_variant_element(e
, 0);
922 e
= json_variant_value(e
, "v1Compatibility");
923 r
= json_parse(json_variant_string(e
), &compat
);
925 log_error("Invalid v1Compatibility JSON");
929 e
= json_variant_value(compat
, "id");
931 strv_free(i
->ancestry
);
932 i
->ancestry
= strv_reverse(strv_uniq(ancestry
));
933 i
->n_ancestry
= strv_length(i
->ancestry
);
934 i
->current_ancestry
= 0;
935 i
->id
= strdup(i
->ancestry
[i
->n_ancestry
- 1]);
941 path
= strjoin(i
->image_root
, "/.dkr-", json_variant_string(e
), NULL
);
948 i
->image_root
= path
;
951 log_info("Required layers:\n");
952 STRV_FOREACH(k
, i
->ancestry
)
953 log_info("\t%s", *k
);
954 log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e
), i
->response_digest
);
956 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
958 r
= dkr_pull_pull_layer_v2(i
);
962 } else if (i
->layer_job
== j
) {
963 assert(i
->temp_path
);
964 assert(i
->final_path
);
966 j
->disk_fd
= safe_close(j
->disk_fd
);
968 if (i
->tar_pid
> 0) {
969 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
975 r
= aufs_resolve(i
->temp_path
);
977 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
981 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
983 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
987 if (rename(i
->temp_path
, i
->final_path
) < 0) {
988 log_error_errno(errno
, "Failed to rename snaphsot: %m");
992 log_info("Completed writing to layer %s.", i
->final_path
);
994 i
->layer_job
= pull_job_unref(i
->layer_job
);
998 i
->final_path
= NULL
;
1000 i
->current_ancestry
++;
1001 r
= dkr_pull_pull_layer_v2(i
);
1005 } else if (i
->json_job
!= j
)
1006 assert_not_reached("Got finished event for unknown curl object");
1008 if (!dkr_pull_is_done(i
))
1011 dkr_pull_report_progress(i
, DKR_COPYING
);
1013 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V2
);
1021 i
->on_finished(i
, r
, i
->userdata
);
1023 sd_event_exit(i
->event
, r
);
1027 static void dkr_pull_job_on_finished(PullJob
*j
) {
1032 assert(j
->userdata
);
1035 if (j
->error
!= 0) {
1036 if (j
== i
->images_job
)
1037 log_error_errno(j
->error
, "Failed to retrieve images list. (Wrong index URL?)");
1038 else if (j
== i
->tags_job
)
1039 log_error_errno(j
->error
, "Failed to retrieve tags list.");
1040 else if (j
== i
->ancestry_job
)
1041 log_error_errno(j
->error
, "Failed to retrieve ancestry list.");
1042 else if (j
== i
->json_job
)
1043 log_error_errno(j
->error
, "Failed to retrieve json data.");
1045 log_error_errno(j
->error
, "Failed to retrieve layer data.");
1051 if (i
->images_job
== j
) {
1054 assert(!i
->tags_job
);
1055 assert(!i
->ancestry_job
);
1056 assert(!i
->json_job
);
1057 assert(!i
->layer_job
);
1059 if (strv_isempty(i
->response_registries
)) {
1061 log_error("Didn't get registry information.");
1065 log_info("Index lookup succeeded, directed to registry %s.", i
->response_registries
[0]);
1066 dkr_pull_report_progress(i
, DKR_RESOLVING
);
1068 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/repositories/", i
->name
, "/tags/", i
->reference
);
1069 r
= pull_job_new(&i
->tags_job
, url
, i
->glue
, i
);
1071 log_error_errno(r
, "Failed to allocate tags job: %m");
1075 r
= dkr_pull_add_token(i
, i
->tags_job
);
1081 i
->tags_job
->on_finished
= dkr_pull_job_on_finished
;
1082 i
->tags_job
->on_progress
= dkr_pull_job_on_progress
;
1084 r
= pull_job_begin(i
->tags_job
);
1086 log_error_errno(r
, "Failed to start tags job: %m");
1090 } else if (i
->tags_job
== j
) {
1094 assert(!i
->ancestry_job
);
1095 assert(!i
->json_job
);
1096 assert(!i
->layer_job
);
1098 r
= parse_id(j
->payload
, j
->payload_size
, &id
);
1100 log_error_errno(r
, "Failed to parse JSON id.");
1107 log_info("Tag lookup succeeded, resolved to layer %s.", i
->id
);
1108 dkr_pull_report_progress(i
, DKR_METADATA
);
1110 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/ancestry");
1111 r
= pull_job_new(&i
->ancestry_job
, url
, i
->glue
, i
);
1113 log_error_errno(r
, "Failed to allocate ancestry job: %m");
1117 r
= dkr_pull_add_token(i
, i
->ancestry_job
);
1123 i
->ancestry_job
->on_finished
= dkr_pull_job_on_finished
;
1124 i
->ancestry_job
->on_progress
= dkr_pull_job_on_progress
;
1126 url
= strjoina(PROTOCOL_PREFIX
, i
->response_registries
[0], "/v1/images/", i
->id
, "/json");
1127 r
= pull_job_new(&i
->json_job
, url
, i
->glue
, i
);
1129 log_error_errno(r
, "Failed to allocate json job: %m");
1133 r
= dkr_pull_add_token(i
, i
->json_job
);
1139 i
->json_job
->on_finished
= dkr_pull_job_on_finished
;
1140 i
->json_job
->on_progress
= dkr_pull_job_on_progress
;
1142 r
= pull_job_begin(i
->ancestry_job
);
1144 log_error_errno(r
, "Failed to start ancestry job: %m");
1148 r
= pull_job_begin(i
->json_job
);
1150 log_error_errno(r
, "Failed to start json job: %m");
1154 } else if (i
->ancestry_job
== j
) {
1155 char **ancestry
= NULL
, **k
;
1158 assert(!i
->layer_job
);
1160 r
= parse_ancestry(j
->payload
, j
->payload_size
, &ancestry
);
1162 log_error_errno(r
, "Failed to parse JSON id.");
1166 n
= strv_length(ancestry
);
1167 if (n
<= 0 || !streq(ancestry
[n
-1], i
->id
)) {
1168 log_error("Ancestry doesn't end in main layer.");
1169 strv_free(ancestry
);
1174 log_info("Ancestor lookup succeeded, requires layers:\n");
1175 STRV_FOREACH(k
, ancestry
)
1176 log_info("\t%s", *k
);
1178 strv_free(i
->ancestry
);
1179 i
->ancestry
= ancestry
;
1181 i
->current_ancestry
= 0;
1183 dkr_pull_report_progress(i
, DKR_DOWNLOADING
);
1185 r
= dkr_pull_pull_layer(i
);
1189 } else if (i
->layer_job
== j
) {
1190 assert(i
->temp_path
);
1191 assert(i
->final_path
);
1193 j
->disk_fd
= safe_close(j
->disk_fd
);
1195 if (i
->tar_pid
> 0) {
1196 r
= wait_for_terminate_and_warn("tar", i
->tar_pid
, true);
1202 r
= aufs_resolve(i
->temp_path
);
1204 log_error_errno(r
, "Failed to resolve aufs whiteouts: %m");
1208 r
= btrfs_subvol_set_read_only(i
->temp_path
, true);
1210 log_error_errno(r
, "Failed to mark snapshot read-only: %m");
1214 if (rename(i
->temp_path
, i
->final_path
) < 0) {
1215 log_error_errno(errno
, "Failed to rename snaphsot: %m");
1219 log_info("Completed writing to layer %s.", i
->final_path
);
1221 i
->layer_job
= pull_job_unref(i
->layer_job
);
1222 i
->temp_path
= mfree(i
->temp_path
);
1223 i
->final_path
= mfree(i
->final_path
);
1225 i
->current_ancestry
++;
1226 r
= dkr_pull_pull_layer(i
);
1230 } else if (i
->json_job
!= j
)
1231 assert_not_reached("Got finished event for unknown curl object");
1233 if (!dkr_pull_is_done(i
))
1236 dkr_pull_report_progress(i
, DKR_COPYING
);
1238 r
= dkr_pull_make_local_copy(i
, DKR_PULL_V1
);
1245 i
->on_finished(i
, r
, i
->userdata
);
1247 sd_event_exit(i
->event
, r
);
1250 static int get_protocol_address(char **protocol
, char **address
, const char *url
) {
1251 const char *sep
, *dot
;
1252 _cleanup_free_
char *a
= NULL
, *p
= NULL
;
1254 sep
= strstr(url
, "://");
1258 dot
= strrchr(url
, '.');
1263 p
= strndup(url
, (sep
- url
) + 3);
1267 while (dot
> (sep
+ 3) && *dot
!= '.')
1270 a
= strdup(dot
+ 1);
1281 int dkr_pull_start(DkrPull
*i
, const char *name
, const char *reference
, const char *local
, bool force_local
, DkrPullVersion version
) {
1287 if (!dkr_name_is_valid(name
))
1290 if (reference
&& !dkr_ref_is_valid(reference
))
1293 if (local
&& !machine_name_is_valid(local
))
1300 reference
= "latest";
1302 free(i
->index_protocol
);
1303 free(i
->index_address
);
1304 r
= get_protocol_address(&i
->index_protocol
, &i
->index_address
, i
->index_url
);
1308 r
= free_and_strdup(&i
->local
, local
);
1311 i
->force_local
= force_local
;
1313 r
= free_and_strdup(&i
->name
, name
);
1316 r
= free_and_strdup(&i
->reference
, reference
);
1320 url
= strjoina(i
->index_url
, "/v1/repositories/", name
, "/images");
1322 r
= pull_job_new(&i
->images_job
, url
, i
->glue
, i
);
1326 r
= dkr_pull_add_token(i
, i
->images_job
);
1330 if (version
== DKR_PULL_V1
)
1331 i
->images_job
->on_finished
= dkr_pull_job_on_finished
;
1333 i
->images_job
->on_finished
= dkr_pull_job_on_finished_v2
;
1335 i
->images_job
->on_header
= dkr_pull_job_on_header
;
1336 i
->images_job
->on_progress
= dkr_pull_job_on_progress
;
1338 return pull_job_begin(i
->images_job
);