diff options
Diffstat (limited to 'xorg-server/os/io.c')
-rw-r--r-- | xorg-server/os/io.c | 2214 |
1 files changed, 1107 insertions, 1107 deletions
diff --git a/xorg-server/os/io.c b/xorg-server/os/io.c index 64b64ae75..a963d8653 100644 --- a/xorg-server/os/io.c +++ b/xorg-server/os/io.c @@ -1,1107 +1,1107 @@ -/*********************************************************** - -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) - { - 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; - move_header = FALSE; - 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); - 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 */ - 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; - } - 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 *)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); - 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); - } - 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) - { - 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(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 = (OsCommPtr)who->osPrivate; - ConnectionOutputPtr oco = oc->output; - int padBytes; - const char *buf = __buf; -#ifdef DEBUG_COMMUNICATION - Bool multicount = FALSE; -#endif - if (!count) - return(0); -#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; - } - 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. */ - 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 */ -} - -static ConnectionInputPtr -AllocateInputBuffer(void) -{ - ConnectionInputPtr oci; - - oci = xalloc(sizeof(ConnectionInput)); - if (!oci) - return NULL; - oci->buffer = xalloc(BUFSIZE); - if (!oci->buffer) - { - xfree(oci); - return NULL; - } - oci->size = BUFSIZE; - oci->bufptr = oci->buffer; - oci->bufcnt = 0; - oci->lenLastReq = 0; - return oci; -} - -static ConnectionOutputPtr -AllocateOutputBuffer(void) -{ - ConnectionOutputPtr oco; - - oco = xalloc(sizeof(ConnectionOutput)); - if (!oco) - return NULL; - oco->buf = xcalloc(1, BUFSIZE); - if (!oco->buf) - { - xfree(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) - { - 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; - } - } -} - -void -ResetOsBuffers(void) -{ - ConnectionInputPtr oci; - ConnectionOutputPtr oco; - - while ((oci = FreeInputs)) - { - FreeInputs = oci->next; - xfree(oci->buffer); - xfree(oci); - } - while ((oco = FreeOutputs)) - { - FreeOutputs = oco->next; - xfree(oco->buf); - xfree(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 (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 */
+ 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 *)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);
+ }
+ 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 = (OsCommPtr)who->osPrivate;
+ ConnectionOutputPtr oco = oc->output;
+ int padBytes;
+ const char *buf = __buf;
+#ifdef DEBUG_COMMUNICATION
+ Bool multicount = FALSE;
+#endif
+ if (!count)
+ return(0);
+#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;
+ }
+ 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. */
+ 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;
+ 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);
+ }
+}
|