]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-dkr.c
shared: add process-util.[ch]
[thirdparty/systemd.git] / src / import / pull-dkr.c
CommitLineData
72648326
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2014 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
22#include <curl/curl.h>
23#include <sys/prctl.h>
24
7079cfef 25#include "sd-daemon.h"
72648326
LP
26#include "json.h"
27#include "strv.h"
72648326 28#include "btrfs-util.h"
91f4347e 29#include "utf8.h"
ff2670ad 30#include "mkdir.h"
c6878637 31#include "rm-rf.h"
26166c88 32#include "path-util.h"
3d7415f4 33#include "import-util.h"
ff2670ad
LP
34#include "curl-util.h"
35#include "aufs-util.h"
dc2c282b
LP
36#include "pull-job.h"
37#include "pull-common.h"
b6e676ce 38#include "import-common.h"
dc2c282b 39#include "pull-dkr.h"
0b452006 40#include "process-util.h"
72648326 41
7079cfef
LP
42typedef enum DkrProgress {
43 DKR_SEARCHING,
44 DKR_RESOLVING,
45 DKR_METADATA,
46 DKR_DOWNLOADING,
47 DKR_COPYING,
48} DkrProgress;
49
dc2c282b 50struct DkrPull {
ff2670ad
LP
51 sd_event *event;
52 CurlGlue *glue;
72648326 53
ff2670ad
LP
54 char *index_url;
55 char *image_root;
72648326 56
dc2c282b
LP
57 PullJob *images_job;
58 PullJob *tags_job;
59 PullJob *ancestry_job;
60 PullJob *json_job;
61 PullJob *layer_job;
72648326
LP
62
63 char *name;
64 char *tag;
65 char *id;
72648326 66
ff2670ad
LP
67 char *response_token;
68 char **response_registries;
72648326
LP
69
70 char **ancestry;
7079cfef 71 unsigned n_ancestry;
72648326
LP
72 unsigned current_ancestry;
73
dc2c282b 74 DkrPullFinished on_finished;
ff2670ad 75 void *userdata;
ea1ae8c3 76
ff2670ad
LP
77 char *local;
78 bool force_local;
26166c88 79 bool grow_machine_directory;
72648326 80
ff2670ad
LP
81 char *temp_path;
82 char *final_path;
14ed8b92 83
ff2670ad 84 pid_t tar_pid;
72648326
LP
85};
86
87#define PROTOCOL_PREFIX "https://"
72648326
LP
88
89#define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
90#define HEADER_REGISTRY "X-Do" /*the HTTP header for the registry */ "cker-Endpoints:"
91
72648326
LP
92#define LAYERS_MAX 2048
93
dc2c282b 94static void dkr_pull_job_on_finished(PullJob *j);
72648326 95
dc2c282b 96DkrPull* dkr_pull_unref(DkrPull *i) {
ff2670ad 97 if (!i)
72648326
LP
98 return NULL;
99
ff2670ad
LP
100 if (i->tar_pid > 1) {
101 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
102 (void) wait_for_terminate(i->tar_pid, NULL);
72648326
LP
103 }
104
dc2c282b
LP
105 pull_job_unref(i->images_job);
106 pull_job_unref(i->tags_job);
107 pull_job_unref(i->ancestry_job);
108 pull_job_unref(i->json_job);
109 pull_job_unref(i->layer_job);
72648326 110
ff2670ad
LP
111 curl_glue_unref(i->glue);
112 sd_event_unref(i->event);
72648326 113
ff2670ad 114 if (i->temp_path) {
d9e2daaf 115 (void) rm_rf(i->temp_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
ff2670ad
LP
116 free(i->temp_path);
117 }
72648326 118
ff2670ad
LP
119 free(i->name);
120 free(i->tag);
121 free(i->id);
122 free(i->response_token);
123 free(i->response_registries);
124 strv_free(i->ancestry);
125 free(i->final_path);
126 free(i->index_url);
127 free(i->image_root);
128 free(i->local);
129 free(i);
72648326
LP
130
131 return NULL;
132}
133
dc2c282b
LP
134int dkr_pull_new(
135 DkrPull **ret,
ff2670ad
LP
136 sd_event *event,
137 const char *index_url,
138 const char *image_root,
dc2c282b 139 DkrPullFinished on_finished,
ff2670ad 140 void *userdata) {
72648326 141
dc2c282b 142 _cleanup_(dkr_pull_unrefp) DkrPull *i = NULL;
ff2670ad
LP
143 char *e;
144 int r;
72648326 145
ff2670ad
LP
146 assert(ret);
147 assert(index_url);
72648326 148
ff2670ad
LP
149 if (!http_url_is_valid(index_url))
150 return -EINVAL;
72648326 151
dc2c282b 152 i = new0(DkrPull, 1);
ff2670ad
LP
153 if (!i)
154 return -ENOMEM;
72648326 155
ff2670ad
LP
156 i->on_finished = on_finished;
157 i->userdata = userdata;
72648326 158
ff2670ad
LP
159 i->image_root = strdup(image_root ?: "/var/lib/machines");
160 if (!i->image_root)
161 return -ENOMEM;
72648326 162
26166c88
LP
163 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
164
ff2670ad
LP
165 i->index_url = strdup(index_url);
166 if (!i->index_url)
167 return -ENOMEM;
72648326 168
ff2670ad
LP
169 e = endswith(i->index_url, "/");
170 if (e)
171 *e = 0;
72648326 172
ff2670ad
LP
173 if (event)
174 i->event = sd_event_ref(event);
175 else {
176 r = sd_event_default(&i->event);
177 if (r < 0)
178 return r;
179 }
72648326 180
ff2670ad
LP
181 r = curl_glue_new(&i->glue, i->event);
182 if (r < 0)
183 return r;
72648326 184
dc2c282b 185 i->glue->on_finished = pull_job_curl_on_finished;
ff2670ad 186 i->glue->userdata = i;
14ed8b92 187
ff2670ad
LP
188 *ret = i;
189 i = NULL;
14ed8b92 190
ff2670ad 191 return 0;
72648326
LP
192}
193
dc2c282b 194static void dkr_pull_report_progress(DkrPull *i, DkrProgress p) {
7079cfef
LP
195 unsigned percent;
196
197 assert(i);
198
199 switch (p) {
200
201 case DKR_SEARCHING:
202 percent = 0;
203 if (i->images_job)
204 percent += i->images_job->progress_percent * 5 / 100;
205 break;
206
207 case DKR_RESOLVING:
208 percent = 5;
209 if (i->tags_job)
210 percent += i->tags_job->progress_percent * 5 / 100;
211 break;
212
213 case DKR_METADATA:
214 percent = 10;
215 if (i->ancestry_job)
216 percent += i->ancestry_job->progress_percent * 5 / 100;
217 if (i->json_job)
218 percent += i->json_job->progress_percent * 5 / 100;
219 break;
220
221 case DKR_DOWNLOADING:
222 percent = 20;
223 percent += 75 * i->current_ancestry / MAX(1U, i->n_ancestry);
224 if (i->layer_job)
225 percent += i->layer_job->progress_percent * 75 / MAX(1U, i->n_ancestry) / 100;
226
227 break;
228
229 case DKR_COPYING:
230 percent = 95;
231 break;
232
233 default:
234 assert_not_reached("Unknown progress state");
235 }
236
237 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
238 log_debug("Combined progress %u%%", percent);
239}
240
72648326
LP
241static int parse_id(const void *payload, size_t size, char **ret) {
242 _cleanup_free_ char *buf = NULL, *id = NULL, *other = NULL;
243 union json_value v = {};
244 void *json_state = NULL;
245 const char *p;
246 int t;
247
248 assert(payload);
249 assert(ret);
250
251 if (size <= 0)
252 return -EBADMSG;
253
254 if (memchr(payload, 0, size))
255 return -EBADMSG;
256
257 buf = strndup(payload, size);
258 if (!buf)
259 return -ENOMEM;
260
261 p = buf;
262 t = json_tokenize(&p, &id, &v, &json_state, NULL);
263 if (t < 0)
264 return t;
265 if (t != JSON_STRING)
266 return -EBADMSG;
267
268 t = json_tokenize(&p, &other, &v, &json_state, NULL);
269 if (t < 0)
270 return t;
271 if (t != JSON_END)
272 return -EBADMSG;
273
91f4347e 274 if (!dkr_id_is_valid(id))
72648326
LP
275 return -EBADMSG;
276
277 *ret = id;
278 id = NULL;
279
280 return 0;
281}
282
283static int parse_ancestry(const void *payload, size_t size, char ***ret) {
284 _cleanup_free_ char *buf = NULL;
285 void *json_state = NULL;
286 const char *p;
287 enum {
288 STATE_BEGIN,
289 STATE_ITEM,
290 STATE_COMMA,
291 STATE_END,
292 } state = STATE_BEGIN;
293 _cleanup_strv_free_ char **l = NULL;
294 size_t n = 0, allocated = 0;
295
296 if (size <= 0)
297 return -EBADMSG;
298
299 if (memchr(payload, 0, size))
300 return -EBADMSG;
301
302 buf = strndup(payload, size);
303 if (!buf)
304 return -ENOMEM;
305
306 p = buf;
307 for (;;) {
308 _cleanup_free_ char *str;
309 union json_value v = {};
310 int t;
311
312 t = json_tokenize(&p, &str, &v, &json_state, NULL);
313 if (t < 0)
314 return t;
315
316 switch (state) {
317
318 case STATE_BEGIN:
319 if (t == JSON_ARRAY_OPEN)
320 state = STATE_ITEM;
321 else
322 return -EBADMSG;
323
324 break;
325
326 case STATE_ITEM:
327 if (t == JSON_STRING) {
91f4347e 328 if (!dkr_id_is_valid(str))
72648326
LP
329 return -EBADMSG;
330
331 if (n+1 > LAYERS_MAX)
332 return -EFBIG;
333
334 if (!GREEDY_REALLOC(l, allocated, n + 2))
335 return -ENOMEM;
336
337 l[n++] = str;
338 str = NULL;
339 l[n] = NULL;
340
341 state = STATE_COMMA;
342
343 } else if (t == JSON_ARRAY_CLOSE)
344 state = STATE_END;
345 else
346 return -EBADMSG;
347
348 break;
349
350 case STATE_COMMA:
351 if (t == JSON_COMMA)
352 state = STATE_ITEM;
353 else if (t == JSON_ARRAY_CLOSE)
354 state = STATE_END;
355 else
356 return -EBADMSG;
357 break;
358
359 case STATE_END:
360 if (t == JSON_END) {
361
362 if (strv_isempty(l))
363 return -EBADMSG;
364
365 if (!strv_is_uniq(l))
366 return -EBADMSG;
367
368 l = strv_reverse(l);
369
370 *ret = l;
371 l = NULL;
372 return 0;
373 } else
374 return -EBADMSG;
375 }
376
377 }
378}
379
dc2c282b 380static const char *dkr_pull_current_layer(DkrPull *i) {
ff2670ad 381 assert(i);
72648326 382
ff2670ad 383 if (strv_isempty(i->ancestry))
72648326
LP
384 return NULL;
385
ff2670ad 386 return i->ancestry[i->current_ancestry];
72648326
LP
387}
388
dc2c282b 389static const char *dkr_pull_current_base_layer(DkrPull *i) {
ff2670ad 390 assert(i);
72648326 391
ff2670ad 392 if (strv_isempty(i->ancestry))
72648326
LP
393 return NULL;
394
ff2670ad 395 if (i->current_ancestry <= 0)
72648326
LP
396 return NULL;
397
ff2670ad 398 return i->ancestry[i->current_ancestry-1];
72648326
LP
399}
400
dc2c282b 401static int dkr_pull_add_token(DkrPull *i, PullJob *j) {
ff2670ad 402 const char *t;
72648326 403
ff2670ad
LP
404 assert(i);
405 assert(j);
72648326 406
ff2670ad 407 if (i->response_token)
63c372cb 408 t = strjoina("Authorization: Token ", i->response_token);
ff2670ad
LP
409 else
410 t = HEADER_TOKEN " true";
72648326 411
ff2670ad
LP
412 j->request_header = curl_slist_new("Accept: application/json", t, NULL);
413 if (!j->request_header)
414 return -ENOMEM;
72648326 415
ff2670ad 416 return 0;
72648326
LP
417}
418
dc2c282b 419static bool dkr_pull_is_done(DkrPull *i) {
ff2670ad
LP
420 assert(i);
421 assert(i->images_job);
72648326 422
dc2c282b 423 if (i->images_job->state != PULL_JOB_DONE)
ff2670ad 424 return false;
72648326 425
dc2c282b 426 if (!i->tags_job || i->tags_job->state != PULL_JOB_DONE)
ff2670ad 427 return false;
72648326 428
dc2c282b 429 if (!i->ancestry_job || i->ancestry_job->state != PULL_JOB_DONE)
ff2670ad 430 return false;
72648326 431
dc2c282b 432 if (!i->json_job || i->json_job->state != PULL_JOB_DONE)
ff2670ad 433 return false;
72648326 434
dc2c282b 435 if (i->layer_job && i->layer_job->state != PULL_JOB_DONE)
ff2670ad 436 return false;
72648326 437
dc2c282b 438 if (dkr_pull_current_layer(i))
ff2670ad 439 return false;
72648326 440
ff2670ad
LP
441 return true;
442}
72648326 443
dc2c282b 444static int dkr_pull_make_local_copy(DkrPull *i) {
ff2670ad 445 int r;
72648326 446
ff2670ad 447 assert(i);
72648326 448
ff2670ad
LP
449 if (!i->local)
450 return 0;
72648326 451
ff2670ad
LP
452 if (!i->final_path) {
453 i->final_path = strjoin(i->image_root, "/.dkr-", i->id, NULL);
454 if (!i->final_path)
455 return log_oom();
72648326
LP
456 }
457
dc2c282b 458 r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
ff2670ad
LP
459 if (r < 0)
460 return r;
461
462 return 0;
72648326
LP
463}
464
dc2c282b 465static int dkr_pull_job_on_open_disk(PullJob *j) {
ff2670ad 466 const char *base;
dc2c282b 467 DkrPull *i;
ff2670ad 468 int r;
72648326 469
ff2670ad
LP
470 assert(j);
471 assert(j->userdata);
72648326 472
ff2670ad
LP
473 i = j->userdata;
474 assert(i->layer_job == j);
475 assert(i->final_path);
476 assert(!i->temp_path);
477 assert(i->tar_pid <= 0);
72648326 478
ff2670ad
LP
479 r = tempfn_random(i->final_path, &i->temp_path);
480 if (r < 0)
481 return log_oom();
72648326 482
ff2670ad 483 mkdir_parents_label(i->temp_path, 0700);
72648326 484
dc2c282b 485 base = dkr_pull_current_base_layer(i);
ff2670ad
LP
486 if (base) {
487 const char *base_path;
72648326 488
63c372cb 489 base_path = strjoina(i->image_root, "/.dkr-", base);
e9bc1871 490 r = btrfs_subvol_snapshot(base_path, i->temp_path, BTRFS_SNAPSHOT_FALLBACK_COPY);
ff2670ad
LP
491 } else
492 r = btrfs_subvol_make(i->temp_path);
493 if (r < 0)
494 return log_error_errno(r, "Failed to make btrfs subvolume %s: %m", i->temp_path);
72648326 495
587fec42 496 j->disk_fd = import_fork_tar_x(i->temp_path, &i->tar_pid);
2c140ded
LP
497 if (j->disk_fd < 0)
498 return j->disk_fd;
72648326 499
72648326
LP
500 return 0;
501}
502
dc2c282b
LP
503static void dkr_pull_job_on_progress(PullJob *j) {
504 DkrPull *i;
7079cfef
LP
505
506 assert(j);
507 assert(j->userdata);
508
509 i = j->userdata;
510
dc2c282b 511 dkr_pull_report_progress(
7079cfef
LP
512 i,
513 j == i->images_job ? DKR_SEARCHING :
514 j == i->tags_job ? DKR_RESOLVING :
515 j == i->ancestry_job || j == i->json_job ? DKR_METADATA :
516 DKR_DOWNLOADING);
517}
518
dc2c282b 519static int dkr_pull_pull_layer(DkrPull *i) {
ff2670ad
LP
520 _cleanup_free_ char *path = NULL;
521 const char *url, *layer = NULL;
72648326
LP
522 int r;
523
ff2670ad
LP
524 assert(i);
525 assert(!i->layer_job);
526 assert(!i->temp_path);
527 assert(!i->final_path);
72648326
LP
528
529 for (;;) {
dc2c282b 530 layer = dkr_pull_current_layer(i);
ff2670ad
LP
531 if (!layer)
532 return 0; /* no more layers */
72648326 533
ff2670ad 534 path = strjoin(i->image_root, "/.dkr-", layer, NULL);
72648326
LP
535 if (!path)
536 return log_oom();
537
538 if (laccess(path, F_OK) < 0) {
539 if (errno == ENOENT)
540 break;
541
542 return log_error_errno(errno, "Failed to check for container: %m");
543 }
544
545 log_info("Layer %s already exists, skipping.", layer);
546
ff2670ad 547 i->current_ancestry++;
72648326
LP
548
549 free(path);
550 path = NULL;
551 }
552
ff2670ad 553 log_info("Pulling layer %s...", layer);
72648326 554
ff2670ad
LP
555 i->final_path = path;
556 path = NULL;
72648326 557
63c372cb 558 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", layer, "/layer");
dc2c282b 559 r = pull_job_new(&i->layer_job, url, i->glue, i);
ff2670ad
LP
560 if (r < 0)
561 return log_error_errno(r, "Failed to allocate layer job: %m");
72648326 562
dc2c282b 563 r = dkr_pull_add_token(i, i->layer_job);
72648326
LP
564 if (r < 0)
565 return log_oom();
566
dc2c282b
LP
567 i->layer_job->on_finished = dkr_pull_job_on_finished;
568 i->layer_job->on_open_disk = dkr_pull_job_on_open_disk;
569 i->layer_job->on_progress = dkr_pull_job_on_progress;
26166c88 570 i->layer_job->grow_machine_directory = i->grow_machine_directory;
72648326 571
dc2c282b 572 r = pull_job_begin(i->layer_job);
72648326 573 if (r < 0)
ff2670ad 574 return log_error_errno(r, "Failed to start layer job: %m");
72648326
LP
575
576 return 0;
577}
578
dc2c282b
LP
579static void dkr_pull_job_on_finished(PullJob *j) {
580 DkrPull *i;
72648326
LP
581 int r;
582
ff2670ad
LP
583 assert(j);
584 assert(j->userdata);
585
586 i = j->userdata;
587 if (j->error != 0) {
588 if (j == i->images_job)
589 log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)");
590 else if (j == i->tags_job)
591 log_error_errno(j->error, "Failed to retrieve tags list.");
592 else if (j == i->ancestry_job)
593 log_error_errno(j->error, "Failed to retrieve ancestry list.");
594 else if (j == i->json_job)
595 log_error_errno(j->error, "Failed to retrieve json data.");
596 else
597 log_error_errno(j->error, "Failed to retrieve layer data.");
598
599 r = j->error;
600 goto finish;
601 }
72648326 602
ff2670ad 603 if (i->images_job == j) {
72648326 604 const char *url;
72648326 605
ff2670ad
LP
606 assert(!i->tags_job);
607 assert(!i->ancestry_job);
608 assert(!i->json_job);
609 assert(!i->layer_job);
72648326 610
ff2670ad 611 if (strv_isempty(i->response_registries)) {
72648326 612 r = -EBADMSG;
ff2670ad
LP
613 log_error("Didn't get registry information.");
614 goto finish;
72648326
LP
615 }
616
ff2670ad 617 log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
dc2c282b 618 dkr_pull_report_progress(i, DKR_RESOLVING);
72648326 619
63c372cb 620 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->tag);
dc2c282b 621 r = pull_job_new(&i->tags_job, url, i->glue, i);
ff2670ad
LP
622 if (r < 0) {
623 log_error_errno(r, "Failed to allocate tags job: %m");
624 goto finish;
625 }
626
dc2c282b 627 r = dkr_pull_add_token(i, i->tags_job);
ff2670ad
LP
628 if (r < 0) {
629 log_oom();
630 goto finish;
631 }
632
dc2c282b
LP
633 i->tags_job->on_finished = dkr_pull_job_on_finished;
634 i->tags_job->on_progress = dkr_pull_job_on_progress;
72648326 635
dc2c282b 636 r = pull_job_begin(i->tags_job);
72648326 637 if (r < 0) {
ff2670ad
LP
638 log_error_errno(r, "Failed to start tags job: %m");
639 goto finish;
72648326
LP
640 }
641
ff2670ad 642 } else if (i->tags_job == j) {
72648326 643 const char *url;
ff2670ad 644 char *id = NULL;
72648326 645
ff2670ad
LP
646 assert(!i->ancestry_job);
647 assert(!i->json_job);
648 assert(!i->layer_job);
72648326 649
ff2670ad 650 r = parse_id(j->payload, j->payload_size, &id);
72648326
LP
651 if (r < 0) {
652 log_error_errno(r, "Failed to parse JSON id.");
ff2670ad 653 goto finish;
72648326
LP
654 }
655
ff2670ad
LP
656 free(i->id);
657 i->id = id;
72648326 658
ff2670ad 659 log_info("Tag lookup succeeded, resolved to layer %s.", i->id);
dc2c282b 660 dkr_pull_report_progress(i, DKR_METADATA);
ff2670ad 661
63c372cb 662 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/ancestry");
dc2c282b 663 r = pull_job_new(&i->ancestry_job, url, i->glue, i);
ff2670ad
LP
664 if (r < 0) {
665 log_error_errno(r, "Failed to allocate ancestry job: %m");
666 goto finish;
667 }
668
dc2c282b 669 r = dkr_pull_add_token(i, i->ancestry_job);
ff2670ad
LP
670 if (r < 0) {
671 log_oom();
672 goto finish;
673 }
72648326 674
dc2c282b
LP
675 i->ancestry_job->on_finished = dkr_pull_job_on_finished;
676 i->ancestry_job->on_progress = dkr_pull_job_on_progress;
72648326 677
63c372cb 678 url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/images/", i->id, "/json");
dc2c282b 679 r = pull_job_new(&i->json_job, url, i->glue, i);
72648326 680 if (r < 0) {
ff2670ad
LP
681 log_error_errno(r, "Failed to allocate json job: %m");
682 goto finish;
72648326
LP
683 }
684
dc2c282b 685 r = dkr_pull_add_token(i, i->json_job);
72648326 686 if (r < 0) {
ff2670ad
LP
687 log_oom();
688 goto finish;
72648326
LP
689 }
690
dc2c282b
LP
691 i->json_job->on_finished = dkr_pull_job_on_finished;
692 i->json_job->on_progress = dkr_pull_job_on_progress;
ff2670ad 693
dc2c282b 694 r = pull_job_begin(i->ancestry_job);
ff2670ad
LP
695 if (r < 0) {
696 log_error_errno(r, "Failed to start ancestry job: %m");
697 goto finish;
698 }
699
dc2c282b 700 r = pull_job_begin(i->json_job);
ff2670ad
LP
701 if (r < 0) {
702 log_error_errno(r, "Failed to start json job: %m");
703 goto finish;
704 }
705
706 } else if (i->ancestry_job == j) {
707 char **ancestry = NULL, **k;
72648326
LP
708 unsigned n;
709
ff2670ad
LP
710 assert(!i->layer_job);
711
712 r = parse_ancestry(j->payload, j->payload_size, &ancestry);
72648326
LP
713 if (r < 0) {
714 log_error_errno(r, "Failed to parse JSON id.");
ff2670ad 715 goto finish;
72648326
LP
716 }
717
718 n = strv_length(ancestry);
ff2670ad 719 if (n <= 0 || !streq(ancestry[n-1], i->id)) {
72648326 720 log_error("Ancestry doesn't end in main layer.");
ff2670ad 721 strv_free(ancestry);
72648326 722 r = -EBADMSG;
ff2670ad 723 goto finish;
72648326
LP
724 }
725
726 log_info("Ancestor lookup succeeded, requires layers:\n");
ff2670ad
LP
727 STRV_FOREACH(k, ancestry)
728 log_info("\t%s", *k);
72648326 729
ff2670ad
LP
730 strv_free(i->ancestry);
731 i->ancestry = ancestry;
7079cfef 732 i->n_ancestry = n;
ff2670ad 733 i->current_ancestry = 0;
7079cfef 734
dc2c282b 735 dkr_pull_report_progress(i, DKR_DOWNLOADING);
7079cfef 736
dc2c282b 737 r = dkr_pull_pull_layer(i);
72648326 738 if (r < 0)
ff2670ad 739 goto finish;
72648326 740
ff2670ad
LP
741 } else if (i->layer_job == j) {
742 assert(i->temp_path);
743 assert(i->final_path);
72648326 744
ff2670ad 745 j->disk_fd = safe_close(j->disk_fd);
72648326 746
ff2670ad
LP
747 if (i->tar_pid > 0) {
748 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
749 i->tar_pid = 0;
750 if (r < 0)
751 goto finish;
72648326
LP
752 }
753
ff2670ad 754 r = aufs_resolve(i->temp_path);
72648326 755 if (r < 0) {
ff2670ad
LP
756 log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
757 goto finish;
72648326
LP
758 }
759
ff2670ad 760 r = btrfs_subvol_set_read_only(i->temp_path, true);
72648326 761 if (r < 0) {
cc98b302 762 log_error_errno(r, "Failed to mark snapshot read-only: %m");
ff2670ad 763 goto finish;
72648326
LP
764 }
765
ff2670ad
LP
766 if (rename(i->temp_path, i->final_path) < 0) {
767 log_error_errno(errno, "Failed to rename snaphsot: %m");
768 goto finish;
72648326
LP
769 }
770
ff2670ad 771 log_info("Completed writing to layer %s.", i->final_path);
72648326 772
dc2c282b 773 i->layer_job = pull_job_unref(i->layer_job);
ff2670ad
LP
774 free(i->temp_path);
775 i->temp_path = NULL;
776 free(i->final_path);
777 i->final_path = NULL;
72648326 778
ff2670ad 779 i->current_ancestry ++;
dc2c282b 780 r = dkr_pull_pull_layer(i);
ff2670ad
LP
781 if (r < 0)
782 goto finish;
72648326 783
ff2670ad
LP
784 } else if (i->json_job != j)
785 assert_not_reached("Got finished event for unknown curl object");
72648326 786
dc2c282b 787 if (!dkr_pull_is_done(i))
ff2670ad 788 return;
72648326 789
dc2c282b 790 dkr_pull_report_progress(i, DKR_COPYING);
7079cfef 791
dc2c282b 792 r = dkr_pull_make_local_copy(i);
72648326 793 if (r < 0)
ff2670ad 794 goto finish;
72648326 795
ff2670ad 796 r = 0;
72648326 797
ff2670ad
LP
798finish:
799 if (i->on_finished)
800 i->on_finished(i, r, i->userdata);
801 else
802 sd_event_exit(i->event, r);
72648326
LP
803}
804
dc2c282b 805static int dkr_pull_job_on_header(PullJob *j, const char *header, size_t sz) {
72648326 806 _cleanup_free_ char *registry = NULL;
72648326 807 char *token;
dc2c282b 808 DkrPull *i;
72648326
LP
809 int r;
810
72648326 811 assert(j);
ff2670ad 812 assert(j->userdata);
72648326 813
ff2670ad 814 i = j->userdata;
e9d73334 815
ff2670ad
LP
816 r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
817 if (r < 0)
818 return log_oom();
72648326 819 if (r > 0) {
ff2670ad
LP
820 free(i->response_token);
821 i->response_token = token;
822 return 0;
72648326
LP
823 }
824
ff2670ad
LP
825 r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
826 if (r < 0)
827 return log_oom();
72648326 828 if (r > 0) {
ff2670ad 829 char **l, **k;
72648326
LP
830
831 l = strv_split(registry, ",");
ff2670ad
LP
832 if (!l)
833 return log_oom();
72648326 834
ff2670ad
LP
835 STRV_FOREACH(k, l) {
836 if (!hostname_is_valid(*k)) {
72648326
LP
837 log_error("Registry hostname is not valid.");
838 strv_free(l);
ff2670ad 839 return -EBADMSG;
72648326
LP
840 }
841 }
842
ff2670ad
LP
843 strv_free(i->response_registries);
844 i->response_registries = l;
72648326
LP
845 }
846
72648326
LP
847 return 0;
848}
849
dc2c282b 850int dkr_pull_start(DkrPull *i, const char *name, const char *tag, const char *local, bool force_local) {
ff2670ad 851 const char *url;
72648326
LP
852 int r;
853
ff2670ad 854 assert(i);
72648326 855
ff2670ad
LP
856 if (!dkr_name_is_valid(name))
857 return -EINVAL;
72648326 858
ff2670ad
LP
859 if (tag && !dkr_tag_is_valid(tag))
860 return -EINVAL;
72648326 861
ff2670ad
LP
862 if (local && !machine_name_is_valid(local))
863 return -EINVAL;
72648326 864
ff2670ad
LP
865 if (i->images_job)
866 return -EBUSY;
72648326 867
ff2670ad
LP
868 if (!tag)
869 tag = "latest";
72648326 870
ff2670ad 871 r = free_and_strdup(&i->local, local);
72648326
LP
872 if (r < 0)
873 return r;
ff2670ad 874 i->force_local = force_local;
72648326 875
ff2670ad 876 r = free_and_strdup(&i->name, name);
72648326
LP
877 if (r < 0)
878 return r;
ff2670ad 879 r = free_and_strdup(&i->tag, tag);
72648326
LP
880 if (r < 0)
881 return r;
882
63c372cb 883 url = strjoina(i->index_url, "/v1/repositories/", name, "/images");
72648326 884
dc2c282b 885 r = pull_job_new(&i->images_job, url, i->glue, i);
72648326
LP
886 if (r < 0)
887 return r;
888
dc2c282b 889 r = dkr_pull_add_token(i, i->images_job);
72648326
LP
890 if (r < 0)
891 return r;
892
dc2c282b
LP
893 i->images_job->on_finished = dkr_pull_job_on_finished;
894 i->images_job->on_header = dkr_pull_job_on_header;
895 i->images_job->on_progress = dkr_pull_job_on_progress;
72648326 896
dc2c282b 897 return pull_job_begin(i->images_job);
72648326 898}