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