summaryrefslogtreecommitdiff
path: root/os/io.c
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:54 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:54 +0000
commitded6147bfb5d75ff1e67c858040a628b61bc17d1 (patch)
tree82355105e93cdac89ef7d987424351c77545faf0 /os/io.c
parentcb6ef07bf01e72d1a6e6e83ceb7f76d6534da941 (diff)
R6.6 is the Xorg base-lineXORG-MAIN
Diffstat (limited to 'os/io.c')
-rw-r--r--os/io.c1262
1 files changed, 1262 insertions, 0 deletions
diff --git a/os/io.c b/os/io.c
new file mode 100644
index 000000000..96bfd6885
--- /dev/null
+++ b/os/io.c
@@ -0,0 +1,1262 @@
+/***********************************************************
+
+Copyright 1987, 1989, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+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.
+
+******************************************************************/
+/* $Xorg: io.c,v 1.6 2001/02/09 02:05:23 xorgcvs Exp $ */
+/*****************************************************************
+ * i/o functions
+ *
+ * WriteToClient, ReadRequestFromClient
+ * InsertFakeRequest, ResetCurrentRequest
+ *
+ *****************************************************************/
+
+#ifdef WIN32
+#include <X11/Xwinsock.h>
+#endif
+#include <stdio.h>
+#include <X11/Xtrans.h>
+#ifdef X_NOT_STDC_ENV
+extern int errno;
+#endif
+#include "Xmd.h"
+#include <errno.h>
+#ifndef WIN32
+#include <sys/uio.h>
+#endif
+#include "X.h"
+#define NEED_REPLIES
+#include "Xproto.h"
+#include "os.h"
+#include "Xpoll.h"
+#include "osdep.h"
+#include "opaque.h"
+#include "dixstruct.h"
+#include "misc.h"
+#ifdef LBX
+#include "lbxserve.h"
+#endif
+
+CallbackListPtr ReplyCallback;
+CallbackListPtr FlushCallback;
+
+/* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
+ * systems are broken and return EWOULDBLOCK when they should return EAGAIN
+ */
+#if defined(EAGAIN) && defined(EWOULDBLOCK)
+#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
+#else
+#ifdef EAGAIN
+#define ETEST(err) (err == EAGAIN)
+#else
+#define ETEST(err) (err == EWOULDBLOCK)
+#endif
+#endif
+
+extern fd_set ClientsWithInput, IgnoredClientsWithInput, AllClients;
+extern fd_set ClientsWriteBlocked;
+extern fd_set OutputPending;
+extern int ConnectionTranslation[];
+extern Bool NewOutputPending;
+extern Bool AnyClientsWriteBlocked;
+
+Bool CriticalOutputPending;
+int timesThisConnection = 0;
+ConnectionInputPtr FreeInputs = (ConnectionInputPtr)NULL;
+ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr)NULL;
+OsCommPtr AvailableInput = (OsCommPtr)NULL;
+
+#define get_req_len(req,cli) ((cli)->swapped ? \
+ lswaps((req)->length) : (req)->length)
+
+#ifdef BIGREQS
+#include "bigreqstr.h"
+
+#define get_big_req_len(req,cli) ((cli)->swapped ? \
+ lswapl(((xBigReq *)(req))->length) : \
+ ((xBigReq *)(req))->length)
+#endif
+
+#define MAX_TIMES_PER 10
+
+/*
+ * A lot of the code in this file manipulates a ConnectionInputPtr:
+ *
+ * -----------------------------------------------
+ * |------- bufcnt ------->| | |
+ * | |- gotnow ->| | |
+ * | |-------- needed ------>| |
+ * |-----------+--------- size --------+---------->|
+ * -----------------------------------------------
+ * ^ ^
+ * | |
+ * buffer bufptr
+ *
+ * buffer is a pointer to the start of the buffer.
+ * bufptr points to the start of the current request.
+ * bufcnt counts how many bytes are in the buffer.
+ * size is the size of the buffer in bytes.
+ *
+ * In several of the functions, gotnow and needed are local variables
+ * that do the following:
+ *
+ * gotnow is the number of bytes of the request that we're
+ * trying to read that are currently in the buffer.
+ * Typically, gotnow = (buffer + bufcnt) - bufptr
+ *
+ * needed = the length of the request that we're trying to
+ * read. Watch out: needed sometimes counts bytes and sometimes
+ * counts CARD32's.
+ */
+
+
+/*****************************************************************
+ * ReadRequestFromClient
+ * Returns one request in client->requestBuffer. The request
+ * length will be in client->req_len. Return status is:
+ *
+ * > 0 if successful, specifies length in bytes of the request
+ * = 0 if entire request is not yet available
+ * < 0 if client should be terminated
+ *
+ * The request returned must be contiguous so that it can be
+ * cast in the dispatcher to the correct request type. Because requests
+ * are variable length, ReadRequestFromClient() must look at the first 4
+ * or 8 bytes of a request to determine the length (the request length is
+ * in the 3rd and 4th bytes of the request unless it is a Big Request
+ * (see the Big Request Extension), in which case the 3rd and 4th bytes
+ * are zero and the following 4 bytes are the request length.
+ *
+ * Note: in order to make the server scheduler (WaitForSomething())
+ * "fair", the ClientsWithInput mask is used. This mask tells which
+ * clients have FULL requests left in their buffers. Clients with
+ * partial requests require a read. Basically, client buffers
+ * are drained before select() is called again. But, we can't keep
+ * reading from a client that is sending buckets of data (or has
+ * a partial request) because others clients need to be scheduled.
+ *****************************************************************/
+
+#define YieldControl() \
+ { isItTimeToYield = TRUE; \
+ timesThisConnection = 0; }
+#define YieldControlNoInput() \
+ { YieldControl(); \
+ FD_CLR(fd, &ClientsWithInput); }
+#define YieldControlDeath() \
+ { timesThisConnection = 0; }
+
+#ifdef hpux_not_tog
+#define LBX_NEED_OLD_SYMBOL_FOR_LOADABLES
+#endif
+
+#ifdef LBX
+#ifdef LBX_NEED_OLD_SYMBOL_FOR_LOADABLES
+#undef ReadRequestFromClient
+int
+ReadRequestFromClient(client)
+ ClientPtr client;
+{
+ return (*client->readRequest)(client);
+}
+#endif
+int
+StandardReadRequestFromClient(client)
+ ClientPtr client;
+#else
+int
+ReadRequestFromClient(client)
+ ClientPtr client;
+#endif
+{
+ OsCommPtr oc = (OsCommPtr)client->osPrivate;
+ register ConnectionInputPtr oci = oc->input;
+ int fd = oc->fd;
+ register unsigned int gotnow, needed;
+ int result;
+ register xReq *request;
+ Bool need_header;
+#ifdef BIGREQS
+ Bool move_header;
+#endif
+
+ /* If an input buffer was empty, either free it if it is too big
+ * or link it into our list of free input buffers. This means that
+ * different clients can share the same input buffer (at different
+ * times). This was done to save memory.
+ */
+
+ if (AvailableInput)
+ {
+ if (AvailableInput != oc)
+ {
+ register ConnectionInputPtr aci = AvailableInput->input;
+ if (aci->size > BUFWATERMARK)
+ {
+ xfree(aci->buffer);
+ xfree(aci);
+ }
+ else
+ {
+ aci->next = FreeInputs;
+ FreeInputs = aci;
+ }
+ AvailableInput->input = (ConnectionInputPtr)NULL;
+ }
+ AvailableInput = (OsCommPtr)NULL;
+ }
+
+ /* make sure we have an input buffer */
+
+ if (!oci)
+ {
+ if (oci = FreeInputs)
+ {
+ FreeInputs = oci->next;
+ }
+ else if (!(oci = AllocateInputBuffer()))
+ {
+ YieldControlDeath();
+ return -1;
+ }
+ oc->input = oci;
+ }
+
+ /* advance to start of next request */
+
+ oci->bufptr += oci->lenLastReq;
+
+ need_header = FALSE;
+#ifdef BIGREQS
+ move_header = FALSE;
+#endif
+ gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
+ if (gotnow < sizeof(xReq))
+ {
+ /* We don't have an entire xReq yet. Can't tell how big
+ * the request will be until we get the whole xReq.
+ */
+ needed = sizeof(xReq);
+ need_header = TRUE;
+ }
+ else
+ {
+ /* We have a whole xReq. We can tell how big the whole
+ * request will be unless it is a Big Request.
+ */
+ request = (xReq *)oci->bufptr;
+ needed = get_req_len(request, client);
+#ifdef BIGREQS
+ if (!needed && client->big_requests)
+ {
+ /* It's a Big Request. */
+ move_header = TRUE;
+ if (gotnow < sizeof(xBigReq))
+ {
+ /* Still need more data to tell just how big. */
+ needed = sizeof(xBigReq) >> 2; /* needed is in CARD32s now */
+ need_header = TRUE;
+ }
+ else
+ needed = get_big_req_len(request, client);
+ }
+#endif
+ client->req_len = needed;
+ needed <<= 2; /* needed is in bytes now */
+ }
+ if (gotnow < needed)
+ {
+ /* Need to read more data, either so that we can get a
+ * complete xReq (if need_header is TRUE), a complete
+ * xBigReq (if move_header is TRUE), or the rest of the
+ * request (if need_header and move_header are both FALSE).
+ */
+
+ oci->lenLastReq = 0;
+ if (needed > MAXBUFSIZE)
+ {
+ /* request is too big for us to handle */
+ YieldControlDeath();
+ return -1;
+ }
+ if ((gotnow == 0) ||
+ ((oci->bufptr - oci->buffer + needed) > oci->size))
+ {
+ /* no data, or the request is too big to fit in the buffer */
+
+ if ((gotnow > 0) && (oci->bufptr != oci->buffer))
+ /* save the data we've already read */
+ memmove(oci->buffer, oci->bufptr, gotnow);
+ if (needed > oci->size)
+ {
+ /* make buffer bigger to accomodate request */
+ char *ibuf;
+
+ ibuf = (char *)xrealloc(oci->buffer, needed);
+ if (!ibuf)
+ {
+ YieldControlDeath();
+ return -1;
+ }
+ oci->size = needed;
+ oci->buffer = ibuf;
+ }
+ oci->bufptr = oci->buffer;
+ oci->bufcnt = gotnow;
+ }
+ /* XXX this is a workaround. This function is sometimes called
+ * after the trans_conn has been freed. In this case trans_conn
+ * will be null. Really ought to restructure things so that we
+ * never get here in those circumstances.
+ */
+ if (!oc->trans_conn)
+ {
+ /* treat as if an error occured on the read, which is what
+ * used to happen
+ */
+ YieldControlDeath();
+ return -1;
+ }
+#ifdef LBX
+ if (oc->proxy && oc->proxy->compHandle)
+ result = (*oc->proxy->streamOpts.streamCompRead)(fd,
+ (unsigned char *)oci->buffer + oci->bufcnt,
+ oci->size - oci->bufcnt);
+ else
+#endif
+ result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
+ oci->size - oci->bufcnt);
+ if (result <= 0)
+ {
+ if ((result < 0) && ETEST(errno))
+ {
+ YieldControlNoInput();
+ return 0;
+ }
+ YieldControlDeath();
+ return -1;
+ }
+ oci->bufcnt += result;
+ gotnow += result;
+ /* free up some space after huge requests */
+ if ((oci->size > BUFWATERMARK) &&
+ (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE))
+ {
+ char *ibuf;
+
+ ibuf = (char *)xrealloc(oci->buffer, BUFSIZE);
+ if (ibuf)
+ {
+ oci->size = BUFSIZE;
+ oci->buffer = ibuf;
+ oci->bufptr = ibuf + oci->bufcnt - gotnow;
+ }
+ }
+ if (need_header && gotnow >= needed)
+ {
+ /* We wanted an xReq, now we've gotten it. */
+ request = (xReq *)oci->bufptr;
+ needed = get_req_len(request, client);
+#ifdef BIGREQS
+ if (!needed && client->big_requests)
+ {
+ move_header = TRUE;
+ if (gotnow < sizeof(xBigReq))
+ needed = sizeof(xBigReq) >> 2;
+ else
+ needed = get_big_req_len(request, client);
+ }
+#endif
+ client->req_len = needed;
+ needed <<= 2;
+ }
+ if (gotnow < needed)
+ {
+ /* Still don't have enough; punt. */
+ YieldControlNoInput();
+ return 0;
+ }
+ }
+ if (needed == 0)
+ {
+#ifdef BIGREQS
+ if (client->big_requests)
+ needed = sizeof(xBigReq);
+ else
+#endif
+ needed = sizeof(xReq);
+ }
+ oci->lenLastReq = needed;
+
+ /*
+ * Check to see if client has at least one whole request in the
+ * buffer beyond the request we're returning to the caller.
+ * If there is only a partial request, treat like buffer
+ * is empty so that select() will be called again and other clients
+ * can get into the queue.
+ */
+
+ gotnow -= needed;
+ if (gotnow >= sizeof(xReq))
+ {
+ request = (xReq *)(oci->bufptr + needed);
+ if (gotnow >= (result = (get_req_len(request, client) << 2))
+#ifdef BIGREQS
+ && (result ||
+ (client->big_requests &&
+ (gotnow >= sizeof(xBigReq) &&
+ gotnow >= (get_big_req_len(request, client) << 2))))
+#endif
+ )
+ FD_SET(fd, &ClientsWithInput);
+ else
+ YieldControlNoInput();
+ }
+ else
+ {
+ if (!gotnow)
+ AvailableInput = oc;
+ YieldControlNoInput();
+ }
+ if (++timesThisConnection >= MAX_TIMES_PER)
+ YieldControl();
+#ifdef BIGREQS
+ if (move_header)
+ {
+ request = (xReq *)oci->bufptr;
+ oci->bufptr += (sizeof(xBigReq) - sizeof(xReq));
+ *(xReq *)oci->bufptr = *request;
+ oci->lenLastReq -= (sizeof(xBigReq) - sizeof(xReq));
+ client->req_len -= (sizeof(xBigReq) - sizeof(xReq)) >> 2;
+ }
+#endif
+ client->requestBuffer = (pointer)oci->bufptr;
+ return needed;
+}
+
+/*****************************************************************
+ * InsertFakeRequest
+ * Splice a consed up (possibly partial) request in as the next request.
+ *
+ **********************/
+
+Bool
+InsertFakeRequest(client, data, count)
+ ClientPtr client;
+ char *data;
+ int count;
+{
+ OsCommPtr oc = (OsCommPtr)client->osPrivate;
+ register ConnectionInputPtr oci = oc->input;
+ int fd = oc->fd;
+ register int gotnow, moveup;
+
+ if (AvailableInput)
+ {
+ if (AvailableInput != oc)
+ {
+ register ConnectionInputPtr aci = AvailableInput->input;
+ if (aci->size > BUFWATERMARK)
+ {
+ xfree(aci->buffer);
+ xfree(aci);
+ }
+ else
+ {
+ aci->next = FreeInputs;
+ FreeInputs = aci;
+ }
+ AvailableInput->input = (ConnectionInputPtr)NULL;
+ }
+ AvailableInput = (OsCommPtr)NULL;
+ }
+ if (!oci)
+ {
+ if (oci = FreeInputs)
+ FreeInputs = oci->next;
+ else if (!(oci = AllocateInputBuffer()))
+ return FALSE;
+ oc->input = oci;
+ }
+ oci->bufptr += oci->lenLastReq;
+ oci->lenLastReq = 0;
+ gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
+ if ((gotnow + count) > oci->size)
+ {
+ char *ibuf;
+
+ ibuf = (char *)xrealloc(oci->buffer, gotnow + count);
+ if (!ibuf)
+ return(FALSE);
+ oci->size = gotnow + count;
+ oci->buffer = ibuf;
+ oci->bufptr = ibuf + oci->bufcnt - gotnow;
+ }
+ moveup = count - (oci->bufptr - oci->buffer);
+ if (moveup > 0)
+ {
+ if (gotnow > 0)
+ memmove(oci->bufptr + moveup, oci->bufptr, gotnow);
+ oci->bufptr += moveup;
+ oci->bufcnt += moveup;
+ }
+ memmove(oci->bufptr - count, data, count);
+ oci->bufptr -= count;
+ gotnow += count;
+ if ((gotnow >= sizeof(xReq)) &&
+ (gotnow >= (int)(get_req_len((xReq *)oci->bufptr, client) << 2)))
+ FD_SET(fd, &ClientsWithInput);
+ else
+ YieldControlNoInput();
+ return(TRUE);
+}
+
+/*****************************************************************
+ * ResetRequestFromClient
+ * Reset to reexecute the current request, and yield.
+ *
+ **********************/
+
+ResetCurrentRequest(client)
+ ClientPtr client;
+{
+ OsCommPtr oc = (OsCommPtr)client->osPrivate;
+ register ConnectionInputPtr oci = oc->input;
+ int fd = oc->fd;
+ register xReq *request;
+ int gotnow, needed;
+#ifdef LBX
+ Bool part;
+ LbxClientPtr lbxClient = LbxClient(client);
+
+ if (lbxClient) {
+ LbxSetForBlock(lbxClient);
+ if (!oci) {
+ AppendFakeRequest(client,
+ client->requestBuffer, client->req_len << 2);
+ return;
+ }
+ }
+#endif
+ if (AvailableInput == oc)
+ AvailableInput = (OsCommPtr)NULL;
+ oci->lenLastReq = 0;
+ gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
+ if (gotnow < sizeof(xReq))
+ {
+ YieldControlNoInput();
+ }
+ else
+ {
+ request = (xReq *)oci->bufptr;
+ needed = get_req_len(request, client);
+#ifdef BIGREQS
+ if (!needed && client->big_requests)
+ {
+ oci->bufptr -= sizeof(xBigReq) - sizeof(xReq);
+ *(xReq *)oci->bufptr = *request;
+ ((xBigReq *)oci->bufptr)->length = client->req_len;
+ if (client->swapped)
+ {
+ char n;
+ swapl(&((xBigReq *)oci->bufptr)->length, n);
+ }
+ }
+#endif
+ if (gotnow >= (needed << 2))
+ {
+ if (FD_ISSET(fd, &AllClients))
+ {
+ FD_SET(fd, &ClientsWithInput);
+ }
+ else
+ {
+ FD_SET(fd, &IgnoredClientsWithInput);
+ }
+ YieldControl();
+ }
+ else
+ YieldControlNoInput();
+ }
+}
+
+
+
+/*****************************************************************
+ * PeekNextRequest and SkipRequests were implemented to support DBE
+ * idioms, but can certainly be used outside of DBE. There are two
+ * related macros in os.h, ReqLen and CastxReq. See the porting
+ * layer document for more details.
+ *
+ **********************/
+
+
+/*****************************************************************
+ * PeekNextRequest
+ * lets you look ahead at the unexecuted requests in a
+ * client's request buffer.
+ *
+ * Note: this implementation of PeekNextRequest ignores the
+ * readmore parameter.
+ *
+ **********************/
+
+xReqPtr
+PeekNextRequest(req, client, readmore)
+ xReqPtr req; /* request we're starting from */
+ ClientPtr client; /* client whose requests we're skipping */
+ Bool readmore; /* attempt to read more if next request isn't there? */
+{
+ register ConnectionInputPtr oci = ((OsCommPtr)client->osPrivate)->input;
+ xReqPtr pnextreq;
+ int needed, gotnow, reqlen;
+
+ if (!oci) return NULL;
+
+ if (!req)
+ {
+ /* caller wants the request after the one currently being executed */
+ pnextreq = (xReqPtr)
+ (((CARD32 *)client->requestBuffer) + client->req_len);
+ }
+ else
+ {
+ /* caller wants the request after the one specified by req */
+ reqlen = get_req_len(req, client);
+#ifdef BIGREQS
+ if (!reqlen) reqlen = get_big_req_len(req, client);
+#endif
+ pnextreq = (xReqPtr)(((char *)req) + (reqlen << 2));
+ }
+
+ /* see how much of the next request we have available */
+
+ gotnow = oci->bufcnt - (((char *)pnextreq) - oci->buffer);
+
+ if (gotnow < sizeof(xReq))
+ return NULL;
+
+ needed = get_req_len(pnextreq, client) << 2;
+#ifdef BIGREQS
+ if (!needed)
+ {
+ /* it's a big request */
+ if (gotnow < sizeof(xBigReq))
+ return NULL;
+ needed = get_big_req_len(pnextreq, client) << 2;
+ }
+#endif
+
+ /* if we have less than we need, return NULL */
+
+ return (gotnow < needed) ? NULL : pnextreq;
+}
+
+/*****************************************************************
+ * SkipRequests
+ * lets you skip over some of the requests in a client's
+ * request buffer. Presumably the caller has used PeekNextRequest
+ * to examine the requests being skipped and has performed whatever
+ * actions they dictate.
+ *
+ **********************/
+
+CallbackListPtr SkippedRequestsCallback = NULL;
+
+void
+SkipRequests(req, client, numskipped)
+ xReqPtr req; /* last request being skipped */
+ ClientPtr client; /* client whose requests we're skipping */
+ int numskipped; /* how many requests we're skipping */
+{
+ OsCommPtr oc = (OsCommPtr)client->osPrivate;
+ register ConnectionInputPtr oci = oc->input;
+ int reqlen;
+
+ /* see if anyone wants to snoop the skipped requests */
+
+ if (SkippedRequestsCallback)
+ {
+ SkippedRequestInfoRec skipinfo;
+ skipinfo.req = req;
+ skipinfo.client = client;
+ skipinfo.numskipped = numskipped;
+ CallCallbacks(&SkippedRequestsCallback, &skipinfo);
+ }
+
+ /* adjust the sequence number */
+ client->sequence += numskipped;
+
+ /* twiddle the oci to skip over the requests */
+
+ reqlen = get_req_len(req, client);
+#ifdef BIGREQS
+ if (!reqlen) reqlen = get_big_req_len(req, client);
+#endif
+ reqlen <<= 2;
+ oci->bufptr = (char *)req;
+ oci->lenLastReq = reqlen;
+
+ /* see if any requests left in the buffer */
+
+ if ( ((char *)req + reqlen) == (oci->buffer + oci->bufcnt) )
+ {
+ /* no requests; mark input buffer as available and client
+ * as having no input
+ */
+ int fd = oc->fd;
+ AvailableInput = oc;
+ YieldControlNoInput();
+ }
+}
+
+
+ /* lookup table for adding padding bytes to data that is read from
+ or written to the X socket. */
+static int padlength[4] = {0, 3, 2, 1};
+
+ /********************
+ * FlushAllOutput()
+ * Flush all clients with output. However, if some client still
+ * has input in the queue (more requests), then don't flush. This
+ * will prevent the output queue from being flushed every time around
+ * the round robin queue. Now, some say that it SHOULD be flushed
+ * every time around, but...
+ *
+ **********************/
+
+void
+FlushAllOutput()
+{
+ register int index, base, mask;
+ OsCommPtr oc;
+ register ClientPtr client;
+ Bool newoutput = NewOutputPending;
+#ifdef WIN32
+ fd_set newOutputPending;
+#endif
+
+ if (FlushCallback)
+ CallCallbacks(&FlushCallback, NULL);
+
+ if (!newoutput)
+ return;
+
+ /*
+ * It may be that some client still has critical output pending,
+ * but he is not yet ready to receive it anyway, so we will
+ * simply wait for the select to tell us when he's ready to receive.
+ */
+ CriticalOutputPending = FALSE;
+ NewOutputPending = FALSE;
+
+#ifndef WIN32
+ for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++)
+ {
+ mask = OutputPending.fds_bits[ base ];
+ OutputPending.fds_bits[ base ] = 0;
+ while (mask)
+ {
+ index = ffs(mask) - 1;
+ mask &= ~lowbit(mask);
+ if ((index = ConnectionTranslation[(base << 5) + index]) == 0)
+ continue;
+ client = clients[index];
+ if (client->clientGone)
+ continue;
+ oc = (OsCommPtr)client->osPrivate;
+ if (
+#ifdef LBX
+ !oc->proxy &&
+#endif
+ FD_ISSET(oc->fd, &ClientsWithInput))
+ {
+ FD_SET(oc->fd, &OutputPending); /* set the bit again */
+ NewOutputPending = TRUE;
+ }
+ else
+ (void)FlushClient(client, oc, (char *)NULL, 0);
+ }
+ }
+#else /* WIN32 */
+ FD_ZERO(&newOutputPending);
+ for (base = 0; base < XFD_SETCOUNT(&OutputPending); base++)
+ {
+ index = XFD_FD(&OutputPending, base);
+ if ((index = ConnectionTranslation[index]) == 0)
+ continue;
+ client = clients[index];
+ if (client->clientGone)
+ continue;
+ oc = (OsCommPtr)client->osPrivate;
+ if (
+#ifdef LBX
+ !oc->proxy &&
+#endif
+ FD_ISSET(oc->fd, &ClientsWithInput))
+ {
+ FD_SET(oc->fd, &newOutputPending); /* set the bit again */
+ NewOutputPending = TRUE;
+ }
+ else
+ (void)FlushClient(client, oc, (char *)NULL, 0);
+ }
+ XFD_COPYSET(&newOutputPending, &OutputPending);
+#endif /* WIN32 */
+}
+
+void
+FlushIfCriticalOutputPending()
+{
+ if (CriticalOutputPending)
+ FlushAllOutput();
+}
+
+void
+SetCriticalOutputPending()
+{
+ CriticalOutputPending = TRUE;
+}
+
+/*****************
+ * WriteToClient
+ * Copies buf into ClientPtr.buf if it fits (with padding), else
+ * flushes ClientPtr.buf and buf to client. As of this writing,
+ * every use of WriteToClient is cast to void, and the result
+ * is ignored. Potentially, this could be used by requests
+ * that are sending several chunks of data and want to break
+ * out of a loop on error. Thus, we will leave the type of
+ * this routine as int.
+ *****************/
+
+int
+WriteToClient (who, count, buf)
+ ClientPtr who;
+ char *buf;
+ int count;
+{
+ OsCommPtr oc = (OsCommPtr)who->osPrivate;
+ register ConnectionOutputPtr oco = oc->output;
+ int padBytes;
+
+ if (!count)
+ return(0);
+
+ if (!oco)
+ {
+ if (oco = FreeOutputs)
+ {
+ FreeOutputs = oco->next;
+ }
+ else if (!(oco = AllocateOutputBuffer()))
+ {
+ if (oc->trans_conn) {
+ _XSERVTransDisconnect(oc->trans_conn);
+ _XSERVTransClose(oc->trans_conn);
+ oc->trans_conn = NULL;
+ }
+ MarkClientException(who);
+ return -1;
+ }
+ oc->output = oco;
+ }
+
+ padBytes = padlength[count & 3];
+
+ if(ReplyCallback)
+ {
+ ReplyInfoRec replyinfo;
+
+ replyinfo.client = who;
+ replyinfo.replyData = buf;
+ replyinfo.dataLenBytes = count + padBytes;
+ if (who->replyBytesRemaining)
+ { /* still sending data of an earlier reply */
+ who->replyBytesRemaining -= count + padBytes;
+ replyinfo.startOfReply = FALSE;
+ replyinfo.bytesRemaining = who->replyBytesRemaining;
+ CallCallbacks((&ReplyCallback), (pointer)&replyinfo);
+ }
+ else if (who->clientState == ClientStateRunning
+ && buf[0] == X_Reply)
+ { /* start of new reply */
+ CARD32 replylen;
+ unsigned long bytesleft;
+ char n;
+
+ replylen = ((xGenericReply *)buf)->length;
+ if (who->swapped)
+ swapl(&replylen, n);
+ bytesleft = (replylen * 4) + SIZEOF(xReply) - count - padBytes;
+ replyinfo.startOfReply = TRUE;
+ replyinfo.bytesRemaining = who->replyBytesRemaining = bytesleft;
+ CallCallbacks((&ReplyCallback), (pointer)&replyinfo);
+ }
+ }
+
+ if (oco->count + count + padBytes > oco->size)
+ {
+ FD_CLR(oc->fd, &OutputPending);
+ if(!XFD_ANYSET(&OutputPending)) {
+ CriticalOutputPending = FALSE;
+ NewOutputPending = FALSE;
+ }
+ return FlushClient(who, oc, buf, count);
+ }
+
+ NewOutputPending = TRUE;
+ FD_SET(oc->fd, &OutputPending);
+ memmove((char *)oco->buf + oco->count, buf, count);
+ oco->count += count + padBytes;
+ return(count);
+}
+
+ /********************
+ * FlushClient()
+ * If the client isn't keeping up with us, then we try to continue
+ * buffering the data and set the apropriate bit in ClientsWritable
+ * (which is used by WaitFor in the select). If the connection yields
+ * a permanent error, or we can't allocate any more space, we then
+ * close the connection.
+ *
+ **********************/
+
+#ifdef LBX
+#ifdef LBX_NEED_OLD_SYMBOL_FOR_LOADABLES
+#undef FlushClient
+int
+FlushClient(who, oc, extraBuf, extraCount)
+ ClientPtr who;
+ OsCommPtr oc;
+ char *extraBuf;
+ int extraCount;
+{
+ return (*oc->Flush)(who, oc, extraBuf, extraCount);
+}
+#endif
+int
+StandardFlushClient(who, oc, extraBuf, extraCount)
+#else
+int
+FlushClient(who, oc, extraBuf, extraCount)
+#endif
+ ClientPtr who;
+ OsCommPtr oc;
+ char *extraBuf;
+ int extraCount; /* do not modify... returned below */
+{
+ register ConnectionOutputPtr oco = oc->output;
+ int connection = oc->fd;
+ XtransConnInfo trans_conn = oc->trans_conn;
+ struct iovec iov[3];
+ static char padBuffer[3];
+ long written;
+ long padsize;
+ long notWritten;
+ long todo;
+
+ if (!oco)
+ return 0;
+ written = 0;
+ padsize = padlength[extraCount & 3];
+ notWritten = oco->count + extraCount + padsize;
+ todo = notWritten;
+ while (notWritten) {
+ long before = written; /* amount of whole thing written */
+ long remain = todo; /* amount to try this time, <= notWritten */
+ int i = 0;
+ long len;
+
+ /* You could be very general here and have "in" and "out" iovecs
+ * and write a loop without using a macro, but what the heck. This
+ * translates to:
+ *
+ * how much of this piece is new?
+ * if more new then we are trying this time, clamp
+ * if nothing new
+ * then bump down amount already written, for next piece
+ * else put new stuff in iovec, will need all of next piece
+ *
+ * Note that todo had better be at least 1 or else we'll end up
+ * writing 0 iovecs.
+ */
+#define InsertIOV(pointer, length) \
+ len = (length) - before; \
+ if (len > remain) \
+ len = remain; \
+ if (len <= 0) { \
+ before = (-len); \
+ } else { \
+ iov[i].iov_len = len; \
+ iov[i].iov_base = (pointer) + before; \
+ i++; \
+ remain -= len; \
+ before = 0; \
+ }
+
+ InsertIOV ((char *)oco->buf, oco->count)
+ InsertIOV (extraBuf, extraCount)
+ InsertIOV (padBuffer, padsize)
+
+ errno = 0;
+ if (trans_conn && (len = _XSERVTransWritev(trans_conn, iov, i)) >= 0)
+ {
+ written += len;
+ notWritten -= len;
+ todo = notWritten;
+ }
+ else if (ETEST(errno)
+#ifdef SUNSYSV /* check for another brain-damaged OS bug */
+ || (errno == 0)
+#endif
+#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
+ || ((errno == EMSGSIZE) && (todo == 1))
+#endif
+ )
+ {
+ /* If we've arrived here, then the client is stuffed to the gills
+ and not ready to accept more. Make a note of it and buffer
+ the rest. */
+ FD_SET(connection, &ClientsWriteBlocked);
+ AnyClientsWriteBlocked = TRUE;
+
+ if (written < oco->count)
+ {
+ if (written > 0)
+ {
+ oco->count -= written;
+ memmove((char *)oco->buf,
+ (char *)oco->buf + written,
+ oco->count);
+ written = 0;
+ }
+ }
+ else
+ {
+ written -= oco->count;
+ oco->count = 0;
+ }
+
+ if (notWritten > oco->size)
+ {
+ unsigned char *obuf;
+
+ obuf = (unsigned char *)xrealloc(oco->buf,
+ notWritten + BUFSIZE);
+ if (!obuf)
+ {
+ _XSERVTransDisconnect(oc->trans_conn);
+ _XSERVTransClose(oc->trans_conn);
+ oc->trans_conn = NULL;
+ MarkClientException(who);
+ oco->count = 0;
+ return(-1);
+ }
+ oco->size = notWritten + BUFSIZE;
+ oco->buf = obuf;
+ }
+
+ /* If the amount written extended into the padBuffer, then the
+ difference "extraCount - written" may be less than 0 */
+ if ((len = extraCount - written) > 0)
+ memmove ((char *)oco->buf + oco->count,
+ extraBuf + written,
+ len);
+
+ oco->count = notWritten; /* this will include the pad */
+ /* return only the amount explicitly requested */
+ return extraCount;
+ }
+#ifdef EMSGSIZE /* check for another brain-damaged OS bug */
+ else if (errno == EMSGSIZE)
+ {
+ todo >>= 1;
+ }
+#endif
+ else
+ {
+ if (oc->trans_conn)
+ {
+ _XSERVTransDisconnect(oc->trans_conn);
+ _XSERVTransClose(oc->trans_conn);
+ oc->trans_conn = NULL;
+ }
+ MarkClientException(who);
+ oco->count = 0;
+ return(-1);
+ }
+ }
+
+ /* everything was flushed out */
+ oco->count = 0;
+ /* check to see if this client was write blocked */
+ if (AnyClientsWriteBlocked)
+ {
+ FD_CLR(oc->fd, &ClientsWriteBlocked);
+ if (! XFD_ANYSET(&ClientsWriteBlocked))
+ AnyClientsWriteBlocked = FALSE;
+ }
+ if (oco->size > BUFWATERMARK)
+ {
+ xfree(oco->buf);
+ xfree(oco);
+ }
+ else
+ {
+ oco->next = FreeOutputs;
+ FreeOutputs = oco;
+ }
+ oc->output = (ConnectionOutputPtr)NULL;
+ return extraCount; /* return only the amount explicitly requested */
+}
+
+ConnectionInputPtr
+AllocateInputBuffer()
+{
+ register ConnectionInputPtr oci;
+
+ oci = (ConnectionInputPtr)xalloc(sizeof(ConnectionInput));
+ if (!oci)
+ return (ConnectionInputPtr)NULL;
+ oci->buffer = (char *)xalloc(BUFSIZE);
+ if (!oci->buffer)
+ {
+ xfree(oci);
+ return (ConnectionInputPtr)NULL;
+ }
+ oci->size = BUFSIZE;
+ oci->bufptr = oci->buffer;
+ oci->bufcnt = 0;
+ oci->lenLastReq = 0;
+ return oci;
+}
+
+ConnectionOutputPtr
+AllocateOutputBuffer()
+{
+ register ConnectionOutputPtr oco;
+
+ oco = (ConnectionOutputPtr)xalloc(sizeof(ConnectionOutput));
+ if (!oco)
+ return (ConnectionOutputPtr)NULL;
+ oco->buf = (unsigned char *) xalloc(BUFSIZE);
+ if (!oco->buf)
+ {
+ xfree(oco);
+ return (ConnectionOutputPtr)NULL;
+ }
+ oco->size = BUFSIZE;
+ oco->count = 0;
+#ifdef LBX
+ oco->nocompress = FALSE;
+#endif
+ return oco;
+}
+
+void
+FreeOsBuffers(oc)
+ OsCommPtr oc;
+{
+ register ConnectionInputPtr oci;
+ register ConnectionOutputPtr oco;
+
+ if (AvailableInput == oc)
+ AvailableInput = (OsCommPtr)NULL;
+ if (oci = oc->input)
+ {
+ if (FreeInputs)
+ {
+ xfree(oci->buffer);
+ xfree(oci);
+ }
+ else
+ {
+ FreeInputs = oci;
+ oci->next = (ConnectionInputPtr)NULL;
+ oci->bufptr = oci->buffer;
+ oci->bufcnt = 0;
+ oci->lenLastReq = 0;
+ }
+ }
+ if (oco = oc->output)
+ {
+ if (FreeOutputs)
+ {
+ xfree(oco->buf);
+ xfree(oco);
+ }
+ else
+ {
+ FreeOutputs = oco;
+ oco->next = (ConnectionOutputPtr)NULL;
+ oco->count = 0;
+ }
+ }
+#ifdef LBX
+ if (oci = oc->largereq) {
+ xfree(oci->buffer);
+ xfree(oci);
+ }
+#endif
+}
+
+void
+ResetOsBuffers()
+{
+ register ConnectionInputPtr oci;
+ register ConnectionOutputPtr oco;
+
+ while (oci = FreeInputs)
+ {
+ FreeInputs = oci->next;
+ xfree(oci->buffer);
+ xfree(oci);
+ }
+ while (oco = FreeOutputs)
+ {
+ FreeOutputs = oco->next;
+ xfree(oco->buf);
+ xfree(oco);
+ }
+}