Statistics
| Branch: | Revision:

root / drivers / tapdisk-control.c @ abdb293f

History | View | Annotate | Download (25.6 kB)

1
/*
2
 * Copyright (c) 2008, XenSource Inc.
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *     * Redistributions of source code must retain the above copyright
8
 *       notice, this list of conditions and the following disclaimer.
9
 *     * Redistributions in binary form must reproduce the above copyright
10
 *       notice, this list of conditions and the following disclaimer in the
11
 *       documentation and/or other materials provided with the distribution.
12
 *     * Neither the name of XenSource Inc. nor the names of its contributors
13
 *       may be used to endorse or promote products derived from this software
14
 *       without specific prior written permission.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28

    
29
#ifdef HAVE_CONFIG_H
30
#include "config.h"
31
#endif
32

    
33
#include <stdio.h>
34
#include <errno.h>
35
#include <fcntl.h>
36
#include <unistd.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <signal.h>
40
#include <sys/un.h>
41
#include <sys/stat.h>
42
#include <sys/types.h>
43
#include <sys/ioctl.h>
44
#include <sys/socket.h>
45
#include <sys/mman.h>
46

    
47
#include "list.h"
48
#include "tapdisk.h"
49
#include "tapdisk-vbd.h"
50
#include "tapdisk-blktap.h"
51
#include "tapdisk-utils.h"
52
#include "tapdisk-server.h"
53
#include "tapdisk-message.h"
54
#include "tapdisk-disktype.h"
55
#include "tapdisk-stats.h"
56
#include "tapdisk-control.h"
57

    
58
#define TD_CTL_MAX_CONNECTIONS  10
59
#define TD_CTL_SOCK_BACKLOG     32
60
#define TD_CTL_RECV_TIMEOUT     10
61
#define TD_CTL_SEND_TIMEOUT     10
62
#define TD_CTL_SEND_BUFSZ       ((size_t)4096)
63

    
64
#define DBG(_f, _a...)             tlog_syslog(LOG_DEBUG, _f, ##_a)
65
#define ERR(err, _f, _a...)        tlog_error(err, _f, ##_a)
66

    
67
#define ASSERT(_p)                                                        \
68
        if (!(_p)) {                                                        \
69
                EPRINTF("%s:%d: FAILED ASSERTION: '%s'\n",                \
70
                        __FILE__, __LINE__, #_p);                        \
71
                td_panic();                                                \
72
        }
73

    
74
#define WARN_ON(_p)                                                        \
75
        if (_p) {                                                        \
76
                EPRINTF("%s:%d: WARNING: '%s'\n",                        \
77
                        __FILE__, __LINE__, #_p);                        \
78
        }
79

    
80
struct tapdisk_ctl_conn {
81
        int             fd;
82

    
83
        struct {
84
                void           *buf;
85
                size_t          bufsz;
86
                int             event_id;
87
                int             done;
88

    
89
                void           *prod;
90
                void           *cons;
91
        } out;
92

    
93
        struct {
94
                int             event_id;
95
                int             busy;
96
        } in;
97

    
98
        struct tapdisk_control_info *info;
99
};
100

    
101
#define TAPDISK_MSG_REENTER    (1<<0) /* non-blocking, idempotent */
102
#define TAPDISK_MSG_VERBOSE    (1<<1) /* tell syslog about it */
103

    
104
struct tapdisk_control_info {
105
        void (*handler)(struct tapdisk_ctl_conn *, tapdisk_message_t *);
106
        int flags;
107
};
108

    
109
struct tapdisk_control {
110
        char              *path;
111
        int                uuid;
112
        int                socket;
113
        int                event_id;
114
        int                busy;
115

    
116
        int                n_conn;
117
        struct tapdisk_ctl_conn __conn[TD_CTL_MAX_CONNECTIONS];
118
        struct tapdisk_ctl_conn *conn[TD_CTL_MAX_CONNECTIONS];
119
};
120

    
121
static struct tapdisk_control td_control;
122

    
123
static inline size_t
124
page_align(size_t size)
125
{
126
        size_t page_size = sysconf(_SC_PAGE_SIZE);
127
        return (size + page_size - 1) & ~(page_size - 1);
128
}
129

    
130
static void
131
tapdisk_ctl_conn_uninit(struct tapdisk_ctl_conn *conn)
132
{
133
        if (conn->out.buf) {
134
                munmap(conn->out.buf, conn->out.bufsz);
135
                conn->out.buf = NULL;
136
        }
137
}
138

    
139
static int
140
tapdisk_ctl_conn_init(struct tapdisk_ctl_conn *conn, size_t bufsz)
141
{
142
        int prot, flags, err;
143

    
144
        memset(conn, 0, sizeof(*conn));
145
        conn->out.event_id = -1;
146
        conn->in.event_id  = -1;
147

    
148
        prot  = PROT_READ|PROT_WRITE;
149
        flags = MAP_ANONYMOUS|MAP_PRIVATE;
150

    
151
        conn->out.buf = mmap(NULL, bufsz, prot, flags, -1, 0);
152
        if (conn->out.buf == MAP_FAILED) {
153
                conn->out.buf = NULL;
154
                err = -ENOMEM;
155
                goto fail;
156
        }
157
        conn->out.bufsz = page_align(bufsz);
158

    
159
        return 0;
160

    
161
fail:
162
        tapdisk_ctl_conn_uninit(conn);
163
        return err;
164
}
165

    
166
static int
167
tapdisk_ctl_conn_connected(struct tapdisk_ctl_conn *conn)
168
{
169
        return conn->fd >= 1;
170
}
171

    
172
static void
173
tapdisk_ctl_conn_free(struct tapdisk_ctl_conn *conn)
174
{
175
        struct tapdisk_ctl_conn *prev, *next;
176
        int i;
177

    
178
        i = --td_control.n_conn;
179
        /* NB. bubble the freed connection off the active list. */
180
        prev = conn;
181
        do {
182
                ASSERT(i >= 0);
183
                next = td_control.conn[i];
184
                td_control.conn[i] = prev;
185
                prev = next;
186
                i--;
187
        } while (next != conn);
188
}
189

    
190
static void
191
tapdisk_ctl_conn_close(struct tapdisk_ctl_conn *conn)
192
{
193
        if (conn->out.event_id >= 0) {
194
                tapdisk_server_unregister_event(conn->out.event_id);
195
                conn->out.event_id = -1;
196
        }
197

    
198
        if (conn->fd >= 0) {
199
                close(conn->fd);
200
                conn->fd = -1;
201

    
202
                tapdisk_ctl_conn_free(conn);
203
                tapdisk_server_mask_event(td_control.event_id, 0);
204
        }
205
}
206

    
207
static void
208
tapdisk_ctl_conn_mask_out(struct tapdisk_ctl_conn *conn)
209
{
210
        tapdisk_server_mask_event(conn->out.event_id, 1);
211
}
212

    
213
static void
214
tapdisk_ctl_conn_unmask_out(struct tapdisk_ctl_conn *conn)
215
{
216
        tapdisk_server_mask_event(conn->out.event_id, 0);
217
}
218

    
219
static ssize_t
220
tapdisk_ctl_conn_send_buf(struct tapdisk_ctl_conn *conn)
221
{
222
        ssize_t size;
223

    
224
        size = conn->out.prod - conn->out.cons;
225
        if (!size)
226
                return 0;
227

    
228
        size = send(conn->fd, conn->out.cons, size, MSG_DONTWAIT);
229
        if (size < 0)
230
                return -errno;
231

    
232
        conn->out.cons += size;
233

    
234
        return size;
235
}
236

    
237
static void
238
tapdisk_ctl_conn_send_event(event_id_t id, char mode, void *private)
239
{
240
        struct tapdisk_ctl_conn *conn = private;
241
        ssize_t rv;
242

    
243
        do {
244
                rv = tapdisk_ctl_conn_send_buf(conn);
245
        } while (rv > 0);
246

    
247
        if (rv == -EAGAIN)
248
                return;
249

    
250
        if (rv < 0)
251
                ERR(rv, "failure sending message at offset %td/%td\n",
252
                    conn->out.cons - conn->out.buf,
253
                    conn->out.prod - conn->out.buf);
254

    
255
        if (rv || conn->out.done || mode & SCHEDULER_POLL_TIMEOUT)
256
                tapdisk_ctl_conn_close(conn);
257
        else
258
                tapdisk_ctl_conn_mask_out(conn);
259
}
260

    
261
/*
262
 * NB. the control interface is still not properly integrated into the
263
 * server, therefore neither the scheduler. After the last close, the
264
 * server will exit but we still have a pending close response in the
265
 * output buffer.
266
 */
