Site hosted by Angelfire.com: Build your free website today!

Hands-on Projects for the Linux Graphics Subsystem

New book from Christos Karayiannis

Available in Amazon Kindle format at:

amazon.com   amazon.co.uk   amazon.de   amazon.es   amazon.fr   amazon.it

Read and Process Client Requests

PART I: Read with ReadRequestFromClient()

ReadRequestFromClient() as seen in section 3.2.5 is called from Dispatch() as:

result = ReadRequestFromClient(client);

For ReadRequestFromClient() we read at its comment:

/*****************************************************************
 * 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.
 *****************************************************************/

ReadRequestFromClient() source code uses the client struct, which we met first when EstablishNewConnections() was called at section 2.2. Since this was a short introduction on the server functionality EstablishNewConnections() is examined more detailed at section 3.2.5.5. As we mentioned there struct client is defined in dixstruct.h as:

typedef struct _Client {
    int         index;
    Mask        clientAsMask;
    pointer     requestBuffer;
    pointer     osPrivate;	/* for OS layer, including scheduler */
    Bool        swapped;
    ReplySwapPtr pSwapReplyFunc;
    XID         errorValue;
    int         sequence;
    int         closeDownMode;
    int         clientGone;
    int         noClientException;	/* this client died or needs to be
					 * killed */
    DrawablePtr lastDrawable;
    Drawable    lastDrawableID;
    GCPtr       lastGC;
    GContext    lastGCID;
    SaveSetElt	*saveSet;
    int         numSaved;
    pointer     screenPrivate[MAXSCREENS];
    int         (**requestVector) (
		ClientPtr /* pClient */);
    CARD32	req_len;		/* length of current request */
    Bool	big_requests;		/* supports large requests */
    int		priority;
    ClientState clientState;
    DevUnion	*devPrivates;
#ifdef XKB
    unsigned short	xkbClientFlags;
    unsigned short	mapNotifyMask;
    unsigned short	newKeyboardNotifyMask;
    unsigned short	vMajor,vMinor;
    KeyCode		minKC,maxKC;
#endif

#ifdef DEBUG
    unsigned char requestLog[MAX_REQUEST_LOG];
    int         requestLogIndex;
#endif
#ifdef LBX
    int		(*readRequest)(ClientPtr /*client*/);
#endif
    unsigned long replyBytesRemaining;
#ifdef XCSECURITY
    XID		authId;
    unsigned int trustLevel;
    pointer (* CheckAccess)(
	    ClientPtr /*pClient*/,
	    XID /*id*/,
	    RESTYPE /*classes*/,
	    Mask /*access_mode*/,
	    pointer /*resourceval*/);
#endif
#ifdef XAPPGROUP
    struct _AppGroupRec*	appgroup;
#endif
    struct _FontResolution * (*fontResFunc) (    /* no need for font.h */
		ClientPtr	/* pClient */,
		int *		/* num */);
#ifdef SMART_SCHEDULE
    int	    smart_priority;
    long    smart_start_tick;
    long    smart_stop_tick;
    long    smart_check_tick;
#endif
}           ClientRec;

From the client the osPivate field is used to hold the input buffer for the client. Also the osPrivate fd is used as the file descriptor os the client:

    OsCommPtr oc = (OsCommPtr)client->osPrivate;
    ConnectionInputPtr oci = oc->input;
    int fd = oc->fd;

where OsCommPtr is defined in osdep.h as:

typedef struct _osComm {
    int fd;
    ConnectionInputPtr input;
    ConnectionOutputPtr output;
    XID	auth_id;		/* authorization id */
#ifdef K5AUTH
    k5_state	authstate;	/* state of setup auth conversation */
#endif
    CARD32 conn_time;		/* timestamp if not established, else 0  */
    struct _XtransConnInfo *trans_conn; /* transport connection object */
#ifdef LBX
    OsProxyPtr proxy;
    ConnectionInputPtr largereq;
    OsCloseFunc Close;
    OsFlushFunc Flush;
#endif
} OsCommRec, *OsCommPtr;

