rem: Ns_Log(Error, "%s: duplicate host map: %s", module, host);
add: Ns_Log(Error, "%s: duplicate host map: %s", module, vhost);
context: static Ns_ThreadProc DriverThread;static Ns_ThreadProc ReaderThread;static Sock *SockAccept(Driver *drvPtr);static void SockRelease(Driver *drvPtr, Sock *sockPtr);static void SockClose(Driver *drvPtr, Sock *sockPtr);static void SockTrigger(Driver *drvPtr);static int SockRead(Driver *drvPtr, Sock *sockPtr);static void SockPoll(Driver *drvPtr, SOCKET sock, int events, int *idxPtr,    	    	 Ns_Time *toPtr, Ns_Time *minPtr);static int  SetServer(Conn *connPtr);static Conn *AllocConn(Driver *drvPtr, Sock *sockPtr);static void FreeConn(Conn *connPtr);static int RunPreQueues(Conn *connPtr);static int SockRun(Driver *drvPtr, Sock *sockPtr);static void ParseAuth(Conn *connPtr, char *auth);static void ThreadName(Driver *drvPtr, char *name);static void SockWait(Sock *sockPtr, Ns_Time *nowPtr, int timeout,			  Sock **listPtrPtr);#define SockPush(s, sp)		((s)->nextPtr = *(sp), *(sp) = (s))/* * Static variables defined in this file. */static Driver *firstDrvPtr; /* First in list of all drivers. */static Conn *firstConnPtr;  /* Conn free list. */static Ns_Mutex connlock;   /* Lock around Conn free list. */static Tcl_HashTable hosts; /* Host header to server table. *//* *---------------------------------------------------------------------- * * NsInitDrivers -- * *	Init drivers system. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */voidNsInitDrivers(void){    Ns_MutexSetName(&connlock, "ns:conns");    Tcl_InitHashTable(&hosts, TCL_STRING_KEYS);}/* *---------------------------------------------------------------------- * * Ns_DriverInit -- * *	Initialize a driver. * * Results: *	NS_OK if initialized, NS_ERROR if config or other error. * * Side effects: *	Listen socket will be opened later in NsStartDrivers. * *---------------------------------------------------------------------- */intNs_DriverInit(char *server, char *module, Ns_DriverInitData *init){    char *path,*address, *host, *bindaddr, *defproto;    int i, n, socktimeout, defport;    ServerMap *mapPtr;    Tcl_HashEntry *hPtr;    Ns_DString ds;    Ns_Set *set;    struct in_addr  ia;    struct hostent *he;    Driver *drvPtr;    NsServer *servPtr = NULL;    if (server != NULL && (servPtr = NsGetServer(server)) == NULL) {	Ns_Log(Error, "%s: no such server: %s", module, server);	return NS_ERROR;    }    if (init->version != NS_DRIVER_VERSION_1) {        Ns_Log(Error, "%s: version field of init argument is invalid: %d",                 module, init->version);        return NS_ERROR;    }    path = (init->path ? init->path : Ns_ConfigGetPath(server, module, NULL));    /*     * Determine the hostname used for the local address to bind     * to and/or the HTTP location string.     */    host = Ns_ConfigGetValue(path, "hostname");    bindaddr = address = Ns_ConfigGetValue(path, "address");    /*     * If the listen address was not specified, attempt to determine it     * through a DNS lookup of the specified hostname or the server's     * primary hostname.     */    if (address == NULL) {        he = gethostbyname(host ? host : Ns_InfoHostname());        /*	 * If the lookup suceeded but the resulting hostname does not	 * appear to be fully qualified, attempt a reverse lookup on the	 * address which often returns the fully qualified name.	 *	 * NB: This is a common but sloppy configuration for a Unix	 * network.	 */        if (he != NULL && he->h_name != NULL &&	    strchr(he->h_name, '.') == NULL) {            he = gethostbyaddr(he->h_addr, he->h_length, he->h_addrtype);	}	/*	 * If the lookup suceeded, use the first address in host entry list.	 */        if (he == NULL || he->h_name == NULL) {            Ns_Log(Error, "%s: could not resolve %s: %s", module,		   host ? host : Ns_InfoHostname(), strerror(errno));	    return NS_ERROR;	}        if (*(he->h_addr_list) == NULL) {            Ns_Log(Error, "%s: no addresses for %s", module, he->h_name);	    return NS_ERROR;	}        memcpy(&ia.s_addr, *(he->h_addr_list), sizeof(ia.s_addr));        address = ns_inet_ntoa(ia);	/*	 * Finally, if no hostname was specified, set it to the hostname	 * derived from the lookup(s) above.	 */ 	if (host == NULL) {	    host = he->h_name;	}    }    /*     * If the hostname was not specified and not determined by the lookup     * above, set it to the specified or derived IP address string.     */    if (host == NULL) {	host = address;    }    /*     * Set the protocol and port defaults.     */    if (init->opts & NS_DRIVER_SSL) {	defproto = "https";	defport = 443;    } else {	defproto = "http";	defport = 80;    }    /*     * Allocate a new driver instance and set configurable parameters.     */    Ns_DStringInit(&ds);    drvPtr = ns_calloc(1, sizeof(Driver));    Ns_MutexSetName2(&drvPtr->lock, "ns:drv", module);    if (ns_sockpair(drvPtr->trigger) != 0) {	Ns_Fatal("ns_sockpair() failed: %s", ns_sockstrerror(ns_sockerrno));    }    drvPtr->server = server;    drvPtr->module = module;    drvPtr->name = init->name;    drvPtr->proc = init->proc;    drvPtr->arg = init->arg;    drvPtr->opts = init->opts;    drvPtr->servPtr = servPtr;#define _MAX(x,y)	((x) > (y) ? (x) : (y))#define _MIN(x,y)	((x) > (y) ? (y) : (x))    if (!Ns_ConfigGetInt(path, "bufsize", &n) || n < 1) {         n = 16000; 	/* ~16k */    }    drvPtr->bufsize = _MAX(n, 1024);    if (!Ns_ConfigGetInt(path, "rcvbuf", &n)) {	n = 0;		/* Use OS default. */    }    drvPtr->rcvbuf = _MAX(n, 0);    if (!Ns_ConfigGetInt(path, "sndbuf", &n)) {	n = 0;		/* Use OS default. */    }    drvPtr->sndbuf = _MAX(n, 0);    if (!Ns_ConfigGetInt(path, "socktimeout", &n) || n < 1) {	n = 30;		/* 30 seconds. */    }    socktimeout = n;    if (!Ns_ConfigGetInt(path, "sendwait", &n) || n < 1) {	n = socktimeout; /* Use previous socktimeout option. */    }    drvPtr->sendwait = _MAX(n, 1);    if (!Ns_ConfigGetInt(path, "recvwait", &n) || n < 1) {	n = socktimeout; /* Use previous socktimeout option. */    }    drvPtr->recvwait = _MAX(n, 1);    if (!Ns_ConfigGetInt(path, "closewait", &n) || n < 0) {	n = 2;		/* 2 seconds */    }    drvPtr->closewait = _MAX(n, 0); /* NB: 0 for no graceful close. */    if (!Ns_ConfigGetInt(path, "keepwait", &n) || n < 0) {	n = 30;		/* 30 seconds */    }    drvPtr->keepwait = _MAX(n, 0); /* NB: 0 for no keepalive. */    if (!Ns_ConfigGetInt(path, "backlog", &n) || n < 1) {	n = 5;		/* 5 pending connections. */    }    drvPtr->backlog = _MAX(n, 1);    if (!Ns_ConfigGetInt(path, "maxinput", &n) || n < 1) {	n = 1000 * 1024;	/* 1m. */    }    drvPtr->maxinput = _MAX(n, 1024);    if (!Ns_ConfigGetInt(path, "maxsock", &n) || n < 1) {	n = 100;		/* 100 total sockets. */    }    drvPtr->maxsock = _MAX(n, 1);    if (!Ns_ConfigGetInt(path, "maxreaders", &n) || n < 1) {	n = 10;    }    n = _MAX(n, 1);    n = _MIN(n, drvPtr->maxsock);    drvPtr->readers = ns_calloc(n, sizeof(Ns_Thread));    drvPtr->maxreaders = n;#undef _MAX#undef _MIN    /*     * Determine the port and then set the HTTP location string either     * as specified in the config file or constructed from the     * hostname and port.     */    drvPtr->bindaddr = bindaddr;    drvPtr->address = ns_strdup(address);    if (!Ns_ConfigGetInt(path, "port", &drvPtr->port)) {	drvPtr->port = defport;    }    drvPtr->location = Ns_ConfigGetValue(path, "location");    if (drvPtr->location != NULL) {	drvPtr->location = ns_strdup(drvPtr->location);    } else {	Ns_DStringVarAppend(&ds, defproto, "://", host, NULL);	if (drvPtr->port != defport) {	    Ns_DStringPrintf(&ds, ":%d", drvPtr->port);	}	drvPtr->location = Ns_DStringExport(&ds);    }    drvPtr->nextPtr = firstDrvPtr;    firstDrvPtr = drvPtr;    /*     * Map Host headers for drivers not bound to servers.     */    if (server == NULL) {	path = Ns_ConfigGetPath(NULL, module, "servers", NULL);	set = Ns_ConfigGetSection(path);	for (i = 0; set != NULL && i < Ns_SetSize(set); ++i) {	    server = Ns_SetKey(set, i);	    host = Ns_SetValue(set, i);	    servPtr = NsGetServer(server);	    if (servPtr == NULL) {		Ns_Log(Error, "%s: no such server: %s", module, server);	    } else {		hPtr = Tcl_CreateHashEntry(&hosts, host, &n);		if (!n) {		    Ns_Log(Error, "%s: duplicate host map: %s", module, host);		} else {		    Ns_DStringVarAppend(&ds, defproto, "://", host, NULL);    		    mapPtr = ns_malloc(sizeof(ServerMap) + ds.length);    		    mapPtr->servPtr  = servPtr;		    strcpy(mapPtr->location, ds.string);		    Ns_DStringTrunc(&ds, 0);    		    Tcl_SetHashValue(hPtr, mapPtr);		}	    }	}    }    Ns_DStringFree(&ds);    return NS_OK;}/* *---------------------------------------------------------------------- * * Ns_RegisterDriver -- * *	Register a set of communications driver procs (no longer *	supported). * * Results: *	NULL. * * Side effects: *	None. * *---------------------------------------------------------------------- */void *Ns_RegisterDriver(char *server, char *label, void *procs, void *drvData){    Ns_Log(Error, "driver: loadable drivers no longer supported");    return NULL;}/* *---------------------------------------------------------------------- * * Ns_GetDriverContext -- * *	Return the driver's context (no longer supported) * * Results: *	NULL.  * * Side effects: *	None  * *---------------------------------------------------------------------- */void *Ns_GetDriverContext(Ns_Driver drv){    return NULL;}/* *---------------------------------------------------------------------- * * Ns_RegisterPreQueue -- * *	Arrange for given callback to be invoked just before a *  	a connection is queued for service.  * Results: *	None. * * Side effects: *	Proc will be invoked with arg as data before connection queue. * *---------------------------------------------------------------------- */voidNs_RegisterPreQueue(char *server, Ns_PreQueueProc *proc, void *arg){    PreQueue *preQuePtr;    NsServer *servPtr;        servPtr = NsGetServer(server);    if (servPtr != NULL) {	preQuePtr = ns_malloc(sizeof(PreQueue));	preQuePtr->proc = proc;	preQuePtr->arg = arg;	preQuePtr->nextPtr = servPtr->firstPreQuePtr;	servPtr->firstPreQuePtr = preQuePtr;    }}/* *---------------------------------------------------------------------- * * Ns_QueueWait -- * *	Arrange for connection to wait for requested I/O on given *  	socket. * * Results: *	None. * * Side effects: *	Proc will be called with given arg and sock as arguements *  	when the requested I/O condition is met or the given timeout *  	has expired. The connection will not be queued until all *  	such callbacks are processed. * *---------------------------------------------------------------------- */voidNs_QueueWait(Ns_Conn *conn, SOCKET sock, Ns_QueueWaitProc *proc,    	     void *arg, int when, Ns_Time *timePtr){    Conn *connPtr = (Conn *) conn;    Driver *drvPtr = connPtr->drvPtr;    QueWait *queWaitPtr;     queWaitPtr = drvPtr->freeQueWaitPtr;    if (queWaitPtr != NULL) {	drvPtr->freeQueWaitPtr = queWaitPtr->nextPtr;    } else {	queWaitPtr = ns_malloc(sizeof(QueWait));    }    queWaitPtr->proc = proc;    queWaitPtr->arg = arg;    queWaitPtr->sock = sock;    queWaitPtr->events = 0;    if (when & NS_SOCK_READ) {	queWaitPtr->events |= POLLIN;    }    if (when & NS_SOCK_WRITE) {	queWaitPtr->events |= POLLOUT;    }    queWaitPtr->nextPtr = connPtr->queWaitPtr;    connPtr->queWaitPtr = queWaitPtr;    queWaitPtr->timeout = *timePtr;}/* *---------------------------------------------------------------------- * * NsStartDrivers -- * *	Start all driver threads. * * Results: *	None. * * Side effects: *	See DriverThread. * *---------------------------------------------------------------------- */voidNsStartDrivers(void){    Driver *drvPtr;    drvPtr = firstDrvPtr;    while (drvPtr != NULL) {    	Ns_Log(Notice, "driver: starting: %s", drvPtr->module);	Ns_ThreadCreate(DriverThread, drvPtr, 0, &drvPtr->thread);	drvPtr = drvPtr->nextPtr;    }}/* *---------------------------------------------------------------------- * * NsWaitDriversStartup -- * *	Wait for startup of all DriverThreads.  Current behavior is to *	just wait until all sockets are bound to. * * Results: *	None. * * Side effects: *	None. * *---------------------------------------------------------------------- */voidNsWaitDriversStartup(void){    Driver *drvPtr = firstDrvPtr;    int status = NS_OK;    Ns_Time timeout;    Ns_GetTime(&timeout);    Ns_IncrTime(&timeout, 10, 0);    while (drvPtr != NULL) {	Ns_MutexLock(&drvPtr->lock);	while (!drvPtr->started && status == NS_OK) {	    status = Ns_CondTimedWait(&drvPtr->cond, &drvPtr->lock, &timeout);	}	Ns_MutexUnlock(&drvPtr->lock);	if (status != NS_OK) {	    Ns_Log(Warning, "driver: startup timeout: %s", drvPtr->module);	}	drvPtr = drvPtr->nextPtr;    }}/* *---------------------------------------------------------------------- * * NsStopDrivers -- * *	Trigger the DriverThread to begin shutdown. * * Results: *	None. * * Side effects: *	DriverThread will close listen sockets and then exit after all *	outstanding connections are complete and closed. * *---------------------------------------------------------------------- */voidNsStopDrivers(void){    Driver *drvPtr = firstDrvPtr;        while (drvPtr != NULL) {	Ns_MutexLock(&drvPtr->lock);    	Ns_Log(Notice, "driver: stopping: %s", drvPtr->module);	drvPtr->shutdown = 1;	SockTrigger(drvPtr);	Ns_CondBroadcast(&drvPtr->cond);	Ns_MutexUnlock(&drvPtr->lock);	drvPtr = drvPtr->nextPtr;    }}/* *---------------------------------------------------------------------- * * NsWaitDriversShutdown -- * *	Wait for exit of DriverThread.  This callback is invoke later by *	the timed shutdown thread. * * Results: *	None. * * Side effects: *	Driver thread is joined and trigger pipe closed. * *---------------------------------------------------------------------- */voidNsWaitDriversShutdown(Ns_Time *toPtr){    Driver *drvPtr = firstDrvPtr;    int status = NS_OK;    while (drvPtr != NULL) {	Ns_MutexLock(&drvPtr->lock);	while (!drvPtr->stopped && status == NS_OK) {	    status = Ns_CondTimedWait(&drvPtr->cond, &drvPtr->lock, toPtr);	}	Ns_MutexUnlock(&drvPtr->lock);	if (status != NS_OK) {	    Ns_Log(Warning, "driver: shutdown timeout: %s", drvPtr->module);	} else {	    Ns_Log(Notice, "driver: stopped: %s", drvPtr->module);	    Ns_ThreadJoin(&drvPtr->thread, NULL);	    drvPtr->thread = NULL;	    ns_sockclose(drvPtr->trigger[0]);	    ns_sockclose(drvPtr->trigger[1]);	}	drvPtr = drvPtr->nextPtr;    }}/*  *---------------------------------------------------------------------- * * NsSockSend -- * *	Send buffers via the socket's driver callback. * * Results: *	# of bytes sent or -1 on error. * * Side effects: *	Depends on driver proc. * *---------------------------------------------------------------------- */intNsSockSend(Sock *sockPtr, struct iovec *bufs, int nbufs){    Ns_Sock *sock = (Ns_Sock *) sockPtr;    return (*sockPtr->drvPtr->proc)(DriverSend, sock, bufs, nbufs);}/*  *---------------------------------------------------------------------- * * NsSockClose -- * *	Return a connction to the DriverThread for closing or keepalive. * * Results: *	None. * * Side effects: *	Socket may be reused by a keepalive connection. * *---------------------------------------------------------------------- */voidNsSockClose(Sock *sockPtr, int keep){    Driver *drvPtr = sockPtr->drvPtr;    Ns_Sock *sock = (Ns_Sock *) sockPtr;    int trigger = 0;    if (keep && drvPtr->keepwait > 0	    && (*drvPtr->proc)(DriverKeep, sock, NULL, 0) == 0) {    	sockPtr->state = SOCK_KEEPALIVE;    } else {    	sockPtr->state = SOCK_CLOSEWAIT;	(void) (*drvPtr->proc)(DriverClose, sock, NULL, 0);    }    Ns_MutexLock(&drvPtr->lock);    if (drvPtr->firstClosePtr == NULL) {	trigger = 1;    }    sockPtr->nextPtr = drvPtr->firstClosePtr;    drvPtr->firstClosePtr = sockPtr;    Ns_MutexUnlock(&drvPtr->lock);    if (trigger) {	SockTrigger(drvPtr);    }}/* *---------------------------------------------------------------------- * * NsFreeConn -- * *	Free a Conn structure after processing, called by a the *  	conn threads. * * Results: *	None.  * * Side effects: *	None.  * *---------------------------------------------------------------------- */voidNsFreeConn(Conn *connPtr){    /*     * Close the socket if not already closed by the connection proc.     */         if (connPtr->sockPtr != NULL) {        SockClose(connPtr->drvPtr, connPtr->sockPtr);        connPtr->sockPtr = NULL;    }        FreeConn(connPtr);}/* *---------------------------------------------------------------------- * * DriverThread -- * *	Main communication driver thread. * * Results: *	None. * * Side effects: *	Connections are accepted and processed. * *---------------------------------------------------------------------- */static voidDriverThread(void *arg){    Driver *drvPtr = (Driver *) arg;    int n, stopping, pollto;    Sock *sockPtr, *nextPtr;    QueWait *queWaitPtr;    Conn *connPtr;    char drain[1024];    Ns_Time timeout, diff, maxtimeout;    Sock *queuePtr = NULL;	/* Sock's ready to queue for processing. */    Sock *preqPtr = NULL;	/* Sock's ready for pre-queue callbacks. */    Sock *readPtr = NULL;	/* Sock's ready reader thread. */    Sock *closePtr = NULL;	/* Sock's ready for close or keepalive wait. */    Sock *waitPtr = NULL;	/* Sock's waiting for I/O events. */    ThreadName(drvPtr, "driver");        /*     * Create the listen socket.     */     drvPtr->sock = Ns_SockListenEx(drvPtr->bindaddr, drvPtr->port,				   drvPtr->backlog);    if (drvPtr->sock == INVALID_SOCKET) {	Ns_Fatal("%s: failed to listen on %s:%d: %s", drvPtr->name,	    drvPtr->address, drvPtr->port, ns_sockstrerror(ns_sockerrno));    }    Ns_SockSetNonBlocking(drvPtr->sock);    Ns_MutexLock(&drvPtr->lock);    drvPtr->started = 1;    Ns_CondBroadcast(&drvPtr->cond);    Ns_MutexUnlock(&drvPtr->lock);    /*     * Pre-allocate Sock structures.     */              sockPtr = ns_malloc(sizeof(Sock) * drvPtr->maxsock);    for (n = 0; n < drvPtr->maxsock; ++n) {        sockPtr->nextPtr = drvPtr->firstFreePtr;        drvPtr->firstFreePtr = sockPtr;	++sockPtr;    }        /*     * Loop forever until signalled to shutdown and all     * connections are complete and gracefully closed.     */    Ns_Log(Notice, "%s: listening on %s:%d",		drvPtr->name, drvPtr->address, drvPtr->port);    maxtimeout.sec  = INT_MAX;    maxtimeout.usec = LONG_MAX;    Ns_GetTime(&drvPtr->now);    stopping = 0;    drvPtr->maxfds = 100;    drvPtr->pfds = ns_malloc(sizeof(struct pollfd) * drvPtr->maxfds);    drvPtr->pfds[0].fd = drvPtr->trigger[0];    drvPtr->pfds[0].events = POLLIN;    drvPtr->pfds[1].fd = drvPtr->sock;        while (!stopping || drvPtr->nactive) {	/*	 * Poll the trigger pipe and, if a Sock strucutre is available,	 * the listen socket.	 */	drvPtr->pfds[1].events = (drvPtr->firstFreePtr ? POLLIN : 0);	drvPtr->pfds[1].revents = drvPtr->pfds[0].revents = 0;	drvPtr->nfds = 2;	/*	 * Poll waiting sockets, determining the minimum relative timeout.	 */	sockPtr = waitPtr;	if (sockPtr == NULL) {	    pollto = -1;	} else {	    timeout = maxtimeout;	    while (sockPtr != NULL) {    		SockPoll(drvPtr, sockPtr->sock, POLLIN,		    	 &sockPtr->pidx, &sockPtr->timeout, &timeout);		if (sockPtr->connPtr != NULL) {		    queWaitPtr = sockPtr->connPtr->queWaitPtr;		    while (queWaitPtr != NULL) {		        SockPoll(drvPtr, queWaitPtr->sock, queWaitPtr->events,			 	 &queWaitPtr->pidx, &queWaitPtr->timeout,				 &timeout);		    	queWaitPtr = queWaitPtr->nextPtr;		    }		}		sockPtr = sockPtr->nextPtr;	    }	    if (Ns_DiffTime(&timeout, &drvPtr->now, &diff) > 0)  {		pollto = diff.sec * 1000 + diff.usec / 1000;	    } else {		pollto = 0;	    }	}	/*	 * Poll, drain the trigger pipe if necessary, and get current time.	 */	do {	    n = poll(drvPtr->pfds, drvPtr->nfds, pollto);	} while (n < 0  && errno == EINTR);	if (n < 0) {	    Ns_Fatal("driver: poll() failed: %s",		     ns_sockstrerror(ns_sockerrno));	}	if ((drvPtr->pfds[0].revents & POLLIN)		&& recv(drvPtr->trigger[0], drain, sizeof(drain), 0) <= 0) {	    Ns_Fatal("driver: trigger recv() failed: %s",		     ns_sockstrerror(ns_sockerrno));	}	Ns_GetTime(&drvPtr->now);	/*	 * Update the current time and drain and/or release any	 * closing sockets.	 */	sockPtr = waitPtr;	waitPtr = NULL;	while (sockPtr != NULL) {	    nextPtr = sockPtr->nextPtr;	    switch (sockPtr->state) {	    case SOCK_CLOSEWAIT:	    	if (drvPtr->pfds[sockPtr->pidx].revents & POLLIN) {		    n = recv(sockPtr->sock, drain, sizeof(drain), 0);		    if (n <= 0) {		        /* NB: Timeout Sock on end-of-file or error. */		        sockPtr->timeout = drvPtr->now;		    }	    	}	    	if (Ns_DiffTime(&sockPtr->timeout, &drvPtr->now, NULL) <= 0) {		    SockRelease(drvPtr, sockPtr);	    	} else {		    SockPush(sockPtr, &waitPtr);	    	}		break;	    case SOCK_QUEWAIT:	    	switch (SockRun(drvPtr, sockPtr)) {	    	case SOCK_READY:		    SockPush(sockPtr, &queuePtr);		    break;	    	case SOCK_MORE:		    SockPush(sockPtr, &waitPtr);    /* Still pending. */		    break;	    	default:		    SockRelease(drvPtr, sockPtr);		    break;		}		break;	    case SOCK_READWAIT:	    	if (!(drvPtr->pfds[sockPtr->pidx].revents & POLLIN)) {		    /* NB: Timeout or wait longer for input. */		    if (Ns_DiffTime(&sockPtr->timeout, &drvPtr->now, NULL) <= 0) {		    	SockRelease(drvPtr, sockPtr);		    } else {		    	SockPush(sockPtr, &waitPtr);		    }	    	} else {		    /*		     * With input available, read directly or queue for		     * reader thread.		     */                    if (sockPtr->connPtr == NULL) {                    	sockPtr->connPtr = AllocConn(drvPtr, sockPtr);                    }                		    if (!(drvPtr->opts & NS_DRIVER_ASYNC)) {			SockPush(sockPtr, &readPtr);		    } else {		    	switch (SockRead(drvPtr, sockPtr)) {		    	case SOCK_MORE:		    	    SockWait(sockPtr, &drvPtr->now,					  drvPtr->recvwait, &waitPtr);		    	    break;		    	case SOCK_READY:			    SockPush(sockPtr, &preqPtr);		    	    break;		    	default:			    /* Release socket on read error. */		    	    SockRelease(drvPtr, sockPtr);		    	    break;			}		    }		}		break;	    default:		Ns_Fatal("impossible state");		break;	    }	    sockPtr = nextPtr;	}	Ns_MutexLock(&drvPtr->lock);	/*	 * Move Sock's to read queue, creating or signaling reader threads.	 */	n = 0;	while ((sockPtr = readPtr) != NULL) {	    readPtr = sockPtr->nextPtr;	    sockPtr->nextPtr = drvPtr->firstReadPtr;	    drvPtr->firstReadPtr = sockPtr;	    ++n;	}	while (n > drvPtr->idlereaders		&& drvPtr->nreaders < drvPtr->maxreaders) {	    Ns_ThreadCreate(ReaderThread, drvPtr, 0,			    &drvPtr->readers[drvPtr->nreaders]);	    ++drvPtr->nreaders;	    ++drvPtr->idlereaders;	    --n;	}	if (n > 0) {	    Ns_CondSignal(&drvPtr->cond);	}	/*	 * Get Sock's from readers ready for pre-queue callbacks.	 */	while ((sockPtr = drvPtr->firstReadyPtr) != NULL) {	    drvPtr->firstReadyPtr = sockPtr->nextPtr;	    SockPush(sockPtr, &preqPtr);	}	/*	 * Get the list of closing Sock's.	 */	closePtr = drvPtr->firstClosePtr;	drvPtr->firstClosePtr = NULL;	stopping = drvPtr->shutdown;	Ns_MutexUnlock(&drvPtr->lock);	/*	 * Update the timeout for each closing socket and add to the	 * close list if some data has been read from the socket	 * (i.e., it's not a keep-alive connection).	 */	while ((sockPtr = closePtr) != NULL) {	    closePtr = sockPtr->nextPtr;	    switch (sockPtr->state) {	    case SOCK_KEEPALIVE:	    	sockPtr->state = SOCK_READWAIT;		SockWait(sockPtr, &drvPtr->now, drvPtr->keepwait, &waitPtr);		break;	    case SOCK_CLOSEWAIT:		if (drvPtr->closewait == 0 || shutdown(sockPtr->sock, 1) != 0) {		    SockRelease(drvPtr, sockPtr);		} else {		    SockWait(sockPtr, &drvPtr->now, drvPtr->closewait, &waitPtr);		}	    }	}	/*	 * Process pre-queue sockets.	 */	while ((sockPtr = preqPtr) != NULL) {	    preqPtr = sockPtr->nextPtr;	    if (!SetServer(sockPtr->connPtr)) {		SockRelease(drvPtr, sockPtr);	    } else {            	sockPtr->connPtr->times.ready = drvPtr->now;		if (RunPreQueues(sockPtr->connPtr)) {		    /* NB: Sock timeout now longest que wait. */		    sockPtr->timeout = maxtimeout;		    sockPtr->state = SOCK_QUEWAIT;		    SockPush(sockPtr, &waitPtr);		} else {		    SockPush(sockPtr, &queuePtr);		}	    }	}	/*	 * Queue connections now ready for processing.	 */	while ((sockPtr = queuePtr) != NULL) {	    queuePtr = sockPtr->nextPtr;	    connPtr = sockPtr->connPtr;	    /* NB: Sock no longer responsible for Conn. */	    sockPtr->connPtr = NULL;	    sockPtr->state = SOCK_RUNNING;	    connPtr->times.queue = drvPtr->now;	    NsQueueConn(connPtr);	}	/*	 * Attempt to accept new sockets.	 */  	if ((drvPtr->pfds[1].revents & POLLIN)	    	&& ((sockPtr = SockAccept(drvPtr)) != NULL)) {	    SockWait(sockPtr, &drvPtr->now, drvPtr->recvwait, &waitPtr);	}    }    /*     * Close the listen socket, wait for reader threads, and exit.     */    ns_sockclose(drvPtr->sock);    drvPtr->sock = INVALID_SOCKET;    while (drvPtr->nreaders > 0) {    	--drvPtr->nreaders;	Ns_ThreadJoin(&drvPtr->readers[drvPtr->nreaders], NULL);    }    Ns_MutexLock(&drvPtr->lock);    drvPtr->stopped = 1;    Ns_CondBroadcast(&drvPtr->cond);    Ns_MutexUnlock(&drvPtr->lock);    Ns_Log(Notice, "exiting");}