chiark / gitweb /
import: now that the worker binary is called "systemd-pull" we can shorten the verbs
[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 "utf8.h"
26 #include "strv.h"
27 #include "copy.h"
28 #include "btrfs-util.h"
29 #include "util.h"
30 #include "macro.h"
31 #include "mkdir.h"
32 #include "import-util.h"
33 #include "curl-util.h"
34 #include "import-job.h"
35 #include "import-common.h"
36 #include "import-tar.h"
37
38 struct TarImport {
39         sd_event *event;
40         CurlGlue *glue;
41
42         char *image_root;
43
44         ImportJob *tar_job;
45         ImportJob *checksum_job;
46         ImportJob *signature_job;
47
48         TarImportFinished on_finished;
49         void *userdata;
50
51         char *local;
52         bool force_local;
53
54         pid_t tar_pid;
55
56         char *temp_path;
57         char *final_path;
58
59         ImportVerify verify;
60 };
61
62 TarImport* tar_import_unref(TarImport *i) {
63         if (!i)
64                 return NULL;
65
66         if (i->tar_pid > 1) {
67                 (void) kill_and_sigcont(i->tar_pid, SIGKILL);
68                 (void) wait_for_terminate(i->tar_pid, NULL);
69         }
70
71         import_job_unref(i->tar_job);
72         import_job_unref(i->checksum_job);
73         import_job_unref(i->signature_job);
74
75         curl_glue_unref(i->glue);
76         sd_event_unref(i->event);
77
78         if (i->temp_path) {
79                 (void) btrfs_subvol_remove(i->temp_path);
80                 (void) rm_rf_dangerous(i->temp_path, false, true, false);
81                 free(i->temp_path);
82         }
83
84         free(i->final_path);
85         free(i->image_root);
86         free(i->local);
87         free(i);
88
89         return NULL;
90 }
91
92 int tar_import_new(
93                 TarImport **ret,
94                 sd_event *event,
95                 const char *image_root,
96                 TarImportFinished on_finished,
97                 void *userdata) {
98
99         _cleanup_(tar_import_unrefp) TarImport *i = NULL;
100         int r;
101
102         assert(ret);
103         assert(event);
104
105         i = new0(TarImport, 1);
106         if (!i)
107                 return -ENOMEM;
108
109         i->on_finished = on_finished;
110         i->userdata = userdata;
111
112         i->image_root = strdup(image_root ?: "/var/lib/machines");
113         if (!i->image_root)
114                 return -ENOMEM;
115
116         if (event)
117                 i->event = sd_event_ref(event);
118         else {
119                 r = sd_event_default(&i->event);
120                 if (r < 0)
121                         return r;
122         }
123
124         r = curl_glue_new(&i->glue, i->event);
125         if (r < 0)
126                 return r;
127
128         i->glue->on_finished = import_job_curl_on_finished;
129         i->glue->userdata = i;
130
131         *ret = i;
132         i = NULL;
133
134         return 0;
135 }
136
137 static int tar_import_make_local_copy(TarImport *i) {
138         int r;
139
140         assert(i);
141         assert(i->tar_job);
142
143         if (!i->local)
144                 return 0;
145
146         if (!i->final_path) {
147                 r = import_make_path(i->tar_job->url, i->tar_job->etag, i->image_root, ".tar-", NULL, &i->final_path);
148                 if (r < 0)
149                         return log_oom();
150         }
151
152         r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
153         if (r < 0)
154                 return r;
155
156         return 0;
157 }
158
159 static bool tar_import_is_done(TarImport *i) {
160         assert(i);
161         assert(i->tar_job);
162
163         if (i->tar_job->state != IMPORT_JOB_DONE)
164                 return false;
165         if (i->checksum_job && i->checksum_job->state != IMPORT_JOB_DONE)
166                 return false;
167         if (i->signature_job && i->signature_job->state != IMPORT_JOB_DONE)
168                 return false;
169
170         return true;
171 }
172
173 static void tar_import_job_on_finished(ImportJob *j) {
174         TarImport *i;
175         int r;
176
177         assert(j);
178         assert(j->userdata);
179
180         i = j->userdata;
181         if (j->error != 0) {
182                 if (j == i->checksum_job)
183                         log_error_errno(j->error, "Failed to retrieve SHA256 checksum, cannot verify. (Try --verify=no?)");
184                 else if (j == i->signature_job)
185                         log_error_errno(j->error, "Failed to retrieve signature file, cannot verify. (Try --verify=no?)");
186                 else
187                         log_error_errno(j->error, "Failed to retrieve image file. (Wrong URL?)");
188
189                 r = j->error;
190                 goto finish;
191         }
192
193         /* This is invoked if either the download completed
194          * successfully, or the download was skipped because we
195          * already have the etag. */
196
197         if (!tar_import_is_done(i))
198                 return;
199
200         j->disk_fd = safe_close(i->tar_job->disk_fd);
201
202         if (i->tar_pid > 0) {
203                 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
204                 i->tar_pid = 0;
205                 if (r < 0)
206                         goto finish;
207         }
208
209         if (!i->tar_job->etag_exists) {
210                 /* This is a new download, verify it, and move it into place */
211
212                 r = import_verify(i->tar_job, i->checksum_job, i->signature_job);
213                 if (r < 0)
214                         goto finish;
215
216                 r = import_make_read_only(i->temp_path);
217                 if (r < 0)
218                         goto finish;
219
220                 if (rename(i->temp_path, i->final_path) < 0) {
221                         r = log_error_errno(errno, "Failed to rename to final image name: %m");
222                         goto finish;
223                 }
224
225                 free(i->temp_path);
226                 i->temp_path = NULL;
227         }
228
229         r = tar_import_make_local_copy(i);
230         if (r < 0)
231                 goto finish;
232
233         r = 0;
234
235 finish:
236         if (i->on_finished)
237                 i->on_finished(i, r, i->userdata);
238         else
239                 sd_event_exit(i->event, r);
240 }
241
242 static int tar_import_job_on_open_disk(ImportJob *j) {
243         _cleanup_close_pair_ int pipefd[2] = { -1 , -1 };
244         TarImport *i;
245         int r;
246
247         assert(j);
248         assert(j->userdata);
249
250         i = j->userdata;
251         assert(i->tar_job == j);
252         assert(!i->final_path);
253         assert(!i->temp_path);
254         assert(i->tar_pid <= 0);
255
256         r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
257         if (r < 0)
258                 return log_oom();
259
260         r = tempfn_random(i->final_path, &i->temp_path);
261         if (r < 0)
262                 return log_oom();
263
264         mkdir_parents_label(i->temp_path, 0700);
265
266         r = btrfs_subvol_make(i->temp_path);
267         if (r == -ENOTTY) {
268                 if (mkdir(i->temp_path, 0755) < 0)
269                         return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
270         } else if (r < 0)
271                 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
272
273         if (pipe2(pipefd, O_CLOEXEC) < 0)
274                 return log_error_errno(errno, "Failed to create pipe for tar: %m");
275
276         i->tar_pid = fork();
277         if (i->tar_pid < 0)
278                 return log_error_errno(errno, "Failed to fork off tar: %m");
279         if (i->tar_pid == 0) {
280                 int null_fd;
281
282                 /* Child */
283
284                 reset_all_signal_handlers();
285                 reset_signal_mask();
286                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
287
288                 pipefd[1] = safe_close(pipefd[1]);
289
290                 if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
291                         log_error_errno(errno, "Failed to dup2() fd: %m");
292                         _exit(EXIT_FAILURE);
293                 }
294
295                 if (pipefd[0] != STDIN_FILENO)
296                         pipefd[0] = safe_close(pipefd[0]);
297
298                 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
299                 if (null_fd < 0) {
300                         log_error_errno(errno, "Failed to open /dev/null: %m");
301                         _exit(EXIT_FAILURE);
302                 }
303
304                 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
305                         log_error_errno(errno, "Failed to dup2() fd: %m");
306                         _exit(EXIT_FAILURE);
307                 }
308
309                 if (null_fd != STDOUT_FILENO)
310                         null_fd = safe_close(null_fd);
311
312                 fd_cloexec(STDIN_FILENO, false);
313                 fd_cloexec(STDOUT_FILENO, false);
314                 fd_cloexec(STDERR_FILENO, false);
315
316                 execlp("tar", "tar", "--numeric-owner", "-C", i->temp_path, "-px", NULL);
317                 log_error_errno(errno, "Failed to execute tar: %m");
318                 _exit(EXIT_FAILURE);
319         }
320
321         pipefd[0] = safe_close(pipefd[0]);
322
323         j->disk_fd = pipefd[1];
324         pipefd[1] = -1;
325
326         return 0;
327 }
328
329 int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local, ImportVerify verify) {
330         int r;
331
332         assert(i);
333
334         if (!http_url_is_valid(url))
335                 return -EINVAL;
336
337         if (local && !machine_name_is_valid(local))
338                 return -EINVAL;
339
340         if (i->tar_job)
341                 return -EBUSY;
342
343         r = free_and_strdup(&i->local, local);
344         if (r < 0)
345                 return r;
346         i->force_local = force_local;
347         i->verify = verify;
348
349         r = import_job_new(&i->tar_job, url, i->glue, i);
350         if (r < 0)
351                 return r;
352
353         i->tar_job->on_finished = tar_import_job_on_finished;
354         i->tar_job->on_open_disk = tar_import_job_on_open_disk;
355         i->tar_job->calc_checksum = verify != IMPORT_VERIFY_NO;
356
357         r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
358         if (r < 0)
359                 return r;
360
361         r = import_make_verification_jobs(&i->checksum_job, &i->signature_job, verify, url, i->glue, tar_import_job_on_finished, i);
362         if (r < 0)
363                 return r;
364
365         r = import_job_begin(i->tar_job);
366         if (r < 0)
367                 return r;
368
369         if (i->checksum_job) {
370                 r = import_job_begin(i->checksum_job);
371                 if (r < 0)
372                         return r;
373         }
374
375         if (i->signature_job) {
376                 r = import_job_begin(i->signature_job);
377                 if (r < 0)
378                         return r;
379         }
380
381         return 0;
382 }