]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/pull-raw.c
Merge pull request #1227 from intelfx/systemctl-legacy-tools-polkit
[thirdparty/systemd.git] / src / import / pull-raw.c
CommitLineData
90199220
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 <sys/xattr.h>
dfd1520d 23#include <linux/fs.h>
90199220
LP
24#include <curl/curl.h>
25
7079cfef 26#include "sd-daemon.h"
90199220 27#include "utf8.h"
8620a9a3
LP
28#include "strv.h"
29#include "copy.h"
0d6e763b
LP
30#include "btrfs-util.h"
31#include "util.h"
32#include "macro.h"
33#include "mkdir.h"
c6878637 34#include "rm-rf.h"
26166c88 35#include "path-util.h"
25300b5a 36#include "hostname-util.h"
3d7415f4 37#include "import-util.h"
b6e676ce 38#include "import-common.h"
0d6e763b
LP
39#include "curl-util.h"
40#include "qcow2-util.h"
dc2c282b
LP
41#include "pull-job.h"
42#include "pull-common.h"
43#include "pull-raw.h"
90199220 44
7079cfef
LP
45typedef enum RawProgress {
46 RAW_DOWNLOADING,
47 RAW_VERIFYING,
48 RAW_UNPACKING,
49 RAW_FINALIZING,
50 RAW_COPYING,
51} RawProgress;
90199220 52
dc2c282b 53struct RawPull {
90199220
LP
54 sd_event *event;
55 CurlGlue *glue;
56
087682d1 57 char *image_root;
90199220 58
dc2c282b 59 PullJob *raw_job;
9854730b 60 PullJob *settings_job;
dc2c282b
LP
61 PullJob *checksum_job;
62 PullJob *signature_job;
90199220 63
dc2c282b 64 RawPullFinished on_finished;
0d6e763b 65 void *userdata;
90199220 66
0d6e763b
LP
67 char *local;
68 bool force_local;
26166c88 69 bool grow_machine_directory;
9854730b 70 bool settings;
8620a9a3 71
0d6e763b 72 char *final_path;
9854730b
LP
73 char *temp_path;
74
75 char *settings_path;
76 char *settings_temp_path;
8f695058
LP
77
78 ImportVerify verify;
0d6e763b 79};
49bb233b 80
dc2c282b 81RawPull* raw_pull_unref(RawPull *i) {
0d6e763b 82 if (!i)
90199220
LP
83 return NULL;
84
dc2c282b 85 pull_job_unref(i->raw_job);
9854730b 86 pull_job_unref(i->settings_job);
dc2c282b
LP
87 pull_job_unref(i->checksum_job);
88 pull_job_unref(i->signature_job);
90199220 89
0d6e763b
LP
90 curl_glue_unref(i->glue);
91 sd_event_unref(i->event);
90199220 92
0d6e763b
LP
93 if (i->temp_path) {
94 (void) unlink(i->temp_path);
95 free(i->temp_path);
90199220
LP
96 }
97
9854730b
LP
98 if (i->settings_temp_path) {
99 (void) unlink(i->settings_temp_path);
100 free(i->settings_temp_path);
101 }
102
0d6e763b 103 free(i->final_path);
9854730b 104 free(i->settings_path);
0d6e763b
LP
105 free(i->image_root);
106 free(i->local);
107 free(i);
90199220
LP
108
109 return NULL;
110}
111
dc2c282b
LP
112int raw_pull_new(
113 RawPull **ret,
8b71fce8
LP
114 sd_event *event,
115 const char *image_root,
dc2c282b 116 RawPullFinished on_finished,
8b71fce8
LP
117 void *userdata) {
118
dc2c282b 119 _cleanup_(raw_pull_unrefp) RawPull *i = NULL;
0d6e763b 120 int r;
8620a9a3 121
0d6e763b 122 assert(ret);
8620a9a3 123
dc2c282b 124 i = new0(RawPull, 1);
0d6e763b 125 if (!i)
8620a9a3
LP
126 return -ENOMEM;
127
0d6e763b
LP
128 i->on_finished = on_finished;
129 i->userdata = userdata;
8620a9a3 130
0d6e763b
LP
131 i->image_root = strdup(image_root ?: "/var/lib/machines");
132 if (!i->image_root)
8620a9a3
LP
133 return -ENOMEM;
134
26166c88
LP
135 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
136
0d6e763b
LP
137 if (event)
138 i->event = sd_event_ref(event);
139 else {
140 r = sd_event_default(&i->event);
dfd1520d 141 if (r < 0)
0d6e763b 142 return r;
2f64ba0e 143 }
8620a9a3 144
0d6e763b 145 r = curl_glue_new(&i->glue, i->event);
2f64ba0e 146 if (r < 0)
0d6e763b 147 return r;
8620a9a3 148
dc2c282b 149 i->glue->on_finished = pull_job_curl_on_finished;
0d6e763b 150 i->glue->userdata = i;
8620a9a3 151
0d6e763b
LP
152 *ret = i;
153 i = NULL;
8620a9a3 154
2f64ba0e
LP
155 return 0;
156}
157
dc2c282b 158static void raw_pull_report_progress(RawPull *i, RawProgress p) {
7079cfef
LP
159 unsigned percent;
160
161 assert(i);
162
163 switch (p) {
164
165 case RAW_DOWNLOADING: {
166 unsigned remain = 80;
167
168 percent = 0;
169
9854730b
LP
170 if (i->settings_job) {
171 percent += i->settings_job->progress_percent * 5 / 100;
172 remain -= 5;
173 }
174
7079cfef
LP
175 if (i->checksum_job) {
176 percent += i->checksum_job->progress_percent * 5 / 100;
177 remain -= 5;
178 }
179
180 if (i->signature_job) {
181 percent += i->signature_job->progress_percent * 5 / 100;
182 remain -= 5;
183 }
184
185 if (i->raw_job)
186 percent += i->raw_job->progress_percent * remain / 100;
187 break;
188 }
189
190 case RAW_VERIFYING:
191 percent = 80;
192 break;
193
194 case RAW_UNPACKING:
195 percent = 85;
196 break;
197
198 case RAW_FINALIZING:
199 percent = 90;
200 break;
201
202 case RAW_COPYING:
203 percent = 95;
204 break;
205
206 default:
207 assert_not_reached("Unknown progress state");
208 }
209
210 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
211 log_debug("Combined progress %u%%", percent);
212}
213
dc2c282b 214static int raw_pull_maybe_convert_qcow2(RawPull *i) {
edce2aed
LP
215 _cleanup_close_ int converted_fd = -1;
216 _cleanup_free_ char *t = NULL;
217 int r;
218
0d6e763b
LP
219 assert(i);
220 assert(i->raw_job);
edce2aed 221
0d6e763b 222 r = qcow2_detect(i->raw_job->disk_fd);
edce2aed
LP
223 if (r < 0)
224 return log_error_errno(r, "Failed to detect whether this is a QCOW2 image: %m");
225 if (r == 0)
226 return 0;
227
228 /* This is a QCOW2 image, let's convert it */
14bcf25c 229 r = tempfn_random(i->final_path, NULL, &t);
edce2aed
LP
230 if (r < 0)
231 return log_oom();
232
b6e676ce 233 converted_fd = open(t, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
edce2aed
LP
234 if (converted_fd < 0)
235 return log_error_errno(errno, "Failed to create %s: %m", t);
236
1ed8f8c1 237 r = chattr_fd(converted_fd, FS_NOCOW_FL, FS_NOCOW_FL);
0d6e763b
LP
238 if (r < 0)
239 log_warning_errno(errno, "Failed to set file attributes on %s: %m", t);
240
ec5cb56e
LP
241 log_info("Unpacking QCOW2 file.");
242
0d6e763b 243 r = qcow2_convert(i->raw_job->disk_fd, converted_fd);
edce2aed
LP
244 if (r < 0) {
245 unlink(t);
246 return log_error_errno(r, "Failed to convert qcow2 image: %m");
247 }
248
b6e676ce 249 (void) unlink(i->temp_path);
0d6e763b 250 free(i->temp_path);
0d6e763b 251 i->temp_path = t;
edce2aed
LP
252 t = NULL;
253
0d6e763b
LP
254 safe_close(i->raw_job->disk_fd);
255 i->raw_job->disk_fd = converted_fd;
edce2aed
LP
256 converted_fd = -1;
257
258 return 1;
259}
260
dc2c282b 261static int raw_pull_make_local_copy(RawPull *i) {
0d6e763b
LP
262 _cleanup_free_ char *tp = NULL;
263 _cleanup_close_ int dfd = -1;
264 const char *p;
90199220
LP
265 int r;
266
0d6e763b
LP
267 assert(i);
268 assert(i->raw_job);
90199220 269
0d6e763b 270 if (!i->local)
90199220
LP
271 return 0;
272
9854730b
LP
273 if (!i->final_path) {
274 r = pull_make_path(i->raw_job->url, i->raw_job->etag, i->image_root, ".raw-", ".raw", &i->final_path);
275 if (r < 0)
276 return log_oom();
277 }
278
85dbc41d
LP
279 if (i->raw_job->etag_exists) {
280 /* We have downloaded this one previously, reopen it */
281
282 assert(i->raw_job->disk_fd < 0);
283
0d6e763b
LP
284 i->raw_job->disk_fd = open(i->final_path, O_RDONLY|O_NOCTTY|O_CLOEXEC);
285 if (i->raw_job->disk_fd < 0)
286 return log_error_errno(errno, "Failed to open vendor image: %m");
85dbc41d
LP
287 } else {
288 /* We freshly downloaded the image, use it */
289
290 assert(i->raw_job->disk_fd >= 0);
291
292 if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) == (off_t) -1)
293 return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
90199220
LP
294 }
295
63c372cb 296 p = strjoina(i->image_root, "/", i->local, ".raw");
49bb233b 297
d9e2daaf
LP
298 if (i->force_local)
299 (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
49bb233b 300
14bcf25c 301 r = tempfn_random(p, NULL, &tp);
49bb233b 302 if (r < 0)
0d6e763b 303 return log_oom();
49bb233b 304
0d6e763b
LP
305 dfd = open(tp, O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
306 if (dfd < 0)
307 return log_error_errno(errno, "Failed to create writable copy of image: %m");
49bb233b 308
0d6e763b
LP
309 /* Turn off COW writing. This should greatly improve
310 * performance on COW file systems like btrfs, since it
311 * reduces fragmentation caused by not allowing in-place
312 * writes. */
1ed8f8c1 313 r = chattr_fd(dfd, FS_NOCOW_FL, FS_NOCOW_FL);
0d6e763b
LP
314 if (r < 0)
315 log_warning_errno(errno, "Failed to set file attributes on %s: %m", tp);
90199220 316
59f448cf 317 r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, true);
90199220 318 if (r < 0) {
0d6e763b
LP
319 unlink(tp);
320 return log_error_errno(r, "Failed to make writable copy of image: %m");
90199220
LP
321 }
322
0d6e763b
LP
323 (void) copy_times(i->raw_job->disk_fd, dfd);
324 (void) copy_xattr(i->raw_job->disk_fd, dfd);
1e20b411 325
0d6e763b 326 dfd = safe_close(dfd);
1e20b411 327
0d6e763b
LP
328 r = rename(tp, p);
329 if (r < 0) {
330 unlink(tp);
331 return log_error_errno(errno, "Failed to move writable image into place: %m");
1e20b411
LP
332 }
333
0d6e763b 334 log_info("Created new local image '%s'.", i->local);
9854730b
LP
335
336 if (i->settings) {
337 const char *local_settings;
338 assert(i->settings_job);
339
340 if (!i->settings_path) {
341 r = pull_make_path(i->settings_job->url, i->settings_job->etag, i->image_root, ".settings-", NULL, &i->settings_path);
342 if (r < 0)
343 return log_oom();
344 }
345
346 local_settings = strjoina(i->image_root, "/", i->local, ".nspawn");
347
348 r = copy_file_atomic(i->settings_path, local_settings, 0644, i->force_local, 0);
349 if (r == -EEXIST)
350 log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings);
351 else if (r < 0 && r != -ENOENT)
352 log_warning_errno(r, "Failed to copy settings files %s: %m", local_settings);
353
354 log_info("Create new settings file '%s.nspawn'", i->local);
355 }
356
1e20b411
LP
357 return 0;
358}
359
dc2c282b 360static bool raw_pull_is_done(RawPull *i) {
8b71fce8
LP
361 assert(i);
362 assert(i->raw_job);
363
9854730b 364 if (!PULL_JOB_IS_COMPLETE(i->raw_job))
8b71fce8 365 return false;
9854730b 366 if (i->settings_job && !PULL_JOB_IS_COMPLETE(i->settings_job))
8b71fce8 367 return false;
9854730b
LP
368 if (i->checksum_job && !PULL_JOB_IS_COMPLETE(i->checksum_job))
369 return false;
370 if (i->signature_job && !PULL_JOB_IS_COMPLETE(i->signature_job))
8b71fce8
LP
371 return false;
372
373 return true;
374}
375
dc2c282b
LP
376static void raw_pull_job_on_finished(PullJob *j) {
377 RawPull *i;
8620a9a3
LP
378 int r;
379
0d6e763b
LP
380 assert(j);
381 assert(j->userdata);
8620a9a3 382
0d6e763b 383 i = j->userdata;
9854730b
LP
384 if (j == i->settings_job) {
385 if (j->error != 0)
386 log_info_errno(j->error, "Settings file could not be retrieved, proceeding without.");
387 } else if (j->error != 0) {
98c38001 388 if (j == i->checksum_job)
3576d631
LP
389 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
390 else if (j == i->signature_job)
391 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
392 else
393 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
394
0d6e763b
LP
395 r = j->error;
396 goto finish;
8620a9a3
LP
397 }
398
0d6e763b
LP
399 /* This is invoked if either the download completed
400 * successfully, or the download was skipped because we
85dbc41d 401 * already have the etag. In this case ->etag_exists is
3576d631
LP
402 * true.
403 *
404 * We only do something when we got all three files */
85dbc41d 405
dc2c282b 406 if (!raw_pull_is_done(i))
3576d631 407 return;
8620a9a3 408
9854730b
LP
409 if (i->settings_job)
410 i->settings_job->disk_fd = safe_close(i->settings_job->disk_fd);
411
3576d631 412 if (!i->raw_job->etag_exists) {
98c38001 413 /* This is a new download, verify it, and move it into place */
3576d631
LP
414 assert(i->raw_job->disk_fd >= 0);
415
dc2c282b 416 raw_pull_report_progress(i, RAW_VERIFYING);
7079cfef 417
9854730b 418 r = pull_verify(i->raw_job, i->settings_job, i->checksum_job, i->signature_job);
0d6e763b
LP
419 if (r < 0)
420 goto finish;
8620a9a3 421
dc2c282b 422 raw_pull_report_progress(i, RAW_UNPACKING);
7079cfef 423
dc2c282b 424 r = raw_pull_maybe_convert_qcow2(i);
0d6e763b
LP
425 if (r < 0)
426 goto finish;
85dbc41d 427
dc2c282b 428 raw_pull_report_progress(i, RAW_FINALIZING);
7079cfef 429
b6e676ce 430 r = import_make_read_only_fd(i->raw_job->disk_fd);
3576d631
LP
431 if (r < 0)
432 goto finish;
85dbc41d 433
f85ef957 434 r = rename_noreplace(AT_FDCWD, i->temp_path, AT_FDCWD, i->final_path);
3576d631 435 if (r < 0) {
f85ef957 436 log_error_errno(r, "Failed to move RAW file into place: %m");
3576d631
LP
437 goto finish;
438 }
8f695058 439
9854730b
LP
440 i->temp_path = mfree(i->temp_path);
441
442 if (i->settings_job &&
443 i->settings_job->error == 0 &&
444 !i->settings_job->etag_exists) {
445
446 assert(i->settings_temp_path);
447 assert(i->settings_path);
448
449 r = import_make_read_only(i->settings_temp_path);
450 if (r < 0)
451 goto finish;
452
453 r = rename_noreplace(AT_FDCWD, i->settings_temp_path, AT_FDCWD, i->settings_path);
454 if (r < 0) {
455 log_error_errno(r, "Failed to rename settings file: %m");
456 goto finish;
457 }
458
459 i->settings_temp_path = mfree(i->settings_temp_path);
460 }
8620a9a3
LP
461 }
462
dc2c282b 463 raw_pull_report_progress(i, RAW_COPYING);
7079cfef 464
dc2c282b 465 r = raw_pull_make_local_copy(i);
8620a9a3 466 if (r < 0)
0d6e763b 467 goto finish;
90199220 468
0d6e763b 469 r = 0;
3576d631 470
0d6e763b 471finish:
3576d631
LP
472 if (i->on_finished)
473 i->on_finished(i, r, i->userdata);
474 else
475 sd_event_exit(i->event, r);
0d6e763b 476}
90199220 477
9854730b 478static int raw_pull_job_on_open_disk_raw(PullJob *j) {
dc2c282b 479 RawPull *i;
0d6e763b 480 int r;
90199220 481
0d6e763b
LP
482 assert(j);
483 assert(j->userdata);
90199220 484
0d6e763b 485 i = j->userdata;
8b71fce8
LP
486 assert(i->raw_job == j);
487 assert(!i->final_path);
488 assert(!i->temp_path);
90199220 489
dc2c282b 490 r = pull_make_path(j->url, j->etag, i->image_root, ".raw-", ".raw", &i->final_path);
0d6e763b
LP
491 if (r < 0)
492 return log_oom();
90199220 493
14bcf25c 494 r = tempfn_random(i->final_path, NULL, &i->temp_path);
b6e676ce 495 if (r < 0)
0d6e763b 496 return log_oom();
1e20b411 497
b6e676ce 498 (void) mkdir_parents_label(i->temp_path, 0700);
1e20b411 499
b6e676ce 500 j->disk_fd = open(i->temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
0d6e763b
LP
501 if (j->disk_fd < 0)
502 return log_error_errno(errno, "Failed to create %s: %m", i->temp_path);
1e20b411 503
1ed8f8c1 504 r = chattr_fd(j->disk_fd, FS_NOCOW_FL, FS_NOCOW_FL);
90199220 505 if (r < 0)
0d6e763b 506 log_warning_errno(errno, "Failed to set file attributes on %s: %m", i->temp_path);
90199220
LP
507
508 return 0;
509}
510
9854730b
LP
511static int raw_pull_job_on_open_disk_settings(PullJob *j) {
512 RawPull *i;
513 int r;
514
515 assert(j);
516 assert(j->userdata);
517
518 i = j->userdata;
519 assert(i->settings_job == j);
520 assert(!i->settings_path);
521 assert(!i->settings_temp_path);
522
523 r = pull_make_path(j->url, j->etag, i->image_root, ".settings-", NULL, &i->settings_path);
524 if (r < 0)
525 return log_oom();
526
527 r = tempfn_random(i->settings_path, NULL, &i->settings_temp_path);
528 if (r < 0)
529 return log_oom();
530
531 mkdir_parents_label(i->settings_temp_path, 0700);
532
533 j->disk_fd = open(i->settings_temp_path, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0664);
534 if (j->disk_fd < 0)
535 return log_error_errno(errno, "Failed to create %s: %m", i->settings_temp_path);
536
537 return 0;
538}
539
dc2c282b
LP
540static void raw_pull_job_on_progress(PullJob *j) {
541 RawPull *i;
7079cfef
LP
542
543 assert(j);
544 assert(j->userdata);
545
546 i = j->userdata;
547
dc2c282b 548 raw_pull_report_progress(i, RAW_DOWNLOADING);
7079cfef
LP
549}
550
9854730b
LP
551int raw_pull_start(
552 RawPull *i,
553 const char *url,
554 const char *local,
555 bool force_local,
556 ImportVerify verify,
557 bool settings) {
558
90199220
LP
559 int r;
560
0d6e763b 561 assert(i);
8f695058
LP
562 assert(verify < _IMPORT_VERIFY_MAX);
563 assert(verify >= 0);
90199220 564
0d6e763b
LP
565 if (!http_url_is_valid(url))
566 return -EINVAL;
90199220 567
0d6e763b
LP
568 if (local && !machine_name_is_valid(local))
569 return -EINVAL;
087682d1 570
8b71fce8
LP
571 if (i->raw_job)
572 return -EBUSY;
573
0d6e763b 574 r = free_and_strdup(&i->local, local);
90199220
LP
575 if (r < 0)
576 return r;
9854730b 577
0d6e763b 578 i->force_local = force_local;
8f695058 579 i->verify = verify;
9854730b 580 i->settings = settings;
90199220 581
85dbc41d 582 /* Queue job for the image itself */
dc2c282b 583 r = pull_job_new(&i->raw_job, url, i->glue, i);
90199220
LP
584 if (r < 0)
585 return r;
586
dc2c282b 587 i->raw_job->on_finished = raw_pull_job_on_finished;
9854730b 588 i->raw_job->on_open_disk = raw_pull_job_on_open_disk_raw;
dc2c282b 589 i->raw_job->on_progress = raw_pull_job_on_progress;
98c38001 590 i->raw_job->calc_checksum = verify != IMPORT_VERIFY_NO;
26166c88 591 i->raw_job->grow_machine_directory = i->grow_machine_directory;
90199220 592
dc2c282b 593 r = pull_find_old_etags(url, i->image_root, DT_REG, ".raw-", ".raw", &i->raw_job->old_etags);
90199220
LP
594 if (r < 0)
595 return r;
596
9854730b
LP
597 if (settings) {
598 r = pull_make_settings_job(&i->settings_job, url, i->glue, raw_pull_job_on_finished, i);
599 if (r < 0)
600 return r;
601
602 i->settings_job->on_open_disk = raw_pull_job_on_open_disk_settings;
603 i->settings_job->on_progress = raw_pull_job_on_progress;
604 i->settings_job->calc_checksum = verify != IMPORT_VERIFY_NO;
605
606 r = pull_find_old_etags(i->settings_job->url, i->image_root, DT_REG, ".settings-", NULL, &i->settings_job->old_etags);
607 if (r < 0)
608 return r;
609 }
610
dc2c282b 611 r = pull_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, raw_pull_job_on_finished, i);
98c38001
LP
612 if (r < 0)
613 return r;
85dbc41d 614
dc2c282b 615 r = pull_job_begin(i->raw_job);
85dbc41d
LP
616 if (r < 0)
617 return r;
618
9854730b
LP
619 if (i->settings_job) {
620 r = pull_job_begin(i->settings_job);
621 if (r < 0)
622 return r;
623 }
624
98c38001 625 if (i->checksum_job) {
dc2c282b 626 i->checksum_job->on_progress = raw_pull_job_on_progress;
7079cfef 627
dc2c282b 628 r = pull_job_begin(i->checksum_job);
3576d631
LP
629 if (r < 0)
630 return r;
631 }
632
633 if (i->signature_job) {
dc2c282b 634 i->signature_job->on_progress = raw_pull_job_on_progress;
7079cfef 635
dc2c282b 636 r = pull_job_begin(i->signature_job);
3576d631
LP
637 if (r < 0)
638 return r;
639 }
640
85dbc41d 641 return 0;
90199220 642}