267
static void
268
tapdisk_ctl_conn_drain(struct tapdisk_ctl_conn *conn)
269
{
270
        struct timeval tv = { .tv_sec = TD_CTL_SEND_TIMEOUT,
271
                              .tv_usec = 0 };
272
        fd_set wfds;
273
        int n, mode;
274

    
275
        ASSERT(conn->out.done);
276
        ASSERT(conn->fd >= 0);
277

    
278
        while (tapdisk_ctl_conn_connected(conn)) {
279
                FD_ZERO(&wfds);
280
                FD_SET(conn->fd, &wfds);
281

    
282
                n = select(conn->fd + 1, NULL, &wfds, NULL, &tv);
283
                if (n < 0)
284
                        break;
285

    
286
                if (n)
287
                        mode = SCHEDULER_POLL_WRITE_FD;
288
                else
289
                        mode = SCHEDULER_POLL_TIMEOUT;
290

    
291
                tapdisk_ctl_conn_send_event(conn->out.event_id, mode, conn);
292
        }
293
}
294

    
295

    
296
struct tapdisk_ctl_conn *
297
tapdisk_ctl_conn_open(int fd)
298
{
299
        struct tapdisk_ctl_conn *conn;
300

    
301
        if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
302
                return NULL;
303

    
304
        conn = td_control.conn[td_control.n_conn++];
305

    
306
        conn->out.event_id =
307
                tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD,
308
                                              fd, TD_CTL_SEND_TIMEOUT,
309
                                              tapdisk_ctl_conn_send_event,
310
                                              conn);
