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 <kraxel@redhat.com>
+ *             Alexander Graf <agraf@suse.de>
+ *
+ *  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 <kraxel@redhat.com>
+ *             Alexander Graf <agraf@suse.de>
+ *
+ *  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
+
