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