311
        if (conn->out.event_id < 0)
312
                return NULL;
313

    
314
        conn->fd       = fd;
315
        conn->out.prod = conn->out.buf;
316
        conn->out.cons = conn->out.buf;
317

    
318
        tapdisk_ctl_conn_mask_out(conn);
319

    
320
        if (td_control.n_conn >= TD_CTL_MAX_CONNECTIONS)
321
                tapdisk_server_mask_event(td_control.event_id, 1);
322

    
323
        return conn;
324
}
325

    
326
static size_t
327
tapdisk_ctl_conn_write(struct tapdisk_ctl_conn *conn, void *buf, size_t size)
328
{
329
        size_t rest;
330

    
331
        rest = conn->out.buf + conn->out.bufsz - conn->out.prod;
332
        if (rest < size)
333
                size = rest;
334
        if (!size)
335
                return 0;
336

    
337
        memcpy(conn->out.prod, buf, size);
338
        conn->out.prod += size;
339
        tapdisk_ctl_conn_unmask_out(conn);
340

    
341
        return size;
342
}
343

    
344
static void
345
tapdisk_ctl_conn_release(struct tapdisk_ctl_conn *conn)
346
{
347
        conn->out.done = 1;
348

    
349
        if (conn->out.prod == conn->out.cons)
350
                tapdisk_ctl_conn_close(conn);
351
}
352

    
353
static void
354
tapdisk_control_initialize(void)
355
{
356
        struct tapdisk_ctl_conn *conn;
357
        int i;
358

    
359
        td_control.socket   = -1;
360
        td_control.event_id = -1;
361

    
362
        signal(SIGPIPE, SIG_IGN);
363

    
364
        for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) {
365
                conn = &td_control.__conn[i];
366
                tapdisk_ctl_conn_init(conn, TD_CTL_SEND_BUFSZ);
367
                td_control.conn[i] = conn;
368
        }
369

    
370
        td_control.n_conn = 0;
371

    
372
        DPRINTF("tapdisk-control: init, %d x %zuk buffers\n",
373
                TD_CTL_MAX_CONNECTIONS, TD_CTL_SEND_BUFSZ >> 10);
374
}
375

    
376
void
377
tapdisk_control_close(void)
378
{
379
        struct tapdisk_ctl_conn *conn;
380
        int i;
381

    
382
        DPRINTF("tapdisk-control: draining %d connections\n",
383
                td_control.n_conn);
384

    
385
        while (td_control.n_conn) {
386
                conn = td_control.conn[td_control.n_conn-1];
387
                tapdisk_ctl_conn_drain(conn);
388
        }
389

    
390
        for (i = 0; i < TD_CTL_MAX_CONNECTIONS; i++) {
391
                conn = &td_control.__conn[i];
392
                tapdisk_ctl_conn_uninit(conn);
393
        }
394

    
395
        DPRINTF("tapdisk-control: done\n");
396

    
397
        if (td_control.path) {
398
                unlink(td_control.path);
399
                free(td_control.path);
400
                td_control.path = NULL;
401
        }
402

    
403
        if (td_control.socket != -1) {
404
                close(td_control.socket);
405
                td_control.socket = -1;
406
        }
407
}
408

    
409
static void
410
tapdisk_control_release_connection(struct tapdisk_ctl_conn *conn)
411
{
412
        if (conn->in.event_id) {
413
                tapdisk_server_unregister_event(conn->in.event_id);
414
                conn->in.event_id = -1;
415
        }
416

    
417
        tapdisk_ctl_conn_release(conn);
418
}
419

    
420
static void
421
tapdisk_control_close_connection(struct tapdisk_ctl_conn *conn)
422
{
423
        tapdisk_control_release_connection(conn);
424

    
425
        if (tapdisk_ctl_conn_connected(conn))
426
                /* NB. best effort for write/close sequences. */
427
                tapdisk_ctl_conn_send_buf(conn);
428

    
429
        tapdisk_ctl_conn_close(conn);
430
}
431

    
432

    
433
static int
434
tapdisk_control_read_message(int fd, tapdisk_message_t *message, int timeout)
435
{
436
        const int len = sizeof(tapdisk_message_t);
437
        fd_set readfds;
438
        int ret, offset, err = 0;
439
        struct timeval tv, *t;
440

    
441
        t      = NULL;
442
        offset = 0;
443

    
444
        if (timeout) {
445
                tv.tv_sec  = timeout;
446
                tv.tv_usec = 0;
447
                t = &tv;
448
        }
449

    
450
        memset(message, 0, sizeof(tapdisk_message_t));
451

    
452
        while (offset < len) {
453
                FD_ZERO(&readfds);
454
                FD_SET(fd, &readfds);
455

    
456
                ret = select(fd + 1, &readfds, NULL, NULL, t);
457
                if (ret == -1)
458
                        break;
459
                else if (FD_ISSET(fd, &readfds)) {
460
                        ret = read(fd, message + offset, len - offset);
461
                        if (ret <= 0)
462
                                break;
463
                        offset += ret;
464
                } else
465
                        break;
466
        }
467

    
468
        if (ret < 0)
469
                err = -errno;
470
        else if (offset != len)
471
                err = -EIO;
472
        if (err)
473
                ERR(err, "failure reading message at offset %d/%d\n",
474
                    offset, len);
475

    
476

    
477
        return err;
478
}
479

    
480
static void
481
tapdisk_control_write_message(struct tapdisk_ctl_conn *conn,
482
                              tapdisk_message_t *message)
