]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/import/import-tar.c
sd-dhcp6-client: delay setting the DUID and don't fail constructor
[thirdparty/systemd.git] / src / import / import-tar.c
CommitLineData
56ebfaf1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2015 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/prctl.h>
23#include <curl/curl.h>
24
7079cfef 25#include "sd-daemon.h"
56ebfaf1
LP
26#include "utf8.h"
27#include "strv.h"
28#include "copy.h"
29#include "btrfs-util.h"
30#include "util.h"
31#include "macro.h"
32#include "mkdir.h"
26166c88 33#include "path-util.h"
3d7415f4 34#include "import-util.h"
56ebfaf1
LP
35#include "curl-util.h"
36#include "import-job.h"
3d7415f4 37#include "import-common.h"
56ebfaf1
LP
38#include "import-tar.h"
39
7079cfef
LP
40typedef enum TarProgress {
41 TAR_DOWNLOADING,
42 TAR_VERIFYING,
43 TAR_FINALIZING,
44 TAR_COPYING,
45} TarProgress;
46
56ebfaf1
LP
47struct TarImport {
48 sd_event *event;
49 CurlGlue *glue;
50
51 char *image_root;
52
53 ImportJob *tar_job;
0100b6e1
LP
54 ImportJob *checksum_job;
55 ImportJob *signature_job;
56ebfaf1
LP
56
57 TarImportFinished on_finished;
58 void *userdata;
59
56ebfaf1
LP
60 char *local;
61 bool force_local;
26166c88 62 bool grow_machine_directory;
56ebfaf1
LP
63
64 pid_t tar_pid;
65
66 char *temp_path;
67 char *final_path;
0100b6e1
LP
68
69 ImportVerify verify;
56ebfaf1
LP
70};
71
72TarImport* tar_import_unref(TarImport *i) {
73 if (!i)
74 return NULL;
75
0100b6e1 76 if (i->tar_pid > 1) {
8b71fce8 77 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
0100b6e1 78 (void) wait_for_terminate(i->tar_pid, NULL);
56ebfaf1
LP
79 }
80
81 import_job_unref(i->tar_job);
0100b6e1
LP
82 import_job_unref(i->checksum_job);
83 import_job_unref(i->signature_job);
56ebfaf1
LP
84
85 curl_glue_unref(i->glue);
86 sd_event_unref(i->event);
87
88 if (i->temp_path) {
89 (void) btrfs_subvol_remove(i->temp_path);
90 (void) rm_rf_dangerous(i->temp_path, false, true, false);
0d6e763b 91 free(i->temp_path);
56ebfaf1
LP
92 }
93
94 free(i->final_path);
95 free(i->image_root);
96 free(i->local);
56ebfaf1
LP
97 free(i);
98
99 return NULL;
100}
101
8b71fce8
LP
102int tar_import_new(
103 TarImport **ret,
104 sd_event *event,
105 const char *image_root,
106 TarImportFinished on_finished,
107 void *userdata) {
108
56ebfaf1
LP
109 _cleanup_(tar_import_unrefp) TarImport *i = NULL;
110 int r;
111
112 assert(ret);
113 assert(event);
114
115 i = new0(TarImport, 1);
116 if (!i)
117 return -ENOMEM;
118
119 i->on_finished = on_finished;
120 i->userdata = userdata;
121
122 i->image_root = strdup(image_root ?: "/var/lib/machines");
123 if (!i->image_root)
124 return -ENOMEM;
125
26166c88
LP
126 i->grow_machine_directory = path_startswith(i->image_root, "/var/lib/machines");
127
56ebfaf1
LP
128 if (event)
129 i->event = sd_event_ref(event);
130 else {
131 r = sd_event_default(&i->event);
132 if (r < 0)
133 return r;
134 }
135
136 r = curl_glue_new(&i->glue, i->event);
137 if (r < 0)
138 return r;
139
140 i->glue->on_finished = import_job_curl_on_finished;
141 i->glue->userdata = i;
142
143 *ret = i;
144 i = NULL;
145
146 return 0;
147}
148
7079cfef
LP
149static void tar_import_report_progress(TarImport *i, TarProgress p) {
150 unsigned percent;
151
152 assert(i);
153
154 switch (p) {
155
156 case TAR_DOWNLOADING: {
157 unsigned remain = 85;
158
159 percent = 0;
160
161 if (i->checksum_job) {
162 percent += i->checksum_job->progress_percent * 5 / 100;
163 remain -= 5;
164 }
165
166 if (i->signature_job) {
167 percent += i->signature_job->progress_percent * 5 / 100;
168 remain -= 5;
169 }
170
171 if (i->tar_job)
172 percent += i->tar_job->progress_percent * remain / 100;
173 break;
174 }
175
176 case TAR_VERIFYING:
177 percent = 85;
178 break;
179
180 case TAR_FINALIZING:
181 percent = 90;
182 break;
183
184 case TAR_COPYING:
185 percent = 95;
186 break;
187
188 default:
189 assert_not_reached("Unknown progress state");
190 }
191
192 sd_notifyf(false, "X_IMPORT_PROGRESS=%u", percent);
193 log_debug("Combined progress %u%%", percent);
194}
195
0d6e763b
LP
196static int tar_import_make_local_copy(TarImport *i) {
197 int r;
198
199 assert(i);
200 assert(i->tar_job);
201
202 if (!i->local)
203 return 0;
204
205 if (!i->final_path) {
206 r = import_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
207 if (r < 0)
208 return log_oom();
0d6e763b
LP
209 }
210
0100b6e1
LP
211 r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
212 if (r < 0)
213 return r;
214
0d6e763b
LP
215 return 0;
216}
217
8b71fce8
LP
218static bool tar_import_is_done(TarImport *i) {
219 assert(i);
220 assert(i->tar_job);
221
222 if (i->tar_job->state != IMPORT_JOB_DONE)
223 return false;
224 if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE)
225 return false;
226 if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE)
227 return false;
228
229 return true;
230}
231
56ebfaf1
LP
232static void tar_import_job_on_finished(ImportJob *j) {
233 TarImport *i;
234 int r;
235
236 assert(j);
237 assert(j->userdata);
238
239 i = j->userdata;
56ebfaf1 240 if (j->error != 0) {
0100b6e1
LP
241 if (j == i->checksum_job)
242 log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
243 else if (j == i->signature_job)
244 log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
245 else
246 log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
247
56ebfaf1
LP
248 r = j->error;
249 goto finish;
250 }
251
252 /* This is invoked if either the download completed
253 * successfully, or the download was skipped because we
254 * already have the etag. */
255
8b71fce8 256 if (!tar_import_is_done(i))
0100b6e1
LP
257 return;
258
259 j->disk_fd = safe_close(i->tar_job->disk_fd);
56ebfaf1
LP
260
261 if (i->tar_pid > 0) {
262 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
263 i->tar_pid = 0;
264 if (r < 0)
265 goto finish;
266 }
267
0100b6e1
LP
268 if (!i->tar_job->etag_exists) {
269 /* This is a new download, verify it, and move it into place */
270
7079cfef
LP
271 tar_import_report_progress(i, TAR_VERIFYING);
272
0100b6e1
LP
273 r = import_verify(i->tar_job, i->checksum_job, i->signature_job);
274 if (r < 0)
275 goto finish;
276
7079cfef
LP
277 tar_import_report_progress(i, TAR_FINALIZING);
278
56ebfaf1
LP
279 r = import_make_read_only(i->temp_path);
280 if (r < 0)
281 goto finish;
282
283 if (rename(i->temp_path, i->final_path) < 0) {
284 r = log_error_errno(errno, "Failed to rename to final image name: %m");
285 goto finish;
286 }
56ebfaf1 287
0d6e763b
LP
288 free(i->temp_path);
289 i->temp_path = NULL;
56ebfaf1
LP
290 }
291
7079cfef
LP
292 tar_import_report_progress(i, TAR_COPYING);
293
0d6e763b
LP
294 r = tar_import_make_local_copy(i);
295 if (r < 0)
296 goto finish;
297
56ebfaf1
LP
298 r = 0;
299
300finish:
56ebfaf1
LP
301 if (i->on_finished)
302 i->on_finished(i, r, i->userdata);
303 else
304 sd_event_exit(i->event, r);
305}
306
307static int tar_import_job_on_open_disk(ImportJob *j) {
56ebfaf1
LP
308 TarImport *i;
309 int r;
310
311 assert(j);
312 assert(j->userdata);
313
314 i = j->userdata;
8b71fce8
LP
315 assert(i->tar_job == j);
316 assert(!i->final_path);
317 assert(!i->temp_path);
318 assert(i->tar_pid <= 0);
56ebfaf1
LP
319
320 r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
321 if (r < 0)
322 return log_oom();
323
324 r = tempfn_random(i->final_path, &i->temp_path);
325 if (r < 0)
326 return log_oom();
327
328 mkdir_parents_label(i->temp_path, 0700);
329
330 r = btrfs_subvol_make(i->temp_path);
331 if (r == -ENOTTY) {
332 if (mkdir(i->temp_path, 0755) < 0)
333 return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
334 } else if (r < 0)
335 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
336
2c140ded
LP
337 j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
338 if (j->disk_fd < 0)
339 return j->disk_fd;
56ebfaf1
LP
340
341 return 0;
342}
343
7079cfef
LP
344static void tar_import_job_on_progress(ImportJob *j) {
345 TarImport *i;
346
347 assert(j);
348 assert(j->userdata);
349
350 i = j->userdata;
351
352 tar_import_report_progress(i, TAR_DOWNLOADING);
353}
354
0100b6e1 355int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
56ebfaf1
LP
356 int r;
357
358 assert(i);
359
56ebfaf1
LP
360 if (!http_url_is_valid(url))
361 return -EINVAL;
362
363 if (local && !machine_name_is_valid(local))
364 return -EINVAL;
365
8b71fce8
LP
366 if (i->tar_job)
367 return -EBUSY;
368
56ebfaf1
LP
369 r = free_and_strdup(&i->local, local);
370 if (r < 0)
371 return r;
56ebfaf1 372 i->force_local = force_local;
0100b6e1 373 i->verify = verify;
56ebfaf1
LP
374
375 r = import_job_new(&i->tar_job, url, i->glue, i);
376 if (r < 0)
377 return r;
378
379 i->tar_job->on_finished = tar_import_job_on_finished;
380 i->tar_job->on_open_disk = tar_import_job_on_open_disk;
7079cfef 381 i->tar_job->on_progress = tar_import_job_on_progress;
0100b6e1 382 i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
26166c88 383 i->tar_job->grow_machine_directory = i->grow_machine_directory;
56ebfaf1
LP
384
385 r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
386 if (r < 0)
387 return r;
388
0100b6e1
LP
389 r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i);
390 if (r < 0)
391 return r;
392
393 r = import_job_begin(i->tar_job);
394 if (r < 0)
395 return r;
396
397 if (i->checksum_job) {
7079cfef
LP
398 i->checksum_job->on_progress = tar_import_job_on_progress;
399
0100b6e1
LP
400 r = import_job_begin(i->checksum_job);
401 if (r < 0)
402 return r;
403 }
404
405 if (i->signature_job) {
7079cfef
LP
406 i->signature_job->on_progress = tar_import_job_on_progress;
407
0100b6e1
LP
408 r = import_job_begin(i->signature_job);
409 if (r < 0)
410 return r;
411 }
412
413 return 0;
56ebfaf1 414}