1 /*
2  * vhost-user-scsi sample application
3  *
4  * Copyright (c) 2016 Nutanix Inc. All rights reserved.
5  *
6  * Author:
7  *  Felipe Franciosi <felipe@nutanix.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 only.
10  * See the COPYING file in the top-level directory.
11  */
12 
13 #include "qemu/osdep.h"
14 #include "contrib/libvhost-user/libvhost-user.h"
15 #include "hw/virtio/virtio-scsi.h"
16 #include "iscsi/iscsi.h"
17 
18 #include <glib.h>
19 
20 /* Small compat shim from glib 2.32 */
21 #ifndef G_SOURCE_CONTINUE
22 #define G_SOURCE_CONTINUE TRUE
23 #endif
24 #ifndef G_SOURCE_REMOVE
25 #define G_SOURCE_REMOVE FALSE
26 #endif
27 
28 /* #define VUS_DEBUG 1 */
29 
30 /** Log helpers **/
31 
32 #define PPRE                                                          \
33     struct timespec ts;                                               \
34     char   timebuf[64];                                               \
35     struct tm tm;                                                     \
36     (void)clock_gettime(CLOCK_REALTIME, &ts);                         \
37     (void)strftime(timebuf, 64, "%Y%m%d %T", gmtime_r(&ts.tv_sec, &tm))
38 
39 #define PEXT(lvl, msg, ...) do {                                      \
40     PPRE;                                                             \
41     fprintf(stderr, "%s.%06ld " lvl ": %s:%s():%d: " msg "\n",        \
42             timebuf, ts.tv_nsec / 1000,                               \
43             __FILE__, __func__, __LINE__, ## __VA_ARGS__);            \
44 } while (0)
45 
46 #define PNOR(lvl, msg, ...) do {                                      \
47     PPRE;                                                             \
48     fprintf(stderr, "%s.%06ld " lvl ": " msg "\n",                    \
49             timebuf, ts.tv_nsec / 1000, ## __VA_ARGS__);              \
50 } while (0)
51 
52 #ifdef VUS_DEBUG
53 #define PDBG(msg, ...) PEXT("DBG", msg, ## __VA_ARGS__)
54 #define PERR(msg, ...) PEXT("ERR", msg, ## __VA_ARGS__)
55 #define PLOG(msg, ...) PEXT("LOG", msg, ## __VA_ARGS__)
56 #else
57 #define PDBG(msg, ...) { }
58 #define PERR(msg, ...) PNOR("ERR", msg, ## __VA_ARGS__)
59 #define PLOG(msg, ...) PNOR("LOG", msg, ## __VA_ARGS__)
60 #endif
61 
62 /** vhost-user-scsi specific definitions **/
63 
64  /* Only 1 LUN and device supported today */
65 #define VUS_MAX_LUNS 1
66 #define VUS_MAX_DEVS 1
67 
68 #define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi"
69 
70 typedef struct iscsi_lun {
71     struct iscsi_context *iscsi_ctx;
72     int iscsi_lun;
73 } iscsi_lun_t;
74 
75 typedef struct vhost_scsi_dev {
76     VuDev vu_dev;
77     int server_sock;
78     GMainLoop *loop;
79     GTree *fdmap;   /* fd -> gsource context id */
80     iscsi_lun_t luns[VUS_MAX_LUNS];
81 } vhost_scsi_dev_t;
82 
83 static vhost_scsi_dev_t *vhost_scsi_devs[VUS_MAX_DEVS];
84 
85 /** glib event loop integration for libvhost-user and misc callbacks **/
86 
87 QEMU_BUILD_BUG_ON((int)G_IO_IN != (int)VU_WATCH_IN);
88 QEMU_BUILD_BUG_ON((int)G_IO_OUT != (int)VU_WATCH_OUT);
89 QEMU_BUILD_BUG_ON((int)G_IO_PRI != (int)VU_WATCH_PRI);
90 QEMU_BUILD_BUG_ON((int)G_IO_ERR != (int)VU_WATCH_ERR);
91 QEMU_BUILD_BUG_ON((int)G_IO_HUP != (int)VU_WATCH_HUP);
92 
93 typedef struct vus_gsrc {
94     GSource parent;
95     vhost_scsi_dev_t *vdev_scsi;
96     GPollFD gfd;
97     vu_watch_cb vu_cb;
98 } vus_gsrc_t;
99 
100 static gint vus_fdmap_compare(gconstpointer a, gconstpointer b)
101 {
102     return (b > a) - (b < a);
103 }
104 
105 static gboolean vus_gsrc_prepare(GSource *src, gint *timeout)
106 {
107     assert(timeout);
108 
109     *timeout = -1;
110     return FALSE;
111 }
112 
113 static gboolean vus_gsrc_check(GSource *src)
114 {
115     vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
116 
117     assert(vus_src);
118 
119     return vus_src->gfd.revents & vus_src->gfd.events;
120 }
121 
122 static gboolean vus_gsrc_dispatch(GSource *src, GSourceFunc cb, gpointer data)
123 {
124     vhost_scsi_dev_t *vdev_scsi;
125     vus_gsrc_t *vus_src = (vus_gsrc_t *)src;
126 
127     assert(vus_src);
128     assert(!(vus_src->vu_cb && cb));
129 
130     vdev_scsi = vus_src->vdev_scsi;
131 
132     assert(vdev_scsi);
133 
134     if (cb) {
135         return cb(data);
136     }
137     if (vus_src->vu_cb) {
138         vus_src->vu_cb(&vdev_scsi->vu_dev, vus_src->gfd.revents, data);
139     }
140     return G_SOURCE_CONTINUE;
141 }
142 
143 static GSourceFuncs vus_gsrc_funcs = {
144     vus_gsrc_prepare,
145     vus_gsrc_check,
146     vus_gsrc_dispatch,
147     NULL
148 };
149 
150 static int vus_gsrc_new(vhost_scsi_dev_t *vdev_scsi, int fd, GIOCondition cond,
151                         vu_watch_cb vu_cb, GSourceFunc gsrc_cb, gpointer data)
152 {
153     GSource *vus_gsrc;
154     vus_gsrc_t *vus_src;
155     guint id;
156 
157     assert(vdev_scsi);
158     assert(fd >= 0);
159     assert(vu_cb || gsrc_cb);
160     assert(!(vu_cb && gsrc_cb));
161 
162     vus_gsrc = g_source_new(&vus_gsrc_funcs, sizeof(vus_gsrc_t));
163     if (!vus_gsrc) {
164         PERR("Error creating GSource for new watch");
165         return -1;
166     }
167     vus_src = (vus_gsrc_t *)vus_gsrc;
168 
169     vus_src->vdev_scsi = vdev_scsi;
170     vus_src->gfd.fd = fd;
171     vus_src->gfd.events = cond;
172     vus_src->vu_cb = vu_cb;
173 
174     g_source_add_poll(vus_gsrc, &vus_src->gfd);
175     g_source_set_callback(vus_gsrc, gsrc_cb, data, NULL);
176     id = g_source_attach(vus_gsrc, NULL);
177     assert(id);
178     g_source_unref(vus_gsrc);
179 
180     g_tree_insert(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd,
181                                     (gpointer)(uintptr_t)id);
182 
183     return 0;
184 }
185 
186 /* from libiscsi's scsi-lowlevel.h **
187  *
188  * nb. We can't directly include scsi-lowlevel.h due to a namespace conflict:
189  *     QEMU's scsi.h also defines "SCSI_XFER_NONE".
190  */
191 
192 #define SCSI_CDB_MAX_SIZE           16
193 
194 struct scsi_iovector {
195     struct scsi_iovec *iov;
196     int niov;
197     int nalloc;
198     size_t offset;
199     int consumed;
200 };
201 
202 struct scsi_allocated_memory {
203     struct scsi_allocated_memory *next;
204     char buf[0];
205 };
206 
207 struct scsi_data {
208     int            size;
209     unsigned char *data;
210 };
211 
212 enum scsi_sense_key {
213     SCSI_SENSE_NO_SENSE            = 0x00,
214     SCSI_SENSE_RECOVERED_ERROR     = 0x01,
215     SCSI_SENSE_NOT_READY           = 0x02,
216     SCSI_SENSE_MEDIUM_ERROR        = 0x03,
217     SCSI_SENSE_HARDWARE_ERROR      = 0x04,
218     SCSI_SENSE_ILLEGAL_REQUEST     = 0x05,
219     SCSI_SENSE_UNIT_ATTENTION      = 0x06,
220     SCSI_SENSE_DATA_PROTECTION     = 0x07,
221     SCSI_SENSE_BLANK_CHECK         = 0x08,
222     SCSI_SENSE_VENDOR_SPECIFIC     = 0x09,
223     SCSI_SENSE_COPY_ABORTED        = 0x0a,
224     SCSI_SENSE_COMMAND_ABORTED     = 0x0b,
225     SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
226     SCSI_SENSE_OVERFLOW_COMMAND    = 0x0d,
227     SCSI_SENSE_MISCOMPARE          = 0x0e
228 };
229 
230 struct scsi_sense {
231     unsigned char       error_type;
232     enum scsi_sense_key key;
233     int                 ascq;
234     unsigned            sense_specific:1;
235     unsigned            ill_param_in_cdb:1;
236     unsigned            bit_pointer_valid:1;
237     unsigned char       bit_pointer;
238     uint16_t            field_pointer;
239 };
240 
241 enum scsi_residual {
242     SCSI_RESIDUAL_NO_RESIDUAL = 0,
243     SCSI_RESIDUAL_UNDERFLOW,
244     SCSI_RESIDUAL_OVERFLOW
245 };
246 
247 struct scsi_task {
248     int status;
249     int cdb_size;
250     int xfer_dir;
251     int expxferlen;
252     unsigned char cdb[SCSI_CDB_MAX_SIZE];
253     enum scsi_residual residual_status;
254     size_t residual;
255     struct scsi_sense sense;
256     struct scsi_data datain;
257     struct scsi_allocated_memory *mem;
258     void *ptr;
259 
260     uint32_t itt;
261     uint32_t cmdsn;
262     uint32_t lun;
263 
264     struct scsi_iovector iovector_in;
265     struct scsi_iovector iovector_out;
266 };
267 
268 /** libiscsi integration **/
269 
270 static int iscsi_add_lun(iscsi_lun_t *lun, char *iscsi_uri)
271 {
272     struct iscsi_url *iscsi_url;
273     struct iscsi_context *iscsi_ctx;
274     int ret = 0;
275 
276     assert(lun);
277     assert(iscsi_uri);
278 
279     iscsi_ctx = iscsi_create_context(VUS_ISCSI_INITIATOR);
280     if (!iscsi_ctx) {
281         PERR("Unable to create iSCSI context");
282         return -1;
283     }
284 
285     iscsi_url = iscsi_parse_full_url(iscsi_ctx, iscsi_uri);
286     if (!iscsi_url) {
287         PERR("Unable to parse iSCSI URL: %s", iscsi_get_error(iscsi_ctx));
288         goto fail;
289     }
290 
291     iscsi_set_session_type(iscsi_ctx, ISCSI_SESSION_NORMAL);
292     iscsi_set_header_digest(iscsi_ctx, ISCSI_HEADER_DIGEST_NONE_CRC32C);
293     if (iscsi_full_connect_sync(iscsi_ctx, iscsi_url->portal, iscsi_url->lun)) {
294         PERR("Unable to login to iSCSI portal: %s", iscsi_get_error(iscsi_ctx));
295         goto fail;
296     }
297 
298     lun->iscsi_ctx = iscsi_ctx;
299     lun->iscsi_lun = iscsi_url->lun;
300 
301     PDBG("Context %p created for lun 0: %s", iscsi_ctx, iscsi_uri);
302 
303 out:
304     if (iscsi_url) {
305         iscsi_destroy_url(iscsi_url);
306     }
307     return ret;
308 
309 fail:
310     (void)iscsi_destroy_context(iscsi_ctx);
311     ret = -1;
312     goto out;
313 }
314 
315 static struct scsi_task *scsi_task_new(int cdb_len, uint8_t *cdb, int dir,
316                                        int xfer_len) {
317     struct scsi_task *task;
318 
319     assert(cdb_len > 0);
320     assert(cdb);
321 
322     task = calloc(1, sizeof(struct scsi_task));
323     if (!task) {
324         PERR("Error allocating task: %s", strerror(errno));
325         return NULL;
326     }
327 
328     memcpy(task->cdb, cdb, cdb_len);
329     task->cdb_size = cdb_len;
330     task->xfer_dir = dir;
331     task->expxferlen = xfer_len;
332 
333     return task;
334 }
335 
336 static int get_cdb_len(uint8_t *cdb)
337 {
338     assert(cdb);
339 
340     switch (cdb[0] >> 5) {
341     case 0: return 6;
342     case 1: /* fall through */
343     case 2: return 10;
344     case 4: return 16;
345     case 5: return 12;
346     }
347     PERR("Unable to determine cdb len (0x%02hhX)", cdb[0] >> 5);
348     return -1;
349 }
350 
351 static int handle_cmd_sync(struct iscsi_context *ctx,
352                            VirtIOSCSICmdReq *req,
353                            struct iovec *out, unsigned int out_len,
354                            VirtIOSCSICmdResp *rsp,
355                            struct iovec *in, unsigned int in_len) {
356     struct scsi_task *task;
357     uint32_t dir;
358     uint32_t len;
359     int cdb_len;
360     int i;
361 
362     assert(ctx);
363     assert(req);
364     assert(rsp);
365 
366     if (!(!req->lun[1] && req->lun[2] == 0x40 && !req->lun[3])) {
367         /* Ignore anything different than target=0, lun=0 */
368         PDBG("Ignoring unconnected lun (0x%hhX, 0x%hhX)",
369              req->lun[1], req->lun[3]);
370         rsp->status = SCSI_STATUS_CHECK_CONDITION;
371         memset(rsp->sense, 0, sizeof(rsp->sense));
372         rsp->sense_len = 18;
373         rsp->sense[0] = 0x70;
374         rsp->sense[2] = SCSI_SENSE_ILLEGAL_REQUEST;
375         rsp->sense[7] = 10;
376         rsp->sense[12] = 0x24;
377 
378         return 0;
379     }
380 
381     cdb_len = get_cdb_len(req->cdb);
382     if (cdb_len == -1) {
383         return -1;
384     }
385 
386     len = 0;
387     if (!out_len && !in_len) {
388         dir = SCSI_XFER_NONE;
389     } else if (out_len) {
390         dir = SCSI_XFER_TO_DEV;
391         for (i = 0; i < out_len; i++) {
392             len += out[i].iov_len;
393         }
394     } else {
395         dir = SCSI_XFER_FROM_DEV;
396         for (i = 0; i < in_len; i++) {
397             len += in[i].iov_len;
398         }
399     }
400 
401     task = scsi_task_new(cdb_len, req->cdb, dir, len);
402     if (!task) {
403         PERR("Unable to create iscsi task");
404         return -1;
405     }
406 
407     if (dir == SCSI_XFER_TO_DEV) {
408         task->iovector_out.iov = (struct scsi_iovec *)out;
409         task->iovector_out.niov = out_len;
410     } else if (dir == SCSI_XFER_FROM_DEV) {
411         task->iovector_in.iov = (struct scsi_iovec *)in;
412         task->iovector_in.niov = in_len;
413     }
414 
415     PDBG("Sending iscsi cmd (cdb_len=%d, dir=%d, task=%p)",
416          cdb_len, dir, task);
417     if (!iscsi_scsi_command_sync(ctx, 0, task, NULL)) {
418         PERR("Error serving SCSI command");
419         free(task);
420         return -1;
421     }
422 
423     memset(rsp, 0, sizeof(*rsp));
424 
425     rsp->status = task->status;
426     rsp->resid  = task->residual;
427 
428     if (task->status == SCSI_STATUS_CHECK_CONDITION) {
429         rsp->response = VIRTIO_SCSI_S_FAILURE;
430         rsp->sense_len = task->datain.size - 2;
431         memcpy(rsp->sense, &task->datain.data[2], rsp->sense_len);
432     }
433 
434     free(task);
435 
436     PDBG("Filled in rsp: status=%hhX, resid=%u, response=%hhX, sense_len=%u",
437          rsp->status, rsp->resid, rsp->response, rsp->sense_len);
438 
439     return 0;
440 }
441 
442 /** libvhost-user callbacks **/
443 
444 static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev);
445 
446 static void vus_panic_cb(VuDev *vu_dev, const char *buf)
447 {
448     vhost_scsi_dev_t *vdev_scsi;
449 
450     assert(vu_dev);
451 
452     vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
453 
454     if (buf) {
455         PERR("vu_panic: %s", buf);
456     }
457 
458     if (vdev_scsi) {
459         assert(vdev_scsi->loop);
460         g_main_loop_quit(vdev_scsi->loop);
461     }
462 }
463 
464 static void vus_add_watch_cb(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb,
465                              void *pvt) {
466     vhost_scsi_dev_t *vdev_scsi;
467     guint id;
468 
469     assert(vu_dev);
470     assert(fd >= 0);
471     assert(cb);
472 
473     vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
474     if (!vdev_scsi) {
475         vus_panic_cb(vu_dev, NULL);
476         return;
477     }
478 
479     id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
480                                          (gpointer)(uintptr_t)fd);
481     if (id) {
482         GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
483         assert(vus_src);
484         g_source_destroy(vus_src);
485         (void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
486     }
487 
488     if (vus_gsrc_new(vdev_scsi, fd, vu_evt, cb, NULL, pvt)) {
489         vus_panic_cb(vu_dev, NULL);
490     }
491 }
492 
493 static void vus_del_watch_cb(VuDev *vu_dev, int fd)
494 {
495     vhost_scsi_dev_t *vdev_scsi;
496     guint id;
497 
498     assert(vu_dev);
499     assert(fd >= 0);
500 
501     vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
502     if (!vdev_scsi) {
503         vus_panic_cb(vu_dev, NULL);
504         return;
505     }
506 
507     id = (guint)(uintptr_t)g_tree_lookup(vdev_scsi->fdmap,
508                                          (gpointer)(uintptr_t)fd);
509     if (id) {
510         GSource *vus_src = g_main_context_find_source_by_id(NULL, id);
511         assert(vus_src);
512         g_source_destroy(vus_src);
513         (void)g_tree_remove(vdev_scsi->fdmap, (gpointer)(uintptr_t)fd);
514     }
515 }
516 
517 static void vus_proc_ctl(VuDev *vu_dev, int idx)
518 {
519     /* Control VQ not implemented */
520 }
521 
522 static void vus_proc_evt(VuDev *vu_dev, int idx)
523 {
524     /* Event VQ not implemented */
525 }
526 
527 static void vus_proc_req(VuDev *vu_dev, int idx)
528 {
529     vhost_scsi_dev_t *vdev_scsi;
530     VuVirtq *vq;
531 
532     assert(vu_dev);
533 
534     vdev_scsi = vdev_scsi_find_by_vu(vu_dev);
535     if (!vdev_scsi) {
536         vus_panic_cb(vu_dev, NULL);
537         return;
538     }
539 
540     if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
541         PERR("VQ Index out of range: %d", idx);
542         vus_panic_cb(vu_dev, NULL);
543         return;
544     }
545 
546     vq = vu_get_queue(vu_dev, idx);
547     if (!vq) {
548         PERR("Error fetching VQ (dev=%p, idx=%d)", vu_dev, idx);
549         vus_panic_cb(vu_dev, NULL);
550         return;
551     }
552 
553     PDBG("Got kicked on vq[%d]@%p", idx, vq);
554 
555     while (1) {
556         VuVirtqElement *elem;
557         VirtIOSCSICmdReq *req;
558         VirtIOSCSICmdResp *rsp;
559 
560         elem = vu_queue_pop(vu_dev, vq, sizeof(VuVirtqElement));
561         if (!elem) {
562             PDBG("No more elements pending on vq[%d]@%p", idx, vq);
563             break;
564         }
565         PDBG("Popped elem@%p", elem);
566 
567         assert(!((elem->out_num > 1) && (elem->in_num > 1)));
568         assert((elem->out_num > 0) && (elem->in_num > 0));
569 
570         if (elem->out_sg[0].iov_len < sizeof(VirtIOSCSICmdReq)) {
571             PERR("Invalid virtio-scsi req header");
572             vus_panic_cb(vu_dev, NULL);
573             break;
574         }
575         req = (VirtIOSCSICmdReq *)elem->out_sg[0].iov_base;
576 
577         if (elem->in_sg[0].iov_len < sizeof(VirtIOSCSICmdResp)) {
578             PERR("Invalid virtio-scsi rsp header");
579             vus_panic_cb(vu_dev, NULL);
580             break;
581         }
582         rsp = (VirtIOSCSICmdResp *)elem->in_sg[0].iov_base;
583 
584         if (handle_cmd_sync(vdev_scsi->luns[0].iscsi_ctx,
585                             req, &elem->out_sg[1], elem->out_num - 1,
586                             rsp, &elem->in_sg[1], elem->in_num - 1) != 0) {
587             vus_panic_cb(vu_dev, NULL);
588             break;
589         }
590 
591         vu_queue_push(vu_dev, vq, elem, 0);
592         vu_queue_notify(vu_dev, vq);
593 
594         free(elem);
595     }
596 }
597 
598 static void vus_queue_set_started(VuDev *vu_dev, int idx, bool started)
599 {
600     VuVirtq *vq;
601 
602     assert(vu_dev);
603 
604     if ((idx < 0) || (idx >= VHOST_MAX_NR_VIRTQUEUE)) {
605         PERR("VQ Index out of range: %d", idx);
606         vus_panic_cb(vu_dev, NULL);
607         return;
608     }
609 
610     vq = vu_get_queue(vu_dev, idx);
611 
612     switch (idx) {
613     case 0:
614         vu_set_queue_handler(vu_dev, vq, started ? vus_proc_ctl : NULL);
615         break;
616     case 1:
617         vu_set_queue_handler(vu_dev, vq, started ? vus_proc_evt : NULL);
618         break;
619     default:
620         vu_set_queue_handler(vu_dev, vq, started ? vus_proc_req : NULL);
621     }
622 }
623 
624 static const VuDevIface vus_iface = {
625     .queue_set_started = vus_queue_set_started,
626 };
627 
628 static gboolean vus_vhost_cb(gpointer data)
629 {
630     VuDev *vu_dev = (VuDev *)data;
631 
632     assert(vu_dev);
633 
634     if (!vu_dispatch(vu_dev) != 0) {
635         PERR("Error processing vhost message");
636         vus_panic_cb(vu_dev, NULL);
637         return G_SOURCE_REMOVE;
638     }
639 
640     return G_SOURCE_CONTINUE;
641 }
642 
643 /** misc helpers **/
644 
645 static int unix_sock_new(char *unix_fn)
646 {
647     int sock;
648     struct sockaddr_un un;
649     size_t len;
650 
651     assert(unix_fn);
652 
653     sock = socket(AF_UNIX, SOCK_STREAM, 0);
654     if (sock <= 0) {
655         perror("socket");
656         return -1;
657     }
658 
659     un.sun_family = AF_UNIX;
660     (void)snprintf(un.sun_path, sizeof(un.sun_path), "%s", unix_fn);
661     len = sizeof(un.sun_family) + strlen(un.sun_path);
662 
663     (void)unlink(unix_fn);
664     if (bind(sock, (struct sockaddr *)&un, len) < 0) {
665         perror("bind");
666         goto fail;
667     }
668 
669     if (listen(sock, 1) < 0) {
670         perror("listen");
671         goto fail;
672     }
673 
674     return sock;
675 
676 fail:
677     (void)close(sock);
678 
679     return -1;
680 }
681 
682 /** vhost-user-scsi **/
683 
684 static vhost_scsi_dev_t *vdev_scsi_find_by_vu(VuDev *vu_dev)
685 {
686     int i;
687 
688     assert(vu_dev);
689 
690     for (i = 0; i < VUS_MAX_DEVS; i++) {
691         if (&vhost_scsi_devs[i]->vu_dev == vu_dev) {
692             return vhost_scsi_devs[i];
693         }
694     }
695 
696     PERR("Unknown VuDev %p", vu_dev);
697     return NULL;
698 }
699 
700 static void vdev_scsi_deinit(vhost_scsi_dev_t *vdev_scsi)
701 {
702     if (!vdev_scsi) {
703         return;
704     }
705 
706     if (vdev_scsi->server_sock >= 0) {
707         struct sockaddr_storage ss;
708         socklen_t sslen = sizeof(ss);
709 
710         if (getsockname(vdev_scsi->server_sock, (struct sockaddr *)&ss,
711                         &sslen) == 0) {
712             struct sockaddr_un *su = (struct sockaddr_un *)&ss;
713             (void)unlink(su->sun_path);
714         }
715 
716         (void)close(vdev_scsi->server_sock);
717         vdev_scsi->server_sock = -1;
718     }
719 
720     if (vdev_scsi->loop) {
721         g_main_loop_unref(vdev_scsi->loop);
722         vdev_scsi->loop = NULL;
723     }
724 }
725 
726 static vhost_scsi_dev_t *vdev_scsi_new(char *unix_fn)
727 {
728     vhost_scsi_dev_t *vdev_scsi = NULL;
729 
730     assert(unix_fn);
731 
732     vdev_scsi = calloc(1, sizeof(vhost_scsi_dev_t));
733     if (!vdev_scsi) {
734         PERR("calloc: %s", strerror(errno));
735         return NULL;
736     }
737 
738     vdev_scsi->server_sock = unix_sock_new(unix_fn);
739     if (vdev_scsi->server_sock < 0) {
740         goto err;
741     }
742 
743     vdev_scsi->loop = g_main_loop_new(NULL, FALSE);
744     if (!vdev_scsi->loop) {
745         PERR("Error creating glib event loop");
746         goto err;
747     }
748 
749     vdev_scsi->fdmap = g_tree_new(vus_fdmap_compare);
750     if (!vdev_scsi->fdmap) {
751         PERR("Error creating glib tree for fdmap");
752         goto err;
753     }
754 
755     return vdev_scsi;
756 
757 err:
758     vdev_scsi_deinit(vdev_scsi);
759     free(vdev_scsi);
760 
761     return NULL;
762 }
763 
764 static int vdev_scsi_add_iscsi_lun(vhost_scsi_dev_t *vdev_scsi,
765                                    char *iscsi_uri, uint32_t lun) {
766     assert(vdev_scsi);
767     assert(iscsi_uri);
768     assert(lun < VUS_MAX_LUNS);
769 
770     if (vdev_scsi->luns[lun].iscsi_ctx) {
771         PERR("Lun %d already configured", lun);
772         return -1;
773     }
774 
775     if (iscsi_add_lun(&vdev_scsi->luns[lun], iscsi_uri) != 0) {
776         return -1;
777     }
778 
779     return 0;
780 }
781 
782 static int vdev_scsi_run(vhost_scsi_dev_t *vdev_scsi)
783 {
784     int cli_sock;
785     int ret = 0;
786 
787     assert(vdev_scsi);
788     assert(vdev_scsi->server_sock >= 0);
789     assert(vdev_scsi->loop);
790 
791     cli_sock = accept(vdev_scsi->server_sock, (void *)0, (void *)0);
792     if (cli_sock < 0) {
793         perror("accept");
794         return -1;
795     }
796 
797     vu_init(&vdev_scsi->vu_dev,
798             cli_sock,
799             vus_panic_cb,
800             vus_add_watch_cb,
801             vus_del_watch_cb,
802             &vus_iface);
803 
804     if (vus_gsrc_new(vdev_scsi, cli_sock, G_IO_IN, NULL, vus_vhost_cb,
805                      &vdev_scsi->vu_dev)) {
806         goto fail;
807     }
808 
809     g_main_loop_run(vdev_scsi->loop);
810 
811 out:
812     vu_deinit(&vdev_scsi->vu_dev);
813 
814     return ret;
815 
816 fail:
817     ret = -1;
818     goto out;
819 }
820 
821 int main(int argc, char **argv)
822 {
823     vhost_scsi_dev_t *vdev_scsi = NULL;
824     char *unix_fn = NULL;
825     char *iscsi_uri = NULL;
826     int opt, err = EXIT_SUCCESS;
827 
828     while ((opt = getopt(argc, argv, "u:i:")) != -1) {
829         switch (opt) {
830         case 'h':
831             goto help;
832         case 'u':
833             unix_fn = strdup(optarg);
834             break;
835         case 'i':
836             iscsi_uri = strdup(optarg);
837             break;
838         default:
839             goto help;
840         }
841     }
842     if (!unix_fn || !iscsi_uri) {
843         goto help;
844     }
845 
846     vdev_scsi = vdev_scsi_new(unix_fn);
847     if (!vdev_scsi) {
848         goto err;
849     }
850     vhost_scsi_devs[0] = vdev_scsi;
851 
852     if (vdev_scsi_add_iscsi_lun(vdev_scsi, iscsi_uri, 0) != 0) {
853         goto err;
854     }
855 
856     if (vdev_scsi_run(vdev_scsi) != 0) {
857         goto err;
858     }
859 
860 out:
861     if (vdev_scsi) {
862         vdev_scsi_deinit(vdev_scsi);
863         free(vdev_scsi);
864     }
865     if (unix_fn) {
866         free(unix_fn);
867     }
868     if (iscsi_uri) {
869         free(iscsi_uri);
870     }
871 
872     return err;
873 
874 err:
875     err = EXIT_FAILURE;
876     goto out;
877 
878 help:
879     fprintf(stderr, "Usage: %s [ -u unix_sock_path -i iscsi_uri ] | [ -h ]\n",
880             argv[0]);
881     fprintf(stderr, "          -u path to unix socket\n");
882     fprintf(stderr, "          -i iscsi uri for lun 0\n");
883     fprintf(stderr, "          -h print help and quit\n");
884 
885     goto err;
886 }
887