483
{
484
        size_t size = sizeof(*message), count;
485

    
486
        if (conn->info->flags & TAPDISK_MSG_VERBOSE)
487
                DBG("sending '%s' message (uuid = %u)\n",
488
                    tapdisk_message_name(message->type), message->cookie);
489

    
490
        count = tapdisk_ctl_conn_write(conn, message, size);
491
        WARN_ON(count != size);
492
}
493

    
494
static int
495
tapdisk_control_validate_request(tapdisk_message_t *request)
496
{
497
        if (strnlen(request->u.params.path,
498
                    TAPDISK_MESSAGE_MAX_PATH_LENGTH) >=
499
            TAPDISK_MESSAGE_MAX_PATH_LENGTH)
500
                return EINVAL;
501

    
502
        return 0;
503
}
504

    
505
#if 0
506
static void
507
tapdisk_control_list_minors(struct tapdisk_ctl_conn *conn,
508
                            tapdisk_message_t *request)
509
{
510
        int i;
511
        td_vbd_t *vbd;
512
        struct list_head *head;
513
        tapdisk_message_t response;
514

515
        i = 0;
516
        memset(&response, 0, sizeof(response));
517
        response.type = TAPDISK_MESSAGE_LIST_MINORS_RSP;
518
        response.cookie = request->cookie;
519

520
        head = tapdisk_server_get_all_vbds();
521

522
        list_for_each_entry(vbd, head, next) {
523
                td_blktap_t *tap = vbd->tap;
524
                if (!tap)
525
                        continue;
526

527
                response.u.minors.list[i++] = tap->minor;
528
                if (i >= TAPDISK_MESSAGE_MAX_MINORS) {
529
                        response.type = TAPDISK_MESSAGE_ERROR;
530
                        response.u.response.error = ERANGE;
531
                        break;
532
                }
533
        }
534

535
        response.u.minors.count = i;
536
        tapdisk_ctl_conn_write(conn, &response, 2);
537
}
538
#endif
539

    
540
static void
541
tapdisk_control_list(struct tapdisk_ctl_conn *conn, tapdisk_message_t *request)
542
{
543
        td_vbd_t *vbd;
544
        struct list_head *head;
545
        tapdisk_message_t response;
546
        int count;
547

    
548
        memset(&response, 0, sizeof(response));
549
        response.type = TAPDISK_MESSAGE_LIST_RSP;
550
        response.cookie = request->cookie;
551

    
552
        head = tapdisk_server_get_all_vbds();
553

    
554
        count = 0;
555
        list_for_each_entry(vbd, head, next)
556
                count++;
557

    
558
        list_for_each_entry(vbd, head, next) {
559
                response.u.list.count   = count--;
560
                response.u.list.minor   = vbd->tap ? vbd->tap->minor : -1;
561
                response.u.list.state   = vbd->state;
562
                response.u.list.path[0] = 0;
563

    
564
                if (vbd->name)
565
                        strncpy(response.u.list.path, vbd->name,
566
                                sizeof(response.u.list.path));
567

    
568
                tapdisk_control_write_message(conn, &response);
569
        }
570

    
571
        response.u.list.count   = count;
572
        response.u.list.minor   = -1;
573
        response.u.list.path[0] = 0;
574

    
575
        tapdisk_control_write_message(conn, &response);
576
}
577

    
578
static void
579
tapdisk_control_get_pid(struct tapdisk_ctl_conn *conn,
580
                        tapdisk_message_t *request)
581
{
582
        tapdisk_message_t response;
583

    
584
        memset(&response, 0, sizeof(response));
585
        response.type = TAPDISK_MESSAGE_PID_RSP;
586
        response.cookie = request->cookie;
587
        response.u.tapdisk_pid = getpid();
588

    
589
        tapdisk_control_write_message(conn, &response);
590
}
591

    
592
static void
593
tapdisk_control_attach_vbd(struct tapdisk_ctl_conn *conn,
594
                           tapdisk_message_t *request)
