/** * @file * @section AUTHORS * * Authors: * Eamon Walsh * Daniel De Graaf * * @section LICENSE * * This file is in the public domain. * * @section DESCRIPTION * * This is "glue" code between the functions from QEMU and the core * linpicker-server code. There are three main things here: * * 1. Libvchan setup, teardown, and processing code that handles the vchan * communications channel from linpicker-track. * * 2. Domain scanning code that listens in XenStore for new domains starting * up and handles them (not present in QEMU because each QEMU instance is for * a single domain). * * 3. Glue functions for handling events going to and from the frontend, such * as framebuffer udpate notifications, resizes, and input events. */ #include #include #include #include #include #include #include #include #include #include #include "libvchan.h" #include "xen_backend.h" #include "xen_linpicker.h" #include "sys-queue.h" #include "comm.h" #include "client.h" #include "buffer.h" /* ------------------------------------------------------------- */ struct dominfo { int domid; int fd; struct libvchan *ctrl; struct client *client; struct buffer *buffer; TAILQ_ENTRY(dominfo) next; }; TAILQ_HEAD(dominfo_list, dominfo); static struct dominfo_list vchans = TAILQ_HEAD_INITIALIZER(vchans); static libxl_ctx ctx; static void vchan_queue(struct dominfo *d); /* -------------------------------------------------------------------- */ static void vchan_shutdown(struct dominfo *d) { TAILQ_REMOVE(&vchans, d, next); fd_set_handler(d->fd, NULL, NULL); libvchan_close(d->ctrl); XEN_BE_LOG(NULL, 1, "Tore down vchan for domain %d\n", d->domid); free(d); } static void vchan_update(void *closure) { struct dominfo *d = closure; libvchan_wait(d->ctrl); vchan_queue(d); } static void vchan_queue(struct dominfo *d) { struct lin_message msg; int ret; while (1) { ret = libvchan_recv(d->ctrl, &msg, sizeof(msg)); if (ret == 0) return; if (ret < 0) { vchan_shutdown(d); return; } comm_process(d->client, d->buffer, &msg); } } static int vchan_setup(struct xs_handle *xenstore, xc_interface* xen_xc, const char *path) { int domid; struct libvchan *ctrl; struct dominfo *d; unsigned int len; char *ref; char msg; if (sscanf(path, "/local/domain/%d", &domid) != 1) return -1; TAILQ_FOREACH(d, &vchans, next) if (d->domid == domid) return 0; ref = xs_read(xenstore, 0, path, &len); if (!ref) return 0; free(ref); d = calloc(1, sizeof(struct dominfo)); if (!d) return -1; ctrl = libvchan_client_init(domid, LINPICKER_VPORT); if (!ctrl) { perror("libvchan_client_init"); goto err; } d->domid = domid; d->fd = libvchan_fd_for_select(ctrl); d->ctrl = ctrl; d->client = client_lookup(domid); if (!d->client) goto err; d->buffer = LIST_FIRST(&d->client->buffers); if (!d->buffer) goto err; msg = LINPICKER_REQUEST_UPDATE; libvchan_send(ctrl, &msg, 1); // Note: failure to write is ignored for the moment, as this means we // already have an update request enqueued if (fd_set_handler(d->fd, vchan_update, d) < 0) goto err; vchan_queue(d); TAILQ_INSERT_TAIL(&vchans, d, next); XEN_BE_LOG(NULL, 1, "Set up vchan for domain %d\n", domid); return 0; err: free(d); return -1; } /* -------------------------------------------------------------------- */ static int get_domid() { char* str = xs_read(xenstore, 0, "domid", NULL); if (!str) return 0; int rv = atoi(str); free(str); return rv; } static void domain_shutdown(int domid) { struct dominfo *d; struct client *c; TAILQ_FOREACH(d, &vchans, next) if (d->domid == domid) { vchan_shutdown(d); break; } /* remove client (needed if frontend did not disconnect) */ c = client_lookup(domid); if (c) client_remove(c); } static int domain_setup(struct xs_handle *xenstore, int domid, int newdom) { char buf[128]; int myself = get_domid(); // don't try to set up vfb/vkbd for ourselves, or dom0 if (domid == 0 || domid == myself) return 0; /* watch for vchan */ snprintf(buf, sizeof(buf), "/local/domain/%d/data/vchan/%d/event-channel", domid, LINPICKER_VPORT); if (!xs_watch(xenstore, buf, LINPICKER_TOKEN)) return -1; // xen_be_register(domid, "console", &xen_console_ops); xen_be_register(domid, "vkbd", &xen_kbdmouse_ops); xen_be_register(domid, "vfb", &xen_framebuffer_ops); return 0; } /* -------------------------------------------------------------------- */ static struct sec_color colors[] = { { 0xff, 0x8c, 0x00 }, { 0x00, 0xff, 0x00 }, { 0xff, 0xff, 0x00 }, { 0xff, 0x00, 0x00 }, { 0x00, 0x00, 0xff }, { 0xff, 0xff, 0x00 } }; static int xen_linpicker_label_client(struct client *c, int domid) { c->label.label = malloc(32); if (!c->label.label) return -1; c->label.shortlabel = malloc(32); if (!c->label.shortlabel) { free(c->label.label); return -1; } snprintf(c->label.label, 32, "GUEST DOMAIN %d", domid); snprintf(c->label.shortlabel, 32, "%d", domid); c->label.fg = colors[(domid % 3) * 2]; c->label.bg = colors[(domid % 3) * 2 + 1]; return 0; } static void xen_linpicker_scan_domains(struct xs_handle *xenstore, int newdom) { static fd_set doms; fd_set tmp; unsigned int n, i; int d; char** e; FD_ZERO(&tmp); if (!newdom) FD_ZERO(&doms); e = xs_directory(xenstore, XBT_NULL, "/local/domain", &n); if (!e) { XEN_BE_LOG(NULL, 0, "cannot list domains\n"); return; } for (i = 0; i < n; i++) { d = atoi(e[i]); FD_SET(d, &tmp); } free(e); for (i = 0; i < FD_SETSIZE; i++) { if (FD_ISSET(i, &tmp) && !FD_ISSET(i, &doms)) domain_setup(xenstore, i, newdom); if (!FD_ISSET(i, &tmp) && FD_ISSET(i, &doms)) domain_shutdown(i); } memcpy(&doms, &tmp, sizeof(fd_set)); } int xen_linpicker_update(struct xs_handle *xenstore, xc_interface* xen_xc, const char *path) { if (strstr(path, "/vchan/")) return vchan_setup(xenstore, xen_xc, path); xen_linpicker_scan_domains(xenstore, 1); return 0; } int xen_linpicker_init(struct xs_handle *xenstore, xc_interface* xen_xc) { /* setup */ libxl_ctx_init(&ctx, LIBXL_VERSION, 0); /* watch for domain add/remove */ if (!xs_watch(xenstore, "/local/domain", LINPICKER_TOKEN)) return -1; xen_linpicker_scan_domains(xenstore, 0); return 0; } int xen_linpicker_be_set_state(struct XenDevice *xendev, enum xenbus_state state) { /* * We do nothing here as the monitor program is responsible for * updating the backend state in XenStore. */ return 0; } /* -------------------------------------------------------------------- */ int xen_linpicker_input_connect(struct XenInput *fb) { int domid = fb->c.xendev.dom; struct client *c; /* Get client structure for this domid */ c = client_lookup(domid); if (c == NULL) { if (!(c = client_add(domid, 1024, 1))) return -1; /* Label the client */ xen_linpicker_label_client(c, domid); } /* Set up pointers */ c->input = fb; fb->c.client = c; return 0; } void xen_linpicker_input_disconnect(struct XenInput *fb) { if (fb->c.client) { fb->c.client->input = NULL; fb->c.client = NULL; } } int xen_linpicker_input_send(struct XenInput *xenfb, const struct mouse_event *m) { if (xenfb->abs_pointer_wanted) return xenfb_send_position(xenfb, m->x, m->y, m->dz); else return xenfb_send_motion(xenfb, m->dx, m->dy, m->dz); } /* -------------------------------------------------------------------- */ int xen_linpicker_fb_connect(struct XenFB *fb) { DFBSurfaceDescription sdesc; int domid = fb->c.xendev.dom; struct client *c; struct buffer *b; /* Get client structure for this domid */ c = client_lookup(domid); if (c == NULL) { if (!(c = client_add(domid, 1024, 1))) return -1; /* Label the client */ xen_linpicker_label_client(c, domid); } /* Set up preallocated DirectFB surface with the data */ memset(&sdesc, 0, sizeof(sdesc)); sdesc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_CAPS | DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED; sdesc.width = fb->width; sdesc.height = fb->height; sdesc.pixelformat = DSPF_RGB32; sdesc.preallocated[0].data = fb->pixels + fb->offset; sdesc.preallocated[0].pitch = fb->row_stride; b = buffer_new(c, &sdesc, 0); if (!b) return -1; fb->c.client = c; fb->c.buffer = b; return 0; } int xen_linpicker_fb_resize(struct XenFB *fb) { DFBSurfaceDescription sdesc; /* Sanity check */ if (fb->c.buffer->desc.width == fb->width && fb->c.buffer->desc.height == fb->height) return 0; /* Set up preallocated DirectFB surface with the data */ memset(&sdesc, 0, sizeof(sdesc)); sdesc.flags = DSDESC_WIDTH | DSDESC_HEIGHT | DSDESC_CAPS | DSDESC_PIXELFORMAT | DSDESC_PREALLOCATED; sdesc.width = fb->width; sdesc.height = fb->height; sdesc.pixelformat = DSPF_RGB32; sdesc.preallocated[0].data = fb->pixels + fb->offset; sdesc.preallocated[0].pitch = fb->row_stride; if (buffer_resize(fb->c.buffer, &sdesc) < 0) { buffer_remove(fb->c.buffer); return -1; } return 0; } void xen_linpicker_fb_update(struct XenFB *fb, int x, int y, int w, int h) { buffer_refresh(fb->c.buffer, x, y, w, h); } void xen_linpicker_fb_disconnect(struct XenFB *fb) { if (fb->c.buffer) { buffer_remove(fb->c.buffer); fb->c.buffer = NULL; } if (fb->c.client && LIST_EMPTY(&fb->c.client->buffers)) { client_remove(fb->c.client); fb->c.client = NULL; } }