and ConnectionInputPtr is defined in the same file as:

typedef struct _connectionInput {
    struct _connectionInput *next;
    char *buffer;               /* contains current client input */
    char *bufptr;               /* pointer to current start of data */
    int  bufcnt;                /* count of bytes in buffer */
    int lenLastReq;
    int size;
} ConnectionInput, *ConnectionInputPtr;

If oci (of type ConnectionInputPtr) is NULL then it is allocated in ReadRequestFromClient() by calling AllocateInputBuffer(). Its buffer field (oci->buffer) is BUFSIZE in size, defined in osdep.h to 4096.

At this point is useful to illustrate the comment from io.c:

 *   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.

and then continue with the ReadRequestFromClient() source:

    /* advance to start of next request */

    oci->bufptr += oci->lenLastReq;

At the first time, when AllocateInputBuffer() is called, lenLastReq is initialized to zero.

'gotnow' the bytes obtained so far is calculated and in the case they are less than xReq the bytes 'needed' become the xReq header size

    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;
    }

xReq, the X Protocol request header is defined in Xproto.h as:

/* Request structure */

typedef struct _xReq {
	CARD8 reqType;
	CARD8 data;            /* meaning depends on request type */
	CARD16 length B16;         /* length in 4 bytes quantities 
				  of whole request, including this header */
} xReq;

CARD8 and CARD16 are defined in Xmd.h

In the case a whole xReq is obtained we calculate from its req_len field the 'needed' bytes

    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);
    . . .  
	client->req_len = needed;
	needed <<= 2; /* needed is in bytes now */
    }

macro get_req_len obtains the length of the request from the header struct xReq (little and big endianess is considered):

#define get_req_len(req,cli) ((cli)->swapped ? \
			      lswaps((req)->length) : (req)->length)

needed (the rest of the request) is calculated in bytes by left-shifting by 2. This actually multiplies the four-byte request length quantities (see xReq struct comment) by four and turns it to bytes.

The code bellow examines the condition:

    if (gotnow < needed)

The bytes needed are obtained with a _XSERVTransRead() call. Using the following macro from Xtrans.h:

#define TRANS(func) _XSERVTrans##func

Therefore in xtrans.c TRANS(Read) is replaced by _XSERVTransRead and the following is the implementation of _XSERVTransRead:

int
TRANS(Read) (XtransConnInfo ciptr, char *buf, int size)

{
    return ciptr->transptr->Read (ciptr, buf, size);
}