595
{
596
        tapdisk_message_t response;
597
        char *devname = NULL;
598
        td_vbd_t *vbd;
599
        int minor, err;
600

    
601
        /*
602
         * TODO: check for max vbds per process
603
         */
604

    
605
        vbd = tapdisk_server_get_vbd(request->cookie);
606
        if (vbd) {
607
                err = -EEXIST;
608
                goto out;
609
        }
610

    
611
        minor = request->cookie;
612
        if (minor < 0) {
613
                err = -EINVAL;
614
                goto out;
615
        }
616

    
617
        vbd = tapdisk_vbd_create(minor);
618
        if (!vbd) {
619
                err = -ENOMEM;
620
                goto out;
621
        }
622

    
623
        err = asprintf(&devname, BLKTAP2_RING_DEVICE"%d", minor);
624
        if (err == -1) {
625
                devname = NULL;
626
                err = -ENOMEM;
627
                goto fail_vbd;
628
        }
629

    
630
        err = tapdisk_vbd_attach(vbd, devname, minor);
631
        if (err) {
632
                ERR(err, "failure attaching to %s", devname);
633
                goto fail_vbd;
634
        }
635

    
636
        tapdisk_server_add_vbd(vbd);
637

    
638
out:
639
        if (devname)
640
                free(devname);
641

    
642
        memset(&response, 0, sizeof(response));
643
        response.type = TAPDISK_MESSAGE_ATTACH_RSP;
644
        response.cookie = request->cookie;
645
        response.u.response.error = -err;
646

    
647
        tapdisk_control_write_message(conn, &response);
648

    
649
        return;
650

    
651
fail_vbd:
652
        tapdisk_vbd_detach(vbd);
653
        free(vbd);
654
        goto out;
655
}
656

    
657

    
658
static void
659
tapdisk_control_detach_vbd(struct tapdisk_ctl_conn *conn,
660
                           tapdisk_message_t *request)
661
{
662
        tapdisk_message_t response;
663
        td_vbd_t *vbd;
664
        int err;
665

    
666
        vbd = tapdisk_server_get_vbd(request->cookie);
667
        if (!vbd) {
668
                err = -EINVAL;
669
                goto out;
670
        }
671

    
672
        if (vbd->name) {
673
                err = -EBUSY;
674
                goto out;
675
        }
676

    
677
        tapdisk_vbd_detach(vbd);
678

    
679
        if (list_empty(&vbd->images)) {
680
                tapdisk_server_remove_vbd(vbd);
681
                free(vbd);
682
        }
683

    
684
        err = 0;
685
out:
686
        memset(&response, 0, sizeof(response));
687
        response.type = TAPDISK_MESSAGE_DETACH_RSP;
688
        response.cookie = request->cookie;
689
        response.u.response.error = -err;
690

    
691
        tapdisk_control_write_message(conn, &response);
692
}
693

    
694
static void
695
tapdisk_control_open_image(struct tapdisk_ctl_conn *conn,
696
                           tapdisk_message_t *request)
697
{
698
        int err;
699
        td_vbd_t *vbd;
700
        td_flag_t flags;
701
        tapdisk_message_t response;
702
        td_disk_info_t info;
703

    
704
        vbd = tapdisk_server_get_vbd(request->cookie);
705
        if (!vbd) {
706
                err = -EINVAL;
707
                goto out;
708
        }
709

    
710
        if (!vbd->tap) {
711
                err = -EINVAL;
712
                goto out;
713
        }
714

    
715
        if (vbd->name) {
716
                err = -EALREADY;
717
                goto out;
718
        }
719

    
720
        flags = 0;
721
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_RDONLY)
722
                flags |= TD_OPEN_RDONLY;
723
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SHARED)
724
                flags |= TD_OPEN_SHAREABLE;
725
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_CACHE)
726
                flags |= TD_OPEN_ADD_CACHE;
727
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_VHD_INDEX)
728
                flags |= TD_OPEN_VHD_INDEX;
729
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_LOG_DIRTY)
730
                flags |= TD_OPEN_LOG_DIRTY;
731
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_ADD_LCACHE)
732
                flags |= TD_OPEN_LOCAL_CACHE;
733
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_REUSE_PRT)
734
                flags |= TD_OPEN_REUSE_PARENT;
735
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_STANDBY)
736
                flags |= TD_OPEN_STANDBY;
737
        if (request->u.params.flags & TAPDISK_MESSAGE_FLAG_SECONDARY) {
738
                char *name = strdup(request->u.params.secondary);
739
                if (!name) {
740
                        err = -errno;
741
                        goto out;
742
                }
743
                vbd->secondary_name = name;
744
                flags |= TD_OPEN_SECONDARY;
745
        }
746

    
747
        err = tapdisk_vbd_open_vdi(vbd, request->u.params.path, flags,
748
                                   request->u.params.prt_devnum);
749
        if (err)
750
                goto out;
751

    
752
        err = tapdisk_vbd_get_disk_info(vbd, &info);
753
        if (err)
754
                goto fail_close;
755

    
756
        err = tapdisk_blktap_create_device(vbd->tap, &info,
757
                                           !!(flags & TD_OPEN_RDONLY));
758
        if (err && err != -EEXIST) {
759
                err = -errno;
760
                EPRINTF("create device failed: %d\n", err);
761
                goto fail_close;
762
        }
763

    
764
        err = 0;
765

    
766
out:
767
        memset(&response, 0, sizeof(response));
768
        response.cookie = request->cookie;
