/* $Id: ps_conn.c,v 1.2 90/11/29 18:32:58 altenhof Exp $ */

/*
 * Copyright (C) 1990 by Digital Equipment Corporation.
 * 
 * Author: Michael P. Altenhofen, CEC Karlsruhe e-mail:
 * Altenhofen@kampus.enet.dec.com
 * 
 * This file ist part of Shared X
 * 
 * Permission to use, copy, modify, and distribute this software and its
 * documentation without fee is hereby granted, but only for non-profit  use
 * and distribution,  and provided  that the copyright notice and this notice
 * is preserved on all copies.
 * 
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */



/*****************************************************************
 *  Stuff to create connections --- OS dependent
 *
 *      EstablishNewConnections, CreateWellKnownSockets, ResetWellKnownSockets,
 *      CloseDownConnection, CheckConnections,
 *	OnlyListToOneClient,
 *      ListenToAllClients,
 *
 *      (WaitForSomething is in its own file)
 *
 *      In this implementation, a client socket table is not kept.
 *      Instead, what would be the index into the table is just the
 *      file descriptor of the socket.  This won't work for if the
 *      socket ids aren't small nums (0 - 2^8)
 *
 *****************************************************************/

#include <dbm.h>
#undef NULL

#define PUBLIC extern

#define _USE_STRUCTS_
#define _USE_MASKS_
#define _USE_PROTO_
#define _XEVENTS_

#include "glob.h"

#include "utils.h"
#include "dispatch.h"
#include "map.h"

#undef PUBLIC

#include <X11/Xutil.h>

#include <errno.h>
#include <X11/Xos.h>		/* for strings, file, time */
#include <sys/socket.h>

#include <fcntl.h>

#ifdef hpux
#include <sys/ioctl.h>
#endif

#ifdef TCPCONN
#include <netinet/in.h>
#ifndef hpux
#include <netinet/tcp.h>
#endif
#endif

#ifdef UNIXCONN

/*
 * sites should be careful to have separate /tmp directories for diskless
 * nodes
 */
#include <sys/un.h>
#include <sys/stat.h>
static int unixDomainConnection = -1;
#endif

#include <sys/uio.h>

#ifdef DNETCONN
#include <netdnet/dn.h>
#endif				/* DNETCONN */

typedef long CCID;		/* mask of indices into client socket table */

#ifndef X_UNIX_PATH
#define X_UNIX_DIR	"/tmp/.X11-unix"
#define X_UNIX_PATH	"/tmp/.X11-unix/X"
#endif

extern int 
UnknownWireEvent ();

  int lastfdesc;		/* maximum file descriptor */

  static long NConnBitArrays = mskcnt;
  static long FirstClient;

#ifdef DEBUG
  static Bool debug_conns = TRUE;
#else
  static Bool debug_conns = FALSE;
#endif

  static char whichByteIsFirst;

  static fd_set SavedAllClients;
  static fd_set SavedAllSockets;
  static fd_set SavedClientsWithInput;
  static Bool GrabDone = FALSE;

  int swappedClients[MAXSOCKS];


#ifdef UNIXCONN

  static struct sockaddr_un unsock;