As seen in the code bellow (bold characters) the first argument (oc->trans_conn) which substitutes the ciptr parameter takes a value at EstablishNewConnections() , called from WaitForSomething() as seen in section 3.2.5.2. We examine EstablishNewConnections() at section 3.2.5.5.

    if (gotnow < needed)
    {
	/* Need to read more data, either so that we can get a
	 * complete xReq (if need_header is TRUE), a complete
	 * xBigReq (if move_header is TRUE), or the rest of the
	 * request (if need_header and move_header are both FALSE).
	 */

	oci->lenLastReq = 0;
	if (needed > MAXBUFSIZE)
	{
	    /* request is too big for us to handle */
	    YieldControlDeath();
	    return -1;
	}
	if ((gotnow == 0) ||
	    ((oci->bufptr - oci->buffer + needed) > oci->size))
	{
	    /* no data, or the request is too big to fit in the buffer */

	    if ((gotnow > 0) && (oci->bufptr != oci->buffer))
		/* save the data we've already read */
		memmove(oci->buffer, oci->bufptr, gotnow);
	    if (needed > oci->size)
	    {
		/* make buffer bigger to accomodate request */
		char *ibuf;

		ibuf = (char *)xrealloc(oci->buffer, needed);
		if (!ibuf)
		{
		    YieldControlDeath();
		    return -1;
		}
		oci->size = needed;
		oci->buffer = ibuf;
	    }
	    oci->bufptr = oci->buffer;
	    oci->bufcnt = gotnow;
	}
	/*  XXX this is a workaround.  This function is sometimes called
	 *  after the trans_conn has been freed.  In this case trans_conn
	 *  will be null.  Really ought to restructure things so that we
	 *  never get here in those circumstances.
	 */
	if (!oc->trans_conn)
	{
	    /*  treat as if an error occured on the read, which is what
	     *  used to happen
	     */
	    YieldControlDeath();
	    return -1;
	}
#ifdef LBX
	if (oc->proxy && oc->proxy->compHandle)
	    result = (*oc->proxy->streamOpts.streamCompRead)(fd,
			     (unsigned char *)oci->buffer + oci->bufcnt,
			     oci->size - oci->bufcnt);
	else
#endif
	    result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
				     oci->size - oci->bufcnt); 
	if (result <= 0)
	{
	    if ((result < 0) && ETEST(errno))
	    {
#if defined(SVR4) && defined(i386) && !defined(sun)
#if defined(LBX) && 0
		/*
		 * For LBX connections, we can get a valid EWOULDBLOCK
		 * There is probably a better way of distinguishing LBX
		 * connections, but this works. (DHD)
		 */
		extern int LbxRead();
		if (oc->Read == LbxRead)
#else
		if (0)
#endif
#endif
		{
		    YieldControlNoInput();
		    return 0;
		}
	    }
	    YieldControlDeath();
	    return -1;
	}
	oci->bufcnt += result;
	gotnow += result;

	/* free up some space after huge requests */
	if ((oci->size > BUFWATERMARK) &&
	    (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE))
	{
	    char *ibuf;

	    ibuf = (char *)xrealloc(oci->buffer, BUFSIZE);
	    if (ibuf)
	    {
		oci->size = BUFSIZE;
		oci->buffer = ibuf;
		oci->bufptr = ibuf + oci->bufcnt - gotnow;
	    }
	}
	if (need_header && gotnow >= needed)
	{
	    /* We wanted an xReq, now we've gotten it. */
	    request = (xReq *)oci->bufptr;
	    needed = get_req_len(request, client);
#ifdef BIGREQS
	    if (!needed && client->big_requests)
	    {
		move_header = TRUE;
		if (gotnow < sizeof(xBigReq))
		    needed = sizeof(xBigReq) >> 2;
		else
		    needed = get_big_req_len(request, client);
	    }
#endif
	    client->req_len = needed;
	    needed <<= 2;
	}
	if (gotnow < needed)
	{
	    /* Still don't have enough; punt. */
	    YieldControlNoInput();
	    return 0;
	}
    }

The next part examines the condition

    if (needed == 0)

In that case the current request is obtained in full length and another needs to be read from the buffer. Therefore needed becomes the header of the request that needs to be read:

    if (needed == 0)
    {
#ifdef BIGREQS
	if (client->big_requests)
	    needed = sizeof(xBigReq);
	else
#endif
	    needed = sizeof(xReq);
    }
    oci->lenLastReq = needed;

Last case to examine is gotnow > needed (Notice that needed can also be the xReq header). In that case if 'gotnow' is bigger than xReq and also bigger than the request length (obtained from xReq) the specific client's file descriptor (fd) is placed via FD_SET to ClientsWithInput, the fd set for the clients ready to be read.

As seen in section 1.2 a client can prepare multiple requests and send them with a XFlush(). Therefore other requests can also be fetched to a buffer with the last read().

    gotnow -= needed;
    if (gotnow >= sizeof(xReq)) 
    {
	request = (xReq *)(oci->bufptr + needed);
	if (gotnow >= (result = (get_req_len(request, client) << 2))
#ifdef BIGREQS
	    && (result ||
		(client->big_requests &&
		 (gotnow >= sizeof(xBigReq) &&
		  gotnow >= (get_big_req_len(request, client) << 2))))
#endif
	    )
	    FD_SET(fd, &ClientsWithInput);
	else
	{
#ifdef SMART_SCHEDULE
	    if (!SmartScheduleDisable)
		FD_CLR(fd, &ClientsWithInput);
	    else
#endif
		YieldControlNoInput();
	}
    }