769

    
770
        if (err) {
771
                response.type                = TAPDISK_MESSAGE_ERROR;
772
                response.u.response.error    = -err;
773
        } else {
774
                response.u.image.sectors     = info.size;
775
                response.u.image.sector_size = info.sector_size;
776
                response.u.image.info        = info.info;
777
                response.type                = TAPDISK_MESSAGE_OPEN_RSP;
778
        }
779

    
780
        tapdisk_control_write_message(conn, &response);
781

    
782
        return;
783

    
784
fail_close:
785
        tapdisk_vbd_close_vdi(vbd);
786

    
787
        if (vbd->name) {
788
                free(vbd->name);
789
                vbd->name = NULL;
790
        }
791

    
792
        goto out;
793
}
794

    
795
static void
796
tapdisk_control_close_image(struct tapdisk_ctl_conn *conn,
797
                            tapdisk_message_t *request)
798
{
799
        tapdisk_message_t response;
800
        td_vbd_t *vbd;
801
        int err;
802

    
803
        vbd = tapdisk_server_get_vbd(request->cookie);
804
        if (!vbd) {
805
                err = -ENODEV;
806
                goto out;
807
        }
808

    
809
        do {
810
                err = tapdisk_blktap_remove_device(vbd->tap);
811

    
812
                if (!err || err != -EBUSY)
813
                        break;
814

    
815
                tapdisk_server_iterate();
816

    
817
        } while (conn->fd >= 0);
818

    
819
        if (err)
820
                ERR(err, "failure closing image\n");
821

    
822
        if (err == -ENOTTY) {
823

    
824
                while (!list_empty(&vbd->pending_requests))
825
                        tapdisk_server_iterate();
826

    
827
                err = 0;
828
        }
829

    
830
        if (err)
831
                goto out;
832

    
833
        tapdisk_vbd_close_vdi(vbd);
834

    
835
        /* NB. vbd->name free should probably belong into close_vdi,
836
           but the current blktap1 reopen-stuff likely depends on a
837
           lifetime extended until shutdown. */
838
        free(vbd->name);
839
        vbd->name = NULL;
840

    
841
        if (!vbd->tap) {
842
                tapdisk_server_remove_vbd(vbd);
843
                free(vbd);
844
        }
845

    
846
out:
847
        memset(&response, 0, sizeof(response));
848
        response.type = TAPDISK_MESSAGE_CLOSE_RSP;
849
        response.cookie = request->cookie;
850
        response.u.response.error = -err;
851

    
852
        tapdisk_control_write_message(conn, &response);
853
}
854

    
855
static void
856
tapdisk_control_pause_vbd(struct tapdisk_ctl_conn *conn,
857
                          tapdisk_message_t *request)
858
{
859
        int err;
860
        td_vbd_t *vbd;
861
        tapdisk_message_t response;
862

    
863
        memset(&response, 0, sizeof(response));
864

    
865
        response.type = TAPDISK_MESSAGE_PAUSE_RSP;
866

    
867
        vbd = tapdisk_server_get_vbd(request->cookie);
868
        if (!vbd) {
869
                err = -EINVAL;
870
                goto out;
871
        }
872

    
873
        do {
874
                err = tapdisk_vbd_pause(vbd);
875

    
876
                if (!err || err != -EAGAIN)
877
                        break;
878

    
879
                tapdisk_server_iterate();
880

    
881
        } while (conn->fd >= 0);
882

    
883
out:
884
        response.cookie = request->cookie;
885
        response.u.response.error = -err;
886
        tapdisk_control_write_message(conn, &response);
887
}
888

    
889
static void
890
tapdisk_control_resume_vbd(struct tapdisk_ctl_conn *conn,
891
                           tapdisk_message_t *request)
892
{
893
        int err;
894
        td_vbd_t *vbd;
895
        tapdisk_message_t response;
896
        const char *desc = NULL;
897

    
898
        memset(&response, 0, sizeof(response));
899

    
900
        response.type = TAPDISK_MESSAGE_RESUME_RSP;
901

    
902
        vbd = tapdisk_server_get_vbd(request->cookie);
903
        if (!vbd) {
904
                err = -EINVAL;
905
                goto out;
906
        }
907

    
908
        if (request->u.params.path[0])
909
                desc = request->u.params.path;
910

    
911
        err = tapdisk_vbd_resume(vbd, desc);
912
out:
913
        response.cookie = request->cookie;
914
        response.u.response.error = -err;
915
        tapdisk_control_write_message(conn, &response);
916
}
917

    
918
static void
919
tapdisk_control_stats(struct tapdisk_ctl_conn *conn,
920
                      tapdisk_message_t *request)