static int
open_unix_socket ()
{
  int request;

  unsock.sun_family = AF_UNIX;
#ifdef X_UNIX_DIR
  mkdir (X_UNIX_DIR, 0777);
#endif
  strcpy (unsock.sun_path, X_UNIX_PATH);
  strcat (unsock.sun_path, display_name);
  unlink (unsock.sun_path);
  if ((request = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
    _Notice ("Creating Unix socket");
  }
  else {
    if (bind (request, (struct sockaddr *) & unsock, strlen (unsock.sun_path) + 2))
      Error ("Binding Unix socket");
    if (chmod (unsock.sun_path, 0666))
      Error (" Setting modes on Unix socket ");
    if (listen (request, 5))
      Error ("Unix Listening");
  }
  return request;
}

#endif				/* UNIXCONN */

/*****************
 * CreateWellKnownSockets
 *    At initialization, create the sockets to listen on for new clients.
 *    There are potentially 4: DECnet, UNIX Domain, TCP-IP with MSB first,
 *    with TCP-IP with LSB first.
 *****************/
#define SLEEP_TIME 1

void
CreateWellKnownSockets ()
{
  int request, i;
  int whichbyte;		/* used to figure out whether this is LSB or
				 * MSB */
#ifdef TCPCONN
  struct sockaddr_in insock;
  int tcpportReg;		/* port with same byte order as server */

#ifdef SO_LINGER
  static int linger[2] =
  {0, 0};
#endif				/* SO_LINGER */

#endif				/* TCPCONN */

#ifdef DNETCONN
  struct sockaddr_dn dnsock;
#endif				/* DNETCONN */
  int retry;

  CLEARBITS (BITS (AllSockets));
  CLEARBITS (BITS (AllClients));
  CLEARBITS (BITS (LastSelectMask));
  CLEARBITS (BITS (ClientsWithInput));

  for (i = 0; i < MAXSOCKS; i++)
    Connections[i] = (ClientPtr) NULL;

#ifdef	hpux
  lastfdesc = _NFILE - 1;
#else
  lastfdesc = getdtablesize () - 1;
#endif				/* hpux */

  if (lastfdesc > MAXSOCKS) {
    lastfdesc = MAXSOCKS;
    if (debug_conns)
      ErrorF ("GOT TO END OF SOCKETS %d\n", MAXSOCKS);
  }

  WellKnownConnections = 0;
  OutputBufferSize = BUFSIZ;
  whichbyte = 1;

  if (*(char *) &whichbyte)
    whichByteIsFirst = 'l';
  else
    whichByteIsFirst = 'B';


#ifdef TCPCONN

  tcpportReg = atoi (display_name);
  tcpportReg += X_TCP_PORT;

  if ((request = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
    _Notice ("Creating TCP socket");
  }
  else {
    bzero ((char *) &insock, sizeof (insock));
    insock.sin_family = AF_INET;
    insock.sin_port = htons (tcpportReg);
    insock.sin_addr.s_addr = htonl (INADDR_ANY);
    retry = 20;
    while (i = bind (request, (struct sockaddr *) & insock, sizeof (insock))) {
#ifdef hpux
      /* Necesary to restart the server without a reboot */
      if (errno == EADDRINUSE)
	set_socket_option (request, SO_REUSEADDR);
      if (--retry == 0)
	Error ("Binding TCP socket");
      sleep (1);
#else
      if (--retry == 0)
	Error ("Binding MSB TCP socket");
      sleep (SLEEP_TIME);
#endif				/* hpux */
    }
#ifdef hpux
    /* return the socket option to the original */
    if (errno)
      unset_socket_option (request, SO_REUSEADDR);
#endif				/* hpux */
#ifdef SO_LINGER
    if (setsockopt (request, SOL_SOCKET, SO_LINGER,
		    (char *) linger, sizeof (linger)))
      _Notice ("Setting TCP SO_LINGER\n");
#endif				/* SO_LINGER */
    if (listen (request, 5))
      Error ("Reg TCP Listening");
    WellKnownConnections |= (1 << request);
    /* DefineSelf (request); */
  }

#endif				/* TCPCONN */

#ifdef UNIXCONN
  if ((request = open_unix_socket ()) != -1) {
    WellKnownConnections |= (1L << request);
    unixDomainConnection = request;
    /* DefineSelf (request); */
  }
#endif				/* UNIXCONN */

#ifdef DNETCONN
  if ((request = socket (AF_DECnet, SOCK_STREAM, 0)) < 0) {
    _Notice ("Creating DECnet socket");
  }
  else {
    bzero ((char *) &dnsock, sizeof (dnsock));
    dnsock.sdn_family = AF_DECnet;
    sprintf (dnsock.sdn_objname, "X$X%d", atoi (display_name));
    dnsock.sdn_objnamel = strlen (dnsock.sdn_objname);
    if (bind (request, (struct sockaddr *) & dnsock, sizeof (dnsock)))
      Error ("Binding DECnet socket");
    if (listen (request, 5))
      Error ("DECnet Listening");
    WellKnownConnections |= (1 << request);
    /* DefineSelf (request); */
  }
#endif				/* DNETCONN */
  if (WellKnownConnections == 0)
    Error ("No Listeners, nothing to do");
  signal (SIGPIPE, SIG_IGN);
  signal (SIGHUP, _AutoResetServer);
  signal (SIGINT, _GiveUp);
  signal (SIGTERM, _GiveUp);
  FirstClient = request + 1;
  BITS (AllSockets)[0] = WellKnownConnections;

  for (i = 0; i < MaxClients; i++) {
    inputBuffers[i].buffer = (char *) NULL;
    inputBuffers[i].bufptr = (char *) NULL;
    inputBuffers[i].bufcnt = 0;
    inputBuffers[i].lenLastReq = 0;
    inputBuffers[i].size = 0;
  }
}

#undef SLEEP_TIME

void
ResetWellKnownSockets ()
{
#ifdef UNIXCONN
  if (unixDomainConnection != -1) {

    /*
     * see if the unix domain socket has disappeared
     */
    struct stat statb;

    if (stat (unsock.sun_path, &statb) == -1 ||
	(statb.st_mode & S_IFMT) != S_IFSOCK) {
      ErrorF ("Unix domain socket %s trashed, recreating\n",
	      unsock.sun_path);
      (void) unlink (unsock.sun_path);
      (void) close (unixDomainConnection);
      WellKnownConnections &= ~(1L << unixDomainConnection);
      unixDomainConnection = open_unix_socket ();
      if (unixDomainConnection != -1)
	WellKnownConnections |= (1L << unixDomainConnection);
    }
  }
#endif				/* UNIXCONN */
}

#define VisualIDFromVisual( visual )  ( (visual)->visualid )
#define GCIDFromGC( gc )  ( (gc)->gid )
void
MapDefaults (dpy)
  Display *dpy;
{
  register XID def_root, def_cmap, def_visualid, def_gcid;
  register int i, idmap;
  int n;
  XVisualInfo *vis_infos, vis_tmp;

  idmap = ConnectionNumber (dpy);
  def_root = DefaultRootWindow (dpy);
  def_cmap = DefaultColormap (dpy, DefaultScreen (dpy));
  def_gcid = GCIDFromGC (DefaultGC (dpy, DefaultScreen (dpy)));

  InsertID (def_root,
	    def_root, idmap, FromClient);
  InsertID (def_root,
	    def_root, idmap, FromServer);
  InsertID (def_cmap,
	    def_cmap, idmap, FromClient);
  InsertID (def_cmap,
	    def_cmap, idmap, FromServer);

  vis_tmp.screen = DefaultScreen (dpy);
  vis_infos = XGetVisualInfo (dpy, VisualScreenMask, &vis_tmp, &n);
  for (i = 0; i < n; i++) {

    def_visualid = VisualIDFromVisual (vis_infos[i].visual);

    InsertID (def_visualid,
	      def_visualid, idmap, FromServer);
    InsertID (def_visualid,
	      def_visualid, idmap, FromClient);
  }
  Xfree ((char *) vis_infos);
  InsertID (def_gcid,
	    def_gcid, idmap, FromServer);
  InsertID (def_gcid,
	    def_gcid, idmap, FromClient);
}

Display *
OpenServerConnection (conn, display)
  int conn;
  char *display;
{
  register Display *dpy;
  register int fd;

  if ((dpy = XOpenDisplay (display)) == NULL) {
    ErrorF (" Can't open display '%s'\n ", display);
    return (NULL);
  }

#ifndef NOT_SHARED_X
  XmuXAddDisplays (dpy);
#endif

  dpy->event_vec[0] =
    dpy->event_vec[1] = UnknownWireEvent;

  /*
   * A new client wants to be connected to the X server (via XmuX) so, we
   * have done an XOpenDisplay to establish a new connection. Alas, this
   * causes a problem concerning Xlib's internal serial numbers: during
   * XOpenDisplay, Xlib sends a number of (2 for X11R2: CreateGC and
   * GetProperty) requests to the server thus incrementing the serials. Our
   * new client will send us these requests a second time (he doesn't know
   * anything about the XOpenDisplay we've done here) incrementing both the X
   * server serials and its own internal serials. This discrepancy may
   * confuse our client and let him complain about "sequence lost in reply
   * type xxx" What shall we do? Ignore these "initial requests" from the
   * client? This would be a bad solution (think about changes in
   * XOpenDisplay). We do it this way: a global counter stores the difference
   * between the client's and the server's serials (which may vary during
   * normal (cf. replies.c) and multiplex mode), which is used to "adjust"
   * the serials. The Xlib macro LastKnownRequestProcessed gives us the right
   * value
   */

  BITSET (BITS (AllSockets), ConnectionNumber (dpy));
  BITSET (BITS (AllServers), ConnectionNumber (dpy));

  /* -- be sure that all default ids are mapped ! -- */
  MapDefaults (dpy);

  return (dpy);
}

/*
 * We want to read the connection information.  If the client doesn't send us
 * enough data, however, we want to time out eventually. The scheme is to
 * clear a flag, set an alarm, and keep doing non-blocking reads until we get
 * all the data we want. If the alarm goes off, the handler will clear the
 * flag.  If we see that the flag is cleared, we know we've timed out and
 * return with an error.
 * 
 * There remains one problem with this code: there is a window of vulnerability
 * in which we might get an alarm even though all the data has come in
 * properly.  This is because I can't atomically clear the alarm.
 * 
 * Anyone who sees how to fix this problem should do so and submit a fix.
 */

jmp_buf env;

void 
TimeOut ()
{
  longjmp (env, 1);
}

#define TimeOutValue 1.0

static Bool
ReadBuffer (conn, buffer, charsWanted)
  long conn;
  char *buffer;
  int charsWanted;
{
  char *bptr = buffer;
  int got, fTimeOut;
  struct itimerval itv;

  signal (SIGALRM, TimeOut);
  fTimeOut = FALSE;
  /* only 1 alarm, please, not 1 per minute */
  timerclear (&itv.it_interval);
  itv.it_value.tv_sec = TimeOutValue;
  itv.it_value.tv_usec = 0;
  setitimer (ITIMER_REAL, &itv, (struct itimerval *) NULL);
  /* It better not take a full minute to get to the read call */

  while (charsWanted && (fTimeOut = setjmp (env)) == FALSE) {
    got = read (conn, bptr, charsWanted);
    if (got <= 0)
      return FALSE;
    if (got > 0) {
      charsWanted -= got;
      bptr += got;
      /* Ok, we got something, reset the timer */
      itv.it_value.tv_sec = TimeOutValue;
      itv.it_value.tv_usec = 0;
      setitimer (ITIMER_REAL, &itv, (struct itimerval *) NULL);
    }
  }
  /* disable the timer */
  timerclear (&itv.it_value);
  setitimer (ITIMER_REAL, &itv, (struct itimerval *) NULL);

  /*
   * If we got here and we didn't time out, then return TRUE, because we must
   * have read what we wanted. If we timed out, return FALSE
   */
  if (fTimeOut && debug_conns)
    ErrorF ("Timed out on connection %d\n", conn);
  return (!fTimeOut);
}

/*****************************************************************
 * ClientAuthorized
 *
 *    Sent by the client at connection setup:
 *                typedef struct _xConnClientPrefix {
 *                   CARD8	byteOrder;
 *                   BYTE	pad;
 *                   CARD16	majorVersion, minorVersion;
 *                   CARD16	nbytesAuthProto;
 *                   CARD16	nbytesAuthString;
 *                 } xConnClientPrefix;
 *
 *     	It is hoped that eventually one protocol will be agreed upon.  In the
 *        mean time, a server that implements a different protocol than the
 *        client expects, or a server that only implements the host-based
 *        mechanism, will simply ignore this information.
 *
 *****************************************************************/

Display *
ClientAuthorized (conn, pswapped, reason)
  long conn;
  int *pswapped;
  char **reason;		/* if authorization fails,
				 * put reason in here */
{
  short slen;
  union {
    struct sockaddr sa;
#ifdef UNIXCONN
    struct sockaddr_un un;
#endif				/* UNIXCONN */
#ifdef TCPCONN
    struct sockaddr_in in;
#endif				/* TCPCONN */
#ifdef DNETCONN
    struct sockaddr_dn dn;
#endif				/* DNETCONN */
  } from;
  int fromlen;
  xConnClientPrefix xccp;
  char auth_proto[100];
  char auth_string[100];

  if (!ReadBuffer (conn, (char *) &xccp, sizeof (xConnClientPrefix))) {

    /*
     * If they can't even give us this much, just blow them off without an
     * error message
     */
    *reason = 0;
    return (NULL);
  }
  if (xccp.byteOrder != whichByteIsFirst) {
    /* --- SwapConnClientPrefix(&xccp); --- */
    *pswapped = TRUE;
  }
  else
    *pswapped = FALSE;
  if ((xccp.majorVersion != X_PROTOCOL) ||
      (xccp.minorVersion != X_PROTOCOL_REVISION)) {
#define STR "Protocol version mismatch"
    *reason = (char *) Xmalloc (sizeof (STR));
    strcpy (*reason, STR);
    if (debug_conns)
      ErrorF ("%s\n", STR);
#undef STR
    return (NULL);
  }
  fromlen = sizeof (from);
  if (getpeername (conn, &from.sa, &fromlen)	/* || InvalidHost (&from.sa,
          fromlen) */ ) {
#define STR "Server is not authorized to connect to host"
    *reason = (char *) Xmalloc (sizeof (STR));
    strcpy (*reason, STR);
#undef STR
    return (NULL);
  }

  slen = (xccp.nbytesAuthProto + 3) & ~3;
  if (slen)
    if (!ReadBuffer (conn, auth_proto, slen)) {
#define STR "Length error in xConnClientPrefix for protocol authorization "
      *reason = (char *) Xmalloc (sizeof (STR));
      strcpy (*reason, STR);
      return (NULL);
#undef STR
    }
  auth_proto[slen] = '\0';

  slen = (xccp.nbytesAuthString + 3) & ~3;
  if (slen)
    if (!ReadBuffer (conn, auth_string, slen)) {
#define STR "Length error in xConnClientPrefix for protocol string"
      *reason = (char *) Xmalloc (sizeof (STR));
      strcpy (*reason, STR);
      return (NULL);
#undef STR
    }
  auth_string[slen] = '\0';

  /*
   * At this point, if the client is authorized to change the access control
   * list, we should getpeername() information, and add the client to the
   * selfhosts list.  It's not really the host machine, but the true purpose
   * of the selfhosts list is to see who may change the access control list.
   */

  return (OpenServerConnection (conn, defaultServer));
}

static int padlength[4] =
{0, 3, 2, 1};

/*****************
 * EstablishNewConnections
 *    If anyone is waiting on listened sockets, accept them.
 *    Returns a mask with indices of new clients.  Updates AllClients
 *    and AllSockets.
 *****************/

void
EstablishNewConnections (newclients, nnew)
  ClientPtr *newclients;
  int *nnew;
{
  long readyconnections;	/* mask of listeners that are ready */
  long curconn;			/* fd of listener that's ready */
  long newconn;			/* fd of new client */
  int swapped;			/* set by ClientAuthorized if connection is
				 * swapped */
  Display *dpy;
  char *reason;
  struct iovec iov[2];
  char p[3];

#ifdef TCP_NODELAY
  union {
    struct sockaddr sa;
#ifdef UNIXCONN
    struct sockaddr_un un;
#endif				/* UNIXCONN */
#ifdef TCPCONN
    struct sockaddr_in in;
#endif				/* TCPCONN */
#ifdef DNETCONN
    struct sockaddr_dn dn;
#endif				/* DNETCONN */
  } from;
  int fromlen;
#endif	/* TCP_NODELAY */

  *nnew = 0;
  if (readyconnections = (LastSelectMask.fds_bits[0] & WellKnownConnections)) {
    while (readyconnections) {
      curconn = ffs (readyconnections) - 1;
      if ((newconn = accept (curconn,
			     (struct sockaddr *) NULL,
			     (int *) NULL)) >= 0) {
	if (newconn >= lastfdesc) {
	  if (debug_conns)
	    ErrorF ("Didn't make connection: Out of file descriptors for connections\n");
	  close (newconn);
	}
	else {
	  ClientPtr next = (ClientPtr) NULL;

#ifdef TCP_NODELAY
	  fromlen = sizeof (from);
	  if (!getpeername (newconn, &from.sa, &fromlen)) {
	    if (fromlen && (from.sa.sa_family == AF_INET)) {
	      int mi = 1;
	      setsockopt (newconn, IPPROTO_TCP, TCP_NODELAY,
			  (char *) &mi, sizeof (int));
	    }
	  }
#endif				/* TCP_NODELAY */
	  dpy = ClientAuthorized (newconn, &swapped, &reason);
	  if (dpy) {
#ifdef	hpux

	    /*
	     * HPUX does not have  FNDELAY
	     */
	    {
	      int arg;
	      arg = 1;
	      ioctl (newconn, FIOSNBIO, &arg);
	    }
#else
	    fcntl (newconn, F_SETFL, FNDELAY);
#endif				/* hpux */
	    inputBuffers[newconn].used = 1;
	    if (!inputBuffers[newconn].size) {
	      if ((inputBuffers[newconn].buffer = (char *)
		   Xmalloc (BUFSIZE)) ==
		  NULL)
		FatalError ("Out of memory (Input buffer)!\n");

	      inputBuffers[newconn].size = BUFSIZE;
	      inputBuffers[newconn].bufptr =
		inputBuffers[newconn].buffer;
	    }
	    if (GrabDone) {
	      BITSET (BITS (SavedAllClients), newconn);
	      BITSET (BITS (SavedAllSockets), newconn);
	    }
	    else {
	      BITSET (BITS (AllClients), newconn);
	      BITSET (BITS (AllSockets), newconn);
	    }
	    next = NextAvailableClient ();
	    if (next != (ClientPtr) NULL) {
	      OsCommPtr priv;

	      Connections[newconn] = next;
	      next->xdpy = dpy;
	      next->swapped = swapped;

	      if ((priv = (OsCommPtr)
		   Xmalloc (sizeof (OsCommRec))) == NULL)
		FatalError ("Out of memory (OsCommPtr) !\n");
	      priv->fd = newconn;
	      if ((priv->buf = (unsigned char *)
		   Xmalloc (OutputBufferSize)) == NULL)
		FatalError ("Out of memory(Output buffer)!\n");
	      priv->bufsize = OutputBufferSize;
	      priv->count = 0;
	      next->osPrivate = (pointer) priv;
	      newclients[(*nnew)++] = next;
	    }
	    else {
#define STR "Maximum number of clients exceeded"
	      reason = (char *) Xmalloc (sizeof (STR));
	      strcpy (reason, STR);
#undef STR
	    }
	  }
	  if (next == (ClientPtr) NULL) {
	    xConnSetupPrefix c;

	    if (reason) {
	      c.success = xFalse;
	      c.lengthReason = strlen (reason);
	      c.length = (c.lengthReason + 3) >> 2;
	      c.majorVersion = X_PROTOCOL;
	      c.minorVersion = X_PROTOCOL_REVISION;
	      if (swapped) {
		int n;

		swaps (&c.majorVersion, n);
		swaps (&c.minorVersion, n);
		swaps (&c.length, n);
	      }

	      write (newconn, (char *) &c, sizeof (xConnSetupPrefix));
	      iov[0].iov_len = c.lengthReason;
	      iov[0].iov_base = reason;
	      iov[1].iov_len = padlength[c.lengthReason & 3];
	      iov[1].iov_base = p;
	      writev (newconn, iov, 2);
	      if (debug_conns)
		ErrorF ("Didn't make connection:%s\n", reason);
	    }
	    close (newconn);
	    Xfree (reason);
	  }

	}
      }
      readyconnections &= ~(1 << curconn);
    }
  }
}

/************
 *   CloseDownFileDescriptor:
 *     Remove this file descriptor and it's inputbuffers, etc.
 ************/

void
CloseDownFileDescriptor (connection)
  int connection;
{
  close (connection);

  if (inputBuffers[connection].size) {
    Xfree (inputBuffers[connection].buffer);
    inputBuffers[connection].buffer = (char *) NULL;
    inputBuffers[connection].bufptr = (char *) NULL;
    inputBuffers[connection].size = 0;
  }
  inputBuffers[connection].bufcnt = 0;
  inputBuffers[connection].lenLastReq = 0;
  inputBuffers[connection].used = 0;

  BITCLEAR (BITS (AllSockets), connection);
  BITCLEAR (BITS (AllClients), connection);
  BITCLEAR (BITS (ClientsWithInput), connection);
  BITCLEAR (BITS (ClientsWriteBlocked), connection);
  if (!ANYSET (BITS (ClientsWriteBlocked)))
    AnyClientsWriteBlocked = FALSE;
}

/*****************
 * CheckConections
 *    Some connection has died, go find which one and shut it down
 *    The file descriptor has been closed, but is still in AllClients.
 *    If would truly be wonderful if select() would put the bogus
 *    file descriptors in the exception mask, but nooooo.  So we have
 *    to check each and every socket individually.
 *****************/

void
CheckConnections ()
{
  long mask[mskcnt];
  fd_set tmask;
  register int curclient;
  int i;
  struct timeval notime;
  ClientPtr bad;
  int r;

  notime.tv_sec = 0;
  notime.tv_usec = 0;

#ifdef DEBUG
  fprintf (stderr, "Check connections\n");
#endif
  COPYBITS (BITS (AllClients), mask);
  for (i = 0; i < mskcnt; i++) {
    while (mask[i]) {
      curclient = ffs (mask[i]) - 1 + (i << 5);
      CLEARBITS (BITS (tmask));
      FD_SET (curclient, &tmask);
      r = select (curclient + 1, &tmask, (int *) NULL, (int *) NULL,
		  &notime);
      if (r < 0) {
	if (bad = Connections[curclient])
	  CloseDownClient (bad, FALSE);
	else
	  CloseDownFileDescriptor (curclient);
      }
      BITCLEAR (mask, curclient);
    }
  }
}


/*****************
 * CloseDownClientConnection
 *    Delete client from AllClients and free resources
 *****************/

CloseDownClientConnection (client)
  ClientPtr client;
{
  OsCommPtr oc = (OsCommPtr) client->osPrivate;

  Connections[oc->fd] = (ClientPtr) NULL;
  CloseDownFileDescriptor (oc->fd);
  if (oc->buf != NULL)		/* an Xrealloc may have returned NULL */
    Xfree (oc->buf);
  Xfree (client->osPrivate);
}

/*****************
 * OnlyListenToOneClient:
 *    Only accept requests from  one client.  Continue to handle new
 *    connections, but don't take any protocol requests from the new
 *    ones.  Note that if GrabDone is set, EstablishNewConnections
 *    needs to put new clients into SavedAllSockets and SavedAllClients.
 *    Note also that there is no timeout for this in the protocol.
 *    This routine is "undone" by ListenToAllClients()
 *****************/

OnlyListenToOneClient (client)
  ClientPtr client;
{
  OsCommPtr oc = (OsCommPtr) client->osPrivate;
  int connection = oc->fd;

  if (!GrabDone) {
    COPYBITS (BITS (ClientsWithInput), BITS (SavedClientsWithInput));
    BITCLEAR (BITS (SavedClientsWithInput), connection);
    if (GETBIT (BITS (ClientsWithInput), connection)) {
      CLEARBITS (BITS (ClientsWithInput));
      BITSET (BITS (ClientsWithInput), connection);
    }
    else {
      CLEARBITS (BITS (ClientsWithInput));
    }
    COPYBITS (BITS (AllSockets), BITS (SavedAllSockets));
    COPYBITS (BITS (AllClients), BITS (SavedAllClients));

    UNSETBITS (BITS (AllSockets), BITS (AllClients));
    BITSET (BITS (AllSockets), connection);
    CLEARBITS (BITS (AllClients));
    BITSET (BITS (AllClients), connection);
    GrabDone = TRUE;
  }
}

/****************
 * ListenToAllClients:
 *    Undoes OnlyListentToOneClient()
 ****************/

ListenToAllClients ()
{
  if (GrabDone) {
    ORBITS (BITS (AllSockets), BITS (AllSockets), BITS (SavedAllSockets));
    ORBITS (BITS (AllClients), BITS (AllClients), BITS (SavedAllClients));
    ORBITS (BITS (ClientsWithInput), BITS (ClientsWithInput),
	    BITS (SavedClientsWithInput));
    GrabDone = FALSE;
  }
}

void
CloseDownServerConnection (server_fd)
  int server_fd;
{
  BITCLEAR (BITS (AllServers), server_fd);
  BITCLEAR (BITS (AllSockets), server_fd);
}
