new file mode 100644
@@ -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);
+}
new file mode 100644
@@ -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
+
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 <agraf@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