921
{
922
        tapdisk_message_t response;
923
        td_stats_t _st, *st = &_st;
924
        td_vbd_t *vbd;
925
        size_t rv;
926

    
927
        tapdisk_stats_init(st,
928
                           conn->out.buf + sizeof(response),
929
                           conn->out.bufsz - sizeof(response));
930

    
931
        if (request->cookie != (uint16_t)-1) {
932

    
933
                vbd = tapdisk_server_get_vbd(request->cookie);
934
                if (!vbd) {
935
                        rv = -ENODEV;
936
                        goto out;
937
                }
938

    
939
                tapdisk_vbd_stats(vbd, st);
940

    
941
        } else {
942
                struct list_head *list = tapdisk_server_get_all_vbds();
943

    
944
                tapdisk_stats_enter(st, '[');
945

    
946
                list_for_each_entry(vbd, list, next)
947
                        tapdisk_vbd_stats(vbd, st);
948

    
949
                tapdisk_stats_leave(st, ']');
950
        }
951

    
952
        rv = tapdisk_stats_length(st);
953
out:
954
        memset(&response, 0, sizeof(response));
955
        response.type = TAPDISK_MESSAGE_STATS_RSP;
956
        response.cookie = request->cookie;
957
        response.u.info.length = rv;
958

    
959
        tapdisk_control_write_message(conn, &response);
960
        if (rv > 0)
961
                conn->out.prod += rv;
962
}
963

    
964
struct tapdisk_control_info message_infos[] = {
965
        [TAPDISK_MESSAGE_PID] = {
966
                .handler = tapdisk_control_get_pid,
967
                .flags   = TAPDISK_MSG_REENTER,
968
        },
969
        [TAPDISK_MESSAGE_LIST] = {
970
                .handler = tapdisk_control_list,
971
                .flags   = TAPDISK_MSG_REENTER,
972
        },
973
        [TAPDISK_MESSAGE_ATTACH] = {
974
                .handler = tapdisk_control_attach_vbd,
975
                .flags   = TAPDISK_MSG_VERBOSE,
976
        },
977
        [TAPDISK_MESSAGE_DETACH] = {
978
                .handler = tapdisk_control_detach_vbd,
979
                .flags   = TAPDISK_MSG_VERBOSE,
980
        },
981
        [TAPDISK_MESSAGE_OPEN] = {
982
                .handler = tapdisk_control_open_image,
983
                .flags   = TAPDISK_MSG_VERBOSE,
984
        },
985
        [TAPDISK_MESSAGE_PAUSE] = {
986
                .handler = tapdisk_control_pause_vbd,
987
                .flags   = TAPDISK_MSG_VERBOSE,
988
        },
989
        [TAPDISK_MESSAGE_RESUME] = {
990
                .handler = tapdisk_control_resume_vbd,
991
                .flags   = TAPDISK_MSG_VERBOSE,
992
        },
993
        [TAPDISK_MESSAGE_CLOSE] = {
994
                .handler = tapdisk_control_close_image,
995
                .flags   = TAPDISK_MSG_VERBOSE,
996
        },
997
        [TAPDISK_MESSAGE_STATS] = {
998
                .handler = tapdisk_control_stats,
999
                .flags   = TAPDISK_MSG_REENTER,
1000
        },
1001
};
1002

    
1003

    
1004
static void
1005
tapdisk_control_handle_request(event_id_t id, char mode, void *private)
1006
{
1007
        int err, excl;
1008
        tapdisk_message_t message, response;
1009
        struct tapdisk_ctl_conn *conn = private;
1010
        struct tapdisk_control_info *info;
1011

    
1012
        err = tapdisk_control_read_message(conn->fd, &message, 2);
1013
        if (err)
1014
                goto close;
1015

    
1016
        if (conn->in.busy)
1017
                goto busy;
1018

    
1019
        err = tapdisk_control_validate_request(&message);
1020
        if (err)
1021
                goto invalid;
1022

    
1023
        if (message.type > TAPDISK_MESSAGE_EXIT)
1024
                goto invalid;
1025

    
1026
        info = &message_infos[message.type];
1027

    
1028
        if (!info->handler)
1029
                goto invalid;
1030

    
1031
        if (info->flags & TAPDISK_MSG_VERBOSE)
1032
                DBG("received '%s' message (uuid = %u)\n",
1033
                    tapdisk_message_name(message.type), message.cookie);
1034

    
1035
        excl = !(info->flags & TAPDISK_MSG_REENTER);
1036
        if (excl) {
1037
                if (td_control.busy)
1038
                        goto busy;
1039

    
1040
                td_control.busy = 1;
1041
        }
1042
        conn->in.busy = 1;
1043
        conn->info    = info;
1044

    
1045
        info->handler(conn, &message);
1046

    
1047
        conn->in.busy = 0;
1048
        if (excl)
1049
                td_control.busy = 0;
1050

    
1051
        tapdisk_control_release_connection(conn);
1052
        return;
1053

    
1054
error:
1055
        memset(&response, 0, sizeof(response));
1056
        response.type = TAPDISK_MESSAGE_ERROR;
1057
        response.u.response.error = (err ? -err : EINVAL);
1058
        tapdisk_control_write_message(conn, &response);
1059

    
1060
close:
1061
        tapdisk_control_close_connection(conn);
1062
        return;
1063

    
1064
busy:
1065
        err = -EBUSY;
1066
        ERR(err, "rejecting message '%s' while busy\n",
1067
            tapdisk_message_name(message.type));
1068
        goto error;
1069

    
1070
invalid:
1071
        err = -EINVAL;
1072
        ERR(err, "rejecting unsupported message '%s'\n",
1073
            tapdisk_message_name(message.type));
1074
        goto error;
1075
}
1076

    
1077
static void
1078
tapdisk_control_accept(event_id_t id, char mode, void *private)
1079
{
1080
        int err, fd;
1081
        struct tapdisk_ctl_conn *conn;
1082

    
1083
        fd = accept(td_control.socket, NULL, NULL);
1084
        if (fd == -1) {
1085
                ERR(-errno, "failed to accept new control connection: %d\n", errno);
1086
                return;
1087
        }
1088

    
1089
        conn = tapdisk_ctl_conn_open(fd);
1090
        if (!conn) {
1091
                close(fd);
1092
                ERR(-ENOMEM, "failed to allocate new control connection\n");
1093
                return;
1094
        }
1095

    
1096
        err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
1097
                                            conn->fd, TD_CTL_RECV_TIMEOUT,
1098
                                            tapdisk_control_handle_request,
1099
                                            conn);