In the case gotnow < sizeof(xReq) timesThisConnection is increased by one and if MAX_TIMES_PER is reached macro YieldControl() is called. This is defined as:

#define YieldControl()				\
        { isItTimeToYield = TRUE;		\

This makes the while loop seen in the Dispatch() overview at section 3.2.5 break.

    else
    {
	if (!gotnow)
	    AvailableInput = oc;
#ifdef SMART_SCHEDULE
	if (!SmartScheduleDisable)
	    FD_CLR(fd, &ClientsWithInput);
	else
#endif
	    YieldControlNoInput();
    }
#ifdef SMART_SCHEDULE
    if (SmartScheduleDisable)
#endif
    if (++timesThisConnection >= MAX_TIMES_PER)
	YieldControl();
#ifdef BIGREQS
    if (move_header)
    {
	request = (xReq *)oci->bufptr;
	oci->bufptr += (sizeof(xBigReq) - sizeof(xReq));
	*(xReq *)oci->bufptr = *request;
	oci->lenLastReq -= (sizeof(xBigReq) - sizeof(xReq));
	client->req_len -= (sizeof(xBigReq) - sizeof(xReq)) >> 2;
    }
#endif
    client->requestBuffer = (pointer)oci->bufptr;
#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;
}

The max number the server can read from the same client is represented by MAX_TIMES_PER and it is defined to io.c as:

#define MAX_TIMES_PER         10
Therefore the 'purple' while loop from the Dispatch() overview at section 3.2.5, shown bellow as well, can run at most 10 times for a certain X client.
             while (!isItTimeToYield)
             {

	          if (*icheck[0] != *icheck[1])
		  {
		      ProcessInputEvents();
		      FlushIfCriticalOutputPending();
		  }
	          result = ReadRequestFromClient(client);

		  result = (* client->requestVector[MAJOROP])(client);

             }



PART II: Process Client Requests

We reach now at the heart of the X Server at the point where X Client's requests are dispatched. We previously saw the interaction of X Client- X Server at section 2.3.

The code line that calls the specific routine according to the request type the X Client sent is obtained from requestVector[] using as index the MAJOROP:

		    result = (* client->requestVector[MAJOROP])(client);

MAJOROP is defined in the same file as the request type of the xReq:

#define MAJOROP ((xReq *)client->requestBuffer)->reqType

requestVector[] can either be ProcVector[] or SwappedProcVector[] according to the Big or Small Endianess of the system. Both are defined in tables.c and InitialVector[] intervene in the process before one of the two vectors is selected. This is described in section 3.2.5.5. The contents of the vectors are the functions that are implemented for a certain X Client request using as index in these 'tables' the Request type of the client's message. As seen above this is described also as MAJOROP in the current file. ProcVector[] for instance has the form:

