]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/storage/nbd.c
tree-wide: struct bdev -> struct lxc_storage
[mirror_lxc.git] / src / lxc / storage / nbd.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #define _GNU_SOURCE
25 #include <errno.h>
26 #include <stdbool.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/prctl.h>
32 #include <sys/wait.h>
33
34 #include "log.h"
35 #include "nbd.h"
36 #include "storage.h"
37 #include "storage_utils.h"
38 #include "utils.h"
39
40 lxc_log_define(nbd, lxc);
41
42 struct nbd_attach_data {
43 const char *nbd;
44 const char *path;
45 };
46
47 static bool clone_attach_nbd(const char *nbd, const char *path);
48 static int do_attach_nbd(void *d);
49 static bool nbd_busy(int idx);
50 static void nbd_detach(const char *path);
51 static int nbd_get_partition(const char *src);
52 static bool wait_for_partition(const char *path);
53
54 bool attach_nbd(char *src, struct lxc_conf *conf)
55 {
56 char *orig = alloca(strlen(src)+1), *p, path[50];
57 int i = 0;
58
59 strcpy(orig, src);
60 /* if path is followed by a partition, drop that for now */
61 p = strchr(orig, ':');
62 if (p)
63 *p = '\0';
64 while (1) {
65 sprintf(path, "/dev/nbd%d", i);
66 if (!file_exists(path))
67 return false;
68 if (nbd_busy(i)) {
69 i++;
70 continue;
71 }
72 if (!clone_attach_nbd(path, orig))
73 return false;
74 conf->nbd_idx = i;
75 return true;
76 }
77 }
78
79 void detach_nbd_idx(int idx)
80 {
81 int ret;
82 char path[50];
83
84 ret = snprintf(path, 50, "/dev/nbd%d", idx);
85 if (ret < 0 || ret >= 50)
86 return;
87
88 nbd_detach(path);
89 }
90
91 int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
92 const char *oldname, const char *cname, const char *oldpath,
93 const char *lxcpath, int snap, uint64_t newsize,
94 struct lxc_conf *conf)
95 {
96 return -ENOSYS;
97 }
98
99 int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n,
100 struct bdev_specs *specs)
101 {
102 return -ENOSYS;
103 }
104
105 int nbd_destroy(struct lxc_storage *orig)
106 {
107 return -ENOSYS;
108 }
109
110 bool nbd_detect(const char *path)
111 {
112 if (!strncmp(path, "nbd:", 4))
113 return true;
114
115 return false;
116 }
117
118 int nbd_mount(struct lxc_storage *bdev)
119 {
120 int ret = -1, partition;
121 char *src;
122 char path[50];
123
124 if (strcmp(bdev->type, "nbd"))
125 return -22;
126
127 if (!bdev->src || !bdev->dest)
128 return -22;
129
130 /* nbd_idx should have been copied by bdev_init from the lxc_conf */
131 if (bdev->nbd_idx < 0)
132 return -22;
133
134 src = lxc_storage_get_path(bdev->src, bdev->type);
135 partition = nbd_get_partition(src);
136 if (partition)
137 ret = snprintf(path, 50, "/dev/nbd%dp%d", bdev->nbd_idx,
138 partition);
139 else
140 ret = snprintf(path, 50, "/dev/nbd%d", bdev->nbd_idx);
141 if (ret < 0 || ret >= 50) {
142 ERROR("Error setting up nbd device path");
143 return ret;
144 }
145
146 /* It might take awhile for the partition files to show up */
147 if (partition) {
148 if (!wait_for_partition(path))
149 return -2;
150 }
151 ret = mount_unknown_fs(path, bdev->dest, bdev->mntopts);
152 if (ret < 0)
153 ERROR("Error mounting %s", bdev->src);
154
155 return ret;
156 }
157
158 int nbd_umount(struct lxc_storage *bdev)
159 {
160 if (strcmp(bdev->type, "nbd"))
161 return -22;
162
163 if (!bdev->src || !bdev->dest)
164 return -22;
165
166 return umount(bdev->dest);
167 }
168
169 bool requires_nbd(const char *path)
170 {
171 if (strncmp(path, "nbd:", 4) == 0)
172 return true;
173 return false;
174 }
175
176 static int do_attach_nbd(void *d)
177 {
178 struct nbd_attach_data *data = d;
179 const char *nbd, *path;
180 pid_t pid;
181 sigset_t mask;
182 int sfd;
183 ssize_t s;
184 struct signalfd_siginfo fdsi;
185
186 sigemptyset(&mask);
187 sigaddset(&mask, SIGHUP);
188 sigaddset(&mask, SIGCHLD);
189
190 nbd = data->nbd;
191 path = data->path;
192
193 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
194 SYSERROR("Error blocking signals for nbd watcher");
195 exit(1);
196 }
197
198 sfd = signalfd(-1, &mask, 0);
199 if (sfd == -1) {
200 SYSERROR("Error opening signalfd for nbd task");
201 exit(1);
202 }
203
204 if (prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0) < 0)
205 SYSERROR("Error setting parent death signal for nbd watcher");
206
207 pid = fork();
208 if (pid) {
209 for (;;) {
210 s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
211 if (s != sizeof(struct signalfd_siginfo))
212 SYSERROR("Error reading from signalfd");
213
214 if (fdsi.ssi_signo == SIGHUP) {
215 /* container has exited */
216 nbd_detach(nbd);
217 exit(0);
218 } else if (fdsi.ssi_signo == SIGCHLD) {
219 int status;
220 /* If qemu-nbd fails, or is killed by a signal,
221 * then exit */
222 while (waitpid(-1, &status, WNOHANG) > 0) {
223 if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) ||
224 WIFSIGNALED(status)) {
225 nbd_detach(nbd);
226 exit(1);
227 }
228 }
229 }
230 }
231 }
232
233 close(sfd);
234 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
235 WARN("Warning: unblocking signals for nbd watcher");
236
237 execlp("qemu-nbd", "qemu-nbd", "-c", nbd, path, (char *)NULL);
238 SYSERROR("Error executing qemu-nbd");
239 exit(1);
240 }
241
242 static bool clone_attach_nbd(const char *nbd, const char *path)
243 {
244 pid_t pid;
245 struct nbd_attach_data data;
246
247 data.nbd = nbd;
248 data.path = path;
249
250 pid = lxc_clone(do_attach_nbd, &data, CLONE_NEWPID);
251 if (pid < 0)
252 return false;
253 return true;
254 }
255
256 static bool nbd_busy(int idx)
257 {
258 char path[100];
259 int ret;
260
261 ret = snprintf(path, 100, "/sys/block/nbd%d/pid", idx);
262 if (ret < 0 || ret >= 100)
263 return true;
264 return file_exists(path);
265 }
266
267 static void nbd_detach(const char *path)
268 {
269 int ret;
270 pid_t pid = fork();
271
272 if (pid < 0) {
273 SYSERROR("Error forking to detach nbd");
274 return;
275 }
276 if (pid) {
277 ret = wait_for_pid(pid);
278 if (ret < 0)
279 ERROR("nbd disconnect returned an error");
280 return;
281 }
282 execlp("qemu-nbd", "qemu-nbd", "-d", path, (char *)NULL);
283 SYSERROR("Error executing qemu-nbd");
284 exit(1);
285 }
286
287 /*
288 * Pick the partition # off the end of a nbd:file:p
289 * description. Return 1-9 for the partition id, or 0
290 * for no partition.
291 */
292 static int nbd_get_partition(const char *src)
293 {
294 char *p = strchr(src, ':');
295 if (!p)
296 return 0;
297 p = strchr(p+1, ':');
298 if (!p)
299 return 0;
300 p++;
301 if (*p < '1' || *p > '9')
302 return 0;
303 return *p - '0';
304 }
305
306 static bool wait_for_partition(const char *path)
307 {
308 int count = 0;
309 while (count < 5) {
310 if (file_exists(path))
311 return true;
312 sleep(1);
313 count++;
314 }
315 ERROR("Device %s did not show up after 5 seconds", path);
316 return false;
317 }