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