Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <stdio.h>
36 : : #include <errno.h>
37 : : #include <fcntl.h>
38 : : #include <unistd.h>
39 : : #include <stdlib.h>
40 : : #include <string.h>
41 : : #include <signal.h>
42 : : #include <sys/un.h>
43 : : #include <sys/stat.h>
44 : : #include <sys/types.h>
45 : : #include <sys/ioctl.h>
46 : : #include <sys/socket.h>
47 : : #include <sys/mman.h>
48 : : #include <stdbool.h>
49 : :
50 : : #include "debug.h"
51 : : #include "list.h"
52 : : #include "tapdisk.h"
53 : : #include "tapdisk-vbd.h"
54 : : #include "tapdisk-utils.h"
55 : : #include "tapdisk-server.h"
56 : : #include "tapdisk-message.h"
57 : : #include "tapdisk-disktype.h"
58 : : #include "tapdisk-stats.h"
59 : : #include "tapdisk-control.h"
60 : : #include "tapdisk-nbdserver.h"
61 : : #include "td-blkif.h"
62 : : #include "timeout-math.h"
63 : : #include "util.h"
64 : :
65 : : #define TD_CTL_MAX_CONNECTIONS 10
66 : : #define TD_CTL_SOCK_BACKLOG 32
67 : : #define TD_CTL_RECV_TIMEOUT 10
68 : : #define TD_CTL_SEND_TIMEOUT 10
69 : : #define TD_CTL_SEND_BUFSZ ((size_t)4096)
70 : :
71 : : #define DBG(_f, _a...) tlog_syslog(TLOG_DBG, _f, ##_a)
72 : : #define ERR(err, _f, _a...) tlog_error(err, _f, ##_a)
73 : : #define INFO(_f, _a...) tlog_syslog(TLOG_INFO, "control: " _f, ##_a)
74 : :
75 : : #define WARN_ON(_p) \
76 : : if (_p) { \
77 : : EPRINTF("%s:%d: WARNING: '%s'\n", \
78 : : __FILE__, __LINE__, #_p); \
79 : : }
80 : :
81 : : #define eintr_retry(res, op) \
82 : : do { \
83 : : res = op; \
84 : : } while (res == -1 && errno == EINTR);
85 : :
86 : : struct tapdisk_ctl_conn {
87 : : int fd;
88 : :
89 : : /**
90 : : * processing event
91 : : */
92 : : event_id_t event_id;
93 : :
94 : : /**
95 : : * output event
96 : : */
97 : : struct {
98 : : void *buf;
99 : : size_t bufsz;
100 : : int event_id;
101 : : int done;
102 : :
103 : : void *prod;
104 : : void *cons;
105 : : } out;
106 : :
107 : : /**
108 : : * input event
109 : : */
110 : : struct {
111 : : int event_id;
112 : : int busy;
113 : : } in;
114 : :
115 : : struct tapdisk_control_info *info;
116 : :
117 : : /**
118 : : * for linked lists
119 : : */
120 : : struct list_head entry;
121 : :
122 : : tapdisk_message_t request;
123 : : tapdisk_message_t response;
124 : : };
125 : :
126 : : #define TAPDISK_MSG_REENTER (1<<0) /* non-blocking, idempotent */
127 : : #define TAPDISK_MSG_VERBOSE (1<<1) /* tell syslog about it */
128 : :
129 : : struct tapdisk_control_info {
130 : : int (*handler)(struct tapdisk_ctl_conn *, tapdisk_message_t *,
131 : : tapdisk_message_t * const);
132 : : int flags;
133 : : };
134 : :
135 : : struct tapdisk_control {
136 : : char *path;
137 : : int uuid;
138 : : int socket;
139 : : int event_id;
140 : : int busy;
141 : :
142 : : int n_conn;
143 : : struct tapdisk_ctl_conn __conn[TD_CTL_MAX_CONNECTIONS];
144 : : struct tapdisk_ctl_conn *conn[TD_CTL_MAX_CONNECTIONS];
145 : :
146 : : /**
147 : : * List of connections whose processing has been temporarily paused
148 : : * (masked) because the current connection requires exclusive access.
149 : : */
150 : : struct list_head pending;
151 : : };
152 : :
153 : : static struct tapdisk_control td_control;
154 : :
155 : : static inline size_t
156 : : page_align(size_t size)
157 : : {
158 : 0 : size_t page_size = sysconf(_SC_PAGE_SIZE);
159 : 0 : return (size + page_size - 1) & ~(page_size - 1);
160 : : }
161 : :
162 : : static void
163 : 0 : tapdisk_ctl_conn_uninit(struct tapdisk_ctl_conn *conn)
164 : : {
165 : 0 : if (conn->out.buf) {
166 : 0 : free(conn->out.buf);
167 : 0 : conn->out.buf = NULL;
168 : : }
169 : 0 : }
170 : :
171 : : static int
172 : 0 : tapdisk_ctl_conn_init(struct tapdisk_ctl_conn *conn, size_t bufsz)
173 : : {
174 : : int err;
175 : :
176 : : memset(conn, 0, sizeof(*conn));
177 : 0 : conn->out.event_id = -1;
178 : 0 : conn->in.event_id = -1;
179 : 0 : conn->event_id = 0;
180 : :
181 : 0 : conn->out.buf = malloc(bufsz);
182 : 0 : if (!conn->out.buf) {
183 : 0 : err = -ENOMEM;
184 : : goto fail;
185 : : }
186 : 0 : conn->out.bufsz = page_align(bufsz);
187 : :
188 : 0 : return 0;
189 : :
190 : : fail:
191 : 0 : tapdisk_ctl_conn_uninit(conn);
192 : : return err;
193 : : }
194 : :
195 : : static int
196 : 0 : tapdisk_ctl_conn_connected(struct tapdisk_ctl_conn *conn)
197 : : {
198 : 0 : return conn->fd >= 1;
199 : : }
200 : :
201 : : static void
202 : 0 : tapdisk_ctl_conn_free(struct tapdisk_ctl_conn *conn)
203 : : {
204 : : struct tapdisk_ctl_conn *prev, *next;
205 : : int i;
206 : :
207 : 0 : i = --td_control.n_conn;
208 : : /* NB. bubble the freed connection off the active list. */
209 : 0 : prev = conn;
210 : : do {
211 : 0 : ASSERT(i >= 0);
212 : 0 : next = td_control.conn[i];
213 : 0 : td_control.conn[i] = prev;
214 : 0 : prev = next;
215 : 0 : i--;
216 : 0 : } while (next != conn);
217 : 0 : }
218 : :
219 : : static void
220 : 0 : tapdisk_ctl_conn_close(struct tapdisk_ctl_conn *conn)
221 : : {
222 : 0 : if (conn->out.event_id >= 0) {
223 : 0 : tapdisk_server_unregister_event(conn->out.event_id);
224 : 0 : conn->out.event_id = -1;
225 : : }
226 : :
227 : 0 : if (conn->fd >= 0) {
228 : 0 : close(conn->fd);
229 : 0 : conn->fd = -1;
230 : :
231 : 0 : tapdisk_ctl_conn_free(conn);
232 : 0 : tapdisk_server_mask_event(td_control.event_id, 0);
233 : : }
234 : 0 : }
235 : :
236 : : static void
237 : 0 : tapdisk_ctl_conn_mask_out(struct tapdisk_ctl_conn *conn)
238 : : {
239 : 0 : tapdisk_server_mask_event(conn->out.event_id, 1);
240 : 0 : }
241 : :
242 : : static void
243 : 0 : tapdisk_ctl_conn_unmask_out(struct tapdisk_ctl_conn *conn)
244 : : {
245 : 0 : tapdisk_server_mask_event(conn->out.event_id, 0);
246 : 0 : }
247 : :
248 : : /**
249 : : * Returns number of bytes send, or a negative error code in case of failure.
250 : : */
251 : : static ssize_t
252 : 0 : tapdisk_ctl_conn_send_buf(struct tapdisk_ctl_conn *conn)
253 : : {
254 : : ssize_t size;
255 : :
256 : 0 : size = conn->out.prod - conn->out.cons;
257 : 0 : if (!size)
258 : : return 0;
259 : :
260 : 0 : size = send(conn->fd, conn->out.cons, size, MSG_DONTWAIT);
261 : 0 : if (size < 0)
262 : 0 : return -errno;
263 : :
264 : 0 : conn->out.cons += size;
265 : :
266 : 0 : return size;
267 : : }
268 : :
269 : : static void
270 : 0 : tapdisk_ctl_conn_send_event(event_id_t id, char mode, void *private)
271 : : {
272 : 0 : struct tapdisk_ctl_conn *conn = private;
273 : : ssize_t rv;
274 : :
275 : : do {
276 : 0 : rv = tapdisk_ctl_conn_send_buf(conn);
277 : 0 : } while (rv > 0);
278 : :
279 : 0 : if (rv == -EAGAIN)
280 : 0 : return;
281 : :
282 : 0 : if (rv < 0)
283 : 0 : ERR(rv, "%s: failure sending message at offset %td/%td\n",
284 : : tapdisk_message_name(conn->request.type),
285 : : conn->out.cons - conn->out.buf,
286 : : conn->out.prod - conn->out.buf);
287 : :
288 : 0 : if (rv || conn->out.done || mode & SCHEDULER_POLL_TIMEOUT)
289 : 0 : tapdisk_ctl_conn_close(conn);
290 : : else
291 : 0 : tapdisk_ctl_conn_mask_out(conn);
292 : : }
293 : :
294 : : /*
295 : : * NB. the control interface is still not properly integrated into the
296 : : * server, therefore neither the scheduler. After the last close, the
297 : : * server will exit but we still have a pending close response in the
298 : : * output buffer.
299 : : */
300 : : static void
301 : 0 : tapdisk_ctl_conn_drain(struct tapdisk_ctl_conn *conn)
302 : : {
303 : 0 : struct timeval tv = { .tv_sec = TD_CTL_SEND_TIMEOUT,
304 : : .tv_usec = 0 };
305 : : fd_set wfds;
306 : : int n, mode;
307 : :
308 : 0 : if (!conn->out.done) {
309 : : /* we accepted this connection but haven't received the message
310 : : * body yet. Since this tapdisk is on its way out, just drop
311 : : * the connection. */
312 : 0 : tapdisk_ctl_conn_close(conn);
313 : 0 : return;
314 : : }
315 : :
316 : 0 : ASSERT(conn->fd >= 0);
317 : :
318 : 0 : while (tapdisk_ctl_conn_connected(conn)) {
319 : 0 : FD_ZERO(&wfds);
320 : 0 : FD_SET(conn->fd, &wfds);
321 : :
322 : 0 : eintr_retry(n, select(conn->fd + 1, NULL, &wfds, NULL, &tv))
323 : 0 : if (n < 0)
324 : : break;
325 : :
326 : 0 : if (n)
327 : : mode = SCHEDULER_POLL_WRITE_FD;
328 : : else
329 : 0 : mode = SCHEDULER_POLL_TIMEOUT;
330 : :
331 : 0 : tapdisk_ctl_conn_send_event(conn->out.event_id, mode, conn);
332 : : }
333 : : }
334 : :
335 : :
336 : : struct tapdisk_ctl_conn *
337 : 0 : tapdisk_ctl_conn_open(int fd)
338 : : {
339 : : struct tapdisk_ctl_conn *conn;
340 : :
341 : 0 : if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
342 : : return NULL;
343 : :
344 : 0 : conn = td_control.conn[td_control.n_conn++];
345 : :
346 : 0 : conn->out.event_id =
347 : 0 : tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD,
348 : 0 : fd, TV_SECS(TD_CTL_SEND_TIMEOUT),
349 : : tapdisk_ctl_conn_send_event,
350 : : conn);
351 : 0 : if (conn->out.event_id < 0)
352 : : return NULL;
353 : :
354 : 0 : conn->fd = fd;
355 : 0 : conn->out.prod = conn->out.buf;
356 : 0 : conn->out.cons = conn->out.buf;
357 : 0 : conn->out.done = 0;
358 : :
359 : 0 : tapdisk_ctl_conn_mask_out(conn);
360 : :
361 : 0 : if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
362 : 0 : tapdisk_server_mask_event(td_control.event_id, 1);
363 : :
364 : 0 : return conn;
365 : : }
366 : :
367 : : static size_t
368 : 0 : tapdisk_ctl_conn_write(struct tapdisk_ctl_conn *conn, void *buf, size_t size)
369 : : {
370 : : size_t rest;
371 : :
372 : 0 : rest = conn->out.buf + conn->out.bufsz - conn->out.prod;
373 : 0 : if (rest < size)
374 : 0 : size = rest;
375 : 0 : if (!size)
376 : : return 0;
377 : :
378 : 0 : memcpy(conn->out.prod, buf, size);
379 : 0 : conn->out.prod += size;
380 : 0 : tapdisk_ctl_conn_unmask_out(conn);
381 : :
382 : 0 : return size;
383 : : }
384 : :
385 : : static void
386 : : tapdisk_ctl_conn_release(struct tapdisk_ctl_conn *conn)
387 : : {
388 : 0 : conn->out.done = 1;
389 : :
390 : 0 : if (conn->out.prod == conn->out.cons)
391 : 0 : tapdisk_ctl_conn_close(conn);
392 : : }
393 : :
394 : : static void
395 : 0 : tapdisk_control_initialize(void)
396 : : {
397 : : struct tapdisk_ctl_conn *conn;
398 : : int i;
399 : :
400 : 0 : td_control.socket = -1;
401 : 0 : td_control.event_id = -1;
402 : :
403 : 0 : signal(SIGPIPE, SIG_IGN);
404 : :
405 : 0 : for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) {
406 : 0 : conn = &td_control.__conn[i];
407 : 0 : tapdisk_ctl_conn_init(conn, TD_CTL_SEND_BUFSZ);
408 : 0 : td_control.conn[i] = conn;
409 : : }
410 : :
411 : 0 : td_control.n_conn = 0;
412 : :
413 : : INIT_LIST_HEAD(&td_control.pending);
414 : :
415 : : DPRINTF("tapdisk-control: init, %d x %zuk buffers\n",
416 : : TD_CTL_MAX_CONNECTIONS, TD_CTL_SEND_BUFSZ >> 10);
417 : 0 : }
418 : :
419 : : void
420 : 0 : tapdisk_control_close(void)
421 : : {
422 : : struct tapdisk_ctl_conn *conn;
423 : : int i;
424 : :
425 : 0 : DPRINTF("tapdisk-control: draining %d connections\n",
426 : : td_control.n_conn);
427 : :
428 : 0 : while (td_control.n_conn) {
429 : 0 : conn = td_control.conn[td_control.n_conn-1];
430 : 0 : tapdisk_ctl_conn_drain(conn);
431 : : }
432 : :
433 : 0 : for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) {
434 : 0 : conn = &td_control.__conn[i];
435 : 0 : tapdisk_ctl_conn_uninit(conn);
436 : : }
437 : :
438 : : DPRINTF("tapdisk-control: done\n");
439 : :
440 : 0 : if (td_control.path) {
441 : 0 : unlink(td_control.path);
442 : 0 : free(td_control.path);
443 : 0 : td_control.path = NULL;
444 : : }
445 : :
446 : 0 : if (td_control.socket != -1) {
447 : 0 : close(td_control.socket);
448 : 0 : td_control.socket = -1;
449 : : }
450 : 0 : }
451 : :
452 : : static void
453 : 0 : tapdisk_control_release_connection(struct tapdisk_ctl_conn *conn)
454 : : {
455 : 0 : if (conn->in.event_id) {
456 : 0 : tapdisk_server_unregister_event(conn->in.event_id);
457 : 0 : conn->in.event_id = -1;
458 : : }
459 : :
460 : : tapdisk_ctl_conn_release(conn);
461 : 0 : }
462 : :
463 : : static void
464 : 0 : tapdisk_control_close_connection(struct tapdisk_ctl_conn *conn)
465 : : {
466 : 0 : tapdisk_control_release_connection(conn);
467 : :
468 : 0 : if (tapdisk_ctl_conn_connected(conn))
469 : : /* NB. best effort for write/close sequences. */
470 : 0 : tapdisk_ctl_conn_send_buf(conn);
471 : :
472 : 0 : tapdisk_ctl_conn_close(conn);
473 : 0 : }
474 : :
475 : :
476 : : static int
477 : 0 : tapdisk_control_read_message(int fd, tapdisk_message_t *message, int timeout)
478 : : {
479 : 0 : const int len = sizeof(tapdisk_message_t);
480 : : fd_set readfds;
481 : 0 : int ret, offset, err = 0;
482 : : struct timeval tv, *t;
483 : :
484 : 0 : t = NULL;
485 : 0 : offset = 0;
486 : :
487 : 0 : if (timeout) {
488 : 0 : tv.tv_sec = timeout;
489 : 0 : tv.tv_usec = 0;
490 : 0 : t = &tv;
491 : : }
492 : :
493 : : memset(message, 0, sizeof(tapdisk_message_t));
494 : :
495 : 0 : while (offset < len) {
496 : 0 : FD_ZERO(&readfds);
497 : 0 : FD_SET(fd, &readfds);
498 : :
499 : 0 : eintr_retry(ret, select(fd + 1, &readfds, NULL, NULL, t))
500 : 0 : if (ret == -1)
501 : : break;
502 : 0 : else if (FD_ISSET(fd, &readfds)) {
503 : 0 : eintr_retry(ret, read(fd, message + offset, len - offset))
504 : 0 : if (ret <= 0)
505 : : break;
506 : 0 : offset += ret;
507 : : } else
508 : : break;
509 : : }
510 : :
511 : 0 : if (ret < 0)
512 : 0 : err = -errno;
513 : 0 : else if (offset != len)
514 : 0 : err = -EIO;
515 : 0 : if (err)
516 : 0 : ERR(err, "failure reading message at offset %d/%d\n",
517 : : offset, len);
518 : :
519 : :
520 : 0 : return err;
521 : : }
522 : :
523 : : static void
524 : 0 : tapdisk_control_write_message(struct tapdisk_ctl_conn *conn,
525 : : tapdisk_message_t *message)
526 : : {
527 : 0 : size_t size = sizeof(*message), count;
528 : :
529 : 0 : if (conn->info && conn->info->flags & TAPDISK_MSG_VERBOSE)
530 : 0 : DBG("sending '%s' message (uuid = %u)\n",
531 : : tapdisk_message_name(message->type), message->cookie);
532 : :
533 : 0 : count = tapdisk_ctl_conn_write(conn, message, size);
534 : 0 : WARN_ON(count != size);
535 : 0 : }
536 : :
537 : : static int
538 : : tapdisk_control_validate_request(tapdisk_message_t *request)
539 : : {
540 : 0 : if (strnlen(request->u.params.path,
541 : : TAPDISK_MESSAGE_MAX_PATH_LENGTH) >=
542 : : TAPDISK_MESSAGE_MAX_PATH_LENGTH)
543 : : return EINVAL;
544 : :
545 : : return 0;
546 : : }
547 : :
548 : : static int
549 : 0 : tapdisk_control_list(struct tapdisk_ctl_conn *conn,
550 : : tapdisk_message_t *request, tapdisk_message_t * const response)
551 : : {
552 : : td_vbd_t *vbd;
553 : 0 : struct list_head *head;
554 : : int count;
555 : :
556 : 0 : ASSERT(conn);
557 : 0 : ASSERT(request);
558 : 0 : ASSERT(response);
559 : :
560 : 0 : response->type = TAPDISK_MESSAGE_LIST_RSP;
561 : 0 : response->cookie = request->cookie;
562 : :
563 : 0 : head = tapdisk_server_get_all_vbds();
564 : :
565 : 0 : count = 0;
566 : 0 : list_for_each_entry(vbd, head, next)
567 : 0 : count++;
568 : :
569 : 0 : list_for_each_entry(vbd, head, next) {
570 : 0 : response->u.list.count = count--;
571 : 0 : response->u.list.minor = vbd->uuid;
572 : 0 : response->u.list.state = vbd->state;
573 : 0 : response->u.list.path[0] = 0;
574 : :
575 : 0 : if (vbd->name)
576 : 0 : safe_strncpy(response->u.list.path, vbd->name,
577 : : sizeof(response->u.list.path));
578 : :
579 : 0 : tapdisk_control_write_message(conn, response);
580 : : }
581 : :
582 : 0 : response->u.list.count = count;
583 : 0 : response->u.list.minor = -1;
584 : 0 : response->u.list.path[0] = 0;
585 : :
586 : 0 : return 0;
587 : : }
588 : :
589 : : static int
590 : 0 : tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn,
591 : : tapdisk_message_t *request, tapdisk_message_t * const response)
592 : : {
593 : 0 : ASSERT(response);
594 : :
595 : 0 : response->type = TAPDISK_MESSAGE_PID_RSP;
596 : 0 : response->cookie = request->cookie;
597 : 0 : response->u.tapdisk_pid = getpid();
598 : :
599 : 0 : return 0;
600 : : }
601 : :
602 : : static int
603 : 0 : tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn,
604 : : tapdisk_message_t *request, tapdisk_message_t * const response)
605 : : {
606 : 0 : char *devname = NULL;
607 : : td_vbd_t *vbd;
608 : 0 : int minor, err = 0;
609 : :
610 : 0 : ASSERT(conn);
611 : 0 : ASSERT(request);
612 : 0 : ASSERT(response);
613 : :
614 : : /*
615 : : * TODO: check for max vbds per process
616 : : */
617 : :
618 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
619 : 0 : if (vbd) {
620 : : err = -EEXIST;
621 : : goto out;
622 : : }
623 : :
624 : 0 : minor = request->cookie;
625 : : if (minor < 0) {
626 : : err = -EINVAL;
627 : : goto out;
628 : : }
629 : :
630 : 0 : vbd = tapdisk_vbd_create(minor);
631 : 0 : if (!vbd) {
632 : : err = -ENOMEM;
633 : : goto out;
634 : : }
635 : :
636 : : /* Lock the marker file to prevent freeing in use*/
637 : 0 : err = tapdisk_vbd_lock(vbd);
638 : 0 : if (err) {
639 : 0 : ERR(err, "Failed to lock VBD marker file for %d\n", minor);
640 : : goto fail_vbd;
641 : : }
642 : :
643 : 0 : tapdisk_server_add_vbd(vbd);
644 : :
645 : : out:
646 : : if (devname)
647 : : free(devname);
648 : :
649 : 0 : if (!err) {
650 : 0 : response->type = TAPDISK_MESSAGE_ATTACH_RSP;
651 : 0 : response->cookie = request->cookie;
652 : : }
653 : :
654 : 0 : return err;
655 : :
656 : : fail_vbd:
657 : 0 : free(vbd);
658 : 0 : goto out;
659 : : }
660 : :
661 : : static int
662 : 0 : tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn,
663 : : tapdisk_message_t *request, tapdisk_message_t * const response)
664 : : {
665 : : td_vbd_t *vbd;
666 : 0 : int err = 0;
667 : :
668 : 0 : ASSERT(conn);
669 : 0 : ASSERT(request);
670 : 0 : ASSERT(response);
671 : :
672 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
673 : 0 : if (!vbd) {
674 : : err = -ENODEV;
675 : : goto out;
676 : : }
677 : :
678 : 0 : if (vbd->name) {
679 : : err = -EBUSY;
680 : : goto out;
681 : : }
682 : :
683 : : /* Unlock marker file */
684 : 0 : tapdisk_vbd_unlock(vbd);
685 : :
686 : 0 : if (list_empty(&vbd->images)) {
687 : 0 : tapdisk_server_remove_vbd(vbd);
688 : 0 : tapdisk_vbd_free(vbd);
689 : : }
690 : :
691 : : out:
692 : 0 : if (!err) {
693 : 0 : response->type = TAPDISK_MESSAGE_DETACH_RSP;
694 : 0 : response->cookie = request->cookie;
695 : : }
696 : :
697 : 0 : return err;
698 : : }
699 : :
700 : : static int
701 : 0 : tapdisk_control_open_image(struct tapdisk_ctl_conn *conn,
702 : : tapdisk_message_t *request, tapdisk_message_t * const response)
703 : : {
704 : : int err, ret;
705 : : td_vbd_t *vbd;
706 : : td_flag_t flags;
707 : :
708 : 0 : ASSERT(conn);
709 : 0 : ASSERT(request);
710 : 0 : ASSERT(response);
711 : :
712 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
713 : 0 : if (!vbd) {
714 : : err = -EINVAL;
715 : : goto out;
716 : : }
717 : :
718 : 0 : if (vbd->name) {
719 : : err = -EALREADY;
720 : : goto out;
721 : : }
722 : :
723 : 0 : flags = 0;
724 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_RDONLY)
725 : 0 : flags |= TD_OPEN_RDONLY;
726 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_NO_O_DIRECT)
727 : 0 : flags |= TD_OPEN_NO_O_DIRECT;
728 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SHARED)
729 : 0 : flags |= TD_OPEN_SHAREABLE;
730 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_CACHE)
731 : 0 : flags |= TD_OPEN_ADD_CACHE;
732 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_VHD_INDEX)
733 : 0 : flags |= TD_OPEN_VHD_INDEX;
734 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LOG) {
735 : 0 : char *logpath = malloc(TAPDISK_MESSAGE_MAX_PATH_LENGTH + 1);
736 : 0 : if (!logpath) {
737 : : err = -ENOMEM;
738 : : goto out;
739 : : }
740 : 0 : ret = read(conn->fd, logpath, TAPDISK_MESSAGE_MAX_PATH_LENGTH);
741 : 0 : if (ret < 0) {
742 : 0 : err = -EIO;
743 : 0 : free(logpath);
744 : 0 : goto out;
745 : : }
746 : 0 : *(logpath + TAPDISK_MESSAGE_MAX_PATH_LENGTH) = '\0';
747 : 0 : vbd->logpath = logpath;
748 : 0 : flags |= TD_OPEN_ADD_LOG;
749 : : }
750 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LCACHE)
751 : 0 : flags |= TD_OPEN_LOCAL_CACHE;
752 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_REUSE_PRT)
753 : 0 : flags |= TD_OPEN_REUSE_PARENT;
754 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_STANDBY)
755 : 0 : flags |= TD_OPEN_STANDBY;
756 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) {
757 : 0 : char *name = strdup(request->u.params.secondary);
758 : 0 : if (!name) {
759 : 0 : err = -errno;
760 : 0 : goto out;
761 : : }
762 : 0 : vbd->secondary_name = name;
763 : 0 : flags |= TD_OPEN_SECONDARY;
764 : : }
765 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_OPEN_ENCRYPTED) {
766 : : uint8_t key_size;
767 : : uint8_t *encryption_key;
768 : : ssize_t ret;
769 : :
770 : : DPRINTF("Reading encryption key for VHD\n");
771 : :
772 : 0 : ret = read(conn->fd, &key_size, sizeof(key_size));
773 : 0 : if (ret != sizeof(key_size)) {
774 : : err = -EIO;
775 : 0 : goto out;
776 : : }
777 : :
778 : 0 : if (key_size == 0 || key_size > 128) {
779 : 0 : EPRINTF("Invalid encryption key size %d\n", key_size * 8);
780 : : err = -EINVAL;
781 : : goto out;
782 : : }
783 : :
784 : 0 : DPRINTF("Encryption key for VHD is %d bits\n", key_size * 8);
785 : 0 : encryption_key = malloc(key_size);
786 : 0 : if (!encryption_key) {
787 : : err = -ENOMEM;
788 : : goto out;
789 : : }
790 : 0 : ret = read(conn->fd, encryption_key, key_size);
791 : 0 : if (ret != key_size) {
792 : 0 : err = -EIO;
793 : 0 : free(encryption_key);
794 : 0 : goto out;
795 : : }
796 : : DPRINTF("Read encryption key for VHD\n");
797 : 0 : vbd->encryption.key_size = key_size;
798 : 0 : vbd->encryption.encryption_key = encryption_key;
799 : : }
800 : :
801 : 0 : err = tapdisk_vbd_open_vdi(vbd, request->u.params.path, flags,
802 : 0 : request->u.params.prt_devnum);
803 : 0 : if (err)
804 : : goto out;
805 : :
806 : 0 : err = tapdisk_vbd_get_disk_info(vbd, &vbd->disk_info);
807 : 0 : if (err) {
808 : 0 : EPRINTF("VBD %d failed to get disk info: %s\n", vbd->uuid,
809 : : strerror(-err));
810 : : goto fail_close;
811 : : }
812 : :
813 : 0 : if (request->u.params.req_timeout > 0) {
814 : 0 : vbd->req_timeout = request->u.params.req_timeout;
815 : 0 : DPRINTF("Set request timeout to %d s\n", vbd->req_timeout);
816 : : }
817 : :
818 : : /*
819 : : * For now, let's do this automatically on all 'open' calls. In the
820 : : * future, we'll probably want a separate call to start the NBD server
821 : : */
822 : 0 : err = tapdisk_vbd_start_nbdservers(vbd);
823 : 0 : if (err) {
824 : : EPRINTF("failed to start NBD server: %d\n", err);
825 : : goto fail_close;
826 : : }
827 : :
828 : : err = 0;
829 : :
830 : : out:
831 : 0 : if (!err) {
832 : 0 : response->u.image.sectors = vbd->disk_info.size;
833 : 0 : response->u.image.sector_size = vbd->disk_info.sector_size;
834 : 0 : response->u.image.info = vbd->disk_info.info;
835 : 0 : response->type = TAPDISK_MESSAGE_OPEN_RSP;
836 : : }
837 : 0 : return err;
838 : :
839 : : fail_close:
840 : 0 : tapdisk_vbd_close_vdi(vbd);
841 : :
842 : 0 : if (vbd->name) {
843 : 0 : free(vbd->name);
844 : 0 : vbd->name = NULL;
845 : : }
846 : :
847 : : goto out;
848 : : }
849 : :
850 : : static int
851 : 0 : tapdisk_control_close_image(struct tapdisk_ctl_conn *conn,
852 : : tapdisk_message_t *request, tapdisk_message_t * const response)
853 : : {
854 : : td_vbd_t *vbd;
855 : 0 : int err = 0;
856 : : struct td_xenblkif *blkif, *_blkif;
857 : :
858 : 0 : ASSERT(conn);
859 : 0 : ASSERT(request);
860 : 0 : ASSERT(response);
861 : :
862 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
863 : 0 : if (!vbd) {
864 : 0 : EPRINTF("VBD %d does not exist", request->cookie);
865 : 0 : err = -ENODEV;
866 : 0 : goto out;
867 : : }
868 : :
869 : 0 : if (td_flag_test(vbd->state, TD_VBD_PAUSED))
870 : 0 : EPRINTF("closing paused VBD %d", request->cookie);
871 : :
872 : 0 : if (!list_empty(&vbd->failed_requests))
873 : 0 : EPRINTF("closing VBD %d with failed requests\n", request->cookie);
874 : :
875 : 0 : if (vbd->nbdserver)
876 : 0 : tapdisk_nbdserver_pause(vbd->nbdserver, true);
877 : 0 : if (vbd->nbdserver_new)
878 : 0 : tapdisk_nbdserver_pause(vbd->nbdserver_new, true);
879 : :
880 : 0 : err = 0;
881 : 0 : list_for_each_entry_safe(blkif, _blkif, &vbd->rings, entry) {
882 : 0 : DPRINTF("implicitly disconnecting ring %p domid=%d, devid=%d\n",
883 : : blkif, blkif->domid, blkif->devid);
884 : :
885 : 0 : err = tapdisk_xenblkif_disconnect(blkif->domid, blkif->devid);
886 : 0 : if (unlikely(err)) {
887 : 0 : EPRINTF("failed to disconnect ring %p: %s\n",
888 : : blkif, strerror(-err));
889 : : break;
890 : : }
891 : : }
892 : :
893 : 0 : if (unlikely(err))
894 : : goto out;
895 : :
896 : 0 : if(request->type != TAPDISK_MESSAGE_FORCE_SHUTDOWN) {
897 : :
898 : : /*
899 : : * Wait for requests against dead rings to complete, otherwise, if we
900 : : * proceed with tearing down the VBD, we will free memory that will later
901 : : * be accessed by these requests, and this will lead to a crash.
902 : : */
903 : 0 : while (unlikely(tapdisk_vbd_contains_dead_rings(vbd)))
904 : 0 : tapdisk_server_iterate();
905 : : }
906 : : else {
907 : : DPRINTF("Ignoring dead rings in forced shutdown mode\n");
908 : : }
909 : :
910 : 0 : if (err)
911 : 0 : ERR(err, "failure closing image\n");
912 : :
913 : 0 : if (err == -ENOTTY) {
914 : :
915 : 0 : while (!list_empty(&vbd->pending_requests))
916 : 0 : tapdisk_server_iterate();
917 : :
918 : : err = 0;
919 : : }
920 : :
921 : 0 : if (err)
922 : : goto out;
923 : :
924 : 0 : if (vbd->nbdserver) {
925 : 0 : tapdisk_nbdserver_free(vbd->nbdserver);
926 : 0 : vbd->nbdserver = NULL;
927 : : }
928 : 0 : if (vbd->nbdserver_new) {
929 : 0 : tapdisk_nbdserver_free(vbd->nbdserver_new);
930 : 0 : vbd->nbdserver_new = NULL;
931 : : }
932 : :
933 : 0 : tapdisk_vbd_close_vdi(vbd);
934 : :
935 : : /*
936 : : * NB: vbd->name free should probably belong into close_vdi, but the
937 : : * current blktap1 reopen-stuff likely depends on a lifetime extended
938 : : * until shutdown
939 : : */
940 : 0 : free(vbd->name);
941 : 0 : vbd->name = NULL;
942 : :
943 : : out:
944 : 0 : response->cookie = request->cookie;
945 : 0 : if (!err)
946 : 0 : response->type = TAPDISK_MESSAGE_CLOSE_RSP;
947 : 0 : return err;
948 : : }
949 : :
950 : : static int
951 : 0 : tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn,
952 : : tapdisk_message_t *request, tapdisk_message_t * const response)
953 : : {
954 : 0 : struct timeval now, next = { 0, 0 }, interval = { 0, 10000 };
955 : 0 : int err = 0;
956 : : td_vbd_t *vbd;
957 : :
958 : 0 : ASSERT(conn);
959 : 0 : ASSERT(request);
960 : 0 : ASSERT(response);
961 : :
962 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
963 : 0 : if (!vbd) {
964 : : /* TODO log error */
965 : : err = -ENODEV;
966 : : goto out;
967 : : }
968 : :
969 : 0 : INFO("pause requested\n");
970 : : do {
971 : 0 : gettimeofday(&now, NULL);
972 : 0 : if (TV_AFTER(now, next)) {
973 : 0 : err = tapdisk_vbd_pause(vbd);
974 : :
975 : 0 : if (!err || err != -EAGAIN)
976 : : break;
977 : :
978 : : /*
979 : : * Squash the logging so that we don't fill the logs
980 : : * partition
981 : : */
982 : 0 : tapdisk_vbd_squash_pause_logging(true);
983 : 0 : TV_ADD(now, interval, next);
984 : : }
985 : :
986 : 0 : tapdisk_server_iterate();
987 : :
988 : 0 : } while (conn->fd >= 0);
989 : 0 : tapdisk_vbd_squash_pause_logging(false);
990 : :
991 : : out:
992 : 0 : response->cookie = request->cookie;
993 : 0 : if (!err)
994 : 0 : response->type = TAPDISK_MESSAGE_PAUSE_RSP;
995 : 0 : return err;
996 : : }
997 : :
998 : : static int
999 : 0 : tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn,
1000 : : tapdisk_message_t *request, tapdisk_message_t * const response)
1001 : : {
1002 : : int err, ret;
1003 : : td_vbd_t *vbd;
1004 : 0 : const char *desc = NULL;
1005 : :
1006 : 0 : ASSERT(conn);
1007 : 0 : ASSERT(request);
1008 : 0 : ASSERT(response);
1009 : :
1010 : : /* TODO validate secondary */
1011 : :
1012 : 0 : INFO("resuming VBD %d, flags=0x%08x, secondary=%p\n", request->cookie,
1013 : : request->u.resume.flags, request->u.resume.secondary);
1014 : :
1015 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
1016 : 0 : if (!vbd) {
1017 : : /* TODO log error */
1018 : : err = -ENODEV;
1019 : : goto out;
1020 : : }
1021 : :
1022 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) {
1023 : 0 : char *name = strdup(request->u.params.secondary);
1024 : 0 : if (!name) {
1025 : 0 : err = -errno;
1026 : 0 : goto out;
1027 : : }
1028 : 0 : INFO("resuming VBD %d with secondary '%s'\n", request->cookie, name);
1029 : 0 : vbd->secondary_name = name;
1030 : 0 : vbd->flags |= TD_OPEN_SECONDARY;
1031 : :
1032 : : /* TODO If an error occurs below we're not undoing this. */
1033 : : }
1034 : :
1035 : 0 : if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LOG) {
1036 : 0 : char *logpath = malloc(TAPDISK_MESSAGE_MAX_PATH_LENGTH + 1);
1037 : 0 : if (!logpath) {
1038 : : err = -ENOMEM;
1039 : : goto out;
1040 : : }
1041 : 0 : ret = read(conn->fd, logpath, TAPDISK_MESSAGE_MAX_PATH_LENGTH);
1042 : 0 : if (ret < 0) {
1043 : 0 : err = -EIO;
1044 : 0 : free(logpath);
1045 : 0 : goto out;
1046 : : }
1047 : 0 : *(logpath + TAPDISK_MESSAGE_MAX_PATH_LENGTH) = '\0';
1048 : 0 : vbd->logpath = logpath;
1049 : 0 : vbd->flags |= TD_OPEN_ADD_LOG;
1050 : : } else {
1051 : 0 : if (vbd->logpath) {
1052 : 0 : free (vbd->logpath);
1053 : 0 : vbd->logpath = NULL;
1054 : : }
1055 : 0 : vbd->flags &= ~TD_OPEN_ADD_LOG;
1056 : : }
1057 : :
1058 : 0 : if (request->u.params.path[0])
1059 : 0 : desc = request->u.params.path;
1060 : :
1061 : 0 : err = tapdisk_vbd_resume(vbd, desc);
1062 : : out:
1063 : 0 : response->cookie = request->cookie;
1064 : 0 : if (!err)
1065 : 0 : response->type = TAPDISK_MESSAGE_RESUME_RSP;
1066 : 0 : return err;
1067 : : }
1068 : :
1069 : : static int
1070 : 0 : tapdisk_control_stats(struct tapdisk_ctl_conn *conn,
1071 : : tapdisk_message_t *request, tapdisk_message_t * const response)
1072 : : {
1073 : 0 : td_stats_t _st, *st = &_st;
1074 : : td_vbd_t *vbd;
1075 : : ssize_t rv;
1076 : : void *buf;
1077 : : int new_size;
1078 : :
1079 : 0 : ASSERT(conn);
1080 : 0 : ASSERT(request);
1081 : 0 : ASSERT(response);
1082 : :
1083 : 0 : buf = malloc(TD_CTL_SEND_BUFSZ);
1084 : 0 : if (!buf) {
1085 : : rv = -ENOMEM;
1086 : 0 : return rv;
1087 : : }
1088 : :
1089 : : tapdisk_stats_init(st, buf, TD_CTL_SEND_BUFSZ);
1090 : :
1091 : 0 : if (request->cookie != (uint16_t)-1) {
1092 : :
1093 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
1094 : 0 : if (!vbd) {
1095 : : rv = -ENODEV;
1096 : : goto out;
1097 : : }
1098 : :
1099 : 0 : tapdisk_vbd_stats(vbd, st);
1100 : :
1101 : : } else {
1102 : 0 : struct list_head *list = tapdisk_server_get_all_vbds();
1103 : :
1104 : 0 : tapdisk_stats_enter(st, '[');
1105 : :
1106 : 0 : list_for_each_entry(vbd, list, next)
1107 : 0 : tapdisk_vbd_stats(vbd, st);
1108 : :
1109 : 0 : tapdisk_stats_leave(st, ']');
1110 : : }
1111 : :
1112 : 0 : rv = tapdisk_stats_length(st);
1113 : 0 : if (rv < 0) {
1114 : : goto out;
1115 : : }
1116 : :
1117 : 0 : if (rv > conn->out.bufsz - sizeof(*response)) {
1118 : 0 : ASSERT(conn->out.prod == conn->out.buf);
1119 : 0 : ASSERT(conn->out.cons == conn->out.buf);
1120 : 0 : new_size = rv + sizeof(*response);
1121 : 0 : buf = realloc(conn->out.buf, new_size);
1122 : 0 : if (!buf) {
1123 : : rv = -ENOMEM;
1124 : : goto out;
1125 : : }
1126 : 0 : conn->out.buf = buf;
1127 : 0 : conn->out.bufsz = new_size;
1128 : 0 : conn->out.prod = buf;
1129 : 0 : conn->out.cons = buf;
1130 : : }
1131 : 0 : if (rv > 0) {
1132 : 0 : memcpy(conn->out.buf + sizeof(*response), st->buf, rv);
1133 : : }
1134 : : out:
1135 : 0 : free(st->buf);
1136 : 0 : if (rv > 0) {
1137 : 0 : response->type = TAPDISK_MESSAGE_STATS_RSP;
1138 : 0 : response->u.info.length = rv;
1139 : 0 : tapdisk_control_write_message(conn, response);
1140 : 0 : conn->out.prod += rv;
1141 : 0 : return 0;
1142 : : } else
1143 : 0 : return rv;
1144 : : }
1145 : :
1146 : : /**
1147 : : * Message handler executed for TAPDISK_MESSAGE_XENBLKIF_CONNECT.
1148 : : *
1149 : : * This is the entry point for connecting the tapdisk to the shared ring. It
1150 : : * also sets up the necessary structures/descriptors (TODO explain).
1151 : : */
1152 : : static int
1153 : 0 : tapdisk_control_xenblkif_connect(
1154 : : struct tapdisk_ctl_conn *conn __attribute__((unused)),
1155 : : tapdisk_message_t *request, tapdisk_message_t * const response)
1156 : : {
1157 : : /*
1158 : : * Get the block interface parameters (domain ID, device ID, etc.).
1159 : : */
1160 : : tapdisk_message_blkif_t *blkif;
1161 : :
1162 : 0 : td_vbd_t *vbd = NULL;
1163 : : const char *pool;
1164 : : size_t len;
1165 : : int err;
1166 : 0 : int minor = -1;
1167 : :
1168 : 0 : ASSERT(conn);
1169 : 0 : ASSERT(request);
1170 : 0 : ASSERT(response);
1171 : :
1172 : 0 : minor = request->cookie;
1173 : :
1174 : 0 : vbd = tapdisk_server_get_vbd(minor);
1175 : 0 : if (!vbd) {
1176 : : err = -ENODEV;
1177 : : goto out;
1178 : : }
1179 : :
1180 : 0 : blkif = &request->u.blkif;
1181 : 0 : len = strnlen(blkif->pool, sizeof(blkif->pool));
1182 : 0 : if (!len)
1183 : : pool = NULL;
1184 : 0 : else if (len >= sizeof(blkif->pool)) {
1185 : : err = -EINVAL;
1186 : : goto out;
1187 : : } else
1188 : : pool = blkif->pool;
1189 : :
1190 : 0 : DPRINTF("connecting VBD %d domid=%d, devid=%d, pool %s, evt %d, poll duration %d, poll idle threshold %d\n",
1191 : : vbd->uuid, blkif->domid, blkif->devid, pool, blkif->port, blkif->poll_duration, blkif->poll_idle_threshold);
1192 : :
1193 : 0 : err = tapdisk_xenblkif_connect(blkif->domid, blkif->devid, blkif->gref,
1194 : 0 : blkif->order, blkif->port, blkif->proto, blkif->poll_duration, blkif->poll_idle_threshold, pool, vbd);
1195 : :
1196 : : out:
1197 : 0 : response->cookie = request->cookie;
1198 : 0 : if (!err)
1199 : 0 : response->type = TAPDISK_MESSAGE_XENBLKIF_CONNECT_RSP;
1200 : : else
1201 : 0 : EPRINTF("VBD %d failed to connect to the shared ring: %s\n",
1202 : : minor, strerror(-err));
1203 : :
1204 : 0 : return err;
1205 : : }
1206 : :
1207 : : static int
1208 : 0 : tapdisk_control_xenblkif_disconnect(
1209 : : struct tapdisk_ctl_conn *conn __attribute__((unused)),
1210 : : tapdisk_message_t * request, tapdisk_message_t * const response)
1211 : : {
1212 : : tapdisk_message_blkif_t *blkif_msg;
1213 : : int err;
1214 : :
1215 : 0 : ASSERT(request);
1216 : 0 : ASSERT(response);
1217 : :
1218 : 0 : blkif_msg = &request->u.blkif;
1219 : :
1220 : 0 : ASSERT(blkif_msg);
1221 : :
1222 : 0 : DPRINTF("disconnecting domid=%d, devid=%d\n", blkif_msg->domid,
1223 : : blkif_msg->devid);
1224 : :
1225 : 0 : err = tapdisk_xenblkif_disconnect(blkif_msg->domid, blkif_msg->devid);
1226 : 0 : if (!err)
1227 : 0 : response->type = TAPDISK_MESSAGE_XENBLKIF_DISCONNECT_RSP;
1228 : : else
1229 : 0 : EPRINTF("failed to disconnect domid=%d, devid=%d from the "
1230 : : "ring: %s\n", blkif_msg->domid, blkif_msg->devid,
1231 : : strerror(-err));
1232 : 0 : return err;
1233 : : }
1234 : :
1235 : : static int
1236 : 0 : tapdisk_control_disk_info(
1237 : : struct tapdisk_ctl_conn *conn __attribute__((unused)),
1238 : : tapdisk_message_t * request, tapdisk_message_t * const response)
1239 : : {
1240 : : tapdisk_message_image_t *image;
1241 : 0 : int err = 0;
1242 : 0 : td_vbd_t *vbd = NULL;
1243 : :
1244 : 0 : ASSERT(conn);
1245 : 0 : ASSERT(request);
1246 : 0 : ASSERT(response);
1247 : :
1248 : 0 : image = &response->u.image;
1249 : :
1250 : 0 : vbd = tapdisk_server_get_vbd(request->cookie);
1251 : 0 : if (!vbd) {
1252 : : err = -ENODEV;
1253 : : goto out;
1254 : : }
1255 : :
1256 : 0 : DPRINTF("VBD %d got disk info: sectors=%llu sector size=%ld, info=%d\n",
1257 : : vbd->uuid, (unsigned long long)vbd->disk_info.size,
1258 : : vbd->disk_info.sector_size, vbd->disk_info.info);
1259 : : out:
1260 : 0 : if (!err) {
1261 : 0 : response->type = TAPDISK_MESSAGE_DISK_INFO_RSP;
1262 : 0 : image->sectors = vbd->disk_info.size;
1263 : 0 : image->sector_size = vbd->disk_info.sector_size;
1264 : 0 : image->info = vbd->disk_info.info;
1265 : : }
1266 : 0 : return err;
1267 : : }
1268 : :
1269 : :
1270 : : struct tapdisk_control_info message_infos[] = {
1271 : : [TAPDISK_MESSAGE_PID] = {
1272 : : .handler = tapdisk_control_get_pid,
1273 : : .flags = TAPDISK_MSG_REENTER,
1274 : : },
1275 : : [TAPDISK_MESSAGE_LIST] = {
1276 : : .handler = tapdisk_control_list,
1277 : : .flags = TAPDISK_MSG_REENTER,
1278 : : },
1279 : : [TAPDISK_MESSAGE_ATTACH] = {
1280 : : .handler = tapdisk_control_attach_vbd,
1281 : : .flags = TAPDISK_MSG_VERBOSE,
1282 : : },
1283 : : [TAPDISK_MESSAGE_DETACH] = {
1284 : : .handler = tapdisk_control_detach_vbd,
1285 : : .flags = TAPDISK_MSG_VERBOSE,
1286 : : },
1287 : : [TAPDISK_MESSAGE_XENBLKIF_CONNECT] = {
1288 : : .handler = tapdisk_control_xenblkif_connect,
1289 : : .flags = TAPDISK_MSG_VERBOSE
1290 : : },
1291 : : [TAPDISK_MESSAGE_XENBLKIF_DISCONNECT] = {
1292 : : .handler = tapdisk_control_xenblkif_disconnect,
1293 : : .flags = TAPDISK_MSG_VERBOSE
1294 : : },
1295 : : [TAPDISK_MESSAGE_DISK_INFO] = {
1296 : : .handler = tapdisk_control_disk_info,
1297 : : .flags = TAPDISK_MSG_VERBOSE
1298 : : },
1299 : : [TAPDISK_MESSAGE_OPEN] = {
1300 : : .handler = tapdisk_control_open_image,
1301 : : .flags = TAPDISK_MSG_VERBOSE,
1302 : : },
1303 : : [TAPDISK_MESSAGE_PAUSE] = {
1304 : : .handler = tapdisk_control_pause_vbd,
1305 : : .flags = TAPDISK_MSG_VERBOSE,
1306 : : },
1307 : : [TAPDISK_MESSAGE_RESUME] = {
1308 : : .handler = tapdisk_control_resume_vbd,
1309 : : .flags = TAPDISK_MSG_VERBOSE,
1310 : : },
1311 : : [TAPDISK_MESSAGE_CLOSE] = {
1312 : : .handler = tapdisk_control_close_image,
1313 : : .flags = TAPDISK_MSG_VERBOSE,
1314 : : },
1315 : : [TAPDISK_MESSAGE_FORCE_SHUTDOWN] = {
1316 : : .handler = tapdisk_control_close_image,
1317 : : .flags = TAPDISK_MSG_VERBOSE,
1318 : : },
1319 : : [TAPDISK_MESSAGE_STATS] = {
1320 : : .handler = tapdisk_control_stats,
1321 : : .flags = TAPDISK_MSG_REENTER,
1322 : : },
1323 : : [TAPDISK_MESSAGE_EXIT] = {
1324 : : .handler = NULL,
1325 : : .flags = 0
1326 : : }
1327 : : };
1328 : :
1329 : : static int
1330 : 0 : tapdisk_control_receive_request(struct tapdisk_ctl_conn *conn)
1331 : : {
1332 : : int err;
1333 : :
1334 : 0 : ASSERT(conn);
1335 : :
1336 : 0 : err = tapdisk_control_read_message(conn->fd, &conn->request, 2);
1337 : 0 : if (err)
1338 : : goto close;
1339 : :
1340 : 0 : err = tapdisk_control_validate_request(&conn->request);
1341 : 0 : if (err)
1342 : : goto invalid;
1343 : :
1344 : 0 : if (conn->request.type > TAPDISK_MESSAGE_EXIT)
1345 : : goto invalid;
1346 : :
1347 : 0 : conn->info = &message_infos[conn->request.type];
1348 : :
1349 : 0 : if (!conn->info->handler)
1350 : : goto invalid;
1351 : :
1352 : 0 : if (conn->info->flags & TAPDISK_MSG_VERBOSE)
1353 : 0 : DBG("received '%s' message (uuid = %u)\n",
1354 : : tapdisk_message_name(conn->request.type), conn->request.cookie);
1355 : :
1356 : 0 : if (conn->in.busy)
1357 : : goto busy;
1358 : :
1359 : : return 0;
1360 : :
1361 : : error:
1362 : 0 : memset(&conn->response, 0, sizeof(conn->response));
1363 : 0 : conn->response.type = TAPDISK_MESSAGE_ERROR;
1364 : 0 : conn->response.u.response.error = -err;
1365 : 0 : tapdisk_control_write_message(conn, &conn->response);
1366 : :
1367 : : close:
1368 : 0 : tapdisk_control_close_connection(conn);
1369 : 0 : return err;
1370 : :
1371 : : busy:
1372 : 0 : err = -EBUSY;
1373 : 0 : ERR(err, "rejecting message '%s' while busy\n",
1374 : : tapdisk_message_name(conn->request.type));
1375 : 0 : goto error;
1376 : :
1377 : : invalid:
1378 : 0 : err = -EINVAL;
1379 : 0 : ERR(err, "rejecting unsupported message '%s'\n",
1380 : : tapdisk_message_name(conn->request.type));
1381 : 0 : goto error;
1382 : : }
1383 : :
1384 : : static void
1385 : 0 : tapdisk_control_process_request(event_id_t event_id,
1386 : : char mode __attribute__((unused)), void *private)
1387 : : {
1388 : : int err, excl;
1389 : 0 : struct tapdisk_ctl_conn *conn = private;
1390 : :
1391 : 0 : ASSERT(conn);
1392 : 0 : ASSERT(event_id == conn->event_id);
1393 : :
1394 : 0 : if (conn->event_id)
1395 : 0 : tapdisk_server_unregister_event(conn->event_id);
1396 : :
1397 : 0 : excl = !(conn->info->flags & TAPDISK_MSG_REENTER);
1398 : :
1399 : 0 : if (excl)
1400 : 0 : td_control.busy = 1;
1401 : 0 : conn->in.busy = 1;
1402 : :
1403 : 0 : memset(&conn->response, 0, sizeof(conn->response));
1404 : 0 : conn->response.cookie = conn->request.cookie;
1405 : :
1406 : 0 : err = conn->info->handler(conn, &conn->request, &conn->response);
1407 : 0 : if (err) {
1408 : 0 : conn->response.type = TAPDISK_MESSAGE_ERROR;
1409 : 0 : conn->response.u.response.error = -err;
1410 : : }
1411 : 0 : if (err || conn->response.type != TAPDISK_MESSAGE_STATS_RSP)
1412 : 0 : tapdisk_control_write_message(conn, &conn->response);
1413 : :
1414 : 0 : conn->in.busy = 0;
1415 : 0 : if (excl) {
1416 : 0 : if (!list_empty(&td_control.pending)) {
1417 : 0 : struct tapdisk_ctl_conn *cur = list_first_entry(
1418 : : &td_control.pending, struct tapdisk_ctl_conn, entry);
1419 : 0 : list_del(&cur->entry);
1420 : 0 : err = tapdisk_server_event_set_timeout(cur->event_id, TV_ZERO);
1421 : 0 : ASSERT(!err);
1422 : 0 : tapdisk_server_mask_event(cur->event_id, 0);
1423 : : }
1424 : 0 : td_control.busy = 0;
1425 : : }
1426 : :
1427 : 0 : tapdisk_control_release_connection(conn);
1428 : 0 : }
1429 : :
1430 : :
1431 : : static void
1432 : 0 : tapdisk_control_handle_request(event_id_t id, char mode, void *private)
1433 : : {
1434 : : int err;
1435 : 0 : struct tapdisk_ctl_conn *conn = private;
1436 : :
1437 : 0 : conn->info = NULL;
1438 : :
1439 : 0 : err = tapdisk_control_receive_request(conn);
1440 : 0 : if (err)
1441 : : return;
1442 : :
1443 : 0 : conn->event_id = 0;
1444 : :
1445 : 0 : if (!(conn->info->flags & TAPDISK_MSG_REENTER) && td_control.busy) {
1446 : :
1447 : 0 : err = tapdisk_server_register_event(SCHEDULER_POLL_TIMEOUT, -1,
1448 : 0 : TV_INF, tapdisk_control_process_request,
1449 : : conn);
1450 : :
1451 : 0 : if (err == -1) {
1452 : 0 : memset(&conn->response, 0, sizeof(conn->response));
1453 : 0 : conn->response.type = TAPDISK_MESSAGE_ERROR;
1454 : 0 : conn->response.u.response.error = EINVAL;
1455 : 0 : tapdisk_control_write_message(conn, &conn->response);
1456 : 0 : tapdisk_control_close_connection(conn);
1457 : 0 : ERR(err, "failed to register request process event\n");
1458 : 0 : return;
1459 : : }
1460 : :
1461 : 0 : tapdisk_server_unregister_event(conn->in.event_id);
1462 : 0 : conn->in.event_id = 0;
1463 : :
1464 : 0 : conn->event_id = err;
1465 : 0 : tapdisk_server_mask_event(conn->event_id, 1);
1466 : 0 : list_add_tail(&conn->entry, &td_control.pending);
1467 : :
1468 : : return;
1469 : : }
1470 : :
1471 : 0 : tapdisk_control_process_request(0, 0, conn);
1472 : : }
1473 : :
1474 : : static void
1475 : 0 : tapdisk_control_accept(event_id_t id, char mode, void *private)
1476 : : {
1477 : : int err, fd;
1478 : : struct tapdisk_ctl_conn *conn;
1479 : :
1480 : 0 : fd = accept(td_control.socket, NULL, NULL);
1481 : 0 : if (fd == -1) {
1482 : 0 : ERR(-errno, "failed to accept new control connection: %d\n", errno);
1483 : 0 : return;
1484 : : }
1485 : :
1486 : 0 : conn = tapdisk_ctl_conn_open(fd);
1487 : 0 : if (!conn) {
1488 : 0 : close(fd);
1489 : 0 : ERR(-ENOMEM, "failed to allocate new control connection\n");
1490 : 0 : return;
1491 : : }
1492 : :
1493 : 0 : err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
1494 : 0 : conn->fd, TV_SECS(TD_CTL_RECV_TIMEOUT),
1495 : : tapdisk_control_handle_request,
1496 : : conn);
1497 : 0 : if (err == -1) {
1498 : 0 : tapdisk_control_close_connection(conn);
1499 : 0 : ERR(err, "failed to register new control event\n");
1500 : 0 : return;
1501 : : }
1502 : :
1503 : 0 : conn->in.event_id = err;
1504 : : }
1505 : :
1506 : : static int
1507 : 0 : tapdisk_control_mkdir(const char *dir)
1508 : : {
1509 : : int err;
1510 : : char *ptr, *name, *start;
1511 : :
1512 : 0 : err = access(dir, W_OK | R_OK);
1513 : 0 : if (!err)
1514 : : return 0;
1515 : :
1516 : 0 : name = strdup(dir);
1517 : 0 : if (!name)
1518 : : return -ENOMEM;
1519 : :
1520 : : start = name;
1521 : :
1522 : : for (;;) {
1523 : 0 : ptr = strchr(start + 1, '/');
1524 : 0 : if (ptr)
1525 : 0 : *ptr = '\0';
1526 : :
1527 : 0 : err = mkdir(name, 0755);
1528 : 0 : if (err) {
1529 : 0 : if (errno != EEXIST) {
1530 : 0 : err = -errno;
1531 : : EPRINTF("failed to create directory %s: %d\n",
1532 : : name, err);
1533 : : break;
1534 : : }
1535 : : else
1536 : : err = 0;
1537 : : }
1538 : :
1539 : 0 : if (!ptr)
1540 : : break;
1541 : : else {
1542 : 0 : *ptr = '/';
1543 : 0 : start = ptr + 1;
1544 : : }
1545 : 0 : }
1546 : :
1547 : 0 : free(name);
1548 : 0 : return err;
1549 : : }
1550 : :
1551 : : static int
1552 : 0 : tapdisk_control_create_socket(char **socket_path)
1553 : : {
1554 : : struct sockaddr_un saddr;
1555 : : int err;
1556 : :
1557 : 0 : err = tapdisk_control_mkdir(BLKTAP2_CONTROL_DIR);
1558 : 0 : if (err) {
1559 : : EPRINTF("failed to create directory %s: %d\n",
1560 : : BLKTAP2_CONTROL_DIR, err);
1561 : 0 : return err;
1562 : : }
1563 : :
1564 : 0 : err = asprintf(&td_control.path, "%s/%s%d",
1565 : : BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, getpid());
1566 : 0 : if (err == -1) {
1567 : 0 : td_control.path = NULL;
1568 : 0 : err = (errno ? : ENOMEM);
1569 : 0 : goto fail;
1570 : : }
1571 : :
1572 : 0 : if (unlink(td_control.path) && errno != ENOENT) {
1573 : 0 : err = errno;
1574 : 0 : EPRINTF("failed to unlink %s: %d\n", td_control.path, errno);
1575 : : goto fail;
1576 : : }
1577 : :
1578 : 0 : td_control.socket = socket(AF_UNIX, SOCK_STREAM, 0);
1579 : 0 : if (td_control.socket == -1) {
1580 : 0 : err = errno;
1581 : : EPRINTF("failed to create control socket: %d\n", err);
1582 : : goto fail;
1583 : : }
1584 : :
1585 : : memset(&saddr, 0, sizeof(saddr));
1586 : :
1587 : 0 : if (unlikely(strlen(td_control.path) >= sizeof(saddr.sun_path))) {
1588 : 0 : EPRINTF("socket name too long: %s\n", td_control.path);
1589 : : err = ENAMETOOLONG;
1590 : : goto fail;
1591 : : }
1592 : 0 : safe_strncpy(saddr.sun_path, td_control.path, sizeof(saddr.sun_path));
1593 : :
1594 : 0 : saddr.sun_family = AF_UNIX;
1595 : :
1596 : 0 : err = bind(td_control.socket,
1597 : : (const struct sockaddr *)&saddr, sizeof(saddr));
1598 : 0 : if (err == -1) {
1599 : 0 : err = errno;
1600 : : EPRINTF("failed to bind to %s: %d\n", saddr.sun_path, err);
1601 : : goto fail;
1602 : : }
1603 : :
1604 : 0 : err = listen(td_control.socket, TD_CTL_SOCK_BACKLOG);
1605 : 0 : if (err == -1) {
1606 : 0 : err = errno;
1607 : : EPRINTF("failed to listen: %d\n", err);
1608 : : goto fail;
1609 : : }
1610 : :
1611 : 0 : err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
1612 : 0 : td_control.socket, TV_ZERO,
1613 : : tapdisk_control_accept, NULL);
1614 : 0 : if (err < 0) {
1615 : : EPRINTF("failed to add watch: %d\n", err);
1616 : : goto fail;
1617 : : }
1618 : :
1619 : 0 : td_control.event_id = err;
1620 : 0 : *socket_path = td_control.path;
1621 : :
1622 : 0 : return 0;
1623 : :
1624 : : fail:
1625 : 0 : tapdisk_control_close();
1626 : : return err;
1627 : : }
1628 : :
1629 : : int
1630 : 0 : tapdisk_control_open(char **path)
1631 : : {
1632 : 0 : tapdisk_control_initialize();
1633 : :
1634 : 0 : return tapdisk_control_create_socket(path);
1635 : : }
|