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