chiark / gitweb /
43227f61f32ab61a5011b7a419e3a7bd66dd1ddd
[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 "hashmap.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 "curl-util.h"
34 #include "import-job.h"
35 #include "import-util.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
46         TarImportFinished on_finished;
47         void *userdata;
48
49         bool finished;
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
60 TarImport* tar_import_unref(TarImport *i) {
61         if (!i)
62                 return NULL;
63
64         if (i->tar_pid > 0) {
65                 kill(i->tar_pid, SIGKILL);
66                 wait_for_terminate(i->tar_pid, NULL);
67         }
68
69         import_job_unref(i->tar_job);
70
71         curl_glue_unref(i->glue);
72         sd_event_unref(i->event);
73
74         if (i->temp_path) {
75                 (void) btrfs_subvol_remove(i->temp_path);
76                 (void) rm_rf_dangerous(i->temp_path, false, true, false);
77         }
78
79         free(i->final_path);
80         free(i->image_root);
81         free(i->local);
82
83         free(i);
84
85         return NULL;
86 }
87
88 int tar_import_new(TarImport **ret, sd_event *event, const char *image_root, TarImportFinished on_finished, void *userdata) {
89         _cleanup_(tar_import_unrefp) TarImport *i = NULL;
90         int r;
91
92         assert(ret);
93         assert(event);
94
95         i = new0(TarImport, 1);
96         if (!i)
97                 return -ENOMEM;
98
99         i->on_finished = on_finished;
100         i->userdata = userdata;
101
102         i->image_root = strdup(image_root ?: "/var/lib/machines");
103         if (!i->image_root)
104                 return -ENOMEM;
105
106         if (event)
107                 i->event = sd_event_ref(event);
108         else {
109                 r = sd_event_default(&i->event);
110                 if (r < 0)
111                         return r;
112         }
113
114         r = curl_glue_new(&i->glue, i->event);
115         if (r < 0)
116                 return r;
117
118         i->glue->on_finished = import_job_curl_on_finished;
119         i->glue->userdata = i;
120
121         *ret = i;
122         i = NULL;
123
124         return 0;
125 }
126
127 static void tar_import_job_on_finished(ImportJob *j) {
128         TarImport *i;
129         int r;
130
131         assert(j);
132         assert(j->userdata);
133
134         i = j->userdata;
135
136         if (j->error != 0) {
137                 r = j->error;
138                 goto finish;
139         }
140
141         /* This is invoked if either the download completed
142          * successfully, or the download was skipped because we
143          * already have the etag. */
144
145         j->disk_fd = safe_close(j->disk_fd);
146
147         if (i->tar_pid > 0) {
148                 r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
149                 i->tar_pid = 0;
150                 if (r < 0)
151                         goto finish;
152         }
153
154         if (i->temp_path) {
155                 r = import_make_read_only(i->temp_path);
156                 if (r < 0)
157                         goto finish;
158
159                 if (rename(i->temp_path, i->final_path) < 0) {
160                         r = log_error_errno(errno, "Failed to rename to final image name: %m");
161                         goto finish;
162                 }
163         }
164
165         if (i->local) {
166                 if (!i->final_path) {
167                         r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
168                         if (r < 0)
169                                 goto finish;
170                 }
171
172                 r = import_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
173                 if (r < 0)
174                         goto finish;
175         }
176
177         r = 0;
178
179 finish:
180         i->finished = true;
181
182         if (i->on_finished)
183                 i->on_finished(i, r, i->userdata);
184         else
185                 sd_event_exit(i->event, r);
186 }
187
188 static int tar_import_job_on_open_disk(ImportJob *j) {
189         _cleanup_close_pair_ int pipefd[2] = { -1 , -1 };
190         TarImport *i;
191         int r;
192
193         assert(j);
194         assert(j->userdata);
195
196         i = j->userdata;
197
198         r = import_make_path(j->url, j->etag, i->image_root, ".tar-", NULL, &i->final_path);
199         if (r < 0)
200                 return log_oom();
201
202         r = tempfn_random(i->final_path, &i->temp_path);
203         if (r < 0)
204                 return log_oom();
205
206         mkdir_parents_label(i->temp_path, 0700);
207
208         r = btrfs_subvol_make(i->temp_path);
209         if (r == -ENOTTY) {
210                 if (mkdir(i->temp_path, 0755) < 0)
211                         return log_error_errno(errno, "Failed to create directory %s: %m", i->temp_path);
212         } else if (r < 0)
213                 return log_error_errno(errno, "Failed to create subvolume %s: %m", i->temp_path);
214
215         if (pipe2(pipefd, O_CLOEXEC) < 0)
216                 return log_error_errno(errno, "Failed to create pipe for tar: %m");
217
218         i->tar_pid = fork();
219         if (i->tar_pid < 0)
220                 return log_error_errno(errno, "Failed to fork off tar: %m");
221         if (i->tar_pid == 0) {
222                 int null_fd;
223
224                 reset_all_signal_handlers();
225                 reset_signal_mask();
226                 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
227
228                 pipefd[1] = safe_close(pipefd[1]);
229
230                 if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
231                         log_error_errno(errno, "Failed to dup2() fd: %m");
232                         _exit(EXIT_FAILURE);
233                 }
234
235                 if (pipefd[0] != STDIN_FILENO)
236                         safe_close(pipefd[0]);
237
238                 null_fd = open("/dev/null", O_WRONLY|O_NOCTTY);
239                 if (null_fd < 0) {
240                         log_error_errno(errno, "Failed to open /dev/null: %m");
241                         _exit(EXIT_FAILURE);
242                 }
243
244                 if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) {
245                         log_error_errno(errno, "Failed to dup2() fd: %m");
246                         _exit(EXIT_FAILURE);
247                 }
248
249                 if (null_fd != STDOUT_FILENO)
250                         safe_close(null_fd);
251
252                 execlp("tar", "tar", "--numeric-owner", "-C", i->temp_path, "-px", NULL);
253                 _exit(EXIT_FAILURE);
254         }
255
256         pipefd[0] = safe_close(pipefd[0]);
257
258         j->disk_fd = pipefd[1];
259         pipefd[1] = -1;
260
261         return 0;
262 }
263
264 int tar_import_pull(TarImport *i, const char *url, const char *local, bool force_local) {
265         int r;
266
267         assert(i);
268
269         if (i->tar_job)
270                 return -EBUSY;
271
272         if (!http_url_is_valid(url))
273                 return -EINVAL;
274
275         if (local && !machine_name_is_valid(local))
276                 return -EINVAL;
277
278         r = free_and_strdup(&i->local, local);
279         if (r < 0)
280                 return r;
281
282         i->force_local = force_local;
283
284         r = import_job_new(&i->tar_job, url, i->glue, i);
285         if (r < 0)
286                 return r;
287
288         i->tar_job->on_finished = tar_import_job_on_finished;
289         i->tar_job->on_open_disk = tar_import_job_on_open_disk;
290
291         r = import_find_old_etags(url, i->image_root, DT_DIR, ".tar-", NULL, &i->tar_job->old_etags);
292         if (r < 0)
293                 return r;
294
295         return import_job_begin(i->tar_job);
296 }