chiark / gitweb /
bus-proxy: be more verbose if messages cannot be forwarded
[elogind.git] / src / import / import-tar.c
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
25 #include "sd-daemon.h"
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"
33 #include "import-util.h"
34 #include "curl-util.h"
35 #include "import-job.h"
36 #include "import-common.h"
37 #include "import-tar.h"
38
39 typedef enum TarProgress {
40         TAR_DOWNLOADING,
41         TAR_VERIFYING,
42         TAR_FINALIZING,
43         TAR_COPYING,
44 } TarProgress;
45
46 struct TarImport {
47         sd_event *event;
48         CurlGlue *glue;
49
50         char *image_root;
51
52         ImportJob *tar_job;
53         ImportJob *checksum_job;
54         ImportJob *signature_job;
55
56         TarImportFinished on_finished;
57         void *userdata;
58
59         char *local;
60         bool force_local;
61
62         pid_t tar_pid;
63
64         char *temp_path;
65         char *final_path;
66
67         ImportVerify verify;
68 };
69
70 TarImport* tar_import_unref(TarImport *i) {
71         if (!i)
72                 return NULL;
73
74         if (i->tar_pid > 1) {
75                 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
76                 (void) wait_for_terminate(i->tar_pid, NULL);
77         }
78
79         import_job_unref(i->tar_job);
80         import_job_unref(i->checksum_job);
81         import_job_unref(i->signature_job);
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);
89                 free(i->temp_path);
90         }
91
92         free(i->final_path);
93         free(i->image_root);
94         free(i->local);
95         free(i);
96
97         return NULL;
98 }
99
100 int tar_import_new(
101                 TarImport **ret,
102                 sd_event *event,
103                 const char *image_root,
104                 TarImportFinished on_finished,
105                 void *userdata) {
106
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
145 static 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
192 static 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();
205         }
206
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
211         return 0;
212 }
213
214 static 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
228 static 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;
236         if (j->error != 0) {
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
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
252         if (!tar_import_is_done(i))
253                 return;
254
255         j->disk_fd = safe_close(i->tar_job->disk_fd);
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
264         if (!i->tar_job->etag_exists) {
265                 /* This is a new download, verify it, and move it into place */
266
267                 tar_import_report_progress(i, TAR_VERIFYING);
268
269                 r = import_verify(i->tar_job, i->checksum_job, i->signature_job);
270                 if (r < 0)
271                         goto finish;
272
273                 tar_import_report_progress(i, TAR_FINALIZING);
274
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                 }
283
284                 free(i->temp_path);
285                 i->temp_path = NULL;
286         }
287
288         tar_import_report_progress(i, TAR_COPYING);
289
290         r = tar_import_make_local_copy(i);
291         if (r < 0)
292                 goto finish;
293
294         r = 0;
295
296 finish:
297         if (i->on_finished)
298                 i->on_finished(i, r, i->userdata);
299         else
300                 sd_event_exit(i->event, r);
301 }
302
303 static int tar_import_job_on_open_disk(ImportJob *j) {
304         _cleanup_close_pair_ int pipefd[2] = { -1 , -1 };
305         TarImport *i;
306         int r;
307
308         assert(j);
309         assert(j->userdata);
310
311         i = j->userdata;
312         assert(i->tar_job == j);
313         assert(!i->final_path);
314         assert(!i->temp_path);
315         assert(i->tar_pid <= 0);
316
317         r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
318         if (r < 0)
319                 return log_oom();
320
321         r = tempfn_random(i->final_path, &i->temp_path);
322         if (r < 0)
323                 return log_oom();
324
325         mkdir_parents_label(i->temp_path, 0700);
326
327         r = btrfs_subvol_make(i->temp_path);
328         if (r == -ENOTTY) {
329                 if (mkdir(i->temp_path, 0755) < 0)
330                         return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
331         } else if (r < 0)
332                 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
333
334         j->disk_fd = import_fork_tar(i->temp_path, &i->tar_pid);
335         if (j->disk_fd < 0)
336                 return j->disk_fd;
337
338         return 0;
339 }
340
341 static void tar_import_job_on_progress(ImportJob *j) {
342         TarImport *i;
343
344         assert(j);
345         assert(j->userdata);
346
347         i = j->userdata;
348
349         tar_import_report_progress(i, TAR_DOWNLOADING);
350 }
351
352 int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
353         int r;
354
355         assert(i);
356
357         if (!http_url_is_valid(url))
358                 return -EINVAL;
359
360         if (local && !machine_name_is_valid(local))
361                 return -EINVAL;
362
363         if (i->tar_job)
364                 return -EBUSY;
365
366         r = free_and_strdup(&i->local, local);
367         if (r < 0)
368                 return r;
369         i->force_local = force_local;
370         i->verify = verify;
371
372         r = import_job_new(&i->tar_job, url, i->glue, i);
373         if (r < 0)
374                 return r;
375
376         i->tar_job->on_finished = tar_import_job_on_finished;
377         i->tar_job->on_open_disk = tar_import_job_on_open_disk;
378         i->tar_job->on_progress = tar_import_job_on_progress;
379         i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
380
381         r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
382         if (r < 0)
383                 return r;
384
385         r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i);
386         if (r < 0)
387                 return r;
388
389         r = import_job_begin(i->tar_job);
390         if (r < 0)
391                 return r;
392
393         if (i->checksum_job) {
394                 i->checksum_job->on_progress = tar_import_job_on_progress;
395
396                 r = import_job_begin(i->checksum_job);
397                 if (r < 0)
398                         return r;
399         }
400
401         if (i->signature_job) {
402                 i->signature_job->on_progress = tar_import_job_on_progress;
403
404                 r = import_job_begin(i->signature_job);
405                 if (r < 0)
406                         return r;
407         }
408
409         return 0;
410 }