qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 31/40] xenner: libxc emu: xenstore


From: Alexander Graf
Subject: [Qemu-devel] [PATCH 31/40] xenner: libxc emu: xenstore
Date: Mon, 1 Nov 2010 16:01:44 +0100

Xenner emulates parts of libxc, so we can not use the real xen infrastructure
when running xen pv guests without xen.

This patch adds support for emulation of xenstored.

Signed-off-by: Alexander Graf <address@hidden>
---
 hw/xenner_guest_store.c |  494 +++++++++++++++++++++++++++++++++
 hw/xenner_libxenstore.c |  709 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1203 insertions(+), 0 deletions(-)
 create mode 100644 hw/xenner_guest_store.c
 create mode 100644 hw/xenner_libxenstore.c

diff --git a/hw/xenner_guest_store.c b/hw/xenner_guest_store.c
new file mode 100644
index 0000000..c067275
--- /dev/null
+++ b/hw/xenner_guest_store.c
@@ -0,0 +1,494 @@
+/*
+ *  Copyright (C) 2005 Rusty Russell IBM Corporation
+ *  Copyright (C) Red Hat 2007
+ *  Copyright (C) Novell Inc. 2010
+ *
+ *  Author(s): Gerd Hoffmann <address@hidden>
+ *             Alexander Graf <address@hidden>
+ *
+ *  Xenner emulation -- guest interface to xenstore
+ *
+ *  tools/xenstore/xenstored_domain.c equivalent, some code is from there.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "xen.h"
+#include "xen_interfaces.h"
+#include "xenner.h"
+#include "qemu-char.h"
+
+/* ------------------------------------------------------------- */
+
+static target_phys_addr_t xen_store_mfn;
+
+static struct xs_handle *xs_guest;
+static char xs_buf[1024];
+static char xs_len;
+static int debug = 0;
+
+static int evtchndev;
+static evtchn_port_t evtchnport;
+
+/* ------------------------------------------------------------- */
+
+static const char *msgname[] = {
+    [ XS_DEBUG                 ] = "XS_DEBUG",
+    [ XS_DIRECTORY             ] = "XS_DIRECTORY",
+    [ XS_READ                  ] = "XS_READ",
+    [ XS_GET_PERMS             ] = "XS_GET_PERMS",
+    [ XS_WATCH                 ] = "XS_WATCH",
+    [ XS_UNWATCH               ] = "XS_UNWATCH",
+    [ XS_TRANSACTION_START     ] = "XS_TRANSACTION_START",
+    [ XS_TRANSACTION_END       ] = "XS_TRANSACTION_END",
+    [ XS_INTRODUCE             ] = "XS_INTRODUCE",
+    [ XS_RELEASE               ] = "XS_RELEASE",
+    [ XS_GET_DOMAIN_PATH       ] = "XS_GET_DOMAIN_PATH",
+    [ XS_WRITE                 ] = "XS_WRITE",
+    [ XS_MKDIR                 ] = "XS_MKDIR",
+    [ XS_RM                    ] = "XS_RM",
+    [ XS_SET_PERMS             ] = "XS_SET_PERMS",
+    [ XS_WATCH_EVENT           ] = "XS_WATCH_EVENT",
+    [ XS_ERROR                 ] = "XS_ERROR",
+    [ XS_IS_DOMAIN_INTRODUCED  ] = "XS_IS_DOMAIN_INTRODUCED",
+    [ XS_RESUME                ] = "XS_RESUME",
+};
+
+/* ------------------------------------------------------------- */
+
+static bool check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod)
+{
+    return ((prod - cons) <= XENSTORE_RING_SIZE);
+}
+
+static void *get_output_chunk(XENSTORE_RING_IDX cons,
+                              XENSTORE_RING_IDX prod,
+                              char *buf, uint32_t *len)
+{
+    *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod);
+    if ((XENSTORE_RING_SIZE - (prod - cons)) < *len) {
+        *len = XENSTORE_RING_SIZE - (prod - cons);
+    }
+    return buf + MASK_XENSTORE_IDX(prod);
+}
+
+static const void *get_input_chunk(XENSTORE_RING_IDX cons,
+                                   XENSTORE_RING_IDX prod,
+                                   const char *buf, uint32_t *len)
+{
+    *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons);
+    if ((prod - cons) < *len) {
+        *len = prod - cons;
+    }
+    return buf + MASK_XENSTORE_IDX(cons);
+}
+
+static int domain_write(struct xenstore_domain_interface *intf,
+                        const void *data, unsigned int len)
+{
+    uint32_t avail;
+    void *dest;
+    XENSTORE_RING_IDX cons, prod;
+
+    /* Must read indexes once, and before anything else, and verified. */
+    cons = intf->rsp_cons;
+    prod = intf->rsp_prod;
+    xen_mb();
+
+    if (!check_indexes(cons, prod)) {
+        errno = EIO;
+        return -1;
+    }
+
+    dest = get_output_chunk(cons, prod, intf->rsp, &avail);
+    if (avail < len) {
+        /* write hangover at the beginning */
+        memcpy(intf->rsp, data + avail, len - avail);
+    }
+
+    memcpy(dest, data, avail);
+    xen_mb();
+    intf->rsp_prod += len;
+
+    xc_evtchn.notify(evtchndev, evtchnport);
+    return len;
+}
+
+static int domain_read(struct xenstore_domain_interface *intf,
+                       void *data, unsigned int len)
+{
+    uint32_t avail;
+    const void *src;
+    XENSTORE_RING_IDX cons, prod;
+
+    /* Must read indexes once, and before anything else, and verified. */
+    cons = intf->req_cons;
+    prod = intf->req_prod;
+    xen_mb();
+
+    if (!check_indexes(cons, prod)) {
+        errno = EIO;
+        return -1;
+    }
+
+    src = get_input_chunk(cons, prod, intf->req, &avail);
+    if (avail < len) {
+        len = avail;
+    }
+
+    memcpy(data, src, len);
+    xen_mb();
+    intf->req_cons += len;
+
+    xc_evtchn.notify(evtchndev, evtchnport);
+    return len;
+}
+
+/* ------------------------------------------------------------- */
+
+static int blen;
+static char *backlog = NULL;
+
+static void backlog_create(void *reply, int mlen, int sent)
+{
+    blen = mlen-sent;
+    backlog = qemu_malloc(blen);
+    memcpy(backlog, ((char*)reply) + sent, blen);
+    if (debug) {
+        fprintf(stderr, "%s: backlog created: %d bytes\n",
+                __FUNCTION__, blen);
+    }
+}
+
+static int backlog_shift(struct xenstore_domain_interface *di,
+                         void *reply, int mlen)
+{
+    int rc;
+
+    rc = domain_write(di, backlog, blen);
+    if (rc == blen) {
+        if (debug) {
+            fprintf(stderr, "%s: backlog cleared\n",
+                    __FUNCTION__);
+        }
+        qemu_free(backlog);
+        backlog = NULL;
+        blen = 0;
+    } else {
+        memmove(backlog, backlog+rc, blen-rc);
+        blen -= rc;
+        backlog = qemu_realloc(backlog, blen + mlen);
+        if (reply) {
+            memcpy(backlog + blen, reply, mlen);
+        }
+        blen += mlen;
+        if (debug) {
+            fprintf(stderr, "%s: backlog resized: %d bytes (%d sent, %d 
added)\n",
+                    __FUNCTION__, blen, rc, mlen);
+        }
+    }
+    return blen;
+}
+
+/* ------------------------------------------------------------- */
+
+static struct xenstore_domain_interface *get_xdf(void)
+{
+    struct xenstore_domain_interface *xdf;
+    target_phys_addr_t len = sizeof(*xdf);
+
+    xdf = cpu_physical_memory_map(xen_store_mfn << PAGE_SHIFT, &len, 1);
+
+    if (len < sizeof(*xdf)) {
+        return NULL;
+    }
+
+    return xdf;
+}
+
+static int xen_reply(struct xsd_sockmsg *msg, int type, const void *data, int 
len)
+{
+    struct xenstore_domain_interface *di = get_xdf();
+    struct xsd_sockmsg *reply;
+    int mlen, rc;
+
+    reply = qemu_mallocz(sizeof(*reply) + len);
+    if (!reply) {
+        return -1;
+    }
+    if (msg) {
+        *reply = *msg;
+    }
+    reply->type = type;
+    reply->len = len;
+    if (len) {
+        memcpy(reply+1, data, len);
+    }
+    mlen = sizeof(*reply) + len;
+
+    if (debug) {
+        fprintf(stderr, "%s: %s (#%d) %d:[%.*s]\n", __FUNCTION__,
+                msgname[reply->type], reply->type, len, len, (char*)(reply+1));
+    }
+
+    if (backlog) {
+        if (backlog_shift(di, reply, mlen)) {
+            goto out;
+        }
+    }
+
+    rc = domain_write(di, reply, mlen);
+    if (rc == -1) {
+        fprintf(stderr, "%s: domain_write error\n", __FUNCTION__);
+    } else if (rc != mlen) {
+        backlog_create(reply, mlen, rc);
+    }
+
+out:
+    qemu_free(reply);
+    return 0;
+}
+
+static int xen_reply_str(struct xsd_sockmsg *msg, int type, const char *str)
+{
+    if (str) {
+        return xen_reply(msg, type, str, strlen(str)+1);
+    } else {
+        return xen_reply(msg, type, NULL, 0);
+    }
+}
+
+static int xen_reply_vec(struct xsd_sockmsg *msg, int type, char **vec, int 
vlen)
+{
+    char payload[1024];
+    int i,len,pos;
+
+    if (!vec) {
+        vlen = 0;
+    }
+    for (pos = 0, i = 0; i < vlen; i++) {
+        len = strlen(vec[i])+1;
+        if (pos+len > sizeof(payload)) {
+            fprintf(stderr, "%s: oops: payload too small\n", __FUNCTION__);
+            break;
+        }
+        memcpy(payload+pos, vec[i], len);
+        pos += len;
+    }
+    return xen_reply(msg, type, payload, pos);
+}
+
+static int xen_handle_data(void *data, int len)
+{
+    struct xsd_sockmsg *msg;
+    char *payload, *arg2, *val, **vec, id[16];
+    unsigned int slen,vlen,alen;
+    bool rc;
+
+    if (len < sizeof(*msg)) {
+        if (debug) {
+            fprintf(stderr, "%s: header incomplete (%d/%zd)\n",
+                    __FUNCTION__, len, sizeof(*msg));
+        }
+        return 0;
+    }
+    msg = data;
+    if (len < sizeof(*msg) + msg->len) {
+        if (debug) {
+            fprintf(stderr, "%s: msg incomplete (%d/%zd)\n",
+                    __FUNCTION__, len, sizeof(*msg) + msg->len);
+        }
+        return 0;
+    }
+    payload = data + sizeof(*msg);
+    payload[msg->len] = 0;
+
+    if (debug) {
+        fprintf(stderr, "%s: %s (#%d) %d:[%.*s]\n", __FUNCTION__,
+                msgname[msg->type], msg->type, msg->len, msg->len, payload);
+    }
+
+    switch (msg->type) {
+    case XS_DEBUG:
+        xen_reply_str(msg, XS_DEBUG, "OK");
+        break;
+    case XS_DIRECTORY:
+        vec = xs.directory(xs_guest, msg->tx_id, payload, &vlen);
+        xen_reply_vec(msg, msg->type, vec, vlen);
+        qemu_free(vec);
+        break;
+    case XS_READ:
+        val = xs.read(xs_guest, msg->tx_id, payload, &slen);
+        if (!val) {
+            xen_reply_str(msg, XS_ERROR, "ENOENT");
+        } else {
+            xen_reply_str(msg, msg->type, val);
+            qemu_free(val);
+        }
+        break;
+    case XS_WRITE:
+        arg2 = payload + strlen(payload) + 1;
+        alen = msg->len - (arg2 - payload);
+        if (xs.write(xs_guest, msg->tx_id, payload, arg2, alen)) {
+            xen_reply(msg, msg->type, NULL, 0);
+        } else {
+            xen_reply_str(msg, XS_ERROR, "EINVAL");
+        }
+        break;
+    case XS_WATCH:
+        arg2 = payload + strlen(payload) + 1;
+        if (xs.watch(xs_guest, payload, arg2)) {
+            xen_reply(msg, msg->type, NULL, 0);
+        } else {
+            xen_reply_str(msg, XS_ERROR, "EINVAL");
+        }
+        break;
+    case XS_UNWATCH:
+        arg2 = payload + strlen(payload) + 1;
+        if (xs.unwatch(xs_guest, payload, arg2)) {
+            xen_reply(msg, msg->type, NULL, 0);
+        } else {
+            xen_reply_str(msg, XS_ERROR, "EINVAL");
+        }
+        break;
+    case XS_TRANSACTION_START:
+        snprintf(id, sizeof(id), "%u", xs.transaction_start(xs_guest));
+        xen_reply_str(msg, msg->type, id);
+        break;
+    case XS_TRANSACTION_END:
+        if (payload[0] == 'T') {
+            /* commit */
+            rc = xs.transaction_end(xs_guest, msg->tx_id, 0);
+        } else if (payload[0] == 'F') {
+            /* abort */
+            rc = xs.transaction_end(xs_guest, msg->tx_id, 1);
+        } else {
+            /* Huh? */
+            xen_reply_str(msg, XS_ERROR, "EINVAL");
+            break;
+        }
+        if (rc) {
+            xen_reply(msg, msg->type, NULL, 0);
+        } else {
+            xen_reply_str(msg, XS_ERROR, "EINVAL");
+        }
+        break;
+    case XS_RM:
+        if (xs.rm(xs_guest, msg->tx_id, payload)) {
+            xen_reply(msg, msg->type, NULL, 0);
+        } else {
+            xen_reply_str(msg, XS_ERROR, "EINVAL");
+        }
+        break;
+    default:
+        fprintf(stderr, "xs guest: unknown msg type %d, payload %d\n",
+                msg->type, msg->len);
+        xen_reply_str(msg, XS_ERROR, "EIO");
+        break;
+    }
+    return sizeof(*msg) + msg->len;
+}
+
+static void xen_store_evtchn_event(void *opaque)
+{
+    struct xenstore_domain_interface *di = get_xdf();
+    evtchn_port_t port;
+    int rc;
+
+    port = xc_evtchn.pending(evtchndev);
+    if (port != evtchnport) {
+        fprintf(stderr,"%s: xc_evtchn.pending returned %d (expected %d)\n",
+                __FUNCTION__, port, evtchnport );
+        return;
+    }
+    xc_evtchn.unmask(evtchndev, port);
+
+    rc = domain_read(di, xs_buf + xs_len, sizeof(xs_buf) - xs_len);
+    if (rc <= 0) {
+        if (backlog) {
+            backlog_shift(di, NULL, 0);
+        }
+        return;
+    }
+    xs_len += rc;
+    if (debug) {
+        fprintf(stderr, "%s: got %d bytes\n", __FUNCTION__, rc);
+    }
+
+    rc = domain_read(di, xs_buf + xs_len, sizeof(xs_buf) - xs_len);
+    if (rc > 0) {
+        xs_len += rc;
+        if (debug) {
+            fprintf(stderr, "%s: got %d bytes (ring wrap, part #2)\n", 
__FUNCTION__, rc);
+        }
+    }
+
+    for (;;) {
+        rc = xen_handle_data(xs_buf, xs_len);
+        if (!rc) {
+            break;
+        }
+        if (rc == xs_len) {
+            xs_len = 0;
+            break;
+        }
+        memmove(xs_buf, xs_buf + rc, xs_len - rc);
+        xs_len -= rc;
+    }
+}
+
+static void xen_store_watch_event(void *opaque)
+{
+    char **vec;
+    unsigned int len = 1;
+
+    vec = xs.read_watch(xs_guest, &len);
+    if (!vec) {
+        return;
+    }
+    xen_reply_vec(NULL, XS_WATCH_EVENT, vec, 2);
+}
+
+/* ------------------------------------------------------------- */
+
+void xenner_guest_store_setup(uint64_t guest_mfn, evtchn_port_t guest_evtchn)
+{
+    xen_store_mfn = guest_mfn;
+
+    /* xenstore event channel */
+    evtchndev = xc_evtchn.open();
+    evtchnport = xc_evtchn.bind_interdomain(evtchndev, xen_domid,
+                                            guest_evtchn);
+    qemu_set_fd_handler(xc_evtchn.fd(evtchndev),
+                        xen_store_evtchn_event, NULL, NULL);
+
+    /* guest connection to xenstore  */
+    xs_guest = xs.daemon_open();
+    xs.domid(xs_guest, xen_domid);
+    qemu_set_fd_handler(xs.fileno(xs_guest),
+                        xen_store_watch_event, NULL, NULL);
+}
+
+/* this clears guest watches */
+void xenner_guest_store_reset(void)
+{
+    /* close */
+    qemu_set_fd_handler(xs.fileno(xs_guest), NULL, NULL, NULL);
+    xs.daemon_close(xs_guest);
+
+    /* reopen */
+    xs_guest = xs.daemon_open();
+    xs.domid(xs_guest, xen_domid);
+    qemu_set_fd_handler(xs.fileno(xs_guest),
+                        xen_store_watch_event, NULL, NULL);
+}
diff --git a/hw/xenner_libxenstore.c b/hw/xenner_libxenstore.c
new file mode 100644
index 0000000..4110a13
--- /dev/null
+++ b/hw/xenner_libxenstore.c
@@ -0,0 +1,709 @@
+/*
+ *  Copyright (C) Red Hat 2007
+ *  Copyright (C) Novell Inc. 2010
+ *
+ *  Author(s): Gerd Hoffmann <address@hidden>
+ *             Alexander Graf <address@hidden>
+ *
+ *  Xenner Core -- xenstored
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; under version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "xen.h"
+#include "xen_interfaces.h"
+#include "qemu-char.h"
+#include "console.h"
+#include "monitor.h"
+
+static int debug = 0;
+
+/* ------------------------------------------------------------- */
+
+#define XS_PATH_MAX 256
+
+struct node {
+    char                *path;
+    struct node         *parent;
+    char                *value;
+    int                 len;
+    int                 is_dir;
+    QTAILQ_ENTRY(node)  list;
+};
+static QTAILQ_HEAD(node_head, node) nodes = QTAILQ_HEAD_INITIALIZER(nodes);
+
+struct watch {
+    struct xs_handle    *who;
+    char                *path;
+    char                *token;
+    int                 offset;
+    QTAILQ_ENTRY(watch) list;
+};
+static QTAILQ_HEAD(watch_head, watch) watches = 
QTAILQ_HEAD_INITIALIZER(watches);
+
+struct event {
+    char                **vec;
+    QTAILQ_ENTRY(event)  list;
+};
+
+struct xs_handle {
+    int                 fd_read;
+    int                 fd_write;
+    int                 domid;
+    QTAILQ_HEAD(event_head, event) events;
+};
+
+/* ------------------------------------------------------------- */
+
+static struct node *node_find(const char *path)
+{
+    struct node *node;
+
+    QTAILQ_FOREACH(node, &nodes, list) {
+        if (!strcmp(path, node->path)) {
+            /* move to head of list */
+            QTAILQ_REMOVE(&nodes, node, list);
+            QTAILQ_INSERT_HEAD(&nodes, node, list);
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static struct node *node_add(struct node *parent, const char *path)
+{
+    struct node *node;
+
+    node = qemu_mallocz(sizeof(*node));
+    if (!node) {
+        goto err;
+    }
+    node->path = qemu_strdup(path);
+    if (!node->path) {
+        goto err;
+    }
+    node->parent = parent;
+    QTAILQ_INSERT_HEAD(&nodes, node, list);
+    return node;
+
+err:
+    qemu_free(node);
+    return NULL;
+}
+
+static void node_del(struct node *node)
+{
+    struct node *child;
+    int found_child;
+
+    do {
+        found_child = 0;
+        QTAILQ_FOREACH(child, &nodes, list) {
+            if (child->parent != node) {
+                continue;
+            }
+            found_child = 1;
+            node_del(child);
+            break;
+        }
+    } while (found_child);
+
+    if (debug) {
+        fprintf(stderr, "%s: %s\n", __FUNCTION__, node->path);
+    }
+    QTAILQ_REMOVE(&nodes, node, list);
+    qemu_free(node->path);
+    qemu_free(node->value);
+    qemu_free(node);
+}
+
+static void node_path(struct xs_handle *h, const char *path, char *dest, int 
len)
+{
+    if (path[0] == '/') {
+        snprintf(dest, len, "%s", path);
+    } else {
+        snprintf(dest, len, "/local/domain/%d/%s",
+                 h->domid, path);
+    }
+}
+
+static void parent_path(struct xs_handle *h, const char *path, char *dest, int 
len)
+{
+    char *c;
+
+    node_path(h, path, dest, len);
+    c = strrchr(dest, '/');
+    if (c) {
+        if (c == dest && c[1]) {
+            c++;
+        }
+        *c = 0;
+    }
+}
+
+static void fire_watch(struct node *node, struct watch *watch)
+{
+    struct event *event;
+    char *path, *token, *dst, byte = 0;
+    int r;
+
+    path  = node->path + watch->offset;
+    token = watch->token;
+
+    event = qemu_mallocz(sizeof(*event));
+    if (!event) {
+        return;
+    }
+    event->vec = qemu_malloc(sizeof(char*)*2 +
+                             strlen(path)    +
+                             strlen(token)   +
+                             2);
+    if (!event->vec) {
+        qemu_free(event);
+        return;
+    }
+    dst = (void*)(event->vec+2);
+    event->vec[0] = dst;
+    strcpy(dst, path);
+    dst += strlen(path)+1;
+    event->vec[1] = dst;
+    strcpy(dst, token);
+
+    QTAILQ_INSERT_TAIL(&watch->who->events, event, list);
+    r = write(watch->who->fd_write, &byte, 1);
+}
+
+static void fire_watches(struct node *node)
+{
+    struct watch *watch;
+    int nlen,wlen;
+
+    nlen = strlen(node->path);
+    QTAILQ_FOREACH(watch, &watches, list) {
+        wlen = strlen(watch->path);
+        if (wlen > nlen) {
+            continue;
+        }
+        if (strncmp(watch->path, node->path, wlen)) {
+            continue;
+        }
+        fire_watch(node, watch);
+    }
+}
+
+/* ------------------------------------------------------------- */
+
+static struct xs_handle *_qemu_open(void)
+{
+    struct xs_handle *h;
+    int fd[2];
+
+    h = qemu_mallocz(sizeof(*h));
+    if (!h) {
+        goto err;
+    }
+
+    if (pipe(fd)) {
+        goto err;
+    }
+    h->fd_read  = fd[0];
+    h->fd_write = fd[1];
+    QTAILQ_INIT(&h->events);
+    return h;
+
+err:
+    qemu_free(h);
+    return NULL;
+}
+
+static int qemu_domid(struct xs_handle *h, int domid)
+{
+    h->domid = domid;
+    return 0;
+}
+
+static void qemu_close(struct xs_handle *h)
+{
+    struct watch *watch, *check;
+    struct event *event;
+
+    watch = QTAILQ_FIRST(&watches);
+    while (watch) {
+        check = watch;
+        watch = QTAILQ_NEXT(watch, list);
+        if (h != check->who) {
+            continue;
+        }
+        QTAILQ_REMOVE(&watches, check, list);
+        free(check);
+    }
+
+    while ((event = QTAILQ_FIRST(&h->events))) {
+        QTAILQ_REMOVE(&h->events, event, list);
+        free(event->vec);
+        free(event);
+    }
+
+    close(h->fd_read);
+    close(h->fd_write);
+    qemu_free(h);
+}
+
+static char **qemu_directory(struct xs_handle *h, xs_transaction_t t,
+                             const char *path, unsigned int *num)
+{
+    char npath[XS_PATH_MAX];
+    struct node *parent, *node;
+    int i,pos,size,plen,nlen;
+    char **vec, *dst, *name;
+
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s: %s\n", __FUNCTION__, path);
+    }
+    node_path(h, path, npath, sizeof(npath));
+    plen = strlen(npath);
+    parent = node_find(npath);
+    if (!parent) {
+        return NULL;
+    }
+    if (!parent->is_dir) {
+        return NULL;
+    }
+
+    /* count */
+    *num = 0;
+    size = 0;
+    QTAILQ_FOREACH(node, &nodes, list) {
+        if (node->parent != parent) {
+            continue;
+        }
+        name = node->path + plen + 1;
+        nlen = strlen(name)+1;
+        (*num)++;
+        size += nlen;
+    }
+    if (!*num) {
+        return NULL;
+    }
+
+    /* alloc memory */
+    vec = qemu_malloc(*num * sizeof(char*) + size);
+    dst = (void*)(vec + (*num));
+
+    /* fill data */
+    i = 0;
+    pos = 0;
+    QTAILQ_FOREACH(node, &nodes, list) {
+        if (node->parent != parent) {
+            continue;
+        }
+        name = node->path + plen + 1;
+        nlen = strlen(name)+1;
+        vec[i] = dst + pos;
+        memcpy(vec[i], name, nlen);
+        i++;
+        pos += nlen;
+    }
+    return vec;
+}
+
+static void *qemu_read(struct xs_handle *h, xs_transaction_t t,
+                       const char *path, unsigned int *len)
+{
+    char npath[XS_PATH_MAX];
+    struct node *node;
+    char *ret;
+
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s: %s\n", __FUNCTION__, path);
+    }
+    node_path(h, path, npath, sizeof(npath));
+    node = node_find(npath);
+    if (!node) {
+        *len = 0;
+        return NULL;
+    }
+    ret = qemu_malloc(node->len+1);
+    memcpy(ret, node->value, node->len);
+    ret[node->len] = 0;
+    *len = node->len;
+    return ret;
+}
+
+static bool qemu_mkdir(struct xs_handle *h, xs_transaction_t t,
+                       const char *path)
+{
+    char npath[XS_PATH_MAX], ppath[XS_PATH_MAX];
+    struct node *node;
+
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s: %s\n", __FUNCTION__, path);
+    }
+    node_path(h, path, npath, sizeof(npath));
+    node = node_find(npath);
+    if (node) {
+        return node->is_dir ? true : false;
+    }
+    parent_path(h, path, ppath, sizeof(ppath));
+    if (strlen(ppath)) {
+        node = node_find(ppath);
+        if (!node) {
+            if (!qemu_mkdir(h, t, ppath)) {
+                return false;
+            }
+            node = node_find(ppath);
+        }
+    } else {
+        node = NULL;
+    }
+    node = node_add(node, npath);
+    if (!node) {
+        return false;
+    }
+    node->is_dir = 1;
+    fire_watches(node);
+    return true;
+}
+
+static bool qemu_write(struct xs_handle *h, xs_transaction_t t,
+                       const char *path, const void *data, unsigned int len)
+{
+    char npath[XS_PATH_MAX], ppath[XS_PATH_MAX];
+    struct node *node;
+
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s: %s = %.*s\n", __FUNCTION__, path, len, 
(char*)data);
+    }
+    node_path(h, path, npath, sizeof(npath));
+    if (h->domid != 0) {
+        /* simple access control: guest can write to its own tree only */
+        int domid;
+        if (sscanf(npath, "/local/domain/%d", &domid) != 1) {
+            fprintf(stderr, "deny guest access: %s\n", npath);
+            return false;
+        }
+        if (domid != h->domid) {
+            fprintf(stderr, "deny guest access (domid %d): %s\n", h->domid, 
npath);
+            return false;
+        }
+    }
+    node = node_find(npath);
+    if (!node) {
+        parent_path(h, path, ppath, sizeof(ppath));
+        node = node_find(ppath);
+        if (!node) {
+            if (!qemu_mkdir(h, t, ppath)) {
+                return false;
+            }
+            node = node_find(ppath);
+        }
+        if (!node->is_dir) {
+            return false;
+        }
+        node = node_add(node, npath);
+    }
+    node->len = 0;
+    qemu_free(node->value);
+    if (len) {
+        node->value = qemu_malloc(len);
+        if (!node->value) {
+            return false;
+        }
+    }
+    node->len = len;
+    memcpy(node->value, data, len);
+    if (debug) {
+        fprintf(stderr, "xs: new value: %s = %.*s (%d)\n",
+                npath, len, (char*)data, len);
+    }
+    fire_watches(node);
+    return true;
+}
+
+static bool qemu_rm(struct xs_handle *h, xs_transaction_t t,
+                    const char *path)
+{
+    char npath[XS_PATH_MAX];
+    struct node *node;
+
+    if (debug) {
+        fprintf(stderr, "xs: %s: %s\n", __FUNCTION__, path);
+    }
+
+    node_path(h, path, npath, sizeof(npath));
+    node = node_find(npath);
+    if (node) {
+        fire_watches(node);
+        node_del(node);
+    }
+    return false;
+}
+
+static struct xs_permissions *qemu_get_permissions(struct xs_handle *h,
+                                                   xs_transaction_t t,
+                                                   const char *path, unsigned 
int *num)
+{
+    /* we don't implement permissions */
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s: %s\n", __FUNCTION__, path);
+    }
+    return NULL;
+}
+
+static bool qemu_set_permissions(struct xs_handle *h, xs_transaction_t t,
+                                 const char *path, struct xs_permissions 
*perms,
+                                 unsigned int num_perms)
+{
+    /* we don't implement permissions */
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s: %s\n", __FUNCTION__, path);
+    }
+    return true;
+}
+
+static bool qemu_watch(struct xs_handle *h, const char *path, const char 
*token)
+{
+    char npath[XS_PATH_MAX];
+    struct node *node;
+    struct watch *w;
+
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s: %s token %s\n", __FUNCTION__, path, token);
+    }
+    node_path(h, path, npath, sizeof(npath));
+    w = qemu_mallocz(sizeof(*w));
+    if (!w) {
+        goto err;
+    }
+    w->path = qemu_strdup(npath);
+    if (!w->path) {
+        goto err;
+    }
+    w->token = qemu_strdup(token);
+    if (!w->token) {
+        goto err;
+    }
+    w->who = h;
+    if (path[0] != '/') {
+        /* relative path offset */
+        w->offset = strlen(npath) - strlen(path);
+    }
+    QTAILQ_INSERT_TAIL(&watches, w, list);
+    if (debug) {
+        fprintf(stderr, "xs: new watch: %s (rel %s, token %s)\n",
+                w->path, w->offset ? w->path + w->offset : "-", w->token);
+    }
+    node = node_find(npath);
+    if (node) {
+        fire_watch(node, w);
+    }
+    return true;
+
+err:
+    if (w) {
+        qemu_free(w->path);
+        qemu_free(w->token);
+        qemu_free(w);
+    }
+    return false;
+}
+
+static int qemu_fileno(struct xs_handle *h)
+{
+    return h->fd_read;
+}
+
+static char **qemu_read_watch(struct xs_handle *h, unsigned int *num)
+{
+    struct event *event;
+    char **vec;
+    char byte;
+    int r;
+
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s\n", __FUNCTION__);
+    }
+    r = read(h->fd_read, &byte, 1);
+    if (QTAILQ_EMPTY(&h->events)) {
+        fprintf(stderr, "%s: Huh? fd readable but no event in list?\n",
+                __FUNCTION__);
+        return NULL;
+    }
+    event = QTAILQ_FIRST(&h->events);
+    if (debug) {
+        fprintf(stderr, "xs: get event: %s %s\n",
+                event->vec[0], event->vec[1]);
+    }
+    vec = event->vec;
+    QTAILQ_REMOVE(&h->events, event, list);
+    qemu_free(event);
+    *num = 1;
+    return vec;
+}
+
+static bool qemu_unwatch(struct xs_handle *h, const char *path, const char 
*token)
+{
+    struct watch *watch;
+
+    QTAILQ_FOREACH(watch, &watches, list) {
+        if (strcmp(watch->path + watch->offset, path)) {
+            continue;
+        }
+        if (strcmp(watch->token, token)) {
+            continue;
+        }
+        QTAILQ_REMOVE(&watches, watch, list);
+        qemu_free(watch->path);
+        qemu_free(watch->token);
+        qemu_free(watch);
+        return true;
+    }
+    return false;
+}
+
+static xs_transaction_t qemu_transaction_start(struct xs_handle *h)
+{
+    /* Note: transactions are not implemented */
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s\n", __FUNCTION__);
+    }
+    return 42;
+}
+
+static bool qemu_transaction_end(struct xs_handle *h, xs_transaction_t t,
+                                 bool abort)
+{
+    /* Note: transactions are not implemented */
+    if (debug > 1) {
+        fprintf(stderr, "xs: %s\n", __FUNCTION__);
+    }
+    return true;
+}
+
+static bool qemu_introduce_domain(struct xs_handle *h,
+                                  unsigned int domid,
+                                  unsigned long mfn,
+                                  unsigned int eventchn)
+{
+    /* not needed for us */
+    fprintf(stderr, "xs: %s: not implemented\n", __FUNCTION__);
+    return false;
+}
+
+static bool qemu_resume_domain(struct xs_handle *h, unsigned int domid)
+{
+    /* not needed for us */
+    fprintf(stderr, "xs: %s: not implemented\n", __FUNCTION__);
+    return false;
+}
+
+static bool qemu_release_domain(struct xs_handle *h, unsigned int domid)
+{
+    /* not needed for us */
+    fprintf(stderr, "xs: %s: not implemented\n", __FUNCTION__);
+    return false;
+}
+
+static char *qemu_get_domain_path(struct xs_handle *h, unsigned int domid)
+{
+    char *path;
+
+    path = malloc(32);
+    if (!path) {
+        return NULL;
+    }
+    snprintf(path, 32, "/local/domain/%d", domid);
+    return path;
+}
+
+static bool qemu_is_domain_introduced(struct xs_handle *h, unsigned int domid)
+{
+    /* not needed for us */
+    fprintf(stderr, "xs: %s: not implemented\n", __FUNCTION__);
+    return false;
+}
+
+struct XenStoreOps xs_xenner = {
+    .daemon_open           = _qemu_open,
+    .domain_open           = _qemu_open,
+    .daemon_open_readonly  = _qemu_open,
+    .domid                 = qemu_domid,
+    .daemon_close          = qemu_close,
+    .directory             = qemu_directory,
+    .read                  = qemu_read,
+    .write                 = qemu_write,
+    .mkdir                 = qemu_mkdir,
+    .rm                    = qemu_rm,
+    .get_permissions       = qemu_get_permissions,
+    .set_permissions       = qemu_set_permissions,
+    .watch                 = qemu_watch,
+    .fileno                = qemu_fileno,
+    .read_watch            = qemu_read_watch,
+    .unwatch               = qemu_unwatch,
+    .transaction_start     = qemu_transaction_start,
+    .transaction_end       = qemu_transaction_end,
+    .introduce_domain      = qemu_introduce_domain,
+    .resume_domain         = qemu_resume_domain,
+    .release_domain        = qemu_release_domain,
+    .get_domain_path       = qemu_get_domain_path,
+    .is_domain_introduced  = qemu_is_domain_introduced,
+};
+
+/* ------------------------------------------------------------- */
+
+#if 0
+
+static void print_node(Monitor *mon, struct node *node, int indent)
+{
+    struct node *child;
+    int width;
+    char *name;
+
+    width = 40 - indent;
+    name = strrchr(node->path,'/');
+    if (strcmp(name, "/")) {
+        name++;
+    }
+    monitor_printf(mon, "%*s%-*.*s = ", indent, "", width, width, name);
+    if (node->is_dir) {
+        monitor_printf(mon,"<DIR>\n");
+        QTAILQ_FOREACH(child, &nodes, list) {
+            if (child->parent != node) {
+                continue;
+            }
+            print_node(mon, child, indent+2);
+        }
+    } else {
+        monitor_printf(mon, "\"%.*s\"\n", node->len, node->value);
+    }
+}
+
+void do_info_xenstore(Monitor *mon)
+{
+    struct node *root;
+
+    if (xen_mode != XEN_EMULATE) {
+        monitor_printf(mon, "Not emulating xenstore (use 
/usr/bin/xenstore-ls).\n");
+        return;
+    }
+    root = node_find("/");
+    if (!root) {
+        monitor_printf(mon, "Xenstore is empty.\n");
+        return;
+    }
+    print_node(mon, root, 0);
+}
+
+#endif
+
-- 
1.6.0.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]