aboutsummaryrefslogtreecommitdiff
path: root/xorg-server/os/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'xorg-server/os/io.c')
-rw-r--r--xorg-server/os/io.c2294
1 files changed, 1146 insertions, 1148 deletions
diff --git a/xorg-server/os/io.c b/xorg-server/os/io.c
index d2eb04649..b30f20767 100644
--- a/xorg-server/os/io.c
+++ b/xorg-server/os/io.c
@@ -1,1148 +1,1146 @@
-/***********************************************************
-
-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.
-
-
-******************************************************************/
-/*****************************************************************
- * i/o functions
- *
- * WriteToClient, ReadRequestFromClient
- * InsertFakeRequest, ResetCurrentRequest
- *
- *****************************************************************/
-
-#ifdef HAVE_DIX_CONFIG_H
-#include <dix-config.h>
-#endif
-
-#undef DEBUG_COMMUNICATION
-
-#ifdef WIN32
-#include <X11/Xwinsock.h>
-#endif
-#include <stdio.h>
-#define XSERV_t
-#define TRANS_SERVER
-#define TRANS_REOPEN
-#include <X11/Xtrans/Xtrans.h>
-#include <X11/Xmd.h>
-#include <errno.h>
-#if !defined(WIN32)
-#include <sys/uio.h>
-#endif
-#include <X11/X.h>
-#include <X11/Xproto.h>
-#include "os.h"
-#include "osdep.h"
-#include <X11/Xpoll.h>
-#include "opaque.h"
-#include "dixstruct.h"
-#include "misc.h"
-
-CallbackListPtr ReplyCallback;
-CallbackListPtr FlushCallback;
-
-static ConnectionInputPtr AllocateInputBuffer(void);
-static ConnectionOutputPtr AllocateOutputBuffer(void);
-
-/* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
- * systems are broken and return EWOULDBLOCK when they should return EAGAIN
- */
-#ifndef WIN32
-#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
-#else /* WIN32 The socket errorcodes differ from the normal errors*/
-#define ETEST(err) (err == EAGAIN || err == WSAEWOULDBLOCK)
-#endif
-
-static Bool CriticalOutputPending;
-static int timesThisConnection = 0;
-static ConnectionInputPtr FreeInputs = (ConnectionInputPtr)NULL;
-static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr)NULL;
-static OsCommPtr AvailableInput = (OsCommPtr)NULL;
-
-#define get_req_len(req,cli) ((cli)->swapped ? \
- lswaps((req)->length) : (req)->length)
-
-#include <X11/extensions/bigreqsproto.h>
-
-#define get_big_req_len(req,cli) ((cli)->swapped ? \
- lswapl(((xBigReq *)(req))->length) : \
- ((xBigReq *)(req))->length)
-
-#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.
- *****************************************************************/
-
-static void
-YieldControl(void)
-{
- isItTimeToYield = TRUE;
- timesThisConnection = 0;
-}
-
-static void
-YieldControlNoInput(int fd)
-{
- YieldControl();
- FD_CLR(fd, &ClientsWithInput);
-}
-
-static void
-YieldControlDeath(void)
-{
- timesThisConnection = 0;
-}
-
-int
-ReadRequestFromClient(ClientPtr client)
-{
- OsCommPtr oc = (OsCommPtr)client->osPrivate;
- ConnectionInputPtr oci = oc->input;
- int fd = oc->fd;
- unsigned int gotnow, needed;
- int result;
- register xReq *request;
- Bool need_header;
- Bool move_header;
-
- /* 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)
- {
- free(aci->buffer);
- free(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;
- move_header = FALSE;
- gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
-
- if (oci->ignoreBytes > 0) {
- if (oci->ignoreBytes > oci->size)
- needed = oci->size;
- else
- needed = oci->ignoreBytes;
- }
- else 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);
- 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 = bytes_to_int32(sizeof(xBigReq)); /* needed is in CARD32s now */
- need_header = TRUE;
- }
- else
- needed = get_big_req_len(request, client);
- }
- 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 > maxBigRequestSize << 2)
- {
- /* request is too big for us to handle */
- /*
- * Mark the rest of it as needing to be ignored, and then return
- * the full size. Dispatch() will turn it into a BadLength error.
- */
- oci->ignoreBytes = needed - gotnow;
- oci->lenLastReq = gotnow;
- return needed;
- }
- 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 *)realloc(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;
- }
- result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
- oci->size - oci->bufcnt);
- if (result <= 0)
- {
- if ((result < 0) && ETEST(errno))
- {
-#if defined(SVR4) && defined(__i386__) && !defined(sun)
- if (0)
-#endif
- {
- YieldControlNoInput(fd);
- 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 *)realloc(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);
- if (!needed && client->big_requests)
- {
- move_header = TRUE;
- if (gotnow < sizeof(xBigReq))
- needed = bytes_to_int32(sizeof(xBigReq));
- else
- needed = get_big_req_len(request, client);
- }
- client->req_len = needed;
- needed <<= 2;
- }
- if (gotnow < needed)
- {
- /* Still don't have enough; punt. */
- YieldControlNoInput(fd);
- return 0;
- }
- }
- if (needed == 0)
- {
- if (client->big_requests)
- needed = sizeof(xBigReq);
- else
- needed = sizeof(xReq);
- }
-
- /* If there are bytes to ignore, ignore them now. */
-
- if (oci->ignoreBytes > 0) {
- assert(needed == oci->ignoreBytes || needed == oci->size);
- /*
- * The _XSERVTransRead call above may return more or fewer bytes than we
- * want to ignore. Ignore the smaller of the two sizes.
- */
- if (gotnow < needed) {
- oci->ignoreBytes -= gotnow;
- oci->bufptr += gotnow;
- gotnow = 0;
- } else {
- oci->ignoreBytes -= needed;
- oci->bufptr += needed;
- gotnow -= needed;
- }
- needed = 0;
- }
-
- 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))
- && (result ||
- (client->big_requests &&
- (gotnow >= sizeof(xBigReq) &&
- gotnow >= (get_big_req_len(request, client) << 2))))
- )
- FD_SET(fd, &ClientsWithInput);
- else
- {
- if (!SmartScheduleDisable)
- FD_CLR(fd, &ClientsWithInput);
- else
- YieldControlNoInput(fd);
- }
- }
- else
- {
- if (!gotnow)
- AvailableInput = oc;
- if (!SmartScheduleDisable)
- FD_CLR(fd, &ClientsWithInput);
- else
- YieldControlNoInput(fd);
- }
- if (SmartScheduleDisable)
- if (++timesThisConnection >= MAX_TIMES_PER)
- YieldControl();
- 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 -= bytes_to_int32(sizeof(xBigReq) - sizeof(xReq));
- }
- client->requestBuffer = (pointer)oci->bufptr;
-#ifdef DEBUG_COMMUNICATION
- {
- xReq *req = client->requestBuffer;
- ErrorF("REQUEST: ClientIDX: %i, type: 0x%x data: 0x%x len: %i\n",
- client->index,req->reqType,req->data,req->length);
- }
-#endif
- return needed;
-}
-
-/*****************************************************************
- * InsertFakeRequest
- * Splice a consed up (possibly partial) request in as the next request.
- *
- **********************/
-
-Bool
-InsertFakeRequest(ClientPtr client, char *data, int count)
-{
- OsCommPtr oc = (OsCommPtr)client->osPrivate;
- ConnectionInputPtr oci = oc->input;
- int fd = oc->fd;
- int gotnow, moveup;
-
- if (AvailableInput)
- {
- if (AvailableInput != oc)
- {
- ConnectionInputPtr aci = AvailableInput->input;
- if (aci->size > BUFWATERMARK)
- {
- free(aci->buffer);
- free(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 *)realloc(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(fd);
- return TRUE;
-}
-
-/*****************************************************************
- * ResetRequestFromClient
- * Reset to reexecute the current request, and yield.
- *
- **********************/
-
-void
-ResetCurrentRequest(ClientPtr client)
-{
- OsCommPtr oc = (OsCommPtr)client->osPrivate;
- register ConnectionInputPtr oci = oc->input;
- int fd = oc->fd;
- register xReq *request;
- int gotnow, needed;
- if (AvailableInput == oc)
- AvailableInput = (OsCommPtr)NULL;
- oci->lenLastReq = 0;
- gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
- if (gotnow < sizeof(xReq))
- {
- YieldControlNoInput(fd);
- }
- else
- {
- request = (xReq *)oci->bufptr;
- needed = get_req_len(request, client);
- 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);
- }
- }
- if (gotnow >= (needed << 2))
- {
- if (FD_ISSET(fd, &AllClients))
- {
- FD_SET(fd, &ClientsWithInput);
- }
- else
- {
- FD_SET(fd, &IgnoredClientsWithInput);
- }
- YieldControl();
- }
- else
- YieldControlNoInput(fd);
- }
-}
-
-static const 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(void)
-{
- register int index, base;
- register fd_mask mask; /* raphael */
- OsCommPtr oc;
- register ClientPtr client;
- Bool newoutput = NewOutputPending;
-#if defined(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 * (sizeof(fd_mask)*8)) + index]) == 0)
- continue;
- client = clients[index];
- if (client->clientGone)
- continue;
- oc = (OsCommPtr)client->osPrivate;
- if (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 = GetConnectionTranslation(index)) == 0)
- continue;
- client = clients[index];
- if (client->clientGone)
- continue;
- oc = (OsCommPtr)client->osPrivate;
- if (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(void)
-{
- if (CriticalOutputPending)
- FlushAllOutput();
-}
-
-void
-SetCriticalOutputPending(void)
-{
- 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 (ClientPtr who, int count, const void *__buf)
-{
- OsCommPtr oc;
- ConnectionOutputPtr oco;
- int padBytes;
- const char *buf = __buf;
-#ifdef DEBUG_COMMUNICATION
- Bool multicount = FALSE;
-#endif
- if (!count || !who || who == serverClient || who->clientGone)
- return 0;
- oc = who->osPrivate;
- oco = oc->output;
-#ifdef DEBUG_COMMUNICATION
- {
- char info[128];
- xError *err;
- xGenericReply *rep;
- xEvent *ev;
-
- if (!who->replyBytesRemaining) {
- switch(buf[0]) {
- case X_Reply:
- rep = (xGenericReply*)buf;
- if (rep->sequenceNumber == who->sequence) {
- snprintf(info,127,"Xreply: type: 0x%x data: 0x%x "
- "len: %i seq#: 0x%x", rep->type, rep->data1,
- rep->length, rep->sequenceNumber);
- multicount = TRUE;
- }
- break;
- case X_Error:
- err = (xError*)buf;
- snprintf(info,127,"Xerror: Code: 0x%x resID: 0x%x maj: 0x%x "
- "min: %x", err->errorCode,err->resourceID,
- err->minorCode,err->majorCode);
- break;
- default:
- if ((buf[0] & 0x7f) == KeymapNotify)
- snprintf(info,127,"KeymapNotifyEvent: %i",buf[0]);
- else {
- ev = (xEvent*)buf;
- snprintf(info,127,"XEvent: type: 0x%x detail: 0x%x "
- "seq#: 0x%x", ev->u.u.type, ev->u.u.detail,
- ev->u.u.sequenceNumber);
- }
- }
- ErrorF("REPLY: ClientIDX: %i %s\n",who->index, info);
- } else
- multicount = TRUE;
- }
-#endif
-
- 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);
- }
- }
-#ifdef DEBUG_COMMUNICATION
- else if (multicount) {
- if (who->replyBytesRemaining) {
- who->replyBytesRemaining -= (count + padBytes);
- } else {
- CARD32 replylen;
- replylen = ((xGenericReply *)buf)->length;
- who->replyBytesRemaining =
- (replylen * 4) + SIZEOF(xReply) - count - padBytes;
- }
- }
-#endif
- if (oco->count + count + padBytes > oco->size)
- {
- FD_CLR(oc->fd, &OutputPending);
- if(!XFD_ANYSET(&OutputPending)) {
- CriticalOutputPending = FALSE;
- NewOutputPending = FALSE;
- }
-
- if (FlushCallback)
- CallCallbacks(&FlushCallback, NULL);
-
- 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.
- *
- **********************/
-
-int
-FlushClient(ClientPtr who, OsCommPtr oc, const void *__extraBuf, int extraCount)
-{
- ConnectionOutputPtr oco = oc->output;
- int connection = oc->fd;
- XtransConnInfo trans_conn = oc->trans_conn;
- struct iovec iov[3];
- static char padBuffer[3];
- const char *extraBuf = __extraBuf;
- 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 ((char *)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. */
- errno=0;
- 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 *)realloc(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)
- {
- free(oco->buf);
- free(oco);
- }
- else
- {
- oco->next = FreeOutputs;
- FreeOutputs = oco;
- }
- oc->output = (ConnectionOutputPtr)NULL;
- return extraCount; /* return only the amount explicitly requested */
-}
-
-static ConnectionInputPtr
-AllocateInputBuffer(void)
-{
- ConnectionInputPtr oci;
-
- oci = malloc(sizeof(ConnectionInput));
- if (!oci)
- return NULL;
- oci->buffer = malloc(BUFSIZE);
- if (!oci->buffer)
- {
- free(oci);
- return NULL;
- }
- oci->size = BUFSIZE;
- oci->bufptr = oci->buffer;
- oci->bufcnt = 0;
- oci->lenLastReq = 0;
- oci->ignoreBytes = 0;
- return oci;
-}
-
-static ConnectionOutputPtr
-AllocateOutputBuffer(void)
-{
- ConnectionOutputPtr oco;
-
- oco = malloc(sizeof(ConnectionOutput));
- if (!oco)
- return NULL;
- oco->buf = calloc(1, BUFSIZE);
- if (!oco->buf)
- {
- free(oco);
- return NULL;
- }
- oco->size = BUFSIZE;
- oco->count = 0;
- return oco;
-}
-
-void
-FreeOsBuffers(OsCommPtr oc)
-{
- ConnectionInputPtr oci;
- ConnectionOutputPtr oco;
-
- if (AvailableInput == oc)
- AvailableInput = (OsCommPtr)NULL;
- if ((oci = oc->input))
- {
- if (FreeInputs)
- {
- free(oci->buffer);
- free(oci);
- }
- else
- {
- FreeInputs = oci;
- oci->next = (ConnectionInputPtr)NULL;
- oci->bufptr = oci->buffer;
- oci->bufcnt = 0;
- oci->lenLastReq = 0;
- }
- }
- if ((oco = oc->output))
- {
- if (FreeOutputs)
- {
- free(oco->buf);
- free(oco);
- }
- else
- {
- FreeOutputs = oco;
- oco->next = (ConnectionOutputPtr)NULL;
- oco->count = 0;
- }
- }
-}
-
-void
-ResetOsBuffers(void)
-{
- ConnectionInputPtr oci;
- ConnectionOutputPtr oco;
-
- while ((oci = FreeInputs))
- {
- FreeInputs = oci->next;
- free(oci->buffer);
- free(oci);
- }
- while ((oco = FreeOutputs))
- {
- FreeOutputs = oco->next;
- free(oco->buf);
- free(oco);
- }
-}
+/***********************************************************
+
+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.
+
+
+******************************************************************/
+/*****************************************************************
+ * i/o functions
+ *
+ * WriteToClient, ReadRequestFromClient
+ * InsertFakeRequest, ResetCurrentRequest
+ *
+ *****************************************************************/
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#undef DEBUG_COMMUNICATION
+
+#ifdef WIN32
+#include <X11/Xwinsock.h>
+#endif
+#include <stdio.h>
+#define XSERV_t
+#define TRANS_SERVER
+#define TRANS_REOPEN
+#include <X11/Xtrans/Xtrans.h>
+#include <X11/Xmd.h>
+#include <errno.h>
+#if !defined(WIN32)
+#include <sys/uio.h>
+#endif
+#include <X11/X.h>
+#include <X11/Xproto.h>
+#include "os.h"
+#include "osdep.h"
+#include <X11/Xpoll.h>
+#include "opaque.h"
+#include "dixstruct.h"
+#include "misc.h"
+
+CallbackListPtr ReplyCallback;
+CallbackListPtr FlushCallback;
+
+static ConnectionInputPtr AllocateInputBuffer(void);
+static ConnectionOutputPtr AllocateOutputBuffer(void);
+
+/* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
+ * systems are broken and return EWOULDBLOCK when they should return EAGAIN
+ */
+#ifndef WIN32
+#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
+#else /* WIN32 The socket errorcodes differ from the normal errors*/
+#define ETEST(err) (err == EAGAIN || err == WSAEWOULDBLOCK)
+#endif
+
+static Bool CriticalOutputPending;
+static int timesThisConnection = 0;
+static ConnectionInputPtr FreeInputs = (ConnectionInputPtr)NULL;
+static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr)NULL;
+static OsCommPtr AvailableInput = (OsCommPtr)NULL;
+
+#define get_req_len(req,cli) ((cli)->swapped ? \
+ lswaps((req)->length) : (req)->length)
+
+#include <X11/extensions/bigreqsproto.h>
+
+#define get_big_req_len(req,cli) ((cli)->swapped ? \
+ lswapl(((xBigReq *)(req))->length) : \
+ ((xBigReq *)(req))->length)
+
+#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.
+ *****************************************************************/
+
+static void
+YieldControl(void)
+{
+ isItTimeToYield = TRUE;
+ timesThisConnection = 0;
+}
+
+static void
+YieldControlNoInput(int fd)
+{
+ YieldControl();
+ FD_CLR(fd, &ClientsWithInput);
+}
+
+static void
+YieldControlDeath(void)
+{
+ timesThisConnection = 0;
+}
+
+int
+ReadRequestFromClient(ClientPtr client)
+{
+ OsCommPtr oc = (OsCommPtr)client->osPrivate;
+ ConnectionInputPtr oci = oc->input;
+ int fd = oc->fd;
+ unsigned int gotnow, needed;
+ int result;
+ register xReq *request;
+ Bool need_header;
+ Bool move_header;
+
+ /* 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)
+ {
+ free(aci->buffer);
+ free(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;
+ move_header = FALSE;
+ gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
+
+ if (oci->ignoreBytes > 0) {
+ if (oci->ignoreBytes > oci->size)
+ needed = oci->size;
+ else
+ needed = oci->ignoreBytes;
+ }
+ else 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);
+ 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 = bytes_to_int32(sizeof(xBigReq)); /* needed is in CARD32s now */
+ need_header = TRUE;
+ }
+ else
+ needed = get_big_req_len(request, client);
+ }
+ 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 > maxBigRequestSize << 2)
+ {
+ /* request is too big for us to handle */
+ /*
+ * Mark the rest of it as needing to be ignored, and then return
+ * the full size. Dispatch() will turn it into a BadLength error.
+ */
+ oci->ignoreBytes = needed - gotnow;
+ oci->lenLastReq = gotnow;
+ return needed;
+ }
+ 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 *)realloc(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;
+ }
+ result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
+ oci->size - oci->bufcnt);
+ if (result <= 0)
+ {
+ if ((result < 0) && ETEST(errno))
+ {
+#if defined(SVR4) && defined(__i386__) && !defined(sun)
+ if (0)
+#endif
+ {
+ YieldControlNoInput(fd);
+ 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 *)realloc(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);
+ if (!needed && client->big_requests)
+ {
+ move_header = TRUE;
+ if (gotnow < sizeof(xBigReq))
+ needed = bytes_to_int32(sizeof(xBigReq));
+ else
+ needed = get_big_req_len(request, client);
+ }
+ client->req_len = needed;
+ needed <<= 2;
+ }
+ if (gotnow < needed)
+ {
+ /* Still don't have enough; punt. */
+ YieldControlNoInput(fd);
+ return 0;
+ }
+ }
+ if (needed == 0)
+ {
+ if (client->big_requests)
+ needed = sizeof(xBigReq);
+ else
+ needed = sizeof(xReq);
+ }
+
+ /* If there are bytes to ignore, ignore them now. */
+
+ if (oci->ignoreBytes > 0) {
+ assert(needed == oci->ignoreBytes || needed == oci->size);
+ /*
+ * The _XSERVTransRead call above may return more or fewer bytes than we
+ * want to ignore. Ignore the smaller of the two sizes.
+ */
+ if (gotnow < needed) {
+ oci->ignoreBytes -= gotnow;
+ oci->bufptr += gotnow;
+ gotnow = 0;
+ } else {
+ oci->ignoreBytes -= needed;
+ oci->bufptr += needed;
+ gotnow -= needed;
+ }
+ needed = 0;
+ }
+
+ 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))
+ && (result ||
+ (client->big_requests &&
+ (gotnow >= sizeof(xBigReq) &&
+ gotnow >= (get_big_req_len(request, client) << 2))))
+ )
+ FD_SET(fd, &ClientsWithInput);
+ else
+ {
+ if (!SmartScheduleDisable)
+ FD_CLR(fd, &ClientsWithInput);
+ else
+ YieldControlNoInput(fd);
+ }
+ }
+ else
+ {
+ if (!gotnow)
+ AvailableInput = oc;
+ if (!SmartScheduleDisable)
+ FD_CLR(fd, &ClientsWithInput);
+ else
+ YieldControlNoInput(fd);
+ }
+ if (SmartScheduleDisable)
+ if (++timesThisConnection >= MAX_TIMES_PER)
+ YieldControl();
+ 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 -= bytes_to_int32(sizeof(xBigReq) - sizeof(xReq));
+ }
+ client->requestBuffer = (pointer)oci->bufptr;
+#ifdef DEBUG_COMMUNICATION
+ {
+ xReq *req = client->requestBuffer;
+ ErrorF("REQUEST: ClientIDX: %i, type: 0x%x data: 0x%x len: %i\n",
+ client->index,req->reqType,req->data,req->length);
+ }
+#endif
+ return needed;
+}
+
+/*****************************************************************
+ * InsertFakeRequest
+ * Splice a consed up (possibly partial) request in as the next request.
+ *
+ **********************/
+
+Bool
+InsertFakeRequest(ClientPtr client, char *data, int count)
+{
+ OsCommPtr oc = (OsCommPtr)client->osPrivate;
+ ConnectionInputPtr oci = oc->input;
+ int fd = oc->fd;
+ int gotnow, moveup;
+
+ if (AvailableInput)
+ {
+ if (AvailableInput != oc)
+ {
+ ConnectionInputPtr aci = AvailableInput->input;
+ if (aci->size > BUFWATERMARK)
+ {
+ free(aci->buffer);
+ free(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 *)realloc(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(fd);
+ return TRUE;
+}
+
+/*****************************************************************
+ * ResetRequestFromClient
+ * Reset to reexecute the current request, and yield.
+ *
+ **********************/
+
+void
+ResetCurrentRequest(ClientPtr client)
+{
+ OsCommPtr oc = (OsCommPtr)client->osPrivate;
+ register ConnectionInputPtr oci = oc->input;
+ int fd = oc->fd;
+ register xReq *request;
+ int gotnow, needed;
+ if (AvailableInput == oc)
+ AvailableInput = (OsCommPtr)NULL;
+ oci->lenLastReq = 0;
+ gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
+ if (gotnow < sizeof(xReq))
+ {
+ YieldControlNoInput(fd);
+ }
+ else
+ {
+ request = (xReq *)oci->bufptr;
+ needed = get_req_len(request, client);
+ 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)
+ {
+ swapl(&((xBigReq *)oci->bufptr)->length);
+ }
+ }
+ if (gotnow >= (needed << 2))
+ {
+ if (FD_ISSET(fd, &AllClients))
+ {
+ FD_SET(fd, &ClientsWithInput);
+ }
+ else
+ {
+ FD_SET(fd, &IgnoredClientsWithInput);
+ }
+ YieldControl();
+ }
+ else
+ YieldControlNoInput(fd);
+ }
+}
+
+static const 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(void)
+{
+ register int index, base;
+ register fd_mask mask; /* raphael */
+ OsCommPtr oc;
+ register ClientPtr client;
+ Bool newoutput = NewOutputPending;
+#if defined(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 * (sizeof(fd_mask)*8)) + index]) == 0)
+ continue;
+ client = clients[index];
+ if (client->clientGone)
+ continue;
+ oc = (OsCommPtr)client->osPrivate;
+ if (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 = GetConnectionTranslation(index)) == 0)
+ continue;
+ client = clients[index];
+ if (client->clientGone)
+ continue;
+ oc = (OsCommPtr)client->osPrivate;
+ if (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(void)
+{
+ if (CriticalOutputPending)
+ FlushAllOutput();
+}
+
+void
+SetCriticalOutputPending(void)
+{
+ 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 (ClientPtr who, int count, const void *__buf)
+{
+ OsCommPtr oc;
+ ConnectionOutputPtr oco;
+ int padBytes;
+ const char *buf = __buf;
+#ifdef DEBUG_COMMUNICATION
+ Bool multicount = FALSE;
+#endif
+ if (!count || !who || who == serverClient || who->clientGone)
+ return 0;
+ oc = who->osPrivate;
+ oco = oc->output;
+#ifdef DEBUG_COMMUNICATION
+ {
+ char info[128];
+ xError *err;
+ xGenericReply *rep;
+ xEvent *ev;
+
+ if (!who->replyBytesRemaining) {
+ switch(buf[0]) {
+ case X_Reply:
+ rep = (xGenericReply*)buf;
+ if (rep->sequenceNumber == who->sequence) {
+ snprintf(info,127,"Xreply: type: 0x%x data: 0x%x "
+ "len: %i seq#: 0x%x", rep->type, rep->data1,
+ rep->length, rep->sequenceNumber);
+ multicount = TRUE;
+ }
+ break;
+ case X_Error:
+ err = (xError*)buf;
+ snprintf(info,127,"Xerror: Code: 0x%x resID: 0x%x maj: 0x%x "
+ "min: %x", err->errorCode,err->resourceID,
+ err->minorCode,err->majorCode);
+ break;
+ default:
+ if ((buf[0] & 0x7f) == KeymapNotify)
+ snprintf(info,127,"KeymapNotifyEvent: %i",buf[0]);
+ else {
+ ev = (xEvent*)buf;
+ snprintf(info,127,"XEvent: type: 0x%x detail: 0x%x "
+ "seq#: 0x%x", ev->u.u.type, ev->u.u.detail,
+ ev->u.u.sequenceNumber);
+ }
+ }
+ ErrorF("REPLY: ClientIDX: %i %s\n",who->index, info);
+ } else
+ multicount = TRUE;
+ }
+#endif
+
+ 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;
+
+ replylen = ((xGenericReply *)buf)->length;
+ if (who->swapped)
+ swapl(&replylen);
+ bytesleft = (replylen * 4) + SIZEOF(xReply) - count - padBytes;
+ replyinfo.startOfReply = TRUE;
+ replyinfo.bytesRemaining = who->replyBytesRemaining = bytesleft;
+ CallCallbacks((&ReplyCallback), (pointer)&replyinfo);
+ }
+ }
+#ifdef DEBUG_COMMUNICATION
+ else if (multicount) {
+ if (who->replyBytesRemaining) {
+ who->replyBytesRemaining -= (count + padBytes);
+ } else {
+ CARD32 replylen;
+ replylen = ((xGenericReply *)buf)->length;
+ who->replyBytesRemaining =
+ (replylen * 4) + SIZEOF(xReply) - count - padBytes;
+ }
+ }
+#endif
+ if (oco->count + count + padBytes > oco->size)
+ {
+ FD_CLR(oc->fd, &OutputPending);
+ if(!XFD_ANYSET(&OutputPending)) {
+ CriticalOutputPending = FALSE;
+ NewOutputPending = FALSE;
+ }
+
+ if (FlushCallback)
+ CallCallbacks(&FlushCallback, NULL);
+
+ 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.
+ *
+ **********************/
+
+int
+FlushClient(ClientPtr who, OsCommPtr oc, const void *__extraBuf, int extraCount)
+{
+ ConnectionOutputPtr oco = oc->output;
+ int connection = oc->fd;
+ XtransConnInfo trans_conn = oc->trans_conn;
+ struct iovec iov[3];
+ static char padBuffer[3];
+ const char *extraBuf = __extraBuf;
+ 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 ((char *)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. */
+ errno=0;
+ 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 *)realloc(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)
+ {
+ free(oco->buf);
+ free(oco);
+ }
+ else
+ {
+ oco->next = FreeOutputs;
+ FreeOutputs = oco;
+ }
+ oc->output = (ConnectionOutputPtr)NULL;
+ return extraCount; /* return only the amount explicitly requested */
+}
+
+static ConnectionInputPtr
+AllocateInputBuffer(void)
+{
+ ConnectionInputPtr oci;
+
+ oci = malloc(sizeof(ConnectionInput));
+ if (!oci)
+ return NULL;
+ oci->buffer = malloc(BUFSIZE);
+ if (!oci->buffer)
+ {
+ free(oci);
+ return NULL;
+ }
+ oci->size = BUFSIZE;
+ oci->bufptr = oci->buffer;
+ oci->bufcnt = 0;
+ oci->lenLastReq = 0;
+ oci->ignoreBytes = 0;
+ return oci;
+}
+
+static ConnectionOutputPtr
+AllocateOutputBuffer(void)
+{
+ ConnectionOutputPtr oco;
+
+ oco = malloc(sizeof(ConnectionOutput));
+ if (!oco)
+ return NULL;
+ oco->buf = calloc(1, BUFSIZE);
+ if (!oco->buf)
+ {
+ free(oco);
+ return NULL;
+ }
+ oco->size = BUFSIZE;
+ oco->count = 0;
+ return oco;
+}
+
+void
+FreeOsBuffers(OsCommPtr oc)
+{
+ ConnectionInputPtr oci;
+ ConnectionOutputPtr oco;
+
+ if (AvailableInput == oc)
+ AvailableInput = (OsCommPtr)NULL;
+ if ((oci = oc->input))
+ {
+ if (FreeInputs)
+ {
+ free(oci->buffer);
+ free(oci);
+ }
+ else
+ {
+ FreeInputs = oci;
+ oci->next = (ConnectionInputPtr)NULL;
+ oci->bufptr = oci->buffer;
+ oci->bufcnt = 0;
+ oci->lenLastReq = 0;
+ }
+ }
+ if ((oco = oc->output))
+ {
+ if (FreeOutputs)
+ {
+ free(oco->buf);
+ free(oco);
+ }
+ else
+ {
+ FreeOutputs = oco;
+ oco->next = (ConnectionOutputPtr)NULL;
+ oco->count = 0;
+ }
+ }
+}
+
+void
+ResetOsBuffers(void)
+{
+ ConnectionInputPtr oci;
+ ConnectionOutputPtr oco;
+
+ while ((oci = FreeInputs))
+ {
+ FreeInputs = oci->next;
+ free(oci->buffer);
+ free(oci);
+ }
+ while ((oco = FreeOutputs))
+ {
+ FreeOutputs = oco->next;
+ free(oco->buf);
+ free(oco);
+ }
+}