int (* ProcVector[256]) (
	ClientPtr /* client */
    ) =
{
    ProcBadRequest,
    ProcCreateWindow,
    ProcChangeWindowAttributes,
    ProcGetWindowAttributes,
    ProcDestroyWindow,
    ProcDestroySubwindows,		/* 5 */
    ProcChangeSaveSet,
    ProcReparentWindow,
    ProcMapWindow,
    ProcMapSubwindows,
    ProcUnmapWindow,			/* 10 */
    ProcUnmapSubwindows,
    ProcConfigureWindow,
    ProcCirculateWindow,
    ProcGetGeometry,
    ProcQueryTree,			/* 15 */
    ProcInternAtom,
    ProcGetAtomName,
    ProcChangeProperty,
    ProcDeleteProperty,
    ProcGetProperty,			/* 20 */
    ProcListProperties,
    ProcSetSelectionOwner,
    ProcGetSelectionOwner,
    ProcConvertSelection,
    ProcSendEvent,			/* 25 */
    ProcGrabPointer,
    ProcUngrabPointer,
    ProcGrabButton,
    ProcUngrabButton,
    ProcChangeActivePointerGrab,	/* 30 */
    ProcGrabKeyboard,
    ProcUngrabKeyboard,
    ProcGrabKey,
    ProcUngrabKey,
    ProcAllowEvents,			/* 35 */
    ProcGrabServer,
    ProcUngrabServer,
    ProcQueryPointer,
    ProcGetMotionEvents,
    ProcTranslateCoords,		/* 40 */
    ProcWarpPointer,
    ProcSetInputFocus,
    ProcGetInputFocus,
    ProcQueryKeymap,
    ProcOpenFont,			/* 45 */
    ProcCloseFont,
    ProcQueryFont,
    ProcQueryTextExtents,
    ProcListFonts,
    ProcListFontsWithInfo,		/* 50 */
    ProcSetFontPath,
    ProcGetFontPath,
    ProcCreatePixmap,
    ProcFreePixmap,
    ProcCreateGC,			/* 55 */
    ProcChangeGC,
    ProcCopyGC,
    ProcSetDashes,
    ProcSetClipRectangles,
    ProcFreeGC,				/* 60 */
    ProcClearToBackground,
    ProcCopyArea,
    ProcCopyPlane,
    ProcPolyPoint,
    ProcPolyLine,			/* 65 */
    ProcPolySegment,
    ProcPolyRectangle,
    ProcPolyArc,
    ProcFillPoly,
    ProcPolyFillRectangle,		/* 70 */
    ProcPolyFillArc,
    ProcPutImage,
    ProcGetImage,
    ProcPolyText,
    ProcPolyText,			/* 75 */
    ProcImageText8,
    ProcImageText16,
    ProcCreateColormap,
    ProcFreeColormap,
    ProcCopyColormapAndFree,		/* 80 */
    ProcInstallColormap,
    ProcUninstallColormap,
    ProcListInstalledColormaps,
    ProcAllocColor,
    ProcAllocNamedColor,		/* 85 */
    ProcAllocColorCells,
    ProcAllocColorPlanes,
    ProcFreeColors,
    ProcStoreColors,
    ProcStoreNamedColor,		/* 90 */
    ProcQueryColors,
    ProcLookupColor,
    ProcCreateCursor,
    ProcCreateGlyphCursor,
    ProcFreeCursor,			/* 95 */
    ProcRecolorCursor,
    ProcQueryBestSize,
    ProcQueryExtension,
    ProcListExtensions,
    ProcChangeKeyboardMapping,		/* 100 */
    ProcGetKeyboardMapping,
    ProcChangeKeyboardControl,
    ProcGetKeyboardControl,
    ProcBell,
    ProcChangePointerControl,		/* 105 */
    ProcGetPointerControl,
    ProcSetScreenSaver,
    ProcGetScreenSaver,
    ProcChangeHosts,
    ProcListHosts,			/* 110 */
    ProcChangeAccessControl,
    ProcChangeCloseDownMode,
    ProcKillClient,
    ProcRotateProperties,
    ProcForceScreenSaver,		/* 115 */
    ProcSetPointerMapping,
    ProcGetPointerMapping,
    ProcSetModifierMapping,
    ProcGetModifierMapping,
    0,					/* 120 */
    0,
    0,
    0,
    0,
    0,					/* 125 */
    0,
    ProcNoOperation    
};

As argument the specific routine takes the client struct for the certain client that sent the request.

		if (result != Success) 
		{
		    if (client->noClientException != Success)
                        CloseDownClient(client);
                    else
		        SendErrorToClient(client, MAJOROP,
					  MinorOpcodeOfRequest(client),
					  client->errorValue, result);
		    break;
	        }