tools/libs/store/xenstore.pc
tools/libs/store/xs_lib.c
tools/libs/store/include/xenstore_lib.h
+tools/libs/vchan/headers.chk
+tools/libs/vchan/libxenvchan.map
+tools/libs/vchan/xenvchan.pc
tools/console/xenconsole
tools/console/xenconsoled
tools/console/client/_paths.h
tools/include/xen-xsm/*
tools/include/xen-foreign/*.(c|h|size)
tools/include/xen-foreign/checker
-tools/libvchan/xenvchan.pc
tools/libxl/_libxl.api-for-check
tools/libxl/*.api-ok
tools/libxl/*.pc
tools/misc/xenwatchdogd
tools/misc/xen-hvmcrash
tools/misc/xen-lowmemd
-tools/libvchan/vchan-node[12]
-tools/libvchan/vchan-socket-proxy
+tools/vchan/vchan-node[12]
+tools/vchan/vchan-socket-proxy
tools/ocaml/*/.ocamldep.make
tools/ocaml/*/*.cm[ixao]
tools/ocaml/*/*.cmxa
SUBDIRS-y += xenstat
SUBDIRS-$(CONFIG_NetBSD) += xenbackendd
SUBDIRS-y += libfsimage
-SUBDIRS-$(CONFIG_Linux) += libvchan
+SUBDIRS-$(CONFIG_Linux) += vchan
# do not recurse in to a dir we are about to delete
ifneq "$(MAKECMDGOALS)" "distclean"
# Currently libxlutil lives in the same directory as libxenlight
XEN_libxlutil = $(XEN_libxenlight)
XEN_libxenstat = $(XEN_ROOT)/tools/xenstat/libxenstat/src
-XEN_libxenvchan = $(XEN_ROOT)/tools/libvchan
CFLAGS_xeninclude = -I$(XEN_INCLUDE)
LDLIBS_libxenstat = $(SHDEPS_libxenstat) $(XEN_libxenstat)/libxenstat$(libextension)
SHLIB_libxenstat = $(SHDEPS_libxenstat) -Wl,-rpath-link=$(XEN_libxenstat)
-CFLAGS_libxenvchan = -I$(XEN_libxenvchan) $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn)
-SHDEPS_libxenvchan = $(SHLIB_libxentoollog) $(SHLIB_libxenstore) $(SHLIB_libxenevtchn) $(SHLIB_libxengnttab)
-LDLIBS_libxenvchan = $(SHDEPS_libxenvchan) $(XEN_libxenvchan)/libxenvchan$(libextension)
-SHLIB_libxenvchan = $(SHDEPS_libxenvchan) -Wl,-rpath-link=$(XEN_libxenvchan)
-
ifeq ($(debug),y)
# Disable optimizations
CFLAGS += -O0 -fno-omit-frame-pointer
SUBDIRS-y += guest
SUBDIRS-y += hypfs
SUBDIRS-y += store
+SUBDIRS-$(CONFIG_Linux) += vchan
ifeq ($(CONFIG_RUMP),y)
SUBDIRS-y := toolcore
USELIBS_guest := evtchn ctrl
LIBS_LIBS += store
USELIBS_store := toolcore
+LIBS_LIBS += vchan
+USELIBS_vchan := toollog store gnttab evtchn
--- /dev/null
+XEN_ROOT = $(CURDIR)/../../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+CFLAGS += $(CFLAGS_libxenctrl)
+
+LIBHEADER := libxenvchan.h
+
+SRCS-y += init.c
+SRCS-y += io.c
+
+include $(XEN_ROOT)/tools/libs/libs.mk
+
+$(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(XEN_libxenvchan)/include
+$(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude)
+
+clean: cleanlocal
+
+.PHONY: cleanlocal
+cleanlocal:
+ rm -f libxenvchan.map
--- /dev/null
+/**
+ * @file
+ * @section AUTHORS
+ *
+ * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ *
+ * Authors:
+ * Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ * Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ *
+ * @section LICENSE
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section DESCRIPTION
+ *
+ * Originally borrowed from the Qubes OS Project, http://www.qubes-os.org,
+ * this code has been substantially rewritten to use the gntdev and gntalloc
+ * devices instead of raw MFNs and map_foreign_range.
+ *
+ * This is a library for inter-domain communication. A standard Xen ring
+ * buffer is used, with a datagram-based interface built on top. The grant
+ * reference and event channels are shared in XenStore under the path
+ * /local/domain/<srv-id>/data/vchan/<cli-id>/<port>/{ring-ref,event-channel}
+ *
+ * The ring.h macros define an asymmetric interface to a shared data structure
+ * that assumes all rings reside in a single contiguous memory space. This is
+ * not suitable for vchan because the interface to the ring is symmetric except
+ * for the setup. Unlike the producer-consumer rings defined in ring.h, the
+ * size of the rings used in vchan are determined at execution time instead of
+ * compile time, so the macros in ring.h cannot be used to access the rings.
+ */
+
+#include <xen/io/libxenvchan.h>
+#include <xen/xen.h>
+#include <xen/sys/evtchn.h>
+#include <xenevtchn.h>
+#include <xengnttab.h>
+
+/* Callers who don't care don't need to #include <xentoollog.h> */
+struct xentoollog_logger;
+
+struct libxenvchan_ring {
+ /* Pointer into the shared page. Offsets into buffer. */
+ struct ring_shared* shr;
+ /* ring data; may be its own shared page(s) depending on order */
+ void* buffer;
+ /**
+ * The size of the ring is (1 << order); offsets wrap around when they
+ * exceed this. This copy is required because we can't trust the order
+ * in the shared page to remain constant.
+ */
+ int order;
+};
+
+/**
+ * struct libxenvchan: control structure passed to all library calls
+ */
+struct libxenvchan {
+ /* Mapping handle for shared ring page */
+ union {
+ xengntshr_handle *gntshr; /* for server */
+ xengnttab_handle *gnttab; /* for client */
+ };
+ /* Pointer to shared ring page */
+ struct vchan_interface *ring;
+ /* event channel interface */
+ xenevtchn_handle *event;
+ uint32_t event_port;
+ /* informative flags: are we acting as server? */
+ int is_server:1;
+ /* true if server remains active when client closes (allows reconnection) */
+ int server_persist:1;
+ /* true if operations should block instead of returning 0 */
+ int blocking:1;
+ /* communication rings */
+ struct libxenvchan_ring read, write;
+};
+
+/**
+ * Set up a vchan, including granting pages
+ * @param logger Logger for libxc errors
+ * @param domain The peer domain that will be connecting
+ * @param xs_path Base xenstore path for storing ring/event data
+ * @param send_min The minimum size (in bytes) of the send ring (left)
+ * @param recv_min The minimum size (in bytes) of the receive ring (right)
+ * @return The structure, or NULL in case of an error
+ */
+struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger,
+ int domain, const char* xs_path,
+ size_t read_min, size_t write_min);
+/**
+ * Connect to an existing vchan. Note: you can reconnect to an existing vchan
+ * safely, however no locking is performed, so you must prevent multiple clients
+ * from connecting to a single server.
+ *
+ * @param logger Logger for libxc errors
+ * @param domain The peer domain to connect to
+ * @param xs_path Base xenstore path for storing ring/event data
+ * @return The structure, or NULL in case of an error
+ */
+struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger,
+ int domain, const char* xs_path);
+/**
+ * Close a vchan. This deallocates the vchan and attempts to free its
+ * resources. The other side is notified of the close, but can still read any
+ * data pending prior to the close.
+ */
+void libxenvchan_close(struct libxenvchan *ctrl);
+
+/**
+ * Packet-based receive: always reads exactly $size bytes.
+ * @param ctrl The vchan control structure
+ * @param data Buffer for data that was read
+ * @param size Size of the buffer and amount of data to read
+ * @return -1 on error, 0 if nonblocking and insufficient data is available, or $size
+ */
+int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size);
+/**
+ * Stream-based receive: reads as much data as possible.
+ * @param ctrl The vchan control structure
+ * @param data Buffer for data that was read
+ * @param size Size of the buffer
+ * @return -1 on error, otherwise the amount of data read (which may be zero if
+ * the vchan is nonblocking)
+ */
+int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size);
+/**
+ * Packet-based send: send entire buffer if possible
+ * @param ctrl The vchan control structure
+ * @param data Buffer for data to send
+ * @param size Size of the buffer and amount of data to send
+ * @return -1 on error, 0 if nonblocking and insufficient space is available, or $size
+ */
+int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size);
+/**
+ * Stream-based send: send as much data as possible.
+ * @param ctrl The vchan control structure
+ * @param data Buffer for data to send
+ * @param size Size of the buffer
+ * @return -1 on error, otherwise the amount of data sent (which may be zero if
+ * the vchan is nonblocking)
+ */
+int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size);
+/**
+ * Waits for reads or writes to unblock, or for a close
+ */
+int libxenvchan_wait(struct libxenvchan *ctrl);
+/**
+ * Returns the event file descriptor for this vchan. When this FD is readable,
+ * libxenvchan_wait() will not block, and the state of the vchan has changed since
+ * the last invocation of libxenvchan_wait().
+ */
+int libxenvchan_fd_for_select(struct libxenvchan *ctrl);
+/**
+ * Query the state of the vchan shared page:
+ * return 0 when one side has called libxenvchan_close() or crashed
+ * return 1 when both sides are open
+ * return 2 [server only] when no client has yet connected
+ */
+int libxenvchan_is_open(struct libxenvchan* ctrl);
+/** Amount of data ready to read, in bytes */
+int libxenvchan_data_ready(struct libxenvchan *ctrl);
+/** Amount of data it is possible to send without blocking */
+int libxenvchan_buffer_space(struct libxenvchan *ctrl);
--- /dev/null
+/**
+ * @file
+ * @section AUTHORS
+ *
+ * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ *
+ * Authors:
+ * Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ * Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ *
+ * @section LICENSE
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section DESCRIPTION
+ *
+ * This file contains the setup code used to establish the ring buffer.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/user.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <xenstore.h>
+#include <xen/xen.h>
+#include <xen/sys/evtchn.h>
+#include <xen/sys/gntalloc.h>
+#include <xen/sys/gntdev.h>
+#include <libxenvchan.h>
+
+#ifndef PAGE_SHIFT
+#define PAGE_SHIFT 12
+#endif
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+#define SMALL_RING_SHIFT 10
+#define LARGE_RING_SHIFT 11
+
+#define MAX_SMALL_RING (1 << SMALL_RING_SHIFT)
+#define SMALL_RING_OFFSET 1024
+#define MAX_LARGE_RING (1 << LARGE_RING_SHIFT)
+#define LARGE_RING_OFFSET 2048
+
+// if you go over this size, you'll have too many grants to fit in the shared page.
+#define MAX_RING_SHIFT 20
+#define MAX_RING_SIZE (1 << MAX_RING_SHIFT)
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#define max(a,b) ((a > b) ? a : b)
+
+static int init_gnt_srv(struct libxenvchan *ctrl, int domain)
+{
+ int pages_left = ctrl->read.order >= PAGE_SHIFT ? 1 << (ctrl->read.order - PAGE_SHIFT) : 0;
+ int pages_right = ctrl->write.order >= PAGE_SHIFT ? 1 << (ctrl->write.order - PAGE_SHIFT) : 0;
+ uint32_t ring_ref = -1;
+ void *ring;
+
+ ring = xengntshr_share_page_notify(ctrl->gntshr, domain,
+ &ring_ref, 1, offsetof(struct vchan_interface, srv_live),
+ ctrl->event_port);
+
+ if (!ring)
+ goto out;
+
+ memset(ring, 0, PAGE_SIZE);
+
+ ctrl->ring = ring;
+ ctrl->read.shr = &ctrl->ring->left;
+ ctrl->write.shr = &ctrl->ring->right;
+ ctrl->ring->left_order = ctrl->read.order;
+ ctrl->ring->right_order = ctrl->write.order;
+ ctrl->ring->cli_live = 2;
+ ctrl->ring->srv_live = 1;
+ ctrl->ring->cli_notify = VCHAN_NOTIFY_WRITE;
+
+ switch (ctrl->read.order) {
+ case SMALL_RING_SHIFT:
+ ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
+ break;
+ case LARGE_RING_SHIFT:
+ ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
+ break;
+ default:
+ ctrl->read.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
+ pages_left, ctrl->ring->grants, 1);
+ if (!ctrl->read.buffer)
+ goto out_ring;
+ }
+
+ switch (ctrl->write.order) {
+ case SMALL_RING_SHIFT:
+ ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
+ break;
+ case LARGE_RING_SHIFT:
+ ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
+ break;
+ default:
+ ctrl->write.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
+ pages_right, ctrl->ring->grants + pages_left, 1);
+ if (!ctrl->write.buffer)
+ goto out_unmap_left;
+ }
+
+out:
+ return ring_ref;
+out_unmap_left:
+ if (pages_left)
+ xengntshr_unshare(ctrl->gntshr, ctrl->read.buffer, pages_left);
+out_ring:
+ xengntshr_unshare(ctrl->gntshr, ring, 1);
+ ring_ref = -1;
+ ctrl->ring = NULL;
+ ctrl->write.order = ctrl->read.order = 0;
+ goto out;
+}
+
+static int init_gnt_cli(struct libxenvchan *ctrl, int domain, uint32_t ring_ref)
+{
+ int rv = -1;
+ uint32_t *grants;
+
+ ctrl->ring = xengnttab_map_grant_ref_notify(ctrl->gnttab,
+ domain, ring_ref, PROT_READ|PROT_WRITE,
+ offsetof(struct vchan_interface, cli_live), ctrl->event_port);
+
+ if (!ctrl->ring)
+ goto out;
+
+ ctrl->write.order = ctrl->ring->left_order;
+ ctrl->read.order = ctrl->ring->right_order;
+ ctrl->write.shr = &ctrl->ring->left;
+ ctrl->read.shr = &ctrl->ring->right;
+ if (ctrl->write.order < SMALL_RING_SHIFT || ctrl->write.order > MAX_RING_SHIFT)
+ goto out_unmap_ring;
+ if (ctrl->read.order < SMALL_RING_SHIFT || ctrl->read.order > MAX_RING_SHIFT)
+ goto out_unmap_ring;
+ if (ctrl->read.order == ctrl->write.order && ctrl->read.order < PAGE_SHIFT)
+ goto out_unmap_ring;
+
+ grants = ctrl->ring->grants;
+
+ switch (ctrl->write.order) {
+ case SMALL_RING_SHIFT:
+ ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
+ break;
+ case LARGE_RING_SHIFT:
+ ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
+ break;
+ default:
+ {
+ int pages_left = 1 << (ctrl->write.order - PAGE_SHIFT);
+ ctrl->write.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
+ pages_left, domain, grants, PROT_READ|PROT_WRITE);
+ if (!ctrl->write.buffer)
+ goto out_unmap_ring;
+ grants += pages_left;
+ }
+ }
+
+ switch (ctrl->read.order) {
+ case SMALL_RING_SHIFT:
+ ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
+ break;
+ case LARGE_RING_SHIFT:
+ ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
+ break;
+ default:
+ {
+ int pages_right = 1 << (ctrl->read.order - PAGE_SHIFT);
+ ctrl->read.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
+ pages_right, domain, grants, PROT_READ);
+ if (!ctrl->read.buffer)
+ goto out_unmap_left;
+ }
+ }
+
+ rv = 0;
+ out:
+ return rv;
+ out_unmap_left:
+ if (ctrl->write.order >= PAGE_SHIFT)
+ xengnttab_unmap(ctrl->gnttab, ctrl->write.buffer,
+ 1 << (ctrl->write.order - PAGE_SHIFT));
+ out_unmap_ring:
+ xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
+ ctrl->ring = 0;
+ ctrl->write.order = ctrl->read.order = 0;
+ rv = -1;
+ goto out;
+}
+
+static int init_evt_srv(struct libxenvchan *ctrl, int domain,
+ struct xentoollog_logger *logger)
+{
+ xenevtchn_port_or_error_t port;
+
+ ctrl->event = xenevtchn_open(logger, 0);
+ if (!ctrl->event)
+ return -1;
+
+ port = xenevtchn_bind_unbound_port(ctrl->event, domain);
+ if (port < 0)
+ goto fail;
+ ctrl->event_port = port;
+
+ if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
+ goto fail;
+
+ return 0;
+
+fail:
+ if (port >= 0)
+ xenevtchn_unbind(ctrl->event, port);
+
+ xenevtchn_close(ctrl->event);
+ ctrl->event = NULL;
+
+ return -1;
+}
+
+static int init_xs_srv(struct libxenvchan *ctrl, int domain, const char* xs_base, int ring_ref)
+{
+ int ret = -1;
+ struct xs_handle *xs;
+ struct xs_permissions perms[2];
+ char buf[64];
+ char ref[16];
+ char* domid_str = NULL;
+ xs_transaction_t xs_trans = XBT_NULL;
+ xs = xs_domain_open();
+ if (!xs)
+ goto fail;
+ domid_str = xs_read(xs, 0, "domid", NULL);
+ if (!domid_str)
+ goto fail_xs_open;
+
+ // owner domain is us
+ perms[0].id = atoi(domid_str);
+ // permissions for domains not listed = none
+ perms[0].perms = XS_PERM_NONE;
+ // other domains
+ perms[1].id = domain;
+ perms[1].perms = XS_PERM_READ;
+
+retry_transaction:
+ xs_trans = xs_transaction_start(xs);
+ if (!xs_trans)
+ goto fail_xs_open;
+
+ snprintf(ref, sizeof ref, "%d", ring_ref);
+ snprintf(buf, sizeof buf, "%s/ring-ref", xs_base);
+ if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
+ goto fail_xs_open;
+ if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
+ goto fail_xs_open;
+
+ snprintf(ref, sizeof ref, "%d", ctrl->event_port);
+ snprintf(buf, sizeof buf, "%s/event-channel", xs_base);
+ if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
+ goto fail_xs_open;
+ if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
+ goto fail_xs_open;
+
+ if (!xs_transaction_end(xs, xs_trans, 0)) {
+ if (errno == EAGAIN)
+ goto retry_transaction;
+ } else {
+ ret = 0;
+ }
+ fail_xs_open:
+ free(domid_str);
+ xs_daemon_close(xs);
+ fail:
+ return ret;
+}
+
+static int min_order(size_t siz)
+{
+ int rv = PAGE_SHIFT;
+ while (siz > (1 << rv))
+ rv++;
+ return rv;
+}
+
+struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger,
+ int domain, const char* xs_path,
+ size_t left_min, size_t right_min)
+{
+ struct libxenvchan *ctrl;
+ int ring_ref;
+ if (left_min > MAX_RING_SIZE || right_min > MAX_RING_SIZE)
+ return 0;
+
+ ctrl = malloc(sizeof(*ctrl));
+ if (!ctrl)
+ return 0;
+
+ ctrl->ring = NULL;
+ ctrl->event = NULL;
+ ctrl->is_server = 1;
+ ctrl->server_persist = 0;
+
+ ctrl->read.order = min_order(left_min);
+ ctrl->write.order = min_order(right_min);
+
+ // if we can avoid allocating extra pages by using in-page rings, do so
+ if (left_min <= MAX_SMALL_RING && right_min <= MAX_LARGE_RING) {
+ ctrl->read.order = SMALL_RING_SHIFT;
+ ctrl->write.order = LARGE_RING_SHIFT;
+ } else if (left_min <= MAX_LARGE_RING && right_min <= MAX_SMALL_RING) {
+ ctrl->read.order = LARGE_RING_SHIFT;
+ ctrl->write.order = SMALL_RING_SHIFT;
+ } else if (left_min <= MAX_LARGE_RING) {
+ ctrl->read.order = LARGE_RING_SHIFT;
+ } else if (right_min <= MAX_LARGE_RING) {
+ ctrl->write.order = LARGE_RING_SHIFT;
+ }
+
+ ctrl->gntshr = xengntshr_open(logger, 0);
+ if (!ctrl->gntshr) {
+ free(ctrl);
+ return 0;
+ }
+
+ if (init_evt_srv(ctrl, domain, logger))
+ goto out;
+ ring_ref = init_gnt_srv(ctrl, domain);
+ if (ring_ref < 0)
+ goto out;
+ if (init_xs_srv(ctrl, domain, xs_path, ring_ref))
+ goto out;
+ return ctrl;
+out:
+ libxenvchan_close(ctrl);
+ return 0;
+}
+
+static int init_evt_cli(struct libxenvchan *ctrl, int domain,
+ struct xentoollog_logger *logger)
+{
+ xenevtchn_port_or_error_t port;
+
+ ctrl->event = xenevtchn_open(logger, 0);
+ if (!ctrl->event)
+ return -1;
+
+ port = xenevtchn_bind_interdomain(ctrl->event,
+ domain, ctrl->event_port);
+ if (port < 0)
+ goto fail;
+ ctrl->event_port = port;
+
+ if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
+ goto fail;
+
+ return 0;
+
+fail:
+ if (port >= 0)
+ xenevtchn_unbind(ctrl->event, port);
+
+ xenevtchn_close(ctrl->event);
+ ctrl->event = NULL;
+
+ return -1;
+}
+
+
+struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger,
+ int domain, const char* xs_path)
+{
+ struct libxenvchan *ctrl = malloc(sizeof(struct libxenvchan));
+ struct xs_handle *xs = NULL;
+ char buf[64];
+ char *ref;
+ int ring_ref;
+ unsigned int len;
+
+ if (!ctrl)
+ return 0;
+ ctrl->ring = NULL;
+ ctrl->event = NULL;
+ ctrl->gnttab = NULL;
+ ctrl->write.order = ctrl->read.order = 0;
+ ctrl->is_server = 0;
+
+ xs = xs_daemon_open();
+ if (!xs)
+ xs = xs_domain_open();
+ if (!xs)
+ goto fail;
+
+// find xenstore entry
+ snprintf(buf, sizeof buf, "%s/ring-ref", xs_path);
+ ref = xs_read(xs, 0, buf, &len);
+ if (!ref)
+ goto fail;
+ ring_ref = atoi(ref);
+ free(ref);
+ if (!ring_ref)
+ goto fail;
+ snprintf(buf, sizeof buf, "%s/event-channel", xs_path);
+ ref = xs_read(xs, 0, buf, &len);
+ if (!ref)
+ goto fail;
+ ctrl->event_port = atoi(ref);
+ free(ref);
+ if (!ctrl->event_port)
+ goto fail;
+
+ ctrl->gnttab = xengnttab_open(logger, 0);
+ if (!ctrl->gnttab)
+ goto fail;
+
+// set up event channel
+ if (init_evt_cli(ctrl, domain, logger))
+ goto fail;
+
+// set up shared page(s)
+ if (init_gnt_cli(ctrl, domain, ring_ref))
+ goto fail;
+
+ ctrl->ring->cli_live = 1;
+ ctrl->ring->srv_notify = VCHAN_NOTIFY_WRITE;
+
+ /* wake up the server */
+ xenevtchn_notify(ctrl->event, ctrl->event_port);
+
+ out:
+ if (xs)
+ xs_daemon_close(xs);
+ return ctrl;
+ fail:
+ libxenvchan_close(ctrl);
+ ctrl = NULL;
+ goto out;
+}
--- /dev/null
+/**
+ * @file
+ * @section AUTHORS
+ *
+ * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ *
+ * Authors:
+ * Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ * Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ *
+ * @section LICENSE
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section DESCRIPTION
+ *
+ * This file contains the communications interface built on the ring buffer.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <xenctrl.h>
+#include <libxenvchan.h>
+
+#ifndef PAGE_SHIFT
+#define PAGE_SHIFT 12
+#endif
+
+#ifndef PAGE_SIZE
+#define PAGE_SIZE 4096
+#endif
+
+
+static inline uint32_t rd_prod(struct libxenvchan *ctrl)
+{
+ return ctrl->read.shr->prod;
+}
+
+static inline uint32_t* _rd_cons(struct libxenvchan *ctrl)
+{
+ return &ctrl->read.shr->cons;
+}
+#define rd_cons(x) (*_rd_cons(x))
+
+static inline uint32_t* _wr_prod(struct libxenvchan *ctrl)
+{
+ return &ctrl->write.shr->prod;
+}
+#define wr_prod(x) (*_wr_prod(x))
+
+static inline uint32_t wr_cons(struct libxenvchan *ctrl)
+{
+ return ctrl->write.shr->cons;
+}
+
+static inline const void* rd_ring(struct libxenvchan *ctrl)
+{
+ return ctrl->read.buffer;
+}
+
+static inline void* wr_ring(struct libxenvchan *ctrl)
+{
+ return ctrl->write.buffer;
+}
+
+static inline uint32_t wr_ring_size(struct libxenvchan *ctrl)
+{
+ return (1 << ctrl->write.order);
+}
+
+static inline uint32_t rd_ring_size(struct libxenvchan *ctrl)
+{
+ return (1 << ctrl->read.order);
+}
+
+static inline void request_notify(struct libxenvchan *ctrl, uint8_t bit)
+{
+ uint8_t *notify = ctrl->is_server ? &ctrl->ring->cli_notify : &ctrl->ring->srv_notify;
+ __sync_or_and_fetch(notify, bit);
+ xen_mb(); /* post the request /before/ caller re-reads any indexes */
+}
+
+static inline int send_notify(struct libxenvchan *ctrl, uint8_t bit)
+{
+ uint8_t *notify, prev;
+ xen_mb(); /* caller updates indexes /before/ we decode to notify */
+ notify = ctrl->is_server ? &ctrl->ring->srv_notify : &ctrl->ring->cli_notify;
+ prev = __sync_fetch_and_and(notify, ~bit);
+ if (prev & bit)
+ return xenevtchn_notify(ctrl->event, ctrl->event_port);
+ else
+ return 0;
+}
+
+/*
+ * Get the amount of buffer space available, and do nothing about
+ * notifications.
+ */
+static inline int raw_get_data_ready(struct libxenvchan *ctrl)
+{
+ uint32_t ready = rd_prod(ctrl) - rd_cons(ctrl);
+ xen_mb(); /* Ensure 'ready' is read only once. */
+ if (ready > rd_ring_size(ctrl))
+ /* We have no way to return errors. Locking up the ring is
+ * better than the alternatives. */
+ return 0;
+ return ready;
+}
+
+/**
+ * Get the amount of buffer space available and enable notifications if needed.
+ */
+static inline int fast_get_data_ready(struct libxenvchan *ctrl, size_t request)
+{
+ int ready = raw_get_data_ready(ctrl);
+ if (ready >= request)
+ return ready;
+ /* We plan to consume all data; please tell us if you send more */
+ request_notify(ctrl, VCHAN_NOTIFY_WRITE);
+ /*
+ * If the writer moved rd_prod after our read but before request, we
+ * will not get notified even though the actual amount of data ready is
+ * above request. Reread rd_prod to cover this case.
+ */
+ return raw_get_data_ready(ctrl);
+}
+
+int libxenvchan_data_ready(struct libxenvchan *ctrl)
+{
+ /* Since this value is being used outside libxenvchan, request notification
+ * when it changes
+ */
+ request_notify(ctrl, VCHAN_NOTIFY_WRITE);
+ return raw_get_data_ready(ctrl);
+}
+
+/**
+ * Get the amount of buffer space available, and do nothing
+ * about notifications
+ */
+static inline int raw_get_buffer_space(struct libxenvchan *ctrl)
+{
+ uint32_t ready = wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl));
+ xen_mb(); /* Ensure 'ready' is read only once. */
+ if (ready > wr_ring_size(ctrl))
+ /* We have no way to return errors. Locking up the ring is
+ * better than the alternatives. */
+ return 0;
+ return ready;
+}
+
+/**
+ * Get the amount of buffer space available and enable notifications if needed.
+ */
+static inline int fast_get_buffer_space(struct libxenvchan *ctrl, size_t request)
+{
+ int ready = raw_get_buffer_space(ctrl);
+ if (ready >= request)
+ return ready;
+ /* We plan to fill the buffer; please tell us when you've read it */
+ request_notify(ctrl, VCHAN_NOTIFY_READ);
+ /*
+ * If the reader moved wr_cons after our read but before request, we
+ * will not get notified even though the actual amount of buffer space
+ * is above request. Reread wr_cons to cover this case.
+ */
+ return raw_get_buffer_space(ctrl);
+}
+
+int libxenvchan_buffer_space(struct libxenvchan *ctrl)
+{
+ /* Since this value is being used outside libxenvchan, request notification
+ * when it changes
+ */
+ request_notify(ctrl, VCHAN_NOTIFY_READ);
+ return raw_get_buffer_space(ctrl);
+}
+
+int libxenvchan_wait(struct libxenvchan *ctrl)
+{
+ int ret = xenevtchn_pending(ctrl->event);
+ if (ret < 0)
+ return -1;
+ xenevtchn_unmask(ctrl->event, ret);
+ return 0;
+}
+
+/**
+ * returns -1 on error, or size on success
+ *
+ * caller must have checked that enough space is available
+ */
+static int do_send(struct libxenvchan *ctrl, const void *data, size_t size)
+{
+ int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1);
+ int avail_contig = wr_ring_size(ctrl) - real_idx;
+ if (avail_contig > size)
+ avail_contig = size;
+ xen_mb(); /* read indexes /then/ write data */
+ memcpy(wr_ring(ctrl) + real_idx, data, avail_contig);
+ if (avail_contig < size)
+ {
+ // we rolled across the end of the ring
+ memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig);
+ }
+ xen_wmb(); /* write data /then/ notify */
+ wr_prod(ctrl) += size;
+ if (send_notify(ctrl, VCHAN_NOTIFY_WRITE))
+ return -1;
+ return size;
+}
+
+/**
+ * returns 0 if no buffer space is available, -1 on error, or size on success
+ */
+int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size)
+{
+ int avail;
+ while (1) {
+ if (!libxenvchan_is_open(ctrl))
+ return -1;
+ avail = fast_get_buffer_space(ctrl, size);
+ if (size <= avail)
+ return do_send(ctrl, data, size);
+ if (!ctrl->blocking)
+ return 0;
+ if (size > wr_ring_size(ctrl))
+ return -1;
+ if (libxenvchan_wait(ctrl))
+ return -1;
+ }
+}
+
+int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size)
+{
+ int avail;
+ if (!libxenvchan_is_open(ctrl))
+ return -1;
+ if (ctrl->blocking) {
+ size_t pos = 0;
+ while (1) {
+ avail = fast_get_buffer_space(ctrl, size - pos);
+ if (pos + avail > size)
+ avail = size - pos;
+ if (avail)
+ pos += do_send(ctrl, data + pos, avail);
+ if (pos == size)
+ return pos;
+ if (libxenvchan_wait(ctrl))
+ return -1;
+ if (!libxenvchan_is_open(ctrl))
+ return -1;
+ }
+ } else {
+ avail = fast_get_buffer_space(ctrl, size);
+ if (size > avail)
+ size = avail;
+ if (size == 0)
+ return 0;
+ return do_send(ctrl, data, size);
+ }
+}
+
+/**
+ * returns -1 on error, or size on success
+ *
+ * caller must have checked that enough data is available
+ */
+static int do_recv(struct libxenvchan *ctrl, void *data, size_t size)
+{
+ int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1);
+ int avail_contig = rd_ring_size(ctrl) - real_idx;
+ if (avail_contig > size)
+ avail_contig = size;
+ xen_rmb(); /* data read must happen /after/ rd_cons read */
+ memcpy(data, rd_ring(ctrl) + real_idx, avail_contig);
+ if (avail_contig < size)
+ {
+ // we rolled across the end of the ring
+ memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig);
+ }
+ xen_mb(); /* consume /then/ notify */
+ rd_cons(ctrl) += size;
+ if (send_notify(ctrl, VCHAN_NOTIFY_READ))
+ return -1;
+ return size;
+}
+
+/**
+ * reads exactly size bytes from the vchan.
+ * returns 0 if insufficient data is available, -1 on error, or size on success
+ */
+int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size)
+{
+ while (1) {
+ int avail = fast_get_data_ready(ctrl, size);
+ if (size <= avail)
+ return do_recv(ctrl, data, size);
+ if (!libxenvchan_is_open(ctrl))
+ return -1;
+ if (!ctrl->blocking)
+ return 0;
+ if (size > rd_ring_size(ctrl))
+ return -1;
+ if (libxenvchan_wait(ctrl))
+ return -1;
+ }
+}
+
+int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size)
+{
+ while (1) {
+ int avail = fast_get_data_ready(ctrl, size);
+ if (avail && size > avail)
+ size = avail;
+ if (avail)
+ return do_recv(ctrl, data, size);
+ if (!libxenvchan_is_open(ctrl))
+ return -1;
+ if (!ctrl->blocking)
+ return 0;
+ if (libxenvchan_wait(ctrl))
+ return -1;
+ }
+}
+
+int libxenvchan_is_open(struct libxenvchan* ctrl)
+{
+ if (ctrl->is_server)
+ return ctrl->server_persist ? 1 : ctrl->ring->cli_live;
+ else
+ return ctrl->ring->srv_live;
+}
+
+int libxenvchan_fd_for_select(struct libxenvchan *ctrl)
+{
+ return xenevtchn_fd(ctrl->event);
+}
+
+void libxenvchan_close(struct libxenvchan *ctrl)
+{
+ if (!ctrl)
+ return;
+ if (ctrl->read.order >= PAGE_SHIFT)
+ munmap(ctrl->read.buffer, 1 << ctrl->read.order);
+ if (ctrl->write.order >= PAGE_SHIFT)
+ munmap(ctrl->write.buffer, 1 << ctrl->write.order);
+ if (ctrl->ring) {
+ if (ctrl->is_server) {
+ ctrl->ring->srv_live = 0;
+ xengntshr_unshare(ctrl->gntshr, ctrl->ring, 1);
+ } else {
+ ctrl->ring->cli_live = 0;
+ xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
+ }
+ }
+ if (ctrl->event) {
+ if (ctrl->ring)
+ xenevtchn_notify(ctrl->event, ctrl->event_port);
+ xenevtchn_close(ctrl->event);
+ }
+ if (ctrl->is_server) {
+ if (ctrl->gntshr)
+ xengntshr_close(ctrl->gntshr);
+ } else {
+ if (ctrl->gnttab)
+ xengnttab_close(ctrl->gnttab);
+ }
+ free(ctrl);
+}
+++ /dev/null
-#
-# tools/libvchan/Makefile
-#
-
-XEN_ROOT = $(CURDIR)/../..
-include $(XEN_ROOT)/tools/Rules.mk
-
-LIBVCHAN_OBJS = init.o io.o
-NODE_OBJS = node.o
-NODE2_OBJS = node-select.o
-
-LIBVCHAN_PIC_OBJS = $(patsubst %.o,%.opic,$(LIBVCHAN_OBJS))
-LIBVCHAN_LIBS = $(LDLIBS_libxenstore) $(LDLIBS_libxengnttab) $(LDLIBS_libxenevtchn)
-$(LIBVCHAN_OBJS) $(LIBVCHAN_PIC_OBJS): CFLAGS += $(CFLAGS_libxenstore) $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn)
-$(NODE_OBJS) $(NODE2_OBJS): CFLAGS += $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn)
-vchan-socket-proxy.o: CFLAGS += $(CFLAGS_libxenstore) $(CFLAGS_libxenctrl) $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn)
-
-MAJOR = 4.15
-MINOR = 0
-
-CFLAGS += -I../include -I.
-
-io.o io.opic: CFLAGS += $(CFLAGS_libxenctrl) # for xen_mb et al
-
-PKG_CONFIG := xenvchan.pc
-PKG_CONFIG_NAME := Xenvchan
-PKG_CONFIG_DESC := The Xenvchan library for Xen hypervisor
-PKG_CONFIG_VERSION := $(MAJOR).$(MINOR)
-PKG_CONFIG_USELIBS := $(SHLIB_libxenvchan)
-PKG_CONFIG_LIB := xenvchan
-PKG_CONFIG_REQPRIV := xentoollog,xenstore,xenevtchn,xengnttab
-
-ifneq ($(CONFIG_LIBXC_MINIOS),y)
-PKG_CONFIG_INST := $(PKG_CONFIG)
-$(PKG_CONFIG_INST): PKG_CONFIG_PREFIX = $(prefix)
-$(PKG_CONFIG_INST): PKG_CONFIG_INCDIR = $(includedir)
-$(PKG_CONFIG_INST): PKG_CONFIG_LIBDIR = $(libdir)
-endif
-
-PKG_CONFIG_LOCAL := $(foreach pc,$(PKG_CONFIG),$(PKG_CONFIG_DIR)/$(pc))
-
-$(PKG_CONFIG_LOCAL): PKG_CONFIG_PREFIX = $(XEN_ROOT)
-$(PKG_CONFIG_LOCAL): PKG_CONFIG_INCDIR = $(XEN_libxenvchan)
-$(PKG_CONFIG_LOCAL): PKG_CONFIG_LIBDIR = $(CURDIR)
-$(PKG_CONFIG_LOCAL): PKG_CONFIG_CFLAGS_LOCAL = $(CFLAGS_xeninclude)
-
-.PHONY: all
-all: libxenvchan.so vchan-node1 vchan-node2 vchan-socket-proxy libxenvchan.a $(PKG_CONFIG_INST) $(PKG_CONFIG_LOCAL)
-
-libxenvchan.so: libxenvchan.so.$(MAJOR)
- ln -sf $< $@
-
-libxenvchan.so.$(MAJOR): libxenvchan.so.$(MAJOR).$(MINOR)
- ln -sf $< $@
-
-libxenvchan.so.$(MAJOR).$(MINOR): $(LIBVCHAN_PIC_OBJS)
- $(CC) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libxenvchan.so.$(MAJOR) $(SHLIB_LDFLAGS) -o $@ $^ $(LIBVCHAN_LIBS) $(APPEND_LDFLAGS)
-
-libxenvchan.a: $(LIBVCHAN_OBJS)
- $(AR) rcs libxenvchan.a $^
-
-vchan-node1: $(NODE_OBJS) libxenvchan.so
- $(CC) $(LDFLAGS) -o $@ $(NODE_OBJS) $(LDLIBS_libxenvchan) $(APPEND_LDFLAGS)
-
-vchan-node2: $(NODE2_OBJS) libxenvchan.so
- $(CC) $(LDFLAGS) -o $@ $(NODE2_OBJS) $(LDLIBS_libxenvchan) $(APPEND_LDFLAGS)
-
-vchan-socket-proxy: vchan-socket-proxy.o libxenvchan.so
- $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenvchan) $(LDLIBS_libxenstore) $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
-
-.PHONY: install
-install: all
- $(INSTALL_DIR) $(DESTDIR)$(libdir)
- $(INSTALL_DIR) $(DESTDIR)$(includedir)
- $(INSTALL_DIR) $(DESTDIR)$(bindir)
- $(INSTALL_PROG) libxenvchan.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)
- ln -sf libxenvchan.so.$(MAJOR).$(MINOR) $(DESTDIR)$(libdir)/libxenvchan.so.$(MAJOR)
- ln -sf libxenvchan.so.$(MAJOR) $(DESTDIR)$(libdir)/libxenvchan.so
- $(INSTALL_PROG) vchan-socket-proxy $(DESTDIR)$(bindir)
- $(INSTALL_DATA) libxenvchan.h $(DESTDIR)$(includedir)
- $(INSTALL_DATA) libxenvchan.a $(DESTDIR)$(libdir)
- $(INSTALL_DATA) xenvchan.pc $(DESTDIR)$(PKG_INSTALLDIR)
-
-.PHONY: uninstall
-uninstall:
- rm -f $(DESTDIR)$(PKG_INSTALLDIR)/xenvchan.pc
- rm -f $(DESTDIR)$(libdir)/libxenvchan.a
- rm -f $(DESTDIR)$(includedir)/libxenvchan.h
- rm -f $(DESTDIR)$(libdir)/libxenvchan.so
- rm -f $(DESTDIR)$(libdir)/libxenvchan.so.$(MAJOR)
- rm -f $(DESTDIR)$(libdir)/libxenvchan.so.$(MAJOR).$(MINOR)
-
-.PHONY: clean
-clean:
- $(RM) -f *.o *.opic *.so* *.a vchan-node1 vchan-node2 $(DEPS_RM)
- $(RM) -f xenvchan.pc
-
-distclean: clean
-
--include $(DEPS_INCLUDE)
+++ /dev/null
-/**
- * @file
- * @section AUTHORS
- *
- * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
- *
- * Authors:
- * Rafal Wojtczuk <rafal@invisiblethingslab.com>
- * Daniel De Graaf <dgdegra@tycho.nsa.gov>
- *
- * @section LICENSE
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>.
- *
- * @section DESCRIPTION
- *
- * This file contains the setup code used to establish the ring buffer.
- */
-
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/ioctl.h>
-#include <sys/user.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include <xenstore.h>
-#include <xen/xen.h>
-#include <xen/sys/evtchn.h>
-#include <xen/sys/gntalloc.h>
-#include <xen/sys/gntdev.h>
-#include <libxenvchan.h>
-
-#ifndef PAGE_SHIFT
-#define PAGE_SHIFT 12
-#endif
-
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
-
-#define SMALL_RING_SHIFT 10
-#define LARGE_RING_SHIFT 11
-
-#define MAX_SMALL_RING (1 << SMALL_RING_SHIFT)
-#define SMALL_RING_OFFSET 1024
-#define MAX_LARGE_RING (1 << LARGE_RING_SHIFT)
-#define LARGE_RING_OFFSET 2048
-
-// if you go over this size, you'll have too many grants to fit in the shared page.
-#define MAX_RING_SHIFT 20
-#define MAX_RING_SIZE (1 << MAX_RING_SHIFT)
-
-#ifndef offsetof
-#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
-#endif
-
-#define max(a,b) ((a > b) ? a : b)
-
-static int init_gnt_srv(struct libxenvchan *ctrl, int domain)
-{
- int pages_left = ctrl->read.order >= PAGE_SHIFT ? 1 << (ctrl->read.order - PAGE_SHIFT) : 0;
- int pages_right = ctrl->write.order >= PAGE_SHIFT ? 1 << (ctrl->write.order - PAGE_SHIFT) : 0;
- uint32_t ring_ref = -1;
- void *ring;
-
- ring = xengntshr_share_page_notify(ctrl->gntshr, domain,
- &ring_ref, 1, offsetof(struct vchan_interface, srv_live),
- ctrl->event_port);
-
- if (!ring)
- goto out;
-
- memset(ring, 0, PAGE_SIZE);
-
- ctrl->ring = ring;
- ctrl->read.shr = &ctrl->ring->left;
- ctrl->write.shr = &ctrl->ring->right;
- ctrl->ring->left_order = ctrl->read.order;
- ctrl->ring->right_order = ctrl->write.order;
- ctrl->ring->cli_live = 2;
- ctrl->ring->srv_live = 1;
- ctrl->ring->cli_notify = VCHAN_NOTIFY_WRITE;
-
- switch (ctrl->read.order) {
- case SMALL_RING_SHIFT:
- ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
- break;
- case LARGE_RING_SHIFT:
- ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
- break;
- default:
- ctrl->read.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
- pages_left, ctrl->ring->grants, 1);
- if (!ctrl->read.buffer)
- goto out_ring;
- }
-
- switch (ctrl->write.order) {
- case SMALL_RING_SHIFT:
- ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
- break;
- case LARGE_RING_SHIFT:
- ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
- break;
- default:
- ctrl->write.buffer = xengntshr_share_pages(ctrl->gntshr, domain,
- pages_right, ctrl->ring->grants + pages_left, 1);
- if (!ctrl->write.buffer)
- goto out_unmap_left;
- }
-
-out:
- return ring_ref;
-out_unmap_left:
- if (pages_left)
- xengntshr_unshare(ctrl->gntshr, ctrl->read.buffer, pages_left);
-out_ring:
- xengntshr_unshare(ctrl->gntshr, ring, 1);
- ring_ref = -1;
- ctrl->ring = NULL;
- ctrl->write.order = ctrl->read.order = 0;
- goto out;
-}
-
-static int init_gnt_cli(struct libxenvchan *ctrl, int domain, uint32_t ring_ref)
-{
- int rv = -1;
- uint32_t *grants;
-
- ctrl->ring = xengnttab_map_grant_ref_notify(ctrl->gnttab,
- domain, ring_ref, PROT_READ|PROT_WRITE,
- offsetof(struct vchan_interface, cli_live), ctrl->event_port);
-
- if (!ctrl->ring)
- goto out;
-
- ctrl->write.order = ctrl->ring->left_order;
- ctrl->read.order = ctrl->ring->right_order;
- ctrl->write.shr = &ctrl->ring->left;
- ctrl->read.shr = &ctrl->ring->right;
- if (ctrl->write.order < SMALL_RING_SHIFT || ctrl->write.order > MAX_RING_SHIFT)
- goto out_unmap_ring;
- if (ctrl->read.order < SMALL_RING_SHIFT || ctrl->read.order > MAX_RING_SHIFT)
- goto out_unmap_ring;
- if (ctrl->read.order == ctrl->write.order && ctrl->read.order < PAGE_SHIFT)
- goto out_unmap_ring;
-
- grants = ctrl->ring->grants;
-
- switch (ctrl->write.order) {
- case SMALL_RING_SHIFT:
- ctrl->write.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
- break;
- case LARGE_RING_SHIFT:
- ctrl->write.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
- break;
- default:
- {
- int pages_left = 1 << (ctrl->write.order - PAGE_SHIFT);
- ctrl->write.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
- pages_left, domain, grants, PROT_READ|PROT_WRITE);
- if (!ctrl->write.buffer)
- goto out_unmap_ring;
- grants += pages_left;
- }
- }
-
- switch (ctrl->read.order) {
- case SMALL_RING_SHIFT:
- ctrl->read.buffer = ((void*)ctrl->ring) + SMALL_RING_OFFSET;
- break;
- case LARGE_RING_SHIFT:
- ctrl->read.buffer = ((void*)ctrl->ring) + LARGE_RING_OFFSET;
- break;
- default:
- {
- int pages_right = 1 << (ctrl->read.order - PAGE_SHIFT);
- ctrl->read.buffer = xengnttab_map_domain_grant_refs(ctrl->gnttab,
- pages_right, domain, grants, PROT_READ);
- if (!ctrl->read.buffer)
- goto out_unmap_left;
- }
- }
-
- rv = 0;
- out:
- return rv;
- out_unmap_left:
- if (ctrl->write.order >= PAGE_SHIFT)
- xengnttab_unmap(ctrl->gnttab, ctrl->write.buffer,
- 1 << (ctrl->write.order - PAGE_SHIFT));
- out_unmap_ring:
- xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
- ctrl->ring = 0;
- ctrl->write.order = ctrl->read.order = 0;
- rv = -1;
- goto out;
-}
-
-static int init_evt_srv(struct libxenvchan *ctrl, int domain,
- struct xentoollog_logger *logger)
-{
- xenevtchn_port_or_error_t port;
-
- ctrl->event = xenevtchn_open(logger, 0);
- if (!ctrl->event)
- return -1;
-
- port = xenevtchn_bind_unbound_port(ctrl->event, domain);
- if (port < 0)
- goto fail;
- ctrl->event_port = port;
-
- if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
- goto fail;
-
- return 0;
-
-fail:
- if (port >= 0)
- xenevtchn_unbind(ctrl->event, port);
-
- xenevtchn_close(ctrl->event);
- ctrl->event = NULL;
-
- return -1;
-}
-
-static int init_xs_srv(struct libxenvchan *ctrl, int domain, const char* xs_base, int ring_ref)
-{
- int ret = -1;
- struct xs_handle *xs;
- struct xs_permissions perms[2];
- char buf[64];
- char ref[16];
- char* domid_str = NULL;
- xs_transaction_t xs_trans = XBT_NULL;
- xs = xs_domain_open();
- if (!xs)
- goto fail;
- domid_str = xs_read(xs, 0, "domid", NULL);
- if (!domid_str)
- goto fail_xs_open;
-
- // owner domain is us
- perms[0].id = atoi(domid_str);
- // permissions for domains not listed = none
- perms[0].perms = XS_PERM_NONE;
- // other domains
- perms[1].id = domain;
- perms[1].perms = XS_PERM_READ;
-
-retry_transaction:
- xs_trans = xs_transaction_start(xs);
- if (!xs_trans)
- goto fail_xs_open;
-
- snprintf(ref, sizeof ref, "%d", ring_ref);
- snprintf(buf, sizeof buf, "%s/ring-ref", xs_base);
- if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
- goto fail_xs_open;
- if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
- goto fail_xs_open;
-
- snprintf(ref, sizeof ref, "%d", ctrl->event_port);
- snprintf(buf, sizeof buf, "%s/event-channel", xs_base);
- if (!xs_write(xs, xs_trans, buf, ref, strlen(ref)))
- goto fail_xs_open;
- if (!xs_set_permissions(xs, xs_trans, buf, perms, 2))
- goto fail_xs_open;
-
- if (!xs_transaction_end(xs, xs_trans, 0)) {
- if (errno == EAGAIN)
- goto retry_transaction;
- } else {
- ret = 0;
- }
- fail_xs_open:
- free(domid_str);
- xs_daemon_close(xs);
- fail:
- return ret;
-}
-
-static int min_order(size_t siz)
-{
- int rv = PAGE_SHIFT;
- while (siz > (1 << rv))
- rv++;
- return rv;
-}
-
-struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger,
- int domain, const char* xs_path,
- size_t left_min, size_t right_min)
-{
- struct libxenvchan *ctrl;
- int ring_ref;
- if (left_min > MAX_RING_SIZE || right_min > MAX_RING_SIZE)
- return 0;
-
- ctrl = malloc(sizeof(*ctrl));
- if (!ctrl)
- return 0;
-
- ctrl->ring = NULL;
- ctrl->event = NULL;
- ctrl->is_server = 1;
- ctrl->server_persist = 0;
-
- ctrl->read.order = min_order(left_min);
- ctrl->write.order = min_order(right_min);
-
- // if we can avoid allocating extra pages by using in-page rings, do so
- if (left_min <= MAX_SMALL_RING && right_min <= MAX_LARGE_RING) {
- ctrl->read.order = SMALL_RING_SHIFT;
- ctrl->write.order = LARGE_RING_SHIFT;
- } else if (left_min <= MAX_LARGE_RING && right_min <= MAX_SMALL_RING) {
- ctrl->read.order = LARGE_RING_SHIFT;
- ctrl->write.order = SMALL_RING_SHIFT;
- } else if (left_min <= MAX_LARGE_RING) {
- ctrl->read.order = LARGE_RING_SHIFT;
- } else if (right_min <= MAX_LARGE_RING) {
- ctrl->write.order = LARGE_RING_SHIFT;
- }
-
- ctrl->gntshr = xengntshr_open(logger, 0);
- if (!ctrl->gntshr) {
- free(ctrl);
- return 0;
- }
-
- if (init_evt_srv(ctrl, domain, logger))
- goto out;
- ring_ref = init_gnt_srv(ctrl, domain);
- if (ring_ref < 0)
- goto out;
- if (init_xs_srv(ctrl, domain, xs_path, ring_ref))
- goto out;
- return ctrl;
-out:
- libxenvchan_close(ctrl);
- return 0;
-}
-
-static int init_evt_cli(struct libxenvchan *ctrl, int domain,
- struct xentoollog_logger *logger)
-{
- xenevtchn_port_or_error_t port;
-
- ctrl->event = xenevtchn_open(logger, 0);
- if (!ctrl->event)
- return -1;
-
- port = xenevtchn_bind_interdomain(ctrl->event,
- domain, ctrl->event_port);
- if (port < 0)
- goto fail;
- ctrl->event_port = port;
-
- if (xenevtchn_unmask(ctrl->event, ctrl->event_port))
- goto fail;
-
- return 0;
-
-fail:
- if (port >= 0)
- xenevtchn_unbind(ctrl->event, port);
-
- xenevtchn_close(ctrl->event);
- ctrl->event = NULL;
-
- return -1;
-}
-
-
-struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger,
- int domain, const char* xs_path)
-{
- struct libxenvchan *ctrl = malloc(sizeof(struct libxenvchan));
- struct xs_handle *xs = NULL;
- char buf[64];
- char *ref;
- int ring_ref;
- unsigned int len;
-
- if (!ctrl)
- return 0;
- ctrl->ring = NULL;
- ctrl->event = NULL;
- ctrl->gnttab = NULL;
- ctrl->write.order = ctrl->read.order = 0;
- ctrl->is_server = 0;
-
- xs = xs_daemon_open();
- if (!xs)
- xs = xs_domain_open();
- if (!xs)
- goto fail;
-
-// find xenstore entry
- snprintf(buf, sizeof buf, "%s/ring-ref", xs_path);
- ref = xs_read(xs, 0, buf, &len);
- if (!ref)
- goto fail;
- ring_ref = atoi(ref);
- free(ref);
- if (!ring_ref)
- goto fail;
- snprintf(buf, sizeof buf, "%s/event-channel", xs_path);
- ref = xs_read(xs, 0, buf, &len);
- if (!ref)
- goto fail;
- ctrl->event_port = atoi(ref);
- free(ref);
- if (!ctrl->event_port)
- goto fail;
-
- ctrl->gnttab = xengnttab_open(logger, 0);
- if (!ctrl->gnttab)
- goto fail;
-
-// set up event channel
- if (init_evt_cli(ctrl, domain, logger))
- goto fail;
-
-// set up shared page(s)
- if (init_gnt_cli(ctrl, domain, ring_ref))
- goto fail;
-
- ctrl->ring->cli_live = 1;
- ctrl->ring->srv_notify = VCHAN_NOTIFY_WRITE;
-
- /* wake up the server */
- xenevtchn_notify(ctrl->event, ctrl->event_port);
-
- out:
- if (xs)
- xs_daemon_close(xs);
- return ctrl;
- fail:
- libxenvchan_close(ctrl);
- ctrl = NULL;
- goto out;
-}
+++ /dev/null
-/**
- * @file
- * @section AUTHORS
- *
- * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
- *
- * Authors:
- * Rafal Wojtczuk <rafal@invisiblethingslab.com>
- * Daniel De Graaf <dgdegra@tycho.nsa.gov>
- *
- * @section LICENSE
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>.
- *
- * @section DESCRIPTION
- *
- * This file contains the communications interface built on the ring buffer.
- */
-
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/ioctl.h>
-#include <sys/uio.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <xenctrl.h>
-#include <libxenvchan.h>
-
-#ifndef PAGE_SHIFT
-#define PAGE_SHIFT 12
-#endif
-
-#ifndef PAGE_SIZE
-#define PAGE_SIZE 4096
-#endif
-
-
-static inline uint32_t rd_prod(struct libxenvchan *ctrl)
-{
- return ctrl->read.shr->prod;
-}
-
-static inline uint32_t* _rd_cons(struct libxenvchan *ctrl)
-{
- return &ctrl->read.shr->cons;
-}
-#define rd_cons(x) (*_rd_cons(x))
-
-static inline uint32_t* _wr_prod(struct libxenvchan *ctrl)
-{
- return &ctrl->write.shr->prod;
-}
-#define wr_prod(x) (*_wr_prod(x))
-
-static inline uint32_t wr_cons(struct libxenvchan *ctrl)
-{
- return ctrl->write.shr->cons;
-}
-
-static inline const void* rd_ring(struct libxenvchan *ctrl)
-{
- return ctrl->read.buffer;
-}
-
-static inline void* wr_ring(struct libxenvchan *ctrl)
-{
- return ctrl->write.buffer;
-}
-
-static inline uint32_t wr_ring_size(struct libxenvchan *ctrl)
-{
- return (1 << ctrl->write.order);
-}
-
-static inline uint32_t rd_ring_size(struct libxenvchan *ctrl)
-{
- return (1 << ctrl->read.order);
-}
-
-static inline void request_notify(struct libxenvchan *ctrl, uint8_t bit)
-{
- uint8_t *notify = ctrl->is_server ? &ctrl->ring->cli_notify : &ctrl->ring->srv_notify;
- __sync_or_and_fetch(notify, bit);
- xen_mb(); /* post the request /before/ caller re-reads any indexes */
-}
-
-static inline int send_notify(struct libxenvchan *ctrl, uint8_t bit)
-{
- uint8_t *notify, prev;
- xen_mb(); /* caller updates indexes /before/ we decode to notify */
- notify = ctrl->is_server ? &ctrl->ring->srv_notify : &ctrl->ring->cli_notify;
- prev = __sync_fetch_and_and(notify, ~bit);
- if (prev & bit)
- return xenevtchn_notify(ctrl->event, ctrl->event_port);
- else
- return 0;
-}
-
-/*
- * Get the amount of buffer space available, and do nothing about
- * notifications.
- */
-static inline int raw_get_data_ready(struct libxenvchan *ctrl)
-{
- uint32_t ready = rd_prod(ctrl) - rd_cons(ctrl);
- xen_mb(); /* Ensure 'ready' is read only once. */
- if (ready > rd_ring_size(ctrl))
- /* We have no way to return errors. Locking up the ring is
- * better than the alternatives. */
- return 0;
- return ready;
-}
-
-/**
- * Get the amount of buffer space available and enable notifications if needed.
- */
-static inline int fast_get_data_ready(struct libxenvchan *ctrl, size_t request)
-{
- int ready = raw_get_data_ready(ctrl);
- if (ready >= request)
- return ready;
- /* We plan to consume all data; please tell us if you send more */
- request_notify(ctrl, VCHAN_NOTIFY_WRITE);
- /*
- * If the writer moved rd_prod after our read but before request, we
- * will not get notified even though the actual amount of data ready is
- * above request. Reread rd_prod to cover this case.
- */
- return raw_get_data_ready(ctrl);
-}
-
-int libxenvchan_data_ready(struct libxenvchan *ctrl)
-{
- /* Since this value is being used outside libxenvchan, request notification
- * when it changes
- */
- request_notify(ctrl, VCHAN_NOTIFY_WRITE);
- return raw_get_data_ready(ctrl);
-}
-
-/**
- * Get the amount of buffer space available, and do nothing
- * about notifications
- */
-static inline int raw_get_buffer_space(struct libxenvchan *ctrl)
-{
- uint32_t ready = wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl));
- xen_mb(); /* Ensure 'ready' is read only once. */
- if (ready > wr_ring_size(ctrl))
- /* We have no way to return errors. Locking up the ring is
- * better than the alternatives. */
- return 0;
- return ready;
-}
-
-/**
- * Get the amount of buffer space available and enable notifications if needed.
- */
-static inline int fast_get_buffer_space(struct libxenvchan *ctrl, size_t request)
-{
- int ready = raw_get_buffer_space(ctrl);
- if (ready >= request)
- return ready;
- /* We plan to fill the buffer; please tell us when you've read it */
- request_notify(ctrl, VCHAN_NOTIFY_READ);
- /*
- * If the reader moved wr_cons after our read but before request, we
- * will not get notified even though the actual amount of buffer space
- * is above request. Reread wr_cons to cover this case.
- */
- return raw_get_buffer_space(ctrl);
-}
-
-int libxenvchan_buffer_space(struct libxenvchan *ctrl)
-{
- /* Since this value is being used outside libxenvchan, request notification
- * when it changes
- */
- request_notify(ctrl, VCHAN_NOTIFY_READ);
- return raw_get_buffer_space(ctrl);
-}
-
-int libxenvchan_wait(struct libxenvchan *ctrl)
-{
- int ret = xenevtchn_pending(ctrl->event);
- if (ret < 0)
- return -1;
- xenevtchn_unmask(ctrl->event, ret);
- return 0;
-}
-
-/**
- * returns -1 on error, or size on success
- *
- * caller must have checked that enough space is available
- */
-static int do_send(struct libxenvchan *ctrl, const void *data, size_t size)
-{
- int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1);
- int avail_contig = wr_ring_size(ctrl) - real_idx;
- if (avail_contig > size)
- avail_contig = size;
- xen_mb(); /* read indexes /then/ write data */
- memcpy(wr_ring(ctrl) + real_idx, data, avail_contig);
- if (avail_contig < size)
- {
- // we rolled across the end of the ring
- memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig);
- }
- xen_wmb(); /* write data /then/ notify */
- wr_prod(ctrl) += size;
- if (send_notify(ctrl, VCHAN_NOTIFY_WRITE))
- return -1;
- return size;
-}
-
-/**
- * returns 0 if no buffer space is available, -1 on error, or size on success
- */
-int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size)
-{
- int avail;
- while (1) {
- if (!libxenvchan_is_open(ctrl))
- return -1;
- avail = fast_get_buffer_space(ctrl, size);
- if (size <= avail)
- return do_send(ctrl, data, size);
- if (!ctrl->blocking)
- return 0;
- if (size > wr_ring_size(ctrl))
- return -1;
- if (libxenvchan_wait(ctrl))
- return -1;
- }
-}
-
-int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size)
-{
- int avail;
- if (!libxenvchan_is_open(ctrl))
- return -1;
- if (ctrl->blocking) {
- size_t pos = 0;
- while (1) {
- avail = fast_get_buffer_space(ctrl, size - pos);
- if (pos + avail > size)
- avail = size - pos;
- if (avail)
- pos += do_send(ctrl, data + pos, avail);
- if (pos == size)
- return pos;
- if (libxenvchan_wait(ctrl))
- return -1;
- if (!libxenvchan_is_open(ctrl))
- return -1;
- }
- } else {
- avail = fast_get_buffer_space(ctrl, size);
- if (size > avail)
- size = avail;
- if (size == 0)
- return 0;
- return do_send(ctrl, data, size);
- }
-}
-
-/**
- * returns -1 on error, or size on success
- *
- * caller must have checked that enough data is available
- */
-static int do_recv(struct libxenvchan *ctrl, void *data, size_t size)
-{
- int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1);
- int avail_contig = rd_ring_size(ctrl) - real_idx;
- if (avail_contig > size)
- avail_contig = size;
- xen_rmb(); /* data read must happen /after/ rd_cons read */
- memcpy(data, rd_ring(ctrl) + real_idx, avail_contig);
- if (avail_contig < size)
- {
- // we rolled across the end of the ring
- memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig);
- }
- xen_mb(); /* consume /then/ notify */
- rd_cons(ctrl) += size;
- if (send_notify(ctrl, VCHAN_NOTIFY_READ))
- return -1;
- return size;
-}
-
-/**
- * reads exactly size bytes from the vchan.
- * returns 0 if insufficient data is available, -1 on error, or size on success
- */
-int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size)
-{
- while (1) {
- int avail = fast_get_data_ready(ctrl, size);
- if (size <= avail)
- return do_recv(ctrl, data, size);
- if (!libxenvchan_is_open(ctrl))
- return -1;
- if (!ctrl->blocking)
- return 0;
- if (size > rd_ring_size(ctrl))
- return -1;
- if (libxenvchan_wait(ctrl))
- return -1;
- }
-}
-
-int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size)
-{
- while (1) {
- int avail = fast_get_data_ready(ctrl, size);
- if (avail && size > avail)
- size = avail;
- if (avail)
- return do_recv(ctrl, data, size);
- if (!libxenvchan_is_open(ctrl))
- return -1;
- if (!ctrl->blocking)
- return 0;
- if (libxenvchan_wait(ctrl))
- return -1;
- }
-}
-
-int libxenvchan_is_open(struct libxenvchan* ctrl)
-{
- if (ctrl->is_server)
- return ctrl->server_persist ? 1 : ctrl->ring->cli_live;
- else
- return ctrl->ring->srv_live;
-}
-
-int libxenvchan_fd_for_select(struct libxenvchan *ctrl)
-{
- return xenevtchn_fd(ctrl->event);
-}
-
-void libxenvchan_close(struct libxenvchan *ctrl)
-{
- if (!ctrl)
- return;
- if (ctrl->read.order >= PAGE_SHIFT)
- munmap(ctrl->read.buffer, 1 << ctrl->read.order);
- if (ctrl->write.order >= PAGE_SHIFT)
- munmap(ctrl->write.buffer, 1 << ctrl->write.order);
- if (ctrl->ring) {
- if (ctrl->is_server) {
- ctrl->ring->srv_live = 0;
- xengntshr_unshare(ctrl->gntshr, ctrl->ring, 1);
- } else {
- ctrl->ring->cli_live = 0;
- xengnttab_unmap(ctrl->gnttab, ctrl->ring, 1);
- }
- }
- if (ctrl->event) {
- if (ctrl->ring)
- xenevtchn_notify(ctrl->event, ctrl->event_port);
- xenevtchn_close(ctrl->event);
- }
- if (ctrl->is_server) {
- if (ctrl->gntshr)
- xengntshr_close(ctrl->gntshr);
- } else {
- if (ctrl->gnttab)
- xengnttab_close(ctrl->gnttab);
- }
- free(ctrl);
-}
+++ /dev/null
-/**
- * @file
- * @section AUTHORS
- *
- * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
- *
- * Authors:
- * Rafal Wojtczuk <rafal@invisiblethingslab.com>
- * Daniel De Graaf <dgdegra@tycho.nsa.gov>
- *
- * @section LICENSE
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; If not, see <http://www.gnu.org/licenses/>.
- *
- * @section DESCRIPTION
- *
- * Originally borrowed from the Qubes OS Project, http://www.qubes-os.org,
- * this code has been substantially rewritten to use the gntdev and gntalloc
- * devices instead of raw MFNs and map_foreign_range.
- *
- * This is a library for inter-domain communication. A standard Xen ring
- * buffer is used, with a datagram-based interface built on top. The grant
- * reference and event channels are shared in XenStore under the path
- * /local/domain/<srv-id>/data/vchan/<cli-id>/<port>/{ring-ref,event-channel}
- *
- * The ring.h macros define an asymmetric interface to a shared data structure
- * that assumes all rings reside in a single contiguous memory space. This is
- * not suitable for vchan because the interface to the ring is symmetric except
- * for the setup. Unlike the producer-consumer rings defined in ring.h, the
- * size of the rings used in vchan are determined at execution time instead of
- * compile time, so the macros in ring.h cannot be used to access the rings.
- */
-
-#include <xen/io/libxenvchan.h>
-#include <xen/xen.h>
-#include <xen/sys/evtchn.h>
-#include <xenevtchn.h>
-#include <xengnttab.h>
-
-/* Callers who don't care don't need to #include <xentoollog.h> */
-struct xentoollog_logger;
-
-struct libxenvchan_ring {
- /* Pointer into the shared page. Offsets into buffer. */
- struct ring_shared* shr;
- /* ring data; may be its own shared page(s) depending on order */
- void* buffer;
- /**
- * The size of the ring is (1 << order); offsets wrap around when they
- * exceed this. This copy is required because we can't trust the order
- * in the shared page to remain constant.
- */
- int order;
-};
-
-/**
- * struct libxenvchan: control structure passed to all library calls
- */
-struct libxenvchan {
- /* Mapping handle for shared ring page */
- union {
- xengntshr_handle *gntshr; /* for server */
- xengnttab_handle *gnttab; /* for client */
- };
- /* Pointer to shared ring page */
- struct vchan_interface *ring;
- /* event channel interface */
- xenevtchn_handle *event;
- uint32_t event_port;
- /* informative flags: are we acting as server? */
- int is_server:1;
- /* true if server remains active when client closes (allows reconnection) */
- int server_persist:1;
- /* true if operations should block instead of returning 0 */
- int blocking:1;
- /* communication rings */
- struct libxenvchan_ring read, write;
-};
-
-/**
- * Set up a vchan, including granting pages
- * @param logger Logger for libxc errors
- * @param domain The peer domain that will be connecting
- * @param xs_path Base xenstore path for storing ring/event data
- * @param send_min The minimum size (in bytes) of the send ring (left)
- * @param recv_min The minimum size (in bytes) of the receive ring (right)
- * @return The structure, or NULL in case of an error
- */
-struct libxenvchan *libxenvchan_server_init(struct xentoollog_logger *logger,
- int domain, const char* xs_path,
- size_t read_min, size_t write_min);
-/**
- * Connect to an existing vchan. Note: you can reconnect to an existing vchan
- * safely, however no locking is performed, so you must prevent multiple clients
- * from connecting to a single server.
- *
- * @param logger Logger for libxc errors
- * @param domain The peer domain to connect to
- * @param xs_path Base xenstore path for storing ring/event data
- * @return The structure, or NULL in case of an error
- */
-struct libxenvchan *libxenvchan_client_init(struct xentoollog_logger *logger,
- int domain, const char* xs_path);
-/**
- * Close a vchan. This deallocates the vchan and attempts to free its
- * resources. The other side is notified of the close, but can still read any
- * data pending prior to the close.
- */
-void libxenvchan_close(struct libxenvchan *ctrl);
-
-/**
- * Packet-based receive: always reads exactly $size bytes.
- * @param ctrl The vchan control structure
- * @param data Buffer for data that was read
- * @param size Size of the buffer and amount of data to read
- * @return -1 on error, 0 if nonblocking and insufficient data is available, or $size
- */
-int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size);
-/**
- * Stream-based receive: reads as much data as possible.
- * @param ctrl The vchan control structure
- * @param data Buffer for data that was read
- * @param size Size of the buffer
- * @return -1 on error, otherwise the amount of data read (which may be zero if
- * the vchan is nonblocking)
- */
-int libxenvchan_read(struct libxenvchan *ctrl, void *data, size_t size);
-/**
- * Packet-based send: send entire buffer if possible
- * @param ctrl The vchan control structure
- * @param data Buffer for data to send
- * @param size Size of the buffer and amount of data to send
- * @return -1 on error, 0 if nonblocking and insufficient space is available, or $size
- */
-int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size);
-/**
- * Stream-based send: send as much data as possible.
- * @param ctrl The vchan control structure
- * @param data Buffer for data to send
- * @param size Size of the buffer
- * @return -1 on error, otherwise the amount of data sent (which may be zero if
- * the vchan is nonblocking)
- */
-int libxenvchan_write(struct libxenvchan *ctrl, const void *data, size_t size);
-/**
- * Waits for reads or writes to unblock, or for a close
- */
-int libxenvchan_wait(struct libxenvchan *ctrl);
-/**
- * Returns the event file descriptor for this vchan. When this FD is readable,
- * libxenvchan_wait() will not block, and the state of the vchan has changed since
- * the last invocation of libxenvchan_wait().
- */
-int libxenvchan_fd_for_select(struct libxenvchan *ctrl);
-/**
- * Query the state of the vchan shared page:
- * return 0 when one side has called libxenvchan_close() or crashed
- * return 1 when both sides are open
- * return 2 [server only] when no client has yet connected
- */
-int libxenvchan_is_open(struct libxenvchan* ctrl);
-/** Amount of data ready to read, in bytes */
-int libxenvchan_data_ready(struct libxenvchan *ctrl);
-/** Amount of data it is possible to send without blocking */
-int libxenvchan_buffer_space(struct libxenvchan *ctrl);
+++ /dev/null
-/**
- * @file
- * @section AUTHORS
- *
- * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
- *
- * Authors:
- * Rafal Wojtczuk <rafal@invisiblethingslab.com>
- * Daniel De Graaf <dgdegra@tycho.nsa.gov>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; If not, see <http://www.gnu.org/licenses/>.
- *
- * @section DESCRIPTION
- *
- * This is a test program for libxenvchan. Communications are bidirectional,
- * with either server (grant offeror) or client able to read and write.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <libxenvchan.h>
-
-static void usage(char** argv)
-{
- fprintf(stderr, "usage:\n"
- "\t%s [client|server] domainid nodepath [rbufsiz wbufsiz]\n",
- argv[0]);
- exit(1);
-}
-
-#define BUFSIZE 5000
-char inbuf[BUFSIZE];
-char outbuf[BUFSIZE];
-int insiz = 0;
-int outsiz = 0;
-struct libxenvchan *ctrl = 0;
-
-static void vchan_wr(void) {
- int ret;
-
- if (!insiz)
- return;
- ret = libxenvchan_write(ctrl, inbuf, insiz);
- if (ret < 0) {
- fprintf(stderr, "vchan write failed\n");
- exit(1);
- }
- if (ret > 0) {
- insiz -= ret;
- memmove(inbuf, inbuf + ret, insiz);
- }
-}
-
-static void stdout_wr(void) {
- int ret;
-
- if (!outsiz)
- return;
- ret = write(1, outbuf, outsiz);
- if (ret < 0 && errno != EAGAIN)
- exit(1);
- if (ret > 0) {
- outsiz -= ret;
- memmove(outbuf, outbuf + ret, outsiz);
- }
-}
-
-static int set_nonblocking(int fd, int nonblocking) {
- int flags = fcntl(fd, F_GETFL);
- if (flags == -1)
- return -1;
-
- if (nonblocking)
- flags |= O_NONBLOCK;
- else
- flags &= ~O_NONBLOCK;
-
- if (fcntl(fd, F_SETFL, flags) == -1)
- return -1;
-
- return 0;
-}
-
-/**
- Simple libxenvchan application, both client and server.
- Both sides may write and read, both from the libxenvchan and from
- stdin/stdout (just like netcat).
-*/
-
-int main(int argc, char **argv)
-{
- int ret;
- int libxenvchan_fd;
- if (argc < 4 || argv[3][0] != '/')
- usage(argv);
- if (!strcmp(argv[1], "server")) {
- int rsiz = argc > 4 ? atoi(argv[4]) : 0;
- int wsiz = argc > 5 ? atoi(argv[5]) : 0;
- ctrl = libxenvchan_server_init(NULL, atoi(argv[2]), argv[3], rsiz, wsiz);
- } else if (!strcmp(argv[1], "client"))
- ctrl = libxenvchan_client_init(NULL, atoi(argv[2]), argv[3]);
- else
- usage(argv);
- if (!ctrl) {
- perror("libxenvchan_*_init");
- exit(1);
- }
-
- if (set_nonblocking(0, 1) || set_nonblocking(1, 1)) {
- perror("set_nonblocking");
- exit(1);
- }
-
- libxenvchan_fd = libxenvchan_fd_for_select(ctrl);
- for (;;) {
- fd_set rfds;
- fd_set wfds;
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
- if (insiz != BUFSIZE)
- FD_SET(0, &rfds);
- if (outsiz)
- FD_SET(1, &wfds);
- FD_SET(libxenvchan_fd, &rfds);
- ret = select(libxenvchan_fd + 1, &rfds, &wfds, NULL, NULL);
- if (ret < 0) {
- perror("select");
- exit(1);
- }
- if (FD_ISSET(0, &rfds)) {
- ret = read(0, inbuf + insiz, BUFSIZE - insiz);
- if (ret < 0 && errno != EAGAIN)
- exit(1);
- if (ret == 0) {
- while (insiz) {
- vchan_wr();
- libxenvchan_wait(ctrl);
- }
- return 0;
- }
- if (ret)
- insiz += ret;
- vchan_wr();
- }
- if (FD_ISSET(libxenvchan_fd, &rfds)) {
- libxenvchan_wait(ctrl);
- vchan_wr();
- }
- if (FD_ISSET(1, &wfds))
- stdout_wr();
- while (libxenvchan_data_ready(ctrl) && outsiz < BUFSIZE) {
- ret = libxenvchan_read(ctrl, outbuf + outsiz, BUFSIZE - outsiz);
- if (ret < 0)
- exit(1);
- outsiz += ret;
- stdout_wr();
- }
- if (!libxenvchan_is_open(ctrl)) {
- if (set_nonblocking(1, 0)) {
- perror("set_nonblocking");
- exit(1);
- }
- while (outsiz)
- stdout_wr();
- return 0;
- }
- }
-}
+++ /dev/null
-/**
- * @file
- * @section AUTHORS
- *
- * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
- *
- * Authors:
- * Rafal Wojtczuk <rafal@invisiblethingslab.com>
- * Daniel De Graaf <dgdegra@tycho.nsa.gov>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; If not, see <http://www.gnu.org/licenses/>.
- *
- * @section DESCRIPTION
- *
- * This is a test program for libxenvchan. Communications are in one direction,
- * either server (grant offeror) to client or vice versa.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <time.h>
-
-#include <libxenvchan.h>
-
-int libxenvchan_write_all(struct libxenvchan *ctrl, char *buf, int size)
-{
- int written = 0;
- int ret;
- while (written < size) {
- ret = libxenvchan_write(ctrl, buf + written, size - written);
- if (ret <= 0) {
- perror("write");
- exit(1);
- }
- written += ret;
- }
- return size;
-}
-
-int write_all(int fd, char *buf, int size)
-{
- int written = 0;
- int ret;
- while (written < size) {
- ret = write(fd, buf + written, size - written);
- if (ret <= 0) {
- perror("write");
- exit(1);
- }
- written += ret;
- }
- return size;
-}
-
-void usage(char** argv)
-{
- fprintf(stderr, "usage:\n"
- "%s [client|server] [read|write] domid nodepath\n", argv[0]);
- exit(1);
-}
-
-#define BUFSIZE 5000
-char buf[BUFSIZE];
-void reader(struct libxenvchan *ctrl)
-{
- int size;
- for (;;) {
- size = rand() % (BUFSIZE - 1) + 1;
- size = libxenvchan_read(ctrl, buf, size);
- fprintf(stderr, "#");
- if (size < 0) {
- perror("read vchan");
- libxenvchan_close(ctrl);
- exit(1);
- }
- size = write_all(1, buf, size);
- if (size < 0) {
- perror("stdout write");
- exit(1);
- }
- if (size == 0) {
- perror("write size=0?\n");
- exit(1);
- }
- }
-}
-
-void writer(struct libxenvchan *ctrl)
-{
- int size;
- for (;;) {
- size = rand() % (BUFSIZE - 1) + 1;
- size = read(0, buf, size);
- if (size < 0) {
- perror("read stdin");
- libxenvchan_close(ctrl);
- exit(1);
- }
- if (size == 0)
- break;
- size = libxenvchan_write_all(ctrl, buf, size);
- fprintf(stderr, "#");
- if (size < 0) {
- perror("vchan write");
- exit(1);
- }
- if (size == 0) {
- perror("write size=0?\n");
- exit(1);
- }
- }
-}
-
-
-/**
- Simple libxenvchan application, both client and server.
- One side does writing, the other side does reading; both from
- standard input/output fds.
-*/
-int main(int argc, char **argv)
-{
- int seed = time(0);
- struct libxenvchan *ctrl = 0;
- int wr = 0;
- if (argc < 4)
- usage(argv);
- if (!strcmp(argv[2], "read"))
- wr = 0;
- else if (!strcmp(argv[2], "write"))
- wr = 1;
- else
- usage(argv);
- if (!strcmp(argv[1], "server"))
- ctrl = libxenvchan_server_init(NULL, atoi(argv[3]), argv[4], 0, 0);
- else if (!strcmp(argv[1], "client"))
- ctrl = libxenvchan_client_init(NULL, atoi(argv[3]), argv[4]);
- else
- usage(argv);
- if (!ctrl) {
- perror("libxenvchan_*_init");
- exit(1);
- }
- ctrl->blocking = 1;
-
- srand(seed);
- fprintf(stderr, "seed=%d\n", seed);
- if (wr)
- writer(ctrl);
- else
- reader(ctrl);
- libxenvchan_close(ctrl);
- return 0;
-}
+++ /dev/null
-/**
- * @file
- * @section AUTHORS
- *
- * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
- *
- * Authors:
- * Rafal Wojtczuk <rafal@invisiblethingslab.com>
- * Daniel De Graaf <dgdegra@tycho.nsa.gov>
- * Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
- *
- * @section LICENSE
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; If not, see <http://www.gnu.org/licenses/>.
- *
- * @section DESCRIPTION
- *
- * This is a vchan to unix socket proxy. Vchan server is set, and on client
- * connection, local socket connection is established. Communication is bidirectional.
- * One client is served at a time, clients needs to coordinate this themselves.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <getopt.h>
-
-#include <xenstore.h>
-#include <xenctrl.h>
-#include <libxenvchan.h>
-
-static void usage(char** argv)
-{
- fprintf(stderr, "usage:\n"
- "\t%s [options] domainid nodepath [socket-path|file-no|-]\n"
- "\n"
- "options:\n"
- "\t-m, --mode=client|server - vchan connection mode (client by default)\n"
- "\t-s, --state-path=path - xenstore path where write \"running\" to \n"
- "\t at startup\n"
- "\t-v, --verbose - verbose logging\n"
- "\n"
- "client: client of a vchan connection, fourth parameter can be:\n"
- "\tsocket-path: listen on a UNIX socket at this path and connect to vchan\n"
- "\t whenever new connection is accepted;\n"
- "\t handle multiple _subsequent_ connections, until terminated\n"
- "\n"
- "\tfile-no: except open FD of a socket in listen mode;\n"
- "\t otherwise similar to socket-path\n"
- "\n"
- "\t-: open vchan connection immediately and pass the data\n"
- "\t from stdin/stdout; terminate when vchan connection\n"
- "\t is closed\n"
- "\n"
- "server: server of a vchan connection, fourth parameter can be:\n"
- "\tsocket-path: connect to this UNIX socket when new vchan connection\n"
- "\t is accepted;\n"
- "\t handle multiple _subsequent_ connections, until terminated\n"
- "\n"
- "\tfile-no: pass data to/from this FD; terminate when vchan connection\n"
- "\t is closed\n"
- "\n"
- "\t-: pass data to/from stdin/stdout; terminate when vchan\n"
- "\t connection is closed\n",
- argv[0]);
- exit(1);
-}
-
-#define BUFSIZE 8192
-char inbuf[BUFSIZE];
-char outbuf[BUFSIZE];
-int insiz = 0;
-int outsiz = 0;
-int verbose = 0;
-
-struct vchan_proxy_state {
- struct libxenvchan *ctrl;
- int output_fd;
- int input_fd;
-};
-
-static void vchan_wr(struct libxenvchan *ctrl) {
- int ret;
-
- if (!insiz)
- return;
- ret = libxenvchan_write(ctrl, inbuf, insiz);
- if (ret < 0) {
- fprintf(stderr, "vchan write failed\n");
- exit(1);
- }
- if (verbose)
- fprintf(stderr, "wrote %d bytes to vchan\n", ret);
- if (ret > 0) {
- insiz -= ret;
- memmove(inbuf, inbuf + ret, insiz);
- }
-}
-
-static void socket_wr(int output_fd) {
- int ret;
-
- if (!outsiz)
- return;
- ret = write(output_fd, outbuf, outsiz);
- if (ret < 0 && errno != EAGAIN)
- exit(1);
- if (ret > 0) {
- outsiz -= ret;
- memmove(outbuf, outbuf + ret, outsiz);
- }
-}
-
-static int set_nonblocking(int fd, int nonblocking) {
- int flags = fcntl(fd, F_GETFL);
- if (flags == -1)
- return -1;
-
- if (nonblocking)
- flags |= O_NONBLOCK;
- else
- flags &= ~O_NONBLOCK;
-
- if (fcntl(fd, F_SETFL, flags) == -1)
- return -1;
-
- return 0;
-}
-
-static int connect_socket(const char *path_or_fd) {
- int fd;
- char *endptr;
- struct sockaddr_un addr;
-
- fd = strtoll(path_or_fd, &endptr, 0);
- if (*endptr == '\0') {
- set_nonblocking(fd, 1);
- return fd;
- }
-
- if (strlen(path_or_fd) >= sizeof(addr.sun_path)) {
- fprintf(stderr, "UNIX socket path \"%s\" too long (%zd >= %zd)\n",
- path_or_fd, strlen(path_or_fd), sizeof(addr.sun_path));
- return -1;
- }
-
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd == -1) {
- perror("socket");
- return -1;
- }
-
- addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, path_or_fd);
- if (connect(fd, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
- perror("connect");
- close(fd);
- return -1;
- }
-
- set_nonblocking(fd, 1);
-
- return fd;
-}
-
-static int listen_socket(const char *path_or_fd) {
- int fd;
- char *endptr;
- struct sockaddr_un addr;
-
- fd = strtoll(path_or_fd, &endptr, 0);
- if (*endptr == '\0') {
- return fd;
- }
-
- if (strlen(path_or_fd) >= sizeof(addr.sun_path)) {
- fprintf(stderr, "UNIX socket path \"%s\" too long (%zd >= %zd)\n",
- path_or_fd, strlen(path_or_fd), sizeof(addr.sun_path));
- return -1;
- }
-
- /* if not a number, assume a socket path */
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd == -1) {
- perror("socket");
- return -1;
- }
-
- addr.sun_family = AF_UNIX;
- strcpy(addr.sun_path, path_or_fd);
- if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
- perror("bind");
- close(fd);
- return -1;
- }
- if (listen(fd, 5) != 0) {
- perror("listen");
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-static struct libxenvchan *connect_vchan(int domid, const char *path) {
- struct libxenvchan *ctrl = NULL;
- struct xs_handle *xs = NULL;
- xc_interface *xc = NULL;
- xc_dominfo_t dominfo;
- char **watch_ret;
- unsigned int watch_num;
- int ret;
-
- xs = xs_open(XS_OPEN_READONLY);
- if (!xs) {
- perror("xs_open");
- goto out;
- }
- xc = xc_interface_open(NULL, NULL, XC_OPENFLAG_NON_REENTRANT);
- if (!xc) {
- perror("xc_interface_open");
- goto out;
- }
- /* wait for vchan server to create *path* */
- if (!xs_watch(xs, path, "path")) {
- fprintf(stderr, "xs_watch(%s) failed.\n", path);
- goto out;
- }
- if (!xs_watch(xs, "@releaseDomain", "release")) {
- fprintf(stderr, "xs_watch(@releaseDomain failed.\n");
- goto out;
- }
-
- while ((watch_ret = xs_read_watch(xs, &watch_num))) {
- /* don't care about exact which fired the watch */
- free(watch_ret);
- ctrl = libxenvchan_client_init(NULL, domid, path);
- if (ctrl)
- break;
-
- ret = xc_domain_getinfo(xc, domid, 1, &dominfo);
- /* break the loop if domain is definitely not there anymore, but
- * continue if it is or the call failed (like EPERM) */
- if (ret == -1 && errno == ESRCH)
- break;
- if (ret == 1 && (dominfo.domid != (uint32_t)domid || dominfo.dying))
- break;
- }
-
-out:
- if (xc)
- xc_interface_close(xc);
- if (xs)
- xs_close(xs);
- return ctrl;
-}
-
-
-static void discard_buffers(struct libxenvchan *ctrl) {
- /* discard local buffers */
- insiz = 0;
- outsiz = 0;
-
- /* discard remaining incoming data */
- while (libxenvchan_data_ready(ctrl)) {
- if (libxenvchan_read(ctrl, inbuf, BUFSIZE) == -1) {
- perror("vchan read");
- exit(1);
- }
- }
-}
-
-int data_loop(struct vchan_proxy_state *state)
-{
- int ret;
- int libxenvchan_fd;
- int max_fd;
-
- libxenvchan_fd = libxenvchan_fd_for_select(state->ctrl);
- for (;;) {
- fd_set rfds;
- fd_set wfds;
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
-
- max_fd = -1;
- if (state->input_fd != -1 && insiz != BUFSIZE) {
- FD_SET(state->input_fd, &rfds);
- if (state->input_fd > max_fd)
- max_fd = state->input_fd;
- }
- if (state->output_fd != -1 && outsiz) {
- FD_SET(state->output_fd, &wfds);
- if (state->output_fd > max_fd)
- max_fd = state->output_fd;
- }
- FD_SET(libxenvchan_fd, &rfds);
- if (libxenvchan_fd > max_fd)
- max_fd = libxenvchan_fd;
- ret = select(max_fd + 1, &rfds, &wfds, NULL, NULL);
- if (ret < 0) {
- perror("select");
- exit(1);
- }
- if (FD_ISSET(libxenvchan_fd, &rfds)) {
- libxenvchan_wait(state->ctrl);
- if (!libxenvchan_is_open(state->ctrl)) {
- if (verbose)
- fprintf(stderr, "vchan client disconnected\n");
- while (outsiz)
- socket_wr(state->output_fd);
- close(state->output_fd);
- state->output_fd = -1;
- close(state->input_fd);
- state->input_fd = -1;
- discard_buffers(state->ctrl);
- break;
- }
- vchan_wr(state->ctrl);
- }
-
- if (FD_ISSET(state->input_fd, &rfds)) {
- ret = read(state->input_fd, inbuf + insiz, BUFSIZE - insiz);
- if (ret < 0 && errno != EAGAIN)
- exit(1);
- if (verbose)
- fprintf(stderr, "from-unix: %.*s\n", ret, inbuf + insiz);
- if (ret == 0) {
- /* EOF on socket, write everything in the buffer and close the
- * state->input_fd socket */
- while (insiz) {
- vchan_wr(state->ctrl);
- libxenvchan_wait(state->ctrl);
- }
- close(state->input_fd);
- if (state->input_fd == state->output_fd)
- state->output_fd = -1;
- state->input_fd = -1;
- /* TODO: maybe signal the vchan client somehow? */
- break;
- }
- if (ret)
- insiz += ret;
- vchan_wr(state->ctrl);
- }
- if (FD_ISSET(state->output_fd, &wfds))
- socket_wr(state->output_fd);
- while (libxenvchan_data_ready(state->ctrl) && outsiz < BUFSIZE) {
- ret = libxenvchan_read(state->ctrl, outbuf + outsiz,
- BUFSIZE - outsiz);
- if (ret < 0)
- exit(1);
- if (verbose)
- fprintf(stderr, "from-vchan: %.*s\n", ret, outbuf + outsiz);
- outsiz += ret;
- socket_wr(state->output_fd);
- }
- }
- return 0;
-}
-
-/**
- Simple libxenvchan application, both client and server.
- Both sides may write and read, both from the libxenvchan and from
- stdin/stdout (just like netcat).
-*/
-
-static struct option options[] = {
- { "mode", required_argument, NULL, 'm' },
- { "verbose", no_argument, NULL, 'v' },
- { "state-path", required_argument, NULL, 's' },
- { }
-};
-
-int main(int argc, char **argv)
-{
- int is_server = 0;
- int socket_fd = -1;
- struct vchan_proxy_state state = { .ctrl = NULL,
- .input_fd = -1,
- .output_fd = -1 };
- const char *socket_path;
- int domid;
- const char *vchan_path;
- const char *state_path = NULL;
- int opt;
- int ret;
-
- while ((opt = getopt_long(argc, argv, "m:vs:", options, NULL)) != -1) {
- switch (opt) {
- case 'm':
- if (strcmp(optarg, "server") == 0)
- is_server = 1;
- else if (strcmp(optarg, "client") == 0)
- is_server = 0;
- else {
- fprintf(stderr, "invalid argument for --mode: %s\n", optarg);
- usage(argv);
- return 1;
- }
- break;
- case 'v':
- verbose = 1;
- break;
- case 's':
- state_path = optarg;
- break;
- case '?':
- usage(argv);
- }
- }
-
- if (argc-optind != 3)
- usage(argv);
-
- domid = atoi(argv[optind]);
- vchan_path = argv[optind+1];
- socket_path = argv[optind+2];
-
- if (is_server) {
- state.ctrl = libxenvchan_server_init(NULL, domid, vchan_path, 0, 0);
- if (!state.ctrl) {
- perror("libxenvchan_server_init");
- exit(1);
- }
- } else {
- if (strcmp(socket_path, "-") == 0) {
- state.input_fd = 0;
- state.output_fd = 1;
- } else {
- socket_fd = listen_socket(socket_path);
- if (socket_fd == -1) {
- fprintf(stderr, "listen socket failed\n");
- return 1;
- }
- }
- }
-
- if (state_path) {
- struct xs_handle *xs;
-
- xs = xs_open(0);
- if (!xs) {
- perror("xs_open");
- return 1;
- }
- if (!xs_write(xs, XBT_NULL, state_path, "running", strlen("running"))) {
- perror("xs_write");
- return 1;
- }
- xs_close(xs);
- }
-
- ret = 0;
-
- for (;;) {
- if (is_server) {
- /* wait for vchan connection */
- while (libxenvchan_is_open(state.ctrl) != 1)
- libxenvchan_wait(state.ctrl);
- /* vchan client connected, setup local FD if needed */
- if (strcmp(socket_path, "-") == 0) {
- state.input_fd = 0;
- state.output_fd = 1;
- } else {
- state.input_fd = state.output_fd = connect_socket(socket_path);
- }
- if (state.input_fd == -1) {
- fprintf(stderr, "connect_socket failed\n");
- ret = 1;
- break;
- }
- if (data_loop(&state) != 0)
- break;
- /* keep it running only when get UNIX socket path */
- if (socket_path[0] != '/')
- break;
- } else {
- /* wait for local socket connection */
- if (strcmp(socket_path, "-") != 0)
- state.input_fd = state.output_fd = accept(socket_fd,
- NULL, NULL);
- if (state.input_fd == -1) {
- perror("accept");
- ret = 1;
- break;
- }
- set_nonblocking(state.input_fd, 1);
- set_nonblocking(state.output_fd, 1);
- state.ctrl = connect_vchan(domid, vchan_path);
- if (!state.ctrl) {
- perror("vchan client init");
- ret = 1;
- break;
- }
- if (data_loop(&state) != 0)
- break;
- /* don't reconnect if output was stdout */
- if (strcmp(socket_path, "-") == 0)
- break;
-
- libxenvchan_close(state.ctrl);
- state.ctrl = NULL;
- }
- }
-
- if (state.output_fd >= 0)
- close(state.output_fd);
- if (state.input_fd >= 0)
- close(state.input_fd);
- if (state.ctrl)
- libxenvchan_close(state.ctrl);
- if (socket_fd >= 0)
- close(socket_fd);
-
- return ret;
-}
--- /dev/null
+#
+# tools/vchan/Makefile
+#
+
+XEN_ROOT = $(CURDIR)/../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+NODE_OBJS = node.o
+NODE2_OBJS = node-select.o
+
+$(NODE_OBJS) $(NODE2_OBJS): CFLAGS += $(CFLAGS_libxenvchan) $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn)
+vchan-socket-proxy.o: CFLAGS += $(CFLAGS_libxenvchan) $(CFLAGS_libxenstore) $(CFLAGS_libxenctrl) $(CFLAGS_libxengnttab) $(CFLAGS_libxenevtchn)
+
+.PHONY: all
+all: vchan-node1 vchan-node2 vchan-socket-proxy
+
+vchan-node1: $(NODE_OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(NODE_OBJS) $(LDLIBS_libxenvchan) $(APPEND_LDFLAGS)
+
+vchan-node2: $(NODE2_OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(NODE2_OBJS) $(LDLIBS_libxenvchan) $(APPEND_LDFLAGS)
+
+vchan-socket-proxy: vchan-socket-proxy.o
+ $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenvchan) $(LDLIBS_libxenstore) $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
+
+.PHONY: install
+install: all
+ $(INSTALL_DIR) $(DESTDIR)$(bindir)
+ $(INSTALL_PROG) vchan-socket-proxy $(DESTDIR)$(bindir)
+
+.PHONY: clean
+clean:
+ $(RM) -f *.o vchan-node1 vchan-node2 $(DEPS_RM)
+
+distclean: clean
+
+-include $(DEPS_INCLUDE)
--- /dev/null
+/**
+ * @file
+ * @section AUTHORS
+ *
+ * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ *
+ * Authors:
+ * Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ * Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section DESCRIPTION
+ *
+ * This is a test program for libxenvchan. Communications are bidirectional,
+ * with either server (grant offeror) or client able to read and write.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <libxenvchan.h>
+
+static void usage(char** argv)
+{
+ fprintf(stderr, "usage:\n"
+ "\t%s [client|server] domainid nodepath [rbufsiz wbufsiz]\n",
+ argv[0]);
+ exit(1);
+}
+
+#define BUFSIZE 5000
+char inbuf[BUFSIZE];
+char outbuf[BUFSIZE];
+int insiz = 0;
+int outsiz = 0;
+struct libxenvchan *ctrl = 0;
+
+static void vchan_wr(void) {
+ int ret;
+
+ if (!insiz)
+ return;
+ ret = libxenvchan_write(ctrl, inbuf, insiz);
+ if (ret < 0) {
+ fprintf(stderr, "vchan write failed\n");
+ exit(1);
+ }
+ if (ret > 0) {
+ insiz -= ret;
+ memmove(inbuf, inbuf + ret, insiz);
+ }
+}
+
+static void stdout_wr(void) {
+ int ret;
+
+ if (!outsiz)
+ return;
+ ret = write(1, outbuf, outsiz);
+ if (ret < 0 && errno != EAGAIN)
+ exit(1);
+ if (ret > 0) {
+ outsiz -= ret;
+ memmove(outbuf, outbuf + ret, outsiz);
+ }
+}
+
+static int set_nonblocking(int fd, int nonblocking) {
+ int flags = fcntl(fd, F_GETFL);
+ if (flags == -1)
+ return -1;
+
+ if (nonblocking)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ return -1;
+
+ return 0;
+}
+
+/**
+ Simple libxenvchan application, both client and server.
+ Both sides may write and read, both from the libxenvchan and from
+ stdin/stdout (just like netcat).
+*/
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int libxenvchan_fd;
+ if (argc < 4 || argv[3][0] != '/')
+ usage(argv);
+ if (!strcmp(argv[1], "server")) {
+ int rsiz = argc > 4 ? atoi(argv[4]) : 0;
+ int wsiz = argc > 5 ? atoi(argv[5]) : 0;
+ ctrl = libxenvchan_server_init(NULL, atoi(argv[2]), argv[3], rsiz, wsiz);
+ } else if (!strcmp(argv[1], "client"))
+ ctrl = libxenvchan_client_init(NULL, atoi(argv[2]), argv[3]);
+ else
+ usage(argv);
+ if (!ctrl) {
+ perror("libxenvchan_*_init");
+ exit(1);
+ }
+
+ if (set_nonblocking(0, 1) || set_nonblocking(1, 1)) {
+ perror("set_nonblocking");
+ exit(1);
+ }
+
+ libxenvchan_fd = libxenvchan_fd_for_select(ctrl);
+ for (;;) {
+ fd_set rfds;
+ fd_set wfds;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ if (insiz != BUFSIZE)
+ FD_SET(0, &rfds);
+ if (outsiz)
+ FD_SET(1, &wfds);
+ FD_SET(libxenvchan_fd, &rfds);
+ ret = select(libxenvchan_fd + 1, &rfds, &wfds, NULL, NULL);
+ if (ret < 0) {
+ perror("select");
+ exit(1);
+ }
+ if (FD_ISSET(0, &rfds)) {
+ ret = read(0, inbuf + insiz, BUFSIZE - insiz);
+ if (ret < 0 && errno != EAGAIN)
+ exit(1);
+ if (ret == 0) {
+ while (insiz) {
+ vchan_wr();
+ libxenvchan_wait(ctrl);
+ }
+ return 0;
+ }
+ if (ret)
+ insiz += ret;
+ vchan_wr();
+ }
+ if (FD_ISSET(libxenvchan_fd, &rfds)) {
+ libxenvchan_wait(ctrl);
+ vchan_wr();
+ }
+ if (FD_ISSET(1, &wfds))
+ stdout_wr();
+ while (libxenvchan_data_ready(ctrl) && outsiz < BUFSIZE) {
+ ret = libxenvchan_read(ctrl, outbuf + outsiz, BUFSIZE - outsiz);
+ if (ret < 0)
+ exit(1);
+ outsiz += ret;
+ stdout_wr();
+ }
+ if (!libxenvchan_is_open(ctrl)) {
+ if (set_nonblocking(1, 0)) {
+ perror("set_nonblocking");
+ exit(1);
+ }
+ while (outsiz)
+ stdout_wr();
+ return 0;
+ }
+ }
+}
--- /dev/null
+/**
+ * @file
+ * @section AUTHORS
+ *
+ * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ *
+ * Authors:
+ * Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ * Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section DESCRIPTION
+ *
+ * This is a test program for libxenvchan. Communications are in one direction,
+ * either server (grant offeror) to client or vice versa.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <libxenvchan.h>
+
+int libxenvchan_write_all(struct libxenvchan *ctrl, char *buf, int size)
+{
+ int written = 0;
+ int ret;
+ while (written < size) {
+ ret = libxenvchan_write(ctrl, buf + written, size - written);
+ if (ret <= 0) {
+ perror("write");
+ exit(1);
+ }
+ written += ret;
+ }
+ return size;
+}
+
+int write_all(int fd, char *buf, int size)
+{
+ int written = 0;
+ int ret;
+ while (written < size) {
+ ret = write(fd, buf + written, size - written);
+ if (ret <= 0) {
+ perror("write");
+ exit(1);
+ }
+ written += ret;
+ }
+ return size;
+}
+
+void usage(char** argv)
+{
+ fprintf(stderr, "usage:\n"
+ "%s [client|server] [read|write] domid nodepath\n", argv[0]);
+ exit(1);
+}
+
+#define BUFSIZE 5000
+char buf[BUFSIZE];
+void reader(struct libxenvchan *ctrl)
+{
+ int size;
+ for (;;) {
+ size = rand() % (BUFSIZE - 1) + 1;
+ size = libxenvchan_read(ctrl, buf, size);
+ fprintf(stderr, "#");
+ if (size < 0) {
+ perror("read vchan");
+ libxenvchan_close(ctrl);
+ exit(1);
+ }
+ size = write_all(1, buf, size);
+ if (size < 0) {
+ perror("stdout write");
+ exit(1);
+ }
+ if (size == 0) {
+ perror("write size=0?\n");
+ exit(1);
+ }
+ }
+}
+
+void writer(struct libxenvchan *ctrl)
+{
+ int size;
+ for (;;) {
+ size = rand() % (BUFSIZE - 1) + 1;
+ size = read(0, buf, size);
+ if (size < 0) {
+ perror("read stdin");
+ libxenvchan_close(ctrl);
+ exit(1);
+ }
+ if (size == 0)
+ break;
+ size = libxenvchan_write_all(ctrl, buf, size);
+ fprintf(stderr, "#");
+ if (size < 0) {
+ perror("vchan write");
+ exit(1);
+ }
+ if (size == 0) {
+ perror("write size=0?\n");
+ exit(1);
+ }
+ }
+}
+
+
+/**
+ Simple libxenvchan application, both client and server.
+ One side does writing, the other side does reading; both from
+ standard input/output fds.
+*/
+int main(int argc, char **argv)
+{
+ int seed = time(0);
+ struct libxenvchan *ctrl = 0;
+ int wr = 0;
+ if (argc < 4)
+ usage(argv);
+ if (!strcmp(argv[2], "read"))
+ wr = 0;
+ else if (!strcmp(argv[2], "write"))
+ wr = 1;
+ else
+ usage(argv);
+ if (!strcmp(argv[1], "server"))
+ ctrl = libxenvchan_server_init(NULL, atoi(argv[3]), argv[4], 0, 0);
+ else if (!strcmp(argv[1], "client"))
+ ctrl = libxenvchan_client_init(NULL, atoi(argv[3]), argv[4]);
+ else
+ usage(argv);
+ if (!ctrl) {
+ perror("libxenvchan_*_init");
+ exit(1);
+ }
+ ctrl->blocking = 1;
+
+ srand(seed);
+ fprintf(stderr, "seed=%d\n", seed);
+ if (wr)
+ writer(ctrl);
+ else
+ reader(ctrl);
+ libxenvchan_close(ctrl);
+ return 0;
+}
--- /dev/null
+/**
+ * @file
+ * @section AUTHORS
+ *
+ * Copyright (C) 2010 Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ *
+ * Authors:
+ * Rafal Wojtczuk <rafal@invisiblethingslab.com>
+ * Daniel De Graaf <dgdegra@tycho.nsa.gov>
+ * Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
+ *
+ * @section LICENSE
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @section DESCRIPTION
+ *
+ * This is a vchan to unix socket proxy. Vchan server is set, and on client
+ * connection, local socket connection is established. Communication is bidirectional.
+ * One client is served at a time, clients needs to coordinate this themselves.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <getopt.h>
+
+#include <xenstore.h>
+#include <xenctrl.h>
+#include <libxenvchan.h>
+
+static void usage(char** argv)
+{
+ fprintf(stderr, "usage:\n"
+ "\t%s [options] domainid nodepath [socket-path|file-no|-]\n"
+ "\n"
+ "options:\n"
+ "\t-m, --mode=client|server - vchan connection mode (client by default)\n"
+ "\t-s, --state-path=path - xenstore path where write \"running\" to \n"
+ "\t at startup\n"
+ "\t-v, --verbose - verbose logging\n"
+ "\n"
+ "client: client of a vchan connection, fourth parameter can be:\n"
+ "\tsocket-path: listen on a UNIX socket at this path and connect to vchan\n"
+ "\t whenever new connection is accepted;\n"
+ "\t handle multiple _subsequent_ connections, until terminated\n"
+ "\n"
+ "\tfile-no: except open FD of a socket in listen mode;\n"
+ "\t otherwise similar to socket-path\n"
+ "\n"
+ "\t-: open vchan connection immediately and pass the data\n"
+ "\t from stdin/stdout; terminate when vchan connection\n"
+ "\t is closed\n"
+ "\n"
+ "server: server of a vchan connection, fourth parameter can be:\n"
+ "\tsocket-path: connect to this UNIX socket when new vchan connection\n"
+ "\t is accepted;\n"
+ "\t handle multiple _subsequent_ connections, until terminated\n"
+ "\n"
+ "\tfile-no: pass data to/from this FD; terminate when vchan connection\n"
+ "\t is closed\n"
+ "\n"
+ "\t-: pass data to/from stdin/stdout; terminate when vchan\n"
+ "\t connection is closed\n",
+ argv[0]);
+ exit(1);
+}
+
+#define BUFSIZE 8192
+char inbuf[BUFSIZE];
+char outbuf[BUFSIZE];
+int insiz = 0;
+int outsiz = 0;
+int verbose = 0;
+
+struct vchan_proxy_state {
+ struct libxenvchan *ctrl;
+ int output_fd;
+ int input_fd;
+};
+
+static void vchan_wr(struct libxenvchan *ctrl) {
+ int ret;
+
+ if (!insiz)
+ return;
+ ret = libxenvchan_write(ctrl, inbuf, insiz);
+ if (ret < 0) {
+ fprintf(stderr, "vchan write failed\n");
+ exit(1);
+ }
+ if (verbose)
+ fprintf(stderr, "wrote %d bytes to vchan\n", ret);
+ if (ret > 0) {
+ insiz -= ret;
+ memmove(inbuf, inbuf + ret, insiz);
+ }
+}
+
+static void socket_wr(int output_fd) {
+ int ret;
+
+ if (!outsiz)
+ return;
+ ret = write(output_fd, outbuf, outsiz);
+ if (ret < 0 && errno != EAGAIN)
+ exit(1);
+ if (ret > 0) {
+ outsiz -= ret;
+ memmove(outbuf, outbuf + ret, outsiz);
+ }
+}
+
+static int set_nonblocking(int fd, int nonblocking) {
+ int flags = fcntl(fd, F_GETFL);
+ if (flags == -1)
+ return -1;
+
+ if (nonblocking)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ return -1;
+
+ return 0;
+}
+
+static int connect_socket(const char *path_or_fd) {
+ int fd;
+ char *endptr;
+ struct sockaddr_un addr;
+
+ fd = strtoll(path_or_fd, &endptr, 0);
+ if (*endptr == '\0') {
+ set_nonblocking(fd, 1);
+ return fd;
+ }
+
+ if (strlen(path_or_fd) >= sizeof(addr.sun_path)) {
+ fprintf(stderr, "UNIX socket path \"%s\" too long (%zd >= %zd)\n",
+ path_or_fd, strlen(path_or_fd), sizeof(addr.sun_path));
+ return -1;
+ }
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ perror("socket");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path_or_fd);
+ if (connect(fd, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ perror("connect");
+ close(fd);
+ return -1;
+ }
+
+ set_nonblocking(fd, 1);
+
+ return fd;
+}
+
+static int listen_socket(const char *path_or_fd) {
+ int fd;
+ char *endptr;
+ struct sockaddr_un addr;
+
+ fd = strtoll(path_or_fd, &endptr, 0);
+ if (*endptr == '\0') {
+ return fd;
+ }
+
+ if (strlen(path_or_fd) >= sizeof(addr.sun_path)) {
+ fprintf(stderr, "UNIX socket path \"%s\" too long (%zd >= %zd)\n",
+ path_or_fd, strlen(path_or_fd), sizeof(addr.sun_path));
+ return -1;
+ }
+
+ /* if not a number, assume a socket path */
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd == -1) {
+ perror("socket");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path_or_fd);
+ if (bind(fd, (const struct sockaddr *)&addr, sizeof(addr)) == -1) {
+ perror("bind");
+ close(fd);
+ return -1;
+ }
+ if (listen(fd, 5) != 0) {
+ perror("listen");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static struct libxenvchan *connect_vchan(int domid, const char *path) {
+ struct libxenvchan *ctrl = NULL;
+ struct xs_handle *xs = NULL;
+ xc_interface *xc = NULL;
+ xc_dominfo_t dominfo;
+ char **watch_ret;
+ unsigned int watch_num;
+ int ret;
+
+ xs = xs_open(XS_OPEN_READONLY);
+ if (!xs) {
+ perror("xs_open");
+ goto out;
+ }
+ xc = xc_interface_open(NULL, NULL, XC_OPENFLAG_NON_REENTRANT);
+ if (!xc) {
+ perror("xc_interface_open");
+ goto out;
+ }
+ /* wait for vchan server to create *path* */
+ if (!xs_watch(xs, path, "path")) {
+ fprintf(stderr, "xs_watch(%s) failed.\n", path);
+ goto out;
+ }
+ if (!xs_watch(xs, "@releaseDomain", "release")) {
+ fprintf(stderr, "xs_watch(@releaseDomain failed.\n");
+ goto out;
+ }
+
+ while ((watch_ret = xs_read_watch(xs, &watch_num))) {
+ /* don't care about exact which fired the watch */
+ free(watch_ret);
+ ctrl = libxenvchan_client_init(NULL, domid, path);
+ if (ctrl)
+ break;
+
+ ret = xc_domain_getinfo(xc, domid, 1, &dominfo);
+ /* break the loop if domain is definitely not there anymore, but
+ * continue if it is or the call failed (like EPERM) */
+ if (ret == -1 && errno == ESRCH)
+ break;
+ if (ret == 1 && (dominfo.domid != (uint32_t)domid || dominfo.dying))
+ break;
+ }
+
+out:
+ if (xc)
+ xc_interface_close(xc);
+ if (xs)
+ xs_close(xs);
+ return ctrl;
+}
+
+
+static void discard_buffers(struct libxenvchan *ctrl) {
+ /* discard local buffers */
+ insiz = 0;
+ outsiz = 0;
+
+ /* discard remaining incoming data */
+ while (libxenvchan_data_ready(ctrl)) {
+ if (libxenvchan_read(ctrl, inbuf, BUFSIZE) == -1) {
+ perror("vchan read");
+ exit(1);
+ }
+ }
+}
+
+int data_loop(struct vchan_proxy_state *state)
+{
+ int ret;
+ int libxenvchan_fd;
+ int max_fd;
+
+ libxenvchan_fd = libxenvchan_fd_for_select(state->ctrl);
+ for (;;) {
+ fd_set rfds;
+ fd_set wfds;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ max_fd = -1;
+ if (state->input_fd != -1 && insiz != BUFSIZE) {
+ FD_SET(state->input_fd, &rfds);
+ if (state->input_fd > max_fd)
+ max_fd = state->input_fd;
+ }
+ if (state->output_fd != -1 && outsiz) {
+ FD_SET(state->output_fd, &wfds);
+ if (state->output_fd > max_fd)
+ max_fd = state->output_fd;
+ }
+ FD_SET(libxenvchan_fd, &rfds);
+ if (libxenvchan_fd > max_fd)
+ max_fd = libxenvchan_fd;
+ ret = select(max_fd + 1, &rfds, &wfds, NULL, NULL);
+ if (ret < 0) {
+ perror("select");
+ exit(1);
+ }
+ if (FD_ISSET(libxenvchan_fd, &rfds)) {
+ libxenvchan_wait(state->ctrl);
+ if (!libxenvchan_is_open(state->ctrl)) {
+ if (verbose)
+ fprintf(stderr, "vchan client disconnected\n");
+ while (outsiz)
+ socket_wr(state->output_fd);
+ close(state->output_fd);
+ state->output_fd = -1;
+ close(state->input_fd);
+ state->input_fd = -1;
+ discard_buffers(state->ctrl);
+ break;
+ }
+ vchan_wr(state->ctrl);
+ }
+
+ if (FD_ISSET(state->input_fd, &rfds)) {
+ ret = read(state->input_fd, inbuf + insiz, BUFSIZE - insiz);
+ if (ret < 0 && errno != EAGAIN)
+ exit(1);
+ if (verbose)
+ fprintf(stderr, "from-unix: %.*s\n", ret, inbuf + insiz);
+ if (ret == 0) {
+ /* EOF on socket, write everything in the buffer and close the
+ * state->input_fd socket */
+ while (insiz) {
+ vchan_wr(state->ctrl);
+ libxenvchan_wait(state->ctrl);
+ }
+ close(state->input_fd);
+ if (state->input_fd == state->output_fd)
+ state->output_fd = -1;
+ state->input_fd = -1;
+ /* TODO: maybe signal the vchan client somehow? */
+ break;
+ }
+ if (ret)
+ insiz += ret;
+ vchan_wr(state->ctrl);
+ }
+ if (FD_ISSET(state->output_fd, &wfds))
+ socket_wr(state->output_fd);
+ while (libxenvchan_data_ready(state->ctrl) && outsiz < BUFSIZE) {
+ ret = libxenvchan_read(state->ctrl, outbuf + outsiz,
+ BUFSIZE - outsiz);
+ if (ret < 0)
+ exit(1);
+ if (verbose)
+ fprintf(stderr, "from-vchan: %.*s\n", ret, outbuf + outsiz);
+ outsiz += ret;
+ socket_wr(state->output_fd);
+ }
+ }
+ return 0;
+}
+
+/**
+ Simple libxenvchan application, both client and server.
+ Both sides may write and read, both from the libxenvchan and from
+ stdin/stdout (just like netcat).
+*/
+
+static struct option options[] = {
+ { "mode", required_argument, NULL, 'm' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "state-path", required_argument, NULL, 's' },
+ { }
+};
+
+int main(int argc, char **argv)
+{
+ int is_server = 0;
+ int socket_fd = -1;
+ struct vchan_proxy_state state = { .ctrl = NULL,
+ .input_fd = -1,
+ .output_fd = -1 };
+ const char *socket_path;
+ int domid;
+ const char *vchan_path;
+ const char *state_path = NULL;
+ int opt;
+ int ret;
+
+ while ((opt = getopt_long(argc, argv, "m:vs:", options, NULL)) != -1) {
+ switch (opt) {
+ case 'm':
+ if (strcmp(optarg, "server") == 0)
+ is_server = 1;
+ else if (strcmp(optarg, "client") == 0)
+ is_server = 0;
+ else {
+ fprintf(stderr, "invalid argument for --mode: %s\n", optarg);
+ usage(argv);
+ return 1;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 's':
+ state_path = optarg;
+ break;
+ case '?':
+ usage(argv);
+ }
+ }
+
+ if (argc-optind != 3)
+ usage(argv);
+
+ domid = atoi(argv[optind]);
+ vchan_path = argv[optind+1];
+ socket_path = argv[optind+2];
+
+ if (is_server) {
+ state.ctrl = libxenvchan_server_init(NULL, domid, vchan_path, 0, 0);
+ if (!state.ctrl) {
+ perror("libxenvchan_server_init");
+ exit(1);
+ }
+ } else {
+ if (strcmp(socket_path, "-") == 0) {
+ state.input_fd = 0;
+ state.output_fd = 1;
+ } else {
+ socket_fd = listen_socket(socket_path);
+ if (socket_fd == -1) {
+ fprintf(stderr, "listen socket failed\n");
+ return 1;
+ }
+ }
+ }
+
+ if (state_path) {
+ struct xs_handle *xs;
+
+ xs = xs_open(0);
+ if (!xs) {
+ perror("xs_open");
+ return 1;
+ }
+ if (!xs_write(xs, XBT_NULL, state_path, "running", strlen("running"))) {
+ perror("xs_write");
+ return 1;
+ }
+ xs_close(xs);
+ }
+
+ ret = 0;
+
+ for (;;) {
+ if (is_server) {
+ /* wait for vchan connection */
+ while (libxenvchan_is_open(state.ctrl) != 1)
+ libxenvchan_wait(state.ctrl);
+ /* vchan client connected, setup local FD if needed */
+ if (strcmp(socket_path, "-") == 0) {
+ state.input_fd = 0;
+ state.output_fd = 1;
+ } else {
+ state.input_fd = state.output_fd = connect_socket(socket_path);
+ }
+ if (state.input_fd == -1) {
+ fprintf(stderr, "connect_socket failed\n");
+ ret = 1;
+ break;
+ }
+ if (data_loop(&state) != 0)
+ break;
+ /* keep it running only when get UNIX socket path */
+ if (socket_path[0] != '/')
+ break;
+ } else {
+ /* wait for local socket connection */
+ if (strcmp(socket_path, "-") != 0)
+ state.input_fd = state.output_fd = accept(socket_fd,
+ NULL, NULL);
+ if (state.input_fd == -1) {
+ perror("accept");
+ ret = 1;
+ break;
+ }
+ set_nonblocking(state.input_fd, 1);
+ set_nonblocking(state.output_fd, 1);
+ state.ctrl = connect_vchan(domid, vchan_path);
+ if (!state.ctrl) {
+ perror("vchan client init");
+ ret = 1;
+ break;
+ }
+ if (data_loop(&state) != 0)
+ break;
+ /* don't reconnect if output was stdout */
+ if (strcmp(socket_path, "-") == 0)
+ break;
+
+ libxenvchan_close(state.ctrl);
+ state.ctrl = NULL;
+ }
+ }
+
+ if (state.output_fd >= 0)
+ close(state.output_fd);
+ if (state.input_fd >= 0)
+ close(state.input_fd);
+ if (state.ctrl)
+ libxenvchan_close(state.ctrl);
+ if (socket_fd >= 0)
+ close(socket_fd);
+
+ return ret;
+}