aboutsummaryrefslogtreecommitdiffstats
path: root/tools/xcs/xcs.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/xcs/xcs.c')
-rw-r--r--tools/xcs/xcs.c880
1 files changed, 880 insertions, 0 deletions
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;
+ }
+ }
+}
+