1100
        if (err == -1) {
1101
                tapdisk_control_close_connection(conn);
1102
                ERR(err, "failed to register new control event\n");
1103
                return;
1104
        }
1105

    
1106
        conn->in.event_id = err;
1107
}
1108

    
1109
static int
1110
tapdisk_control_mkdir(const char *dir)
1111
{
1112
        int err;
1113
        char *ptr, *name, *start;
1114

    
1115
        err = access(dir, W_OK | R_OK);
1116
        if (!err)
1117
                return 0;
1118

    
1119
        name = strdup(dir);
1120
        if (!name)
1121
                return -ENOMEM;
1122

    
1123
        start = name;
1124

    
1125
        for (;;) {
1126
                ptr = strchr(start + 1, '/');
1127
                if (ptr)
1128
                        *ptr = '\0';
1129

    
1130
                err = mkdir(name, 0755);
1131
                if (err && errno != EEXIST) {
1132
                        err = -errno;
1133
                        EPRINTF("failed to create directory %s: %d\n",
1134
                                  name, err);
1135
                        break;
1136
                }
1137

    
1138
                if (!ptr)
1139
                        break;
1140
                else {
1141
                        *ptr = '/';
1142
                        start = ptr + 1;
1143
                }
1144
        }
1145

    
1146
        free(name);
1147
        return err;
1148
}
1149

    
1150
static int
1151
tapdisk_control_create_socket(char **socket_path)
1152
{
1153
        struct sockaddr_un saddr;
1154
        int err;
1155

    
1156
        err = tapdisk_control_mkdir(BLKTAP2_CONTROL_DIR);
1157
        if (err) {
1158
                EPRINTF("failed to create directory %s: %d\n",
1159
                        BLKTAP2_CONTROL_DIR, err);
1160
                return err;
1161
        }
1162

    
1163
        err = asprintf(&td_control.path, "%s/%s%d",
1164
                       BLKTAP2_CONTROL_DIR, BLKTAP2_CONTROL_SOCKET, getpid());
1165
        if (err == -1) {
1166
                td_control.path = NULL;
1167
                err = (errno ? : ENOMEM);
1168
                goto fail;
1169
        }
1170

    
1171
        if (unlink(td_control.path) && errno != ENOENT) {
1172
                err = errno;
1173
                EPRINTF("failed to unlink %s: %d\n", td_control.path, errno);
1174
                goto fail;
1175
        }
1176

    
1177
        td_control.socket = socket(AF_UNIX, SOCK_STREAM, 0);
1178
        if (td_control.socket == -1) {
1179
                err = errno;
1180
                EPRINTF("failed to create control socket: %d\n", err);
1181
                goto fail;
1182
        }
1183

    
1184
        memset(&saddr, 0, sizeof(saddr));
1185
        strncpy(saddr.sun_path, td_control.path, sizeof(saddr.sun_path));
1186
        saddr.sun_family = AF_UNIX;
1187

    
1188
        err = bind(td_control.socket,
1189
                   (const struct sockaddr *)&saddr, sizeof(saddr));
1190
        if (err == -1) {
1191
                err = errno;
1192
                EPRINTF("failed to bind to %s: %d\n", saddr.sun_path, err);
1193
                goto fail;
1194
        }
1195

    
1196
        err = listen(td_control.socket, TD_CTL_SOCK_BACKLOG);
1197
        if (err == -1) {
1198
                err = errno;
1199
                EPRINTF("failed to listen: %d\n", err);
1200
                goto fail;
1201
        }
1202

    
1203
        err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
1204
                                            td_control.socket, 0,
1205
                                            tapdisk_control_accept, NULL);
1206
        if (err < 0) {
1207
                EPRINTF("failed to add watch: %d\n", err);
1208
                goto fail;
1209
        }
1210

    
1211
        td_control.event_id = err;
1212
        *socket_path = td_control.path;
1213

    
1214
        return 0;
1215

    
1216
fail:
1217
        tapdisk_control_close();
1218
        return err;
1219
}
1220

    
1221
int
1222
tapdisk_control_open(char **path)
1223
{
1224
        tapdisk_control_initialize();
1225

    
1226
        return tapdisk_control_create_socket(path);
1227
}