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 <ag...@suse.de> --- 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 <kra...@redhat.com> + * Alexander Graf <ag...@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 <kra...@redhat.com> + * Alexander Graf <ag...@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 + -- 1.6.0.2