aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xcs
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xcs')
-rw-r--r--tools/xcs/Makefile48
-rw-r--r--tools/xcs/bindings.c179
-rw-r--r--tools/xcs/connection.c157
-rw-r--r--tools/xcs/ctrl_interface.c269
-rw-r--r--tools/xcs/evtchn.c108
-rw-r--r--tools/xcs/xcs.c880
-rw-r--r--tools/xcs/xcs.h155
-rw-r--r--tools/xcs/xcs_proto.h101
-rw-r--r--tools/xcs/xcsdump.c182
9 files changed, 2079 insertions, 0 deletions
diff --git a/tools/xcs/Makefile b/tools/xcs/Makefile
new file mode 100644
index 0000000000..b24f81101b
--- /dev/null
+++ b/tools/xcs/Makefile
@@ -0,0 +1,48 @@
+# Makefile for XCS
+# Andrew Warfield, 2004
+
+XEN_ROOT=../..
+include $(XEN_ROOT)/tools/Rules.mk
+
+XCS_INSTALL_DIR = /usr/sbin
+
+INSTALL = install
+INSTALL_PROG = $(INSTALL) -m0755
+INSTALL_DIR = $(INSTALL) -d -m0755
+
+CC = gcc
+CFLAGS = -Wall -Werror -g3 -D _XOPEN_SOURCE=600
+
+CFLAGS += -I $(XEN_XC)
+CFLAGS += -I $(XEN_LIBXC)
+CFLAGS += -I $(XEN_LIBXUTIL)
+
+SRCS :=
+SRCS += ctrl_interface.c
+SRCS += bindings.c
+SRCS += connection.c
+SRCS += evtchn.c
+SRCS += xcs.c
+
+HDRS = $(wildcard *.h)
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+BIN = xcs
+
+all: $(BIN) xcsdump
+
+clean:
+ $(RM) *.a *.so *.o *.rpm $(BIN) ctrl_dump
+
+xcsdump: xcsdump.c
+ $(CC) $(CFLAGS) -o xcsdump xcsdump.c -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) \
+ ctrl_interface.c evtchn.c -lxc -lxutil
+
+$(BIN): $(OBJS)
+ $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_LIBXUTIL) -lxc -lxutil
+
+install: xcs xcsdump
+ $(INSTALL_DIR) -p $(DESTDIR)/$(XCS_INSTALL_DIR)
+ $(INSTALL_DIR) -p $(DESTDIR)/usr/include
+ $(INSTALL_PROG) xcs $(DESTDIR)/$(XCS_INSTALL_DIR)
+ $(INSTALL_PROG) xcsdump $(DESTDIR)/$(XCS_INSTALL_DIR)
+ $(INSTALL_PROG) xcs_proto.h $(DESTDIR)/usr/include
diff --git a/tools/xcs/bindings.c b/tools/xcs/bindings.c
new file mode 100644
index 0000000000..9b09f51568
--- /dev/null
+++ b/tools/xcs/bindings.c
@@ -0,0 +1,179 @@
+/* bindings.c
+ *
+ * Manage subscriptions for the control interface switch.
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+/* Interfaces:
+ *
+ * xcs_bind (port, type, connection)
+ * - Register connection to receive messages of this type.
+ * xcs_unbind (port, type, connection)
+ * - Remove an existing registration. (Must be an exact match)
+ * xcs_lookup (port, type)
+ * - Return a list of connections matching a registration.
+ *
+ * - All connections have a connection.bindings list of current bindings.
+ * - (port, type) pairs may be wildcarded with -1.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include "xcs.h"
+
+
+typedef struct binding_ent_st {
+ connection_t *con;
+ struct binding_ent_st *next;
+} binding_ent_t;
+
+#define BINDING_TABLE_SIZE 1024
+
+static binding_ent_t *binding_table[BINDING_TABLE_SIZE];
+
+#define PORT_WILD(_ent) ((_ent)->port == PORT_WILDCARD)
+#define TYPE_WILD(_ent) ((_ent)->type == TYPE_WILDCARD)
+#define FULLY_WILD(_ent) (PORT_WILD(_ent) && TYPE_WILD(_ent))
+
+#define BINDING_HASH(_key) \
+ ((((_key)->port * 11) ^ (_key)->type) % BINDING_TABLE_SIZE)
+
+
+void init_bindings(void)
+{
+ memset(binding_table, 0, sizeof(binding_table));
+}
+
+static int table_add(binding_ent_t *table[],
+ connection_t *con,
+ binding_key_t *key)
+{
+ binding_ent_t **curs, *ent;
+
+ curs = &table[BINDING_HASH(key)];
+
+ while (*curs != NULL) {
+ if ((*curs)->con == con) {
+ DPRINTF("Tried to add an ent that already existed.\n");
+ goto done;
+ }
+ curs = &(*curs)->next;
+ }
+
+ if (connection_add_binding(con, key) != 0)
+ {
+ DPRINTF("couldn't add binding on connection (%lu)\n", con->id);
+ goto fail;
+ }
+ ent = (binding_ent_t *)malloc(sizeof(binding_ent_t));
+ if (ent == 0) {
+ DPRINTF("couldn't alloc binding ent!\n");
+ goto fail;
+ }
+ ent->con = con;
+ ent->next = NULL;
+ *curs = ent;
+
+done:
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+static inline int binding_has_colliding_hashes(connection_t *con,
+ binding_key_t *key)
+{
+ int hash, count = 0;
+ binding_key_ent_t *ent;
+
+ ent = con->bindings;
+ hash = BINDING_HASH(key);
+
+ while (ent != NULL) {
+ if (BINDING_HASH(&ent->key) == hash) count ++;
+ ent = ent->next;
+ }
+
+ return (count > 1);
+}
+static int table_remove(binding_ent_t *table[],
+ connection_t *con,
+ binding_key_t *key)
+{
+ binding_ent_t **curs, *ent;
+
+ if (!binding_has_colliding_hashes(con, key))
+ {
+
+ curs = &table[BINDING_HASH(key)];
+
+ while ((*curs != NULL) && ((*curs)->con != con))
+ curs = &(*curs)->next;
+
+ if (*curs != NULL) {
+ ent = *curs;
+ *curs = (*curs)->next;
+ free(ent);
+ }
+ }
+
+ connection_remove_binding(con, key);
+
+ return 0;
+}
+
+int xcs_bind(connection_t *con, int port, u16 type)
+{
+ binding_key_t key;
+
+ key.port = port;
+ key.type = type;
+
+ return table_add(binding_table, con, &key);
+}
+
+int xcs_unbind(connection_t *con, int port, u16 type)
+{
+ binding_key_t key;
+
+ key.port = port;
+ key.type = type;
+
+ return table_remove(binding_table, con, &key);
+}
+
+
+static void for_each_binding(binding_ent_t *list, binding_key_t *key,
+ void (*f)(connection_t *, void *), void *arg)
+{
+ while (list != NULL)
+ {
+ if (connection_has_binding(list->con, key))
+ f(list->con, arg);
+ list = list->next;
+ }
+}
+
+void xcs_lookup(int port, u16 type, void (*f)(connection_t *, void *),
+ void *arg)
+{
+ binding_key_t key;
+
+ key.port = port; key.type = type;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = port; key.type = TYPE_WILDCARD;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = PORT_WILDCARD; key.type = type;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+
+ key.port = PORT_WILDCARD; key.type = TYPE_WILDCARD;
+ for_each_binding(binding_table[BINDING_HASH(&key)], &key, f, arg);
+}
diff --git a/tools/xcs/connection.c b/tools/xcs/connection.c
new file mode 100644
index 0000000000..3b5747de68
--- /dev/null
+++ b/tools/xcs/connection.c
@@ -0,0 +1,157 @@
+/*
+ * connection.c
+ *
+ * State associated with a client connection to xcs.
+ *
+ * Copyright (c) 2004, Andrew Warfield
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "xcs.h"
+
+connection_t *connection_list = NULL;
+
+#define CONNECTED(_c) (((_c)->ctrl_fd != -1) || ((_c)->data_fd != -1))
+
+connection_t *get_con_by_session(unsigned long session_id)
+{
+ connection_t **c, *ent = NULL;
+
+ c = &connection_list;
+
+ DPRINTF("looking for id: %lu : %lu\n", session_id, (*c)->id);
+
+ while (*c != NULL)
+ {
+ if ((*c)->id == session_id)
+ return (*c);
+ c = &(*c)->next;
+ }
+
+ return ent;
+}
+
+connection_t *connection_new()
+{
+ connection_t *con;
+
+ con = (connection_t *)malloc(sizeof(connection_t));
+ if (con == NULL)
+ {
+ DPRINTF("couldn't allocate a new connection\n");
+ return NULL;
+ }
+
+ con->bindings = NULL;
+ con->data_fd = -1;
+ con->ctrl_fd = -1;
+
+ /* connections need a unique session id.
+ * - this approach probably gets fixed later, but for the moment
+ * is unique, and clearly identifies a connection.
+ */
+ con->id = (unsigned long)con;
+
+ /* add it to the connection list */
+ con->next = connection_list;
+ connection_list = con;
+
+ return (con);
+}
+
+void connection_free(connection_t *con)
+{
+ /* first free all subscribed bindings: */
+
+ while (con->bindings != NULL)
+ xcs_unbind(con, con->bindings->key.port, con->bindings->key.type);
+
+ /* now free the connection. */
+ free(con);
+}
+
+int connection_add_binding(connection_t *con, binding_key_t *key)
+{
+ binding_key_ent_t *key_ent;
+
+ key_ent = (binding_key_ent_t *)malloc(sizeof(binding_key_ent_t));
+ if (key_ent == NULL)
+ {
+ DPRINTF("couldn't alloc key in connection_add_binding\n");
+ return -1;
+ }
+
+ key_ent->key = *key;
+ key_ent->next = con->bindings;
+ con->bindings = key_ent;
+
+ return 0;
+}
+
+int connection_remove_binding(connection_t *con, binding_key_t *key)
+{
+ binding_key_ent_t *key_ent;
+ binding_key_ent_t **curs = &con->bindings;
+
+ while ((*curs != NULL) && (!BINDING_KEYS_EQUAL(&(*curs)->key, key)))
+ curs = &(*curs)->next;
+
+ if (*curs != NULL) {
+ key_ent = *curs;
+ *curs = (*curs)->next;
+ free(key_ent);
+ }
+
+ return 0;
+}
+
+
+int connection_has_binding(connection_t *con, binding_key_t *key)
+{
+ binding_key_ent_t *ent;
+ int ret = 0;
+
+ ent = con->bindings;
+
+ while (ent != NULL)
+ {
+ if (BINDING_KEYS_EQUAL(key, &ent->key))
+ {
+ ret = 1;
+ break;
+ }
+ ent = ent->next;
+ }
+
+ return ret;
+}
+
+
+void gc_connection_list(void)
+{
+ connection_t **c, *ent = NULL;
+ struct timeval now, delta;
+
+ c = &connection_list;
+ gettimeofday(&now, NULL);
+
+ while ( *c != NULL )
+ {
+ if ( !CONNECTED(*c) )
+ {
+ timersub(&now, &(*c)->disconnect_time, &delta);
+ if ( delta.tv_sec >= XCS_SESSION_TIMEOUT )
+ {
+ DPRINTF(" : Freeing connection %lu after %lds\n",
+ (*c)->id, delta.tv_sec);
+ ent = *c;
+ *c = (*c)->next;
+ connection_free(ent);
+ continue;
+ }
+ }
+ c = &(*c)->next;
+ }
+}
diff --git a/tools/xcs/ctrl_interface.c b/tools/xcs/ctrl_interface.c
new file mode 100644
index 0000000000..0896910cb4
--- /dev/null
+++ b/tools/xcs/ctrl_interface.c
@@ -0,0 +1,269 @@
+/* control_interface.c
+ *
+ * Interfaces to control message rings to VMs.
+ *
+ * Most of this is directly based on the original xu interface to python
+ * written by Keir Fraser.
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "xcs.h"
+
+static int xc_handle = -1;
+
+/* Called at start-of-day when using the control channel interface. */
+int ctrl_chan_init(void)
+{
+ if ( (xc_handle = xc_interface_open()) == -1 )
+ {
+ DPRINTF("Could not open Xen control interface");
+ return -1;
+ }
+
+ return 0;
+}
+
+static control_if_t *map_control_interface(int fd, unsigned long pfn,
+ u32 dom)
+{
+ char *vaddr = xc_map_foreign_range( fd, dom, PAGE_SIZE,
+ PROT_READ|PROT_WRITE, pfn );
+ if ( vaddr == NULL )
+ return NULL;
+ return (control_if_t *)(vaddr + 2048);
+}
+
+static void unmap_control_interface(int fd, control_if_t *c)
+{
+ char *vaddr = (char *)c - 2048;
+ (void)munmap(vaddr, PAGE_SIZE);
+}
+
+int ctrl_chan_notify(control_channel_t *cc)
+{
+ return xc_evtchn_send(xc_handle, cc->local_port);
+}
+
+int ctrl_chan_read_request(control_channel_t *cc, xcs_control_msg_t *dmsg)
+{
+ control_msg_t *smsg;
+ RING_IDX c = cc->tx_ring.req_cons;
+
+ if ( !RING_HAS_UNCONSUMED_REQUESTS(CTRL_RING, &cc->tx_ring) )
+ {
+ DPRINTF("no request to read\n");
+ return -1;
+ }
+
+ rmb(); /* make sure we see the data associated with the request */
+ smsg = RING_GET_REQUEST(CTRL_RING, &cc->tx_ring, c);
+ memcpy(&dmsg->msg, smsg, sizeof(*smsg));
+ if ( dmsg->msg.length > sizeof(dmsg->msg.msg) )
+ dmsg->msg.length = sizeof(dmsg->msg.msg);
+ cc->tx_ring.req_cons++;
+ return 0;
+}
+
+int ctrl_chan_write_request(control_channel_t *cc,
+ xcs_control_msg_t *smsg)
+{
+ control_msg_t *dmsg;
+ RING_IDX p = cc->rx_ring.req_prod_pvt;
+
+ if ( RING_FULL(CTRL_RING, &cc->rx_ring) )
+ {
+ DPRINTF("no space to write request");
+ return -ENOSPC;
+ }
+
+ dmsg = RING_GET_REQUEST(CTRL_RING, &cc->rx_ring, p);
+ memcpy(dmsg, &smsg->msg, sizeof(*dmsg));
+
+ wmb();
+ cc->rx_ring.req_prod_pvt++;
+ RING_PUSH_REQUESTS(CTRL_RING, &cc->rx_ring);
+
+ return 0;
+}
+
+int ctrl_chan_read_response(control_channel_t *cc, xcs_control_msg_t *dmsg)
+{
+ control_msg_t *smsg;
+ RING_IDX c = cc->rx_ring.rsp_cons;
+
+ if ( !RING_HAS_UNCONSUMED_RESPONSES(CTRL_RING, &cc->rx_ring) )
+ {
+ DPRINTF("no response to read");
+ return -1;
+ }
+
+ rmb(); /* make sure we see the data associated with the request */
+ smsg = RING_GET_RESPONSE(CTRL_RING, &cc->rx_ring, c);
+ memcpy(&dmsg->msg, smsg, sizeof(*smsg));
+ if ( dmsg->msg.length > sizeof(dmsg->msg.msg) )
+ dmsg->msg.length = sizeof(dmsg->msg.msg);
+ cc->rx_ring.rsp_cons++;
+ return 0;
+}
+
+int ctrl_chan_write_response(control_channel_t *cc,
+ xcs_control_msg_t *smsg)
+{
+ control_msg_t *dmsg;
+ RING_IDX p = cc->tx_ring.rsp_prod_pvt;
+
+ /* akw: if the ring is synchronous, you should never need this test! */
+ /* (but it was in the original code... ) */
+ if ( cc->tx_ring.req_cons == cc->tx_ring.rsp_prod_pvt )
+ {
+ DPRINTF("no space to write response");
+ return -ENOSPC;
+ }
+
+ dmsg = RING_GET_RESPONSE(CTRL_RING, &cc->tx_ring, p);
+ memcpy(dmsg, &smsg->msg, sizeof(*dmsg));
+
+ wmb();
+ cc->tx_ring.rsp_prod_pvt++;
+ RING_PUSH_RESPONSES(CTRL_RING, &cc->tx_ring);
+
+ return 0;
+}
+
+int ctrl_chan_request_to_read(control_channel_t *cc)
+{
+ return (RING_HAS_UNCONSUMED_REQUESTS(CTRL_RING, &cc->tx_ring));
+}
+
+int ctrl_chan_space_to_write_request(control_channel_t *cc)
+{
+ return (!(RING_FULL(CTRL_RING, &cc->rx_ring)));
+}
+
+int ctrl_chan_response_to_read(control_channel_t *cc)
+{
+ return (RING_HAS_UNCONSUMED_RESPONSES(CTRL_RING, &cc->rx_ring));
+}
+
+int ctrl_chan_space_to_write_response(control_channel_t *cc)
+{
+ /* again, there is something fishy here. */
+ return ( cc->tx_ring.req_cons != cc->tx_ring.rsp_prod_pvt );
+}
+
+int ctrl_chan_connect(control_channel_t *cc)
+{
+ xc_dominfo_t info;
+
+ if ( cc->connected )
+ {
+ return 0;
+ }
+
+ if ( (xc_domain_getinfo(xc_handle, cc->remote_dom, 1, &info) != 1) ||
+ (info.domid != cc->remote_dom) )
+ {
+ DPRINTF("Failed to obtain domain status");
+ return -1;
+ }
+
+ cc->interface =
+ map_control_interface(xc_handle, info.shared_info_frame,
+ cc->remote_dom);
+
+ if ( cc->interface == NULL )
+ {
+ DPRINTF("Failed to map domain control interface");
+ return -1;
+ }
+
+ /* Synchronise ring indexes. */
+ BACK_RING_ATTACH(CTRL_RING, &cc->tx_ring, &cc->interface->tx_ring);
+ FRONT_RING_ATTACH(CTRL_RING, &cc->rx_ring, &cc->interface->rx_ring);
+
+ cc->connected = 1;
+
+ return 0;
+}
+
+void ctrl_chan_disconnect(control_channel_t *cc)
+{
+ if ( cc->connected )
+ unmap_control_interface(xc_handle, cc->interface);
+ cc->connected = 0;
+}
+
+
+control_channel_t *ctrl_chan_new(u32 dom, int local_port, int remote_port)
+{
+ control_channel_t *cc;
+
+ cc = (control_channel_t *)malloc(sizeof(control_channel_t));
+ if ( cc == NULL ) return NULL;
+
+ cc->connected = 0;
+ cc->remote_dom = dom;
+
+ if ( dom == 0 )
+ {
+ /*
+ * The control-interface event channel for DOM0 is already set up.
+ * We use an ioctl to discover the port at our end of the channel.
+ */
+ local_port = ioctl(xc_handle, IOCTL_PRIVCMD_INITDOMAIN_EVTCHN,
+ NULL);
+ remote_port = -1; /* We don't need the remote end of the DOM0 link. */
+ if ( local_port < 0 )
+ {
+ DPRINTF("Could not open channel to DOM0");
+ goto fail;
+ }
+ }
+ else if ( xc_evtchn_bind_interdomain(xc_handle,
+ DOMID_SELF, dom,
+ &local_port, &remote_port) != 0 )
+ {
+ DPRINTF("Could not open channel to domain");
+ goto fail;
+ }
+
+ cc->local_port = local_port;
+ cc->remote_port = remote_port;
+
+ if ( ctrl_chan_connect(cc) != 0 )
+ goto fail;
+
+ return cc;
+
+ fail:
+ if ( dom != 0 )
+ (void)xc_evtchn_close(xc_handle, DOMID_SELF, local_port);
+
+ free(cc);
+
+ return NULL;
+}
+
+void ctrl_chan_free(control_channel_t *cc)
+{
+ ctrl_chan_disconnect(cc);
+ if ( cc->remote_dom != 0 )
+ (void)xc_evtchn_close(xc_handle, DOMID_SELF, cc->local_port);
+ free(cc);
+}
+
+
+/* other libxc commands: */
+
+int ctrl_chan_bind_virq(int virq, int *port)
+{
+ return xc_evtchn_bind_virq(xc_handle, virq, port);
+}
diff --git a/tools/xcs/evtchn.c b/tools/xcs/evtchn.c
new file mode 100644
index 0000000000..a96036db37
--- /dev/null
+++ b/tools/xcs/evtchn.c
@@ -0,0 +1,108 @@
+/* evtchn.c
+ *
+ * Interfaces to event channel driver.
+ *
+ * Most of this is directly based on the original xu interface to python
+ * written by Keir Fraser.
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h> /* XOPEN drops makedev, this gets it back. */
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include "xcs.h"
+
+static int evtchn_fd = -1;
+
+/* NB. The following should be kept in sync with the kernel's evtchn driver. */
+#define EVTCHN_DEV_NAME "/dev/xen/evtchn"
+#define EVTCHN_DEV_MAJOR 10
+#define EVTCHN_DEV_MINOR 201
+/* /dev/xen/evtchn ioctls: */
+/* EVTCHN_RESET: Clear and reinit the event buffer. Clear error condition. */
+#define EVTCHN_RESET _IO('E', 1)
+/* EVTCHN_BIND: Bind to teh specified event-channel port. */
+#define EVTCHN_BIND _IO('E', 2)
+/* EVTCHN_UNBIND: Unbind from the specified event-channel port. */
+#define EVTCHN_UNBIND _IO('E', 3)
+
+int evtchn_read()
+{
+ u16 v;
+ int bytes;
+
+ while ( (bytes = read(evtchn_fd, &v, sizeof(v))) == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ /* EAGAIN was cased to return 'None' in the python version... */
+ return -errno;
+ }
+
+ if ( bytes == sizeof(v) )
+ return v;
+
+ /* bad return */
+ return -1;
+}
+
+void evtchn_unmask(u16 idx)
+{
+ (void)write(evtchn_fd, &idx, sizeof(idx));
+}
+
+int evtchn_bind(int idx)
+{
+ if ( ioctl(evtchn_fd, EVTCHN_BIND, idx) != 0 )
+ return -errno;
+
+ return 0;
+}
+
+int evtchn_unbind(int idx)
+{
+ if ( ioctl(evtchn_fd, EVTCHN_UNBIND, idx) != 0 )
+ return -errno;
+
+ return 0;
+}
+
+int evtchn_open(void)
+{
+ struct stat st;
+
+ /* Make sure any existing device file links to correct device. */
+ if ( (lstat(EVTCHN_DEV_NAME, &st) != 0) ||
+ !S_ISCHR(st.st_mode) ||
+ (st.st_rdev != makedev(EVTCHN_DEV_MAJOR, EVTCHN_DEV_MINOR)) )
+ (void)unlink(EVTCHN_DEV_NAME);
+
+ reopen:
+ evtchn_fd = open(EVTCHN_DEV_NAME, O_NONBLOCK|O_RDWR);
+ if ( evtchn_fd == -1 )
+ {
+ if ( (errno == ENOENT) &&
+ ((mkdir("/dev/xen", 0755) == 0) || (errno == EEXIST)) &&
+ (mknod(EVTCHN_DEV_NAME, S_IFCHR|0600,
+ makedev(EVTCHN_DEV_MAJOR,EVTCHN_DEV_MINOR)) == 0) )
+ goto reopen;
+ return -errno;
+ }
+ /*set_cloexec(evtchn_fd); -- no longer required*/
+printf("Eventchan_fd is %d\n", evtchn_fd);
+ return evtchn_fd;
+}
+
+void evtchn_close()
+{
+ (void)close(evtchn_fd);
+ evtchn_fd = -1;
+}
+
diff --git a/tools/xcs/xcs.c b/tools/xcs/xcs.c
new file mode 100644
index 0000000000..f0b1a96a88
--- /dev/null
+++ b/tools/xcs/xcs.c
@@ -0,0 +1,880 @@
+/* xcs.c
+ *
+ * xcs - Xen Control Switch
+ *
+ * Copyright (c) 2004, Andrew Warfield
+ */
+
+/*
+
+ Things we need to select on in xcs:
+
+ 1. Events arriving on /dev/evtchn
+
+ These will kick a function to read everything off the fd, and scan the
+ associated control message rings, resulting in notifications sent on
+ data channels to connected clients.
+
+ 2. New TCP connections on XCS_PORT.
+
+ These will either be control (intially) or associated data connections.
+
+ Control connections will instantiate or rebind to an existing connnection
+ struct. The control channel is used to configure what events will be
+ received on an associated data channel. These two channels are split
+ out because the control channel is synchronous, all messages will return
+ a result from XCS. The data channel is effectively asynchronous, events
+ may arrive in the middle of a control message exchange. Additionally,
+ Having two TCP connections allows the client side to have a blocking
+ listen loop for data messages, while independently interacting on the
+ control channel at other places in the code.
+
+ Data connections attach to an existing control struct, using a session
+ id that is passed during the control connect. There is currently a
+ one-to-one relationship between data and control channels, but there
+ could just as easily be many data channels, if there were a set of
+ clients with identical interests, or if you wanted to trace an existing
+ client's data traffic.
+
+ 3. Messages arriving on open TCP connections.
+ There are three types of open connections:
+
+ 3a. Messages arriving on open control channel file descriptors.
+
+ [description of the control protocol here]
+
+ 3b. Messages arriving on open data channel file descriptors.
+
+ [description of the data protocol here]
+
+ 3c. Messages arriving on (new) unbound connections.
+
+ A connection must issue a XCS_CONNECT message to specify what
+ it is, after which the connection is moved into one of the above
+ two groups.
+
+ Additionally, we need a periodic timer to do housekeeping.
+
+ 4. Every XCS_GC_INTERVAL seconds, we need to clean up outstanding state.
+ Specifically, we garbage collect any sessions (connection_t structs)
+ that have been unconnected for a period of time (XCS_SESSION_TIMEOUT),
+ and close any connections that have been openned, but not connected
+ as a control or data connection (XCS_UFD_TIMEOUT).
+
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <malloc.h>
+#include <fcntl.h>
+#include "xcs.h"
+
+#undef fd_max
+#define fd_max(x,y) ((x) > (y) ? (x) : (y))
+
+/* ------[ Control channel interfaces ]------------------------------------*/
+
+static control_channel_t *cc_list[NR_EVENT_CHANNELS];
+static int *dom_port_map = 0;
+static int dom_port_map_size = 0;
+
+static void map_dom_to_port(u32 dom, int port)
+{
+ if (dom >= dom_port_map_size) {
+ dom_port_map = (int *)realloc(dom_port_map,
+ (dom + 10) * sizeof(dom_port_map[0]));
+
+ if (dom_port_map == NULL) {
+ perror("realloc(dom_port_map)");
+ exit(1);
+ }
+
+ for (; dom_port_map_size < dom + 10; dom_port_map_size++) {
+ dom_port_map[dom_port_map_size] = -1;
+ }
+ }
+
+ dom_port_map[dom] = port;
+}
+
+static int dom_to_port(u32 dom) {
+ if (dom >= dom_port_map_size) return -1;
+
+ return dom_port_map[dom];
+}
+
+static void init_interfaces(void)
+{
+ memset(cc_list, 0, sizeof cc_list);
+}
+
+static control_channel_t *add_interface(u32 dom, int local_port,
+ int remote_port)
+{
+ control_channel_t *cc=NULL, *oldcc;
+ int ret;
+
+ if (cc_list[dom_to_port(dom)] != NULL)
+ {
+ return(cc_list[dom_to_port(dom)]);
+ }
+
+ if (cc_list[local_port] == NULL)
+ {
+ cc = ctrl_chan_new(dom, local_port, remote_port);
+ }
+
+ if (cc == NULL)
+ return NULL;
+
+ DPRINTF("added a new interface: dom: %u (l:%d,r:%d): %p\n",
+ dom, local_port, remote_port, cc);
+ DPRINTF("added a new interface: dom: %u (l:%d,r:%d): %p\n",
+ dom, cc->local_port, cc->remote_port, cc);
+
+ if ((ret = evtchn_bind(cc->local_port)) != 0)
+ {
+ DPRINTF("Got control interface, but couldn't bind evtchan!(%d)\n", ret);
+ ctrl_chan_free(cc);
+ return NULL;
+ }
+
+ if ( cc_list[cc->local_port] != NULL )
+ {
+ oldcc = cc_list[cc->local_port];
+
+ if ((oldcc->remote_dom != cc->remote_dom) ||
+ (oldcc->remote_port != cc->remote_port))
+ {
+ DPRINTF("CC conflict! (port: %d, old dom: %u, new dom: %u)\n",
+ cc->local_port, oldcc->remote_dom, cc->remote_dom);
+ map_dom_to_port(oldcc->remote_dom, -1);
+ ctrl_chan_free(cc_list[cc->local_port]);
+ }
+ }
+
+ cc_list[cc->local_port] = cc;
+ map_dom_to_port(cc->remote_dom, cc->local_port);
+ cc->type = CC_TYPE_INTERDOMAIN;
+ cc->ref_count = 0;
+ return cc;
+}
+
+control_channel_t *add_virq(int virq)
+{
+ control_channel_t *cc;
+ int virq_port;
+
+ if (ctrl_chan_bind_virq(virq, &virq_port) == -1)
+ return NULL;
+
+ if ((cc_list[virq_port] != NULL) &&
+ (cc_list[virq_port]->type != CC_TYPE_VIRQ))
+ return NULL;
+
+ if ((cc_list[virq_port] != NULL) &&
+ (cc_list[virq_port]->type == CC_TYPE_VIRQ))
+ return cc_list[virq_port];
+
+ cc = (control_channel_t *)malloc(sizeof(control_channel_t));
+ if ( cc == NULL ) return NULL;
+
+ cc->type = CC_TYPE_VIRQ;
+ cc->local_port = virq_port;
+ cc->virq = virq;
+
+ return cc;
+}
+
+void get_interface(control_channel_t *cc)
+{
+ if (cc != NULL)
+ cc->ref_count++;
+}
+
+void put_interface(control_channel_t *cc)
+{
+ if (cc != NULL)
+ {
+ cc->ref_count--;
+ if (cc->ref_count <= 0)
+ {
+ DPRINTF("Freeing cc on port %d.\n", cc->local_port);
+ (void)evtchn_unbind(cc->local_port);
+ ctrl_chan_free(cc);
+ }
+ }
+}
+
+/* ------[ Simple helpers ]------------------------------------------------*/
+
+/* listen_socket() is straight from paul sheer's useful select_tut manpage. */
+static int listen_socket (int listen_port)
+{
+ struct sockaddr_in a;
+ int s;
+ int yes;
+
+ if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ perror ("socket");
+ return -1;
+ }
+
+ yes = 1;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &yes, sizeof (yes)) < 0)
+ {
+ perror ("setsockopt");
+ close (s);
+ return -1;
+ }
+
+ memset (&a, 0, sizeof (a));
+ a.sin_port = htons (listen_port);
+ a.sin_family = AF_INET;
+ if (bind(s, (struct sockaddr *) &a, sizeof (a)) < 0)
+ {
+ perror ("bind");
+ close (s);
+ return -1;
+ }
+ printf ("accepting connections on port %d\n", (int) listen_port);
+ listen (s, 10);
+ return s;
+}
+
+/* ------[ Message handlers ]----------------------------------------------*/
+
+#define NO_CHANGE 0
+#define CONNECTED 1
+#define DISCONNECTED 2
+int handle_connect_msg( xcs_msg_t *msg, int fd )
+{
+ xcs_connect_msg_t *cmsg = &msg->u.connect;
+ connection_t *con;
+ int ret = NO_CHANGE;
+
+ switch (msg->type)
+ {
+ case XCS_CONNECT_CTRL:
+ {
+ if ( cmsg->session_id == 0 )
+ {
+ con = connection_new();
+ if ( con == NULL)
+ {
+ msg->result = XCS_RSLT_FAILED;
+ break;
+ }
+ msg->result = XCS_RSLT_OK;
+ cmsg->session_id = con->id;
+ con->ctrl_fd = fd;
+ ret = CONNECTED;
+ DPRINTF("New control connection\n");
+ break;
+ }
+
+ con = get_con_by_session(cmsg->session_id);
+ if ( con == NULL )
+ {
+ msg->result = XCS_RSLT_BADSESSION;
+ break;
+ }
+ if ( con->ctrl_fd != -1 )
+ {
+ msg->result = XCS_RSLT_CONINUSE;
+ break;
+ }
+ con->ctrl_fd = fd;
+ msg->result = XCS_RSLT_OK;
+ ret = CONNECTED;
+ DPRINTF("Rebound to control connection\n");
+ break;
+ }
+ case XCS_CONNECT_DATA:
+ {
+ con = get_con_by_session(cmsg->session_id);
+ if ( con == NULL )
+ {
+ msg->result = XCS_RSLT_BADSESSION;
+ break;
+ }
+ if ( con->data_fd != -1 )
+ {
+ msg->result = XCS_RSLT_CONINUSE;
+ break;
+ }
+ con->data_fd = fd;
+ msg->result = XCS_RSLT_OK;
+ ret = CONNECTED;
+ DPRINTF("Attached data connection\n");
+ break;
+
+ }
+ case XCS_CONNECT_BYE:
+ {
+ close ( fd );
+ ret = DISCONNECTED;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int handle_control_message( connection_t *con, xcs_msg_t *msg )
+{
+ int ret;
+ int reply_needed = 1;
+
+ DPRINTF("Got message, type %u.\n", msg->type);
+
+ switch (msg->type)
+ {
+ case XCS_MSG_BIND:
+ {
+ xcs_bind_msg_t *bmsg = &msg->u.bind;
+
+ if ( ! BIND_MSG_VALID(bmsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ ret = xcs_bind(con, bmsg->port, bmsg->type);
+ if (ret == 0) {
+ msg->result = XCS_RSLT_OK;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+ case XCS_MSG_UNBIND:
+ {
+ xcs_bind_msg_t *bmsg = &msg->u.bind;
+
+ if ( ! BIND_MSG_VALID(bmsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ ret = xcs_unbind(con, bmsg->port, bmsg->type);
+ if (ret == 0) {
+ msg->result = XCS_RSLT_OK;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+ case XCS_VIRQ_BIND:
+ {
+ control_channel_t *cc;
+ xcs_virq_msg_t *vmsg = &msg->u.virq;
+ if ( ! VIRQ_MSG_VALID(vmsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ cc = add_virq(vmsg->virq);
+ if (cc == NULL)
+ {
+ msg->result = XCS_RSLT_FAILED;
+ break;
+ }
+ ret = xcs_bind(con, cc->local_port, TYPE_VIRQ);
+ if (ret == 0) {
+ vmsg->port = cc->local_port;
+ msg->result = XCS_RSLT_OK;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+
+ case XCS_CIF_NEW_CC:
+ {
+ control_channel_t *cc;
+ xcs_interface_msg_t *imsg = &msg->u.interface;
+
+ if ( ! INTERFACE_MSG_VALID(imsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ cc = add_interface(imsg->dom, imsg->local_port, imsg->remote_port);
+ if (cc != NULL) {
+ get_interface(cc);
+ msg->result = XCS_RSLT_OK;
+ imsg->local_port = cc->local_port;
+ imsg->remote_port = cc->remote_port;
+ } else {
+ msg->result = XCS_RSLT_FAILED;
+ }
+ break;
+ }
+
+ case XCS_CIF_FREE_CC:
+ {
+ control_channel_t *cc;
+ xcs_interface_msg_t *imsg = &msg->u.interface;
+
+ if ( ! INTERFACE_MSG_VALID(imsg) )
+ {
+ msg->result = XCS_RSLT_BADREQUEST;
+ break;
+ }
+
+ cc = add_interface(imsg->dom, imsg->local_port, imsg->remote_port);
+ if (cc != NULL) {
+ put_interface(cc);
+ }
+ msg->result = XCS_RSLT_OK;
+ break;
+ }
+ }
+ return reply_needed;
+}
+
+void handle_data_message( connection_t *con, xcs_msg_t *msg )
+{
+ control_channel_t *cc;
+ xcs_control_msg_t *cmsg = &msg->u.control;
+ int port;
+
+ switch (msg->type)
+ {
+ case XCS_REQUEST:
+ if ( cmsg->remote_dom > MAX_DOMS )
+ break;
+
+ port = dom_to_port(cmsg->remote_dom);
+ if (port == -1) break;
+ cc = cc_list[port];
+ if ((cc != NULL) && ( cc->type == CC_TYPE_INTERDOMAIN ))
+ {
+ DPRINTF("DN:REQ: dom:%d port: %d type: %d\n",
+ cc->remote_dom, cc->local_port,
+ cmsg->msg.type);
+ ctrl_chan_write_request(cc, cmsg);
+ ctrl_chan_notify(cc);
+ } else {
+ DPRINTF("tried to send a REQ to a null cc\n.");
+ }
+ break;
+
+ case XCS_RESPONSE:
+ if ( cmsg->remote_dom > MAX_DOMS )
+ break;
+
+ port = dom_to_port(cmsg->remote_dom);
+ if (port == -1) break;
+ cc = cc_list[port];
+ if ((cc != NULL) && ( cc->type == CC_TYPE_INTERDOMAIN ))
+ {
+ DPRINTF("DN:RSP: dom:%d port: %d type: %d\n",
+ cc->remote_dom, cc->local_port,
+ cmsg->msg.type);
+ ctrl_chan_write_response(cc, cmsg);
+ ctrl_chan_notify(cc);
+ }
+ break;
+
+ case XCS_VIRQ:
+ if ( !(PORT_VALID(cmsg->local_port)) )
+ break;
+
+ cc = cc_list[cmsg->local_port];
+
+ if ((cc != NULL) && ( cc->type == CC_TYPE_VIRQ ))
+ {
+ DPRINTF("DN:VIRQ: virq: %d port: %d\n",
+ cc->virq, cc->local_port);
+ ctrl_chan_notify(cc);
+ }
+ break;
+ }
+}
+
+/* ------[ Control interface handler ]-------------------------------------*/
+
+/* passed as a function pointer to the lookup. */
+void send_kmsg(connection_t *c, void *arg)
+{
+ xcs_msg_t *msg = (xcs_msg_t *)arg;
+
+ DPRINTF(" -> CONNECTION %d\n", c->data_fd);
+ if (c->data_fd > 0)
+ {
+ send(c->data_fd, msg, sizeof(xcs_msg_t), 0);
+ }
+}
+
+int handle_ctrl_if(void)
+{
+ control_channel_t *cc;
+ control_msg_t *msg;
+ xcs_msg_t kmsg;
+ int chan, ret;
+
+ DPRINTF("Event thread kicked!\n");
+again:
+ while ((chan = evtchn_read()) > 0)
+ {
+ evtchn_unmask(chan);
+ cc = cc_list[chan];
+ if (cc_list[chan] == NULL) {
+ DPRINTF("event from unknown channel (%d)\n", chan);
+ continue;
+ }
+
+ if ( cc_list[chan]->type == CC_TYPE_VIRQ )
+ {
+ DPRINTF("UP:VIRQ: virq:%d port: %d\n",
+ cc->virq, cc->local_port);
+ kmsg.type = XCS_VIRQ;
+ kmsg.u.control.local_port = cc->local_port;
+ xcs_lookup(cc->local_port, TYPE_VIRQ, send_kmsg, &kmsg);
+ continue;
+ }
+
+ while (ctrl_chan_request_to_read(cc))
+ {
+ msg = &kmsg.u.control.msg;
+ kmsg.type = XCS_REQUEST;
+ kmsg.u.control.remote_dom = cc->remote_dom;
+ kmsg.u.control.local_port = cc->local_port;
+ ret = ctrl_chan_read_request(cc, &kmsg.u.control);
+ DPRINTF("UP:REQ: dom:%d port: %d type: %d len: %d\n",
+ cc->remote_dom, cc->local_port,
+ msg->type, msg->length);
+ if (ret == 0)
+ xcs_lookup(cc->local_port, msg->type, send_kmsg, &kmsg);
+ }
+
+ while (ctrl_chan_response_to_read(cc))
+ {
+ msg = &kmsg.u.control.msg;
+ kmsg.type = XCS_RESPONSE;
+ kmsg.u.control.remote_dom = cc->remote_dom;
+ kmsg.u.control.local_port = cc->local_port;
+ ret = ctrl_chan_read_response(cc, &kmsg.u.control);
+ DPRINTF("UP:RSP: dom:%d port: %d type: %d len: %d\n",
+ cc->remote_dom, cc->local_port,
+ msg->type, msg->length);
+ if (ret == 0)
+ xcs_lookup(cc->local_port, msg->type, send_kmsg, &kmsg);
+ }
+ }
+
+ if (chan == -EINTR)
+ goto again;
+
+ return chan;
+}
+
+
+/* ------[ Main xcs code / big select loop ]-------------------------------*/
+
+
+typedef struct unbound_fd_st {
+ int fd;
+ struct timeval born;
+ struct unbound_fd_st *next;
+} unbound_fd_t;
+
+/* This makes ufd point to the next entry in the list, so need to *
+ * break/continue if called while iterating. */
+void delete_ufd(unbound_fd_t **ufd)
+{
+ unbound_fd_t *del_ufd;
+
+ del_ufd = *ufd;
+ *ufd = (*ufd)->next;
+ free( del_ufd );
+}
+
+void gc_ufd_list( unbound_fd_t **ufd )
+{
+ struct timeval now, delta;
+
+ gettimeofday(&now, NULL);
+
+ while ( *ufd != NULL )
+ {
+ timersub(&now, &(*ufd)->born, &delta);
+ if (delta.tv_sec > XCS_UFD_TIMEOUT)
+ {
+ DPRINTF("GC-UFD: closing fd: %d\n", (*ufd)->fd);
+ close((*ufd)->fd);
+ delete_ufd(ufd);
+ continue;
+ }
+ ufd = &(*ufd)->next;
+ }
+}
+
+int main (int argc, char*argv[])
+{
+ int listen_fd, evtchn_fd;
+ unbound_fd_t *unbound_fd_list = NULL, **ufd;
+ struct timeval timeout = { XCS_GC_INTERVAL, 0 };
+ connection_t **con;
+
+ /* Initialize xc and event connections. */
+ if (ctrl_chan_init() != 0)
+ {
+ printf("Couldn't open conneciton to libxc.\n");
+ exit(-1);
+ }
+
+ if ((evtchn_fd = evtchn_open()) < 0)
+ {
+ printf("Couldn't open event channel driver interface.\n");
+ exit(-1);
+ }
+
+ /* Initialize control interfaces, bindings. */
+ init_interfaces();
+ init_bindings();
+
+ listen_fd = listen_socket(XCS_TCP_PORT);
+
+ /* detach from our controlling tty so that a shell does hang waiting for
+ stopped jobs. */
+ /* we should use getopt() here */
+
+ if (!(argc == 2 && !strcmp(argv[1], "-i"))) {
+ pid_t pid = fork();
+ int fd;
+
+ if (pid == -1) {
+ perror("fork()");
+ } else if (pid) {
+ exit(0);
+ }
+
+ setsid();
+ close(2);
+ close(1);
+ close(0);
+ fd = open("/dev/null", O_RDWR);
+ dup(fd);
+ dup(fd);
+ }
+
+ for (;;)
+ {
+ int n, ret;
+ fd_set rd, wr, er;
+ FD_ZERO ( &rd );
+ FD_ZERO ( &wr );
+ FD_ZERO ( &er );
+
+ /* TCP listen fd: */
+ FD_SET ( listen_fd, &rd );
+ n = fd_max ( n, listen_fd );
+
+ /* Evtchn fd: */
+ FD_SET ( evtchn_fd, &rd );
+ n = fd_max ( n, evtchn_fd );
+
+ /* unbound connection fds: */
+ ufd = &unbound_fd_list;
+ while ((*ufd) != NULL)
+ {
+ FD_SET ( (*ufd)->fd, &rd );
+ n = fd_max ( n, (*ufd)->fd );
+ ufd = &(*ufd)->next;
+ }
+
+ /* control and data fds: */
+ con = &connection_list;
+ while ((*con) != NULL)
+ {
+ if ((*con)->ctrl_fd > 0)
+ {
+ FD_SET ( (*con)->ctrl_fd, &rd );
+ n = fd_max ( n, (*con)->ctrl_fd );
+ }
+ if ((*con)->data_fd > 0)
+ {
+ FD_SET ( (*con)->data_fd, &rd );
+ n = fd_max ( n, (*con)->data_fd );
+ }
+ con = &(*con)->next;
+ }
+
+ ret = select ( n + 1, &rd, &wr, &er, &timeout );
+
+ if ( (timeout.tv_sec == 0) && (timeout.tv_usec == 0) )
+ {
+ gc_ufd_list(&unbound_fd_list);
+ gc_connection_list();
+ timeout.tv_sec = XCS_GC_INTERVAL;
+ }
+
+ if ( (ret == -1) && (errno == EINTR) )
+ continue;
+ if ( ret < 0 )
+ {
+ perror ("select()");
+ exit(-1);
+ }
+
+ /* CASE 1: Events arriving on /dev/evtchn. */
+
+ if ( FD_ISSET (evtchn_fd, &rd ))
+ handle_ctrl_if();
+
+ /* CASE 2: New connection on the listen port. */
+ if ( FD_ISSET ( listen_fd, &rd ))
+ {
+ struct sockaddr_in remote_addr;
+ int size;
+ memset (&remote_addr, 0, sizeof (remote_addr));
+ size = sizeof remote_addr;
+ ret = accept(listen_fd, (struct sockaddr *)&remote_addr, &size);
+ if ( ret < 0 )
+ {
+ perror("accept()");
+ } else {
+ unbound_fd_t *new_ufd;
+
+ new_ufd = (unbound_fd_t *)malloc(sizeof(*new_ufd));
+
+ if (new_ufd != NULL)
+ {
+ gettimeofday(&new_ufd->born, NULL);
+ new_ufd->fd = ret;
+ new_ufd->next = unbound_fd_list;
+ unbound_fd_list = new_ufd;
+ } else {
+ perror("malloc unbound connection");
+ close(ret);
+ }
+ }
+ }
+
+ /* CASE 3a: Handle messages on control connections. */
+
+ con = &connection_list;
+ while ( *con != NULL )
+ {
+ if ( ((*con)->ctrl_fd > 0) && (FD_ISSET((*con)->ctrl_fd, &rd)) )
+ {
+ xcs_msg_t msg;
+ memset (&msg, 0, sizeof(msg));
+ ret = read( (*con)->ctrl_fd, &msg, sizeof(msg) );
+
+ if ( ret < 0 )
+ {
+ perror("reading ctrl fd.");
+ } else if ( ret == 0 )
+ {
+ DPRINTF("Control connection dropped.\n");
+ close ( (*con)->ctrl_fd );
+ (*con)->ctrl_fd = -1;
+ gettimeofday(&(*con)->disconnect_time, NULL);
+ } else
+ {
+ if ( ret != sizeof(msg) )
+ {
+ DPRINTF("Unexpected frame size!\n");
+ continue;
+ }
+
+ ret = handle_control_message( *con, &msg );
+
+ if ( ret == 1 )
+ send( (*con)->ctrl_fd, &msg, sizeof(msg), 0 );
+ }
+ }
+ con = &(*con)->next;
+ }
+
+ /* CASE 3b: Handle messages on data connections. */
+
+ con = &connection_list;
+ while ( *con != NULL )
+ {
+ if ( ((*con)->data_fd > 0) && (FD_ISSET((*con)->data_fd, &rd)) )
+ {
+ xcs_msg_t msg;
+ memset (&msg, 0, sizeof(msg));
+ ret = read( (*con)->data_fd, &msg, sizeof(msg) );
+
+ if ( ret < 0 )
+ {
+ perror("reading data fd.");
+ } else if ( ret == 0 )
+ {
+ DPRINTF("Data connection dropped.\n");
+ close ( (*con)->data_fd );
+ (*con)->data_fd = -1;
+ gettimeofday(&(*con)->disconnect_time, NULL);
+ } else
+ {
+ if ( ret != sizeof(msg) )
+ {
+ DPRINTF("Unexpected frame size!\n");
+ continue;
+ }
+
+ handle_data_message( *con, &msg );
+ }
+ }
+ con = &(*con)->next;
+ }
+
+ /* CASE 3c: Handle messages arriving on unbound connections. */
+ ufd = &unbound_fd_list;
+ while ((*ufd) != NULL)
+ {
+ if ( FD_ISSET( (*ufd)->fd, &rd ) )
+ {
+ xcs_msg_t msg;
+ memset (&msg, 0, sizeof(msg));
+ ret = read( (*ufd)->fd, &msg, sizeof(msg) );
+
+ if ( ret == 0 )
+ {
+ close ( (*ufd)->fd );
+ delete_ufd(ufd);
+ continue; /* we just advanced ufd */
+ } else {
+ if ( ret != sizeof(msg) )
+ {
+ DPRINTF("Unexpected frame size!\n");
+ continue;
+ }
+
+ ret = handle_connect_msg( &msg, (*ufd)->fd );
+
+ if ( (ret == CONNECTED) || (ret == NO_CHANGE) )
+ send( (*ufd)->fd, &msg, sizeof(msg), 0 );
+
+ if ( (ret = CONNECTED) || (ret = DISCONNECTED) )
+ {
+ delete_ufd( ufd );
+ continue;
+ }
+ }
+ }
+ ufd = &(*ufd)->next;
+ }
+ }
+}
+
diff --git a/tools/xcs/xcs.h b/tools/xcs/xcs.h
new file mode 100644
index 0000000000..545fc3d05f
--- /dev/null
+++ b/tools/xcs/xcs.h
@@ -0,0 +1,155 @@
+/* xcs.h
+ *
+ * public interfaces for the control interface switch (xcs).
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+
+#ifndef __XCS_H__
+#define __XCS_H__
+
+#include <pthread.h>
+#include <xc.h>
+#include <xen/xen.h>
+#include <xen/io/domain_controller.h>
+#include <xen/linux/privcmd.h>
+#include <sys/time.h>
+#include "xcs_proto.h"
+
+/* ------[ Debug macros ]--------------------------------------------------*/
+
+#if 0
+#define DPRINTF(_f, _a...) printf ( _f , ## _a )
+#else
+#define DPRINTF(_f, _a...) ((void)0)
+#endif
+
+/* ------[ XCS-specific defines and types ]--------------------------------*/
+
+#define MAX_DOMS 1024
+#define XCS_SESSION_TIMEOUT 10 /* (secs) disconnected session gc timeout */
+#define XCS_UFD_TIMEOUT 5 /* how long can connections be unbound? */
+#define XCS_GC_INTERVAL 5 /* How often to run gc handlers. */
+
+
+/* ------[ Other required defines ]----------------------------------------*/
+
+/* Size of a machine page frame. */
+#define PAGE_SIZE 4096
+
+#if defined(__i386__)
+#define rmb() __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define wmb() __asm__ __volatile__ ( "" : : : "memory" )
+#else
+#error "Define barriers"
+#endif
+
+#ifndef timersub /* XOPEN and __BSD don't cooperate well... */
+#define timersub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif /*timersub*/
+
+/* ------[ Bindings Interface ]--------------------------------------------*/
+
+/*forward declare connection_t */
+typedef struct connection_st connection_t;
+
+typedef struct {
+ int port;
+ u16 type;
+} binding_key_t;
+
+typedef struct binding_key_ent_st {
+ binding_key_t key;
+ struct binding_key_ent_st *next;
+} binding_key_ent_t;
+
+#define BINDING_KEYS_EQUAL(_k1, _k2) \
+ (((_k1)->port == (_k2)->port) && ((_k1)->type == (_k2)->type))
+
+int xcs_bind(connection_t *con, int port, u16 type);
+int xcs_unbind(connection_t *con, int port, u16 type);
+void xcs_lookup(int port, u16 type, void (*f)(connection_t *, void *),
+ void *arg);
+void init_bindings(void);
+
+/* ------[ Connection Interface ]------------------------------------------*/
+
+struct connection_st {
+ unsigned long id; /* Unique session id */
+ int ctrl_fd; /* TCP descriptors */
+ int data_fd; /* */
+ binding_key_ent_t *bindings; /* List of bindings */
+ connection_t *next; /* Linked list of connections */
+ struct timeval disconnect_time; /* " " */
+}; /* previously typedefed as connection_t */
+
+
+extern connection_t *connection_list;
+
+connection_t *get_con_by_session(unsigned long session_id);
+connection_t *connection_new();
+void connection_free(connection_t *con);
+int connection_add_binding(connection_t *con, binding_key_t *key);
+int connection_remove_binding(connection_t *con, binding_key_t *key);
+int connection_has_binding(connection_t *con, binding_key_t *key);
+void gc_connection_list(void);
+
+/* ------[ Control Channel Interfaces ]------------------------------------*/
+
+typedef struct {
+ int connected;
+ int ref_count;
+ int type;
+ u32 remote_dom;
+ int local_port;
+ int remote_port;
+ control_if_t *interface;
+ ctrl_back_ring_t tx_ring;
+ ctrl_front_ring_t rx_ring;
+ int virq;
+} control_channel_t;
+
+/* cc types that we care about */
+#define CC_TYPE_INTERDOMAIN 0
+#define CC_TYPE_VIRQ 1
+
+control_channel_t
+ *ctrl_chan_new(u32 dom, int local_port, int remote_port);
+void ctrl_chan_free(control_channel_t *cc);
+int ctrl_chan_init(void);
+int ctrl_chan_notify(control_channel_t *cc);
+int ctrl_chan_read_request(control_channel_t *cc, xcs_control_msg_t *);
+int ctrl_chan_write_request(control_channel_t *cc,
+ xcs_control_msg_t *smsg);
+int ctrl_chan_read_response(control_channel_t *cc, xcs_control_msg_t *);
+int ctrl_chan_write_response(control_channel_t *cc,
+ xcs_control_msg_t *smsg);
+int ctrl_chan_request_to_read(control_channel_t *cc);
+int ctrl_chan_space_to_write_request(control_channel_t *cc);
+int ctrl_chan_response_to_read(control_channel_t *cc);
+int ctrl_chan_space_to_write_response(control_channel_t *cc);
+int ctrl_chan_connect(control_channel_t *cc);
+void ctrl_chan_disconnect(control_channel_t *cc);
+int ctrl_chan_bind_virq(int virq, int *port);
+
+/* ------[ Event notification interfaces ]---------------------------------*/
+
+
+int evtchn_open(void);
+void evtchn_close();
+int evtchn_bind(int idx);
+int evtchn_unbind(int idx);
+void evtchn_unmask(u16 idx);
+int evtchn_read();
+
+#endif /* __XCS_H__ */
diff --git a/tools/xcs/xcs_proto.h b/tools/xcs/xcs_proto.h
new file mode 100644
index 0000000000..ea227c2ff7
--- /dev/null
+++ b/tools/xcs/xcs_proto.h
@@ -0,0 +1,101 @@
+/* xcs_proto.h
+ *
+ * protocol interfaces for the control interface switch (xcs).
+ *
+ * (c) 2004, Andrew Warfield
+ *
+ */
+
+#ifndef __XCS_PROTO_H__
+#define __XCS_PROTO_H__
+
+#define XCS_TCP_PORT 1633
+
+/* xcs message types: */
+#define XCS_CONNECT_CTRL 0 /* This is a control connection. */
+#define XCS_CONNECT_DATA 1 /* This is a data connection. */
+#define XCS_CONNECT_BYE 2 /* Terminate a session. */
+#define XCS_MSG_BIND 3 /* Register for a message type. */
+#define XCS_MSG_UNBIND 4 /* Unregister for a message type. */
+#define XCS_VIRQ_BIND 5 /* Register for a virq. */
+#define XCS_MSG_WRITELOCK 6 /* Writelock a (dom,type) pair. */
+#define XCS_CIF_NEW_CC 7 /* Create a new control channel. */
+#define XCS_CIF_FREE_CC 8 /* Create a new control channel. */
+#define XCS_REQUEST 9 /* This is a request message. */
+#define XCS_RESPONSE 10 /* this is a response Message. */
+#define XCS_VIRQ 11 /* this is a virq notification. */
+
+/* xcs result values: */
+#define XCS_RSLT_OK 0
+#define XCS_RSLT_FAILED 1 /* something bad happened. */
+#define XCS_RSLT_ARECONNECTED 2 /* attempt to over connect. */
+#define XCS_RSLT_BADSESSION 3 /* request for unknown session id. */
+#define XCS_RSLT_NOSESSION 4 /* tried to do something before NEW. */
+#define XCS_RSLT_CONINUSE 5 /* Requested connection is taken. */
+#define XCS_RSLT_BADREQUEST 6 /* Request message didn't validate. */
+
+/* Binding wildcards */
+#define PORT_WILDCARD 0xefffffff
+#define TYPE_WILDCARD 0xffff
+#define TYPE_VIRQ 0xfffe
+
+typedef struct {
+ u32 session_id;
+} xcs_connect_msg_t;
+
+typedef struct {
+ int port;
+ u16 type;
+} xcs_bind_msg_t;
+
+typedef struct {
+ int port;
+ u16 virq;
+} xcs_virq_msg_t;
+
+typedef struct {
+ u32 dom;
+ int local_port;
+ int remote_port;
+} xcs_interface_msg_t;
+
+typedef struct {
+ u32 remote_dom;
+ int local_port;
+ control_msg_t msg;
+} xcs_control_msg_t;
+
+typedef struct {
+ u32 type;
+ u32 result;
+ union {
+ xcs_connect_msg_t connect; /* These are xcs ctrl message types */
+ xcs_bind_msg_t bind;
+ xcs_virq_msg_t virq;
+ xcs_interface_msg_t interface;
+
+ xcs_control_msg_t control; /* These are xcs data message types */
+ } PACKED u;
+} xcs_msg_t;
+
+/* message validation macros. */
+#define PORT_VALID(_p) \
+ ( (((_p) >= 0) && ((_p) < NR_EVENT_CHANNELS)) \
+ || ((_p) == PORT_WILDCARD) )
+
+#define TYPE_VALID(_t) \
+ ( ((_t) < 256) \
+ || ((_t) == TYPE_VIRQ) \
+ || ((_t) == TYPE_WILDCARD) )
+
+#define BIND_MSG_VALID(_b) \
+ ( PORT_VALID((_b)->port) && TYPE_VALID((_b)->type) )
+
+/* Port is overwritten, and we don't currently validate the requested virq. */
+#define VIRQ_MSG_VALID(_v) ( 1 )
+
+/* Interfaces may return with ports of -1, but may not be requested as such */
+#define INTERFACE_MSG_VALID(_i) \
+ ( PORT_VALID((_i)->local_port) && PORT_VALID((_i)->remote_port) )
+
+#endif /* __XCS_PROTO_H__ */
diff --git a/tools/xcs/xcsdump.c b/tools/xcs/xcsdump.c
new file mode 100644
index 0000000000..dcfd2c9119
--- /dev/null
+++ b/tools/xcs/xcsdump.c
@@ -0,0 +1,182 @@
+/* xcsdump.c
+ *
+ * little tool to sniff control messages.
+ *
+ * Copyright (c) 2004, Andrew Warfield
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <xc.h>
+#include <xen/xen.h>
+#include <xen/io/domain_controller.h>
+#include "xcs_proto.h"
+#include "xcs.h"
+
+static int xcs_ctrl_fd = -1; /* connection to the xcs server. */
+static int xcs_data_fd = -1; /* connection to the xcs server. */
+
+int tcp_connect(char *ip, short port)
+{
+ struct sockaddr_in addr;
+ int ret, fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0)
+ {
+ printf("error creating xcs socket!\n");
+ return -1;
+ }
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = inet_addr(ip);
+ memset(&(addr.sin_zero), '\0', 8);
+
+ ret = connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr));
+ if (ret < 0)
+ {
+ printf("error connecting to xcs!\n");
+ return -1;
+ }
+
+ return fd;
+}
+
+void tcp_disconnect(int *fd)
+{
+ close(*fd);
+ *fd = -1;
+}
+
+void xcs_read(int fd, xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = read(fd, msg, sizeof(xcs_msg_t));
+ if (ret != sizeof(xcs_msg_t)) {
+ printf("read error\n");
+ exit(-1);
+ }
+}
+
+void xcs_send(int fd, xcs_msg_t *msg)
+{
+ int ret;
+
+ ret = send(fd, msg, sizeof(xcs_msg_t), 0);
+ if (ret != sizeof(xcs_msg_t) )
+ {
+ printf("send error\n");
+ exit(-1);
+ }
+}
+
+
+int main(int argc, char* argv[])
+{
+ int ret, i;
+ xcs_msg_t msg;
+ control_msg_t *cmsg;
+ int verbose = 0;
+
+ if (argc > 1)
+ if ((strlen(argv[1]) >=2) && (strncmp(argv[1], "-v", 2) == 0))
+ verbose = 1;
+
+ ret = tcp_connect("127.0.0.1", XCS_TCP_PORT);
+ if (ret < 0)
+ {
+ printf("connect failed!\n");
+ exit(-1);
+ }
+ xcs_ctrl_fd = ret;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.type = XCS_CONNECT_CTRL;
+ xcs_send(xcs_ctrl_fd, &msg);
+ xcs_read(xcs_ctrl_fd, &msg);
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("Error connecting control channel\n");
+ exit(-1);
+ }
+
+ ret = tcp_connect("127.0.0.1", XCS_TCP_PORT);
+ if (ret < 0)
+ {
+ printf("connect failed!\n");
+ exit(-1);
+ }
+ xcs_data_fd = ret;
+
+ msg.type = XCS_CONNECT_DATA;
+ /* session id is set from before... */
+ xcs_send(xcs_data_fd, &msg);
+ xcs_read(xcs_data_fd, &msg);
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("Error connecting data channel\n");
+ exit(-1);
+ }
+
+ msg.type = XCS_MSG_BIND;
+ msg.u.bind.port = PORT_WILDCARD;
+ msg.u.bind.type = TYPE_WILDCARD;
+ xcs_send(xcs_ctrl_fd, &msg);
+ xcs_read(xcs_ctrl_fd, &msg);
+ if (msg.result != XCS_RSLT_OK)
+ {
+ printf("Error binding.\n");
+ exit(-1);
+ }
+
+
+ while (1)
+ {
+ xcs_read(xcs_data_fd, &msg);
+ cmsg = &msg.u.control.msg;
+
+ for (i=0; i<60; i++)
+ if ((!isprint(cmsg->msg[i])) && (cmsg->msg[i] != '\0'))
+ cmsg->msg[i] = '.';
+ cmsg->msg[59] = '\0';
+
+ switch (msg.type)
+ {
+ case XCS_REQUEST:
+ printf("[REQUEST ] : (dom:%u port:%d) (type:(%d,%d) len %d) \n",
+ msg.u.control.remote_dom,
+ msg.u.control.local_port,
+ msg.u.control.msg.type,
+ msg.u.control.msg.subtype,
+ msg.u.control.msg.length);
+ if (verbose)
+ printf(" : %s\n", msg.u.control.msg.msg);
+ break;
+ case XCS_RESPONSE:
+ printf("[RESPONSE] : (dom:%u port:%d) (type:(%d,%d) len %d) \n",
+ msg.u.control.remote_dom,
+ msg.u.control.local_port,
+ msg.u.control.msg.type,
+ msg.u.control.msg.subtype,
+ msg.u.control.msg.length);
+ if (verbose)
+ printf(" : %s\n", msg.u.control.msg.msg);
+ break;
+ case XCS_VIRQ:
+ printf("[VIRQ ] : %d\n", msg.u.control.local_port);
+ default:
+ printf("[UNKNOWN ]\n");
+ }
+ }
+
+ return(0);
+}