xserver

xserver with xephyr scale patch
git clone https://git.neptards.moe/u3shit/xserver.git
Log | Files | Refs | README | LICENSE

io.c (32979B)


      1 /***********************************************************
      2 
      3 Copyright 1987, 1989, 1998  The Open Group
      4 
      5 Permission to use, copy, modify, distribute, and sell this software and its
      6 documentation for any purpose is hereby granted without fee, provided that
      7 the above copyright notice appear in all copies and that both that
      8 copyright notice and this permission notice appear in supporting
      9 documentation.
     10 
     11 The above copyright notice and this permission notice shall be included in
     12 all copies or substantial portions of the Software.
     13 
     14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     20 
     21 Except as contained in this notice, the name of The Open Group shall not be
     22 used in advertising or otherwise to promote the sale, use or other dealings
     23 in this Software without prior written authorization from The Open Group.
     24 
     25 Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
     26 
     27                         All Rights Reserved
     28 
     29 Permission to use, copy, modify, and distribute this software and its
     30 documentation for any purpose and without fee is hereby granted,
     31 provided that the above copyright notice appear in all copies and that
     32 both that copyright notice and this permission notice appear in
     33 supporting documentation, and that the name of Digital not be
     34 used in advertising or publicity pertaining to distribution of the
     35 software without specific, written prior permission.
     36 
     37 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
     38 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
     39 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
     40 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
     41 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     42 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     43 SOFTWARE.
     44 
     45 ******************************************************************/
     46 /*****************************************************************
     47  * i/o functions
     48  *
     49  *   WriteToClient, ReadRequestFromClient
     50  *   InsertFakeRequest, ResetCurrentRequest
     51  *
     52  *****************************************************************/
     53 
     54 #ifdef HAVE_DIX_CONFIG_H
     55 #include <dix-config.h>
     56 #endif
     57 
     58 #undef DEBUG_COMMUNICATION
     59 
     60 #ifdef WIN32
     61 #include <X11/Xwinsock.h>
     62 #endif
     63 #include <stdio.h>
     64 #define XSERV_t
     65 #define TRANS_SERVER
     66 #define TRANS_REOPEN
     67 #include <X11/Xtrans/Xtrans.h>
     68 #include <X11/Xmd.h>
     69 #include <errno.h>
     70 #if !defined(WIN32)
     71 #include <sys/uio.h>
     72 #endif
     73 #include <X11/X.h>
     74 #include <X11/Xproto.h>
     75 #include "os.h"
     76 #include "osdep.h"
     77 #include "opaque.h"
     78 #include "dixstruct.h"
     79 #include "misc.h"
     80 
     81 CallbackListPtr ReplyCallback;
     82 CallbackListPtr FlushCallback;
     83 
     84 typedef struct _connectionInput {
     85     struct _connectionInput *next;
     86     char *buffer;               /* contains current client input */
     87     char *bufptr;               /* pointer to current start of data */
     88     int bufcnt;                 /* count of bytes in buffer */
     89     int lenLastReq;
     90     int size;
     91     unsigned int ignoreBytes;   /* bytes to ignore before the next request */
     92 } ConnectionInput;
     93 
     94 typedef struct _connectionOutput {
     95     struct _connectionOutput *next;
     96     unsigned char *buf;
     97     int size;
     98     int count;
     99 } ConnectionOutput;
    100 
    101 static ConnectionInputPtr AllocateInputBuffer(void);
    102 static ConnectionOutputPtr AllocateOutputBuffer(void);
    103 
    104 static Bool CriticalOutputPending;
    105 static int timesThisConnection = 0;
    106 static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL;
    107 static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL;
    108 static OsCommPtr AvailableInput = (OsCommPtr) NULL;
    109 
    110 #define get_req_len(req,cli) ((cli)->swapped ? \
    111 			      bswap_16((req)->length) : (req)->length)
    112 
    113 #include <X11/extensions/bigreqsproto.h>
    114 
    115 #define get_big_req_len(req,cli) ((cli)->swapped ? \
    116 				  bswap_32(((xBigReq *)(req))->length) : \
    117 				  ((xBigReq *)(req))->length)
    118 
    119 #define BUFSIZE 16384
    120 #define BUFWATERMARK 32768
    121 
    122 /*
    123  *   A lot of the code in this file manipulates a ConnectionInputPtr:
    124  *
    125  *    -----------------------------------------------
    126  *   |------- bufcnt ------->|           |           |
    127  *   |           |- gotnow ->|           |           |
    128  *   |           |-------- needed ------>|           |
    129  *   |-----------+--------- size --------+---------->|
    130  *    -----------------------------------------------
    131  *   ^           ^
    132  *   |           |
    133  *   buffer   bufptr
    134  *
    135  *  buffer is a pointer to the start of the buffer.
    136  *  bufptr points to the start of the current request.
    137  *  bufcnt counts how many bytes are in the buffer.
    138  *  size is the size of the buffer in bytes.
    139  *
    140  *  In several of the functions, gotnow and needed are local variables
    141  *  that do the following:
    142  *
    143  *  gotnow is the number of bytes of the request that we're
    144  *  trying to read that are currently in the buffer.
    145  *  Typically, gotnow = (buffer + bufcnt) - bufptr
    146  *
    147  *  needed = the length of the request that we're trying to
    148  *  read.  Watch out: needed sometimes counts bytes and sometimes
    149  *  counts CARD32's.
    150  */
    151 
    152 /*****************************************************************
    153  * ReadRequestFromClient
    154  *    Returns one request in client->requestBuffer.  The request
    155  *    length will be in client->req_len.  Return status is:
    156  *
    157  *    > 0  if  successful, specifies length in bytes of the request
    158  *    = 0  if  entire request is not yet available
    159  *    < 0  if  client should be terminated
    160  *
    161  *    The request returned must be contiguous so that it can be
    162  *    cast in the dispatcher to the correct request type.  Because requests
    163  *    are variable length, ReadRequestFromClient() must look at the first 4
    164  *    or 8 bytes of a request to determine the length (the request length is
    165  *    in the 3rd and 4th bytes of the request unless it is a Big Request
    166  *    (see the Big Request Extension), in which case the 3rd and 4th bytes
    167  *    are zero and the following 4 bytes are the request length.
    168  *
    169  *    Note: in order to make the server scheduler (WaitForSomething())
    170  *    "fair", the ClientsWithInput mask is used.  This mask tells which
    171  *    clients have FULL requests left in their buffers.  Clients with
    172  *    partial requests require a read.  Basically, client buffers
    173  *    are drained before select() is called again.  But, we can't keep
    174  *    reading from a client that is sending buckets of data (or has
    175  *    a partial request) because others clients need to be scheduled.
    176  *****************************************************************/
    177 
    178 static void
    179 YieldControl(void)
    180 {
    181     isItTimeToYield = TRUE;
    182     timesThisConnection = 0;
    183 }
    184 
    185 static void
    186 YieldControlNoInput(ClientPtr client)
    187 {
    188     OsCommPtr oc = client->osPrivate;
    189     YieldControl();
    190     if (oc->trans_conn)
    191         ospoll_reset_events(server_poll, oc->fd);
    192 }
    193 
    194 static void
    195 YieldControlDeath(void)
    196 {
    197     timesThisConnection = 0;
    198 }
    199 
    200 /* If an input buffer was empty, either free it if it is too big or link it
    201  * into our list of free input buffers.  This means that different clients can
    202  * share the same input buffer (at different times).  This was done to save
    203  * memory.
    204  */
    205 static void
    206 NextAvailableInput(OsCommPtr oc)
    207 {
    208     if (AvailableInput) {
    209         if (AvailableInput != oc) {
    210             ConnectionInputPtr aci = AvailableInput->input;
    211 
    212             if (aci->size > BUFWATERMARK) {
    213                 free(aci->buffer);
    214                 free(aci);
    215             }
    216             else {
    217                 aci->next = FreeInputs;
    218                 FreeInputs = aci;
    219             }
    220             AvailableInput->input = NULL;
    221         }
    222         AvailableInput = NULL;
    223     }
    224 }
    225 
    226 int
    227 ReadRequestFromClient(ClientPtr client)
    228 {
    229     OsCommPtr oc = (OsCommPtr) client->osPrivate;
    230     ConnectionInputPtr oci = oc->input;
    231     unsigned int gotnow, needed;
    232     int result;
    233     register xReq *request;
    234     Bool need_header;
    235     Bool move_header;
    236 
    237     NextAvailableInput(oc);
    238 
    239     /* make sure we have an input buffer */
    240 
    241     if (!oci) {
    242         if ((oci = FreeInputs)) {
    243             FreeInputs = oci->next;
    244         }
    245         else if (!(oci = AllocateInputBuffer())) {
    246             YieldControlDeath();
    247             return -1;
    248         }
    249         oc->input = oci;
    250     }
    251 
    252 #if XTRANS_SEND_FDS
    253     /* Discard any unused file descriptors */
    254     while (client->req_fds > 0) {
    255         int req_fd = ReadFdFromClient(client);
    256         if (req_fd >= 0)
    257             close(req_fd);
    258     }
    259 #endif
    260     /* advance to start of next request */
    261 
    262     oci->bufptr += oci->lenLastReq;
    263 
    264     need_header = FALSE;
    265     move_header = FALSE;
    266     gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
    267 
    268     if (oci->ignoreBytes > 0) {
    269         if (oci->ignoreBytes > oci->size)
    270             needed = oci->size;
    271         else
    272             needed = oci->ignoreBytes;
    273     }
    274     else if (gotnow < sizeof(xReq)) {
    275         /* We don't have an entire xReq yet.  Can't tell how big
    276          * the request will be until we get the whole xReq.
    277          */
    278         needed = sizeof(xReq);
    279         need_header = TRUE;
    280     }
    281     else {
    282         /* We have a whole xReq.  We can tell how big the whole
    283          * request will be unless it is a Big Request.
    284          */
    285         request = (xReq *) oci->bufptr;
    286         needed = get_req_len(request, client);
    287         if (!needed && client->big_requests) {
    288             /* It's a Big Request. */
    289             move_header = TRUE;
    290             if (gotnow < sizeof(xBigReq)) {
    291                 /* Still need more data to tell just how big. */
    292                 needed = bytes_to_int32(sizeof(xBigReq));       /* needed is in CARD32s now */
    293                 need_header = TRUE;
    294             }
    295             else
    296                 needed = get_big_req_len(request, client);
    297         }
    298         client->req_len = needed;
    299         needed <<= 2;           /* needed is in bytes now */
    300     }
    301     if (gotnow < needed) {
    302         /* Need to read more data, either so that we can get a
    303          * complete xReq (if need_header is TRUE), a complete
    304          * xBigReq (if move_header is TRUE), or the rest of the
    305          * request (if need_header and move_header are both FALSE).
    306          */
    307 
    308         oci->lenLastReq = 0;
    309         if (needed > maxBigRequestSize << 2) {
    310             /* request is too big for us to handle */
    311             /*
    312              * Mark the rest of it as needing to be ignored, and then return
    313              * the full size.  Dispatch() will turn it into a BadLength error.
    314              */
    315             oci->ignoreBytes = needed - gotnow;
    316             oci->lenLastReq = gotnow;
    317             return needed;
    318         }
    319         if ((gotnow == 0) || ((oci->bufptr - oci->buffer + needed) > oci->size)) {
    320             /* no data, or the request is too big to fit in the buffer */
    321 
    322             if ((gotnow > 0) && (oci->bufptr != oci->buffer))
    323                 /* save the data we've already read */
    324                 memmove(oci->buffer, oci->bufptr, gotnow);
    325             if (needed > oci->size) {
    326                 /* make buffer bigger to accommodate request */
    327                 char *ibuf;
    328 
    329                 ibuf = (char *) realloc(oci->buffer, needed);
    330                 if (!ibuf) {
    331                     YieldControlDeath();
    332                     return -1;
    333                 }
    334                 oci->size = needed;
    335                 oci->buffer = ibuf;
    336             }
    337             oci->bufptr = oci->buffer;
    338             oci->bufcnt = gotnow;
    339         }
    340         /*  XXX this is a workaround.  This function is sometimes called
    341          *  after the trans_conn has been freed.  In this case trans_conn
    342          *  will be null.  Really ought to restructure things so that we
    343          *  never get here in those circumstances.
    344          */
    345         if (!oc->trans_conn) {
    346             /*  treat as if an error occurred on the read, which is what
    347              *  used to happen
    348              */
    349             YieldControlDeath();
    350             return -1;
    351         }
    352         result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
    353                                  oci->size - oci->bufcnt);
    354         if (result <= 0) {
    355             if ((result < 0) && ETEST(errno)) {
    356                 mark_client_not_ready(client);
    357 #if defined(SVR4) && defined(__i386__) && !defined(__sun)
    358                 if (0)
    359 #endif
    360                 {
    361                     YieldControlNoInput(client);
    362                     return 0;
    363                 }
    364             }
    365             YieldControlDeath();
    366             return -1;
    367         }
    368         oci->bufcnt += result;
    369         gotnow += result;
    370         /* free up some space after huge requests */
    371         if ((oci->size > BUFWATERMARK) &&
    372             (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
    373             char *ibuf;
    374 
    375             ibuf = (char *) realloc(oci->buffer, BUFSIZE);
    376             if (ibuf) {
    377                 oci->size = BUFSIZE;
    378                 oci->buffer = ibuf;
    379                 oci->bufptr = ibuf + oci->bufcnt - gotnow;
    380             }
    381         }
    382         if (need_header && gotnow >= needed) {
    383             /* We wanted an xReq, now we've gotten it. */
    384             request = (xReq *) oci->bufptr;
    385             needed = get_req_len(request, client);
    386             if (!needed && client->big_requests) {
    387                 move_header = TRUE;
    388                 if (gotnow < sizeof(xBigReq))
    389                     needed = bytes_to_int32(sizeof(xBigReq));
    390                 else
    391                     needed = get_big_req_len(request, client);
    392             }
    393             client->req_len = needed;
    394             needed <<= 2;
    395         }
    396         if (gotnow < needed) {
    397             /* Still don't have enough; punt. */
    398             YieldControlNoInput(client);
    399             return 0;
    400         }
    401     }
    402     if (needed == 0) {
    403         if (client->big_requests)
    404             needed = sizeof(xBigReq);
    405         else
    406             needed = sizeof(xReq);
    407     }
    408 
    409     /* If there are bytes to ignore, ignore them now. */
    410 
    411     if (oci->ignoreBytes > 0) {
    412         assert(needed == oci->ignoreBytes || needed == oci->size);
    413         /*
    414          * The _XSERVTransRead call above may return more or fewer bytes than we
    415          * want to ignore.  Ignore the smaller of the two sizes.
    416          */
    417         if (gotnow < needed) {
    418             oci->ignoreBytes -= gotnow;
    419             oci->bufptr += gotnow;
    420             gotnow = 0;
    421         }
    422         else {
    423             oci->ignoreBytes -= needed;
    424             oci->bufptr += needed;
    425             gotnow -= needed;
    426         }
    427         needed = 0;
    428     }
    429 
    430     oci->lenLastReq = needed;
    431 
    432     /*
    433      *  Check to see if client has at least one whole request in the
    434      *  buffer beyond the request we're returning to the caller.
    435      *  If there is only a partial request, treat like buffer
    436      *  is empty so that select() will be called again and other clients
    437      *  can get into the queue.
    438      */
    439 
    440     gotnow -= needed;
    441     if (!gotnow)
    442         AvailableInput = oc;
    443     if (move_header) {
    444         if (client->req_len < bytes_to_int32(sizeof(xBigReq) - sizeof(xReq))) {
    445             YieldControlDeath();
    446             return -1;
    447         }
    448 
    449         request = (xReq *) oci->bufptr;
    450         oci->bufptr += (sizeof(xBigReq) - sizeof(xReq));
    451         *(xReq *) oci->bufptr = *request;
    452         oci->lenLastReq -= (sizeof(xBigReq) - sizeof(xReq));
    453         client->req_len -= bytes_to_int32(sizeof(xBigReq) - sizeof(xReq));
    454     }
    455     client->requestBuffer = (void *) oci->bufptr;
    456 #ifdef DEBUG_COMMUNICATION
    457     {
    458         xReq *req = client->requestBuffer;
    459 
    460         ErrorF("REQUEST: ClientIDX: %i, type: 0x%x data: 0x%x len: %i\n",
    461                client->index, req->reqType, req->data, req->length);
    462     }
    463 #endif
    464     return needed;
    465 }
    466 
    467 int
    468 ReadFdFromClient(ClientPtr client)
    469 {
    470     int fd = -1;
    471 
    472 #if XTRANS_SEND_FDS
    473     if (client->req_fds > 0) {
    474         OsCommPtr oc = (OsCommPtr) client->osPrivate;
    475 
    476         --client->req_fds;
    477         fd = _XSERVTransRecvFd(oc->trans_conn);
    478     } else
    479         LogMessage(X_ERROR, "Request asks for FD without setting req_fds\n");
    480 #endif
    481 
    482     return fd;
    483 }
    484 
    485 int
    486 WriteFdToClient(ClientPtr client, int fd, Bool do_close)
    487 {
    488 #if XTRANS_SEND_FDS
    489     OsCommPtr oc = (OsCommPtr) client->osPrivate;
    490 
    491     return _XSERVTransSendFd(oc->trans_conn, fd, do_close);
    492 #else
    493     return -1;
    494 #endif
    495 }
    496 
    497 /*****************************************************************
    498  * InsertFakeRequest
    499  *    Splice a consed up (possibly partial) request in as the next request.
    500  *
    501  **********************/
    502 
    503 Bool
    504 InsertFakeRequest(ClientPtr client, char *data, int count)
    505 {
    506     OsCommPtr oc = (OsCommPtr) client->osPrivate;
    507     ConnectionInputPtr oci = oc->input;
    508     int gotnow, moveup;
    509 
    510     NextAvailableInput(oc);
    511 
    512     if (!oci) {
    513         if ((oci = FreeInputs))
    514             FreeInputs = oci->next;
    515         else if (!(oci = AllocateInputBuffer()))
    516             return FALSE;
    517         oc->input = oci;
    518     }
    519     oci->bufptr += oci->lenLastReq;
    520     oci->lenLastReq = 0;
    521     gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
    522     if ((gotnow + count) > oci->size) {
    523         char *ibuf;
    524 
    525         ibuf = (char *) realloc(oci->buffer, gotnow + count);
    526         if (!ibuf)
    527             return FALSE;
    528         oci->size = gotnow + count;
    529         oci->buffer = ibuf;
    530         oci->bufptr = ibuf + oci->bufcnt - gotnow;
    531     }
    532     moveup = count - (oci->bufptr - oci->buffer);
    533     if (moveup > 0) {
    534         if (gotnow > 0)
    535             memmove(oci->bufptr + moveup, oci->bufptr, gotnow);
    536         oci->bufptr += moveup;
    537         oci->bufcnt += moveup;
    538     }
    539     memmove(oci->bufptr - count, data, count);
    540     oci->bufptr -= count;
    541     gotnow += count;
    542     if ((gotnow >= sizeof(xReq)) &&
    543         (gotnow >= (int) (get_req_len((xReq *) oci->bufptr, client) << 2)))
    544         mark_client_ready(client);
    545     else
    546         YieldControlNoInput(client);
    547     return TRUE;
    548 }
    549 
    550 /*****************************************************************
    551  * ResetRequestFromClient
    552  *    Reset to reexecute the current request, and yield.
    553  *
    554  **********************/
    555 
    556 void
    557 ResetCurrentRequest(ClientPtr client)
    558 {
    559     OsCommPtr oc = (OsCommPtr) client->osPrivate;
    560 
    561     /* ignore dying clients */
    562     if (!oc)
    563         return;
    564 
    565     register ConnectionInputPtr oci = oc->input;
    566     register xReq *request;
    567     int gotnow, needed;
    568 
    569     if (AvailableInput == oc)
    570         AvailableInput = (OsCommPtr) NULL;
    571     oci->lenLastReq = 0;
    572     gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
    573     if (gotnow < sizeof(xReq)) {
    574         YieldControlNoInput(client);
    575     }
    576     else {
    577         request = (xReq *) oci->bufptr;
    578         needed = get_req_len(request, client);
    579         if (!needed && client->big_requests) {
    580             oci->bufptr -= sizeof(xBigReq) - sizeof(xReq);
    581             *(xReq *) oci->bufptr = *request;
    582             ((xBigReq *) oci->bufptr)->length = client->req_len;
    583             if (client->swapped) {
    584                 swapl(&((xBigReq *) oci->bufptr)->length);
    585             }
    586         }
    587         if (gotnow >= (needed << 2)) {
    588             if (listen_to_client(client))
    589                 mark_client_ready(client);
    590             YieldControl();
    591         }
    592         else
    593             YieldControlNoInput(client);
    594     }
    595 }
    596 
    597  /********************
    598  * FlushAllOutput()
    599  *    Flush all clients with output.  However, if some client still
    600  *    has input in the queue (more requests), then don't flush.  This
    601  *    will prevent the output queue from being flushed every time around
    602  *    the round robin queue.  Now, some say that it SHOULD be flushed
    603  *    every time around, but...
    604  *
    605  **********************/
    606 
    607 void
    608 FlushAllOutput(void)
    609 {
    610     OsCommPtr oc;
    611     register ClientPtr client, tmp;
    612     Bool newoutput = NewOutputPending;
    613 
    614     if (!newoutput)
    615         return;
    616 
    617     /*
    618      * It may be that some client still has critical output pending,
    619      * but he is not yet ready to receive it anyway, so we will
    620      * simply wait for the select to tell us when he's ready to receive.
    621      */
    622     CriticalOutputPending = FALSE;
    623     NewOutputPending = FALSE;
    624 
    625     xorg_list_for_each_entry_safe(client, tmp, &output_pending_clients, output_pending) {
    626         if (client->clientGone)
    627             continue;
    628         if (!client_is_ready(client)) {
    629             oc = (OsCommPtr) client->osPrivate;
    630             (void) FlushClient(client, oc, (char *) NULL, 0);
    631         } else
    632             NewOutputPending = TRUE;
    633     }
    634 }
    635 
    636 void
    637 FlushIfCriticalOutputPending(void)
    638 {
    639     if (CriticalOutputPending)
    640         FlushAllOutput();
    641 }
    642 
    643 void
    644 SetCriticalOutputPending(void)
    645 {
    646     CriticalOutputPending = TRUE;
    647 }
    648 
    649 /*****************
    650  * AbortClient:
    651  *    When a write error occurs to a client, close
    652  *    the connection and clean things up. Mark
    653  *    the client as 'ready' so that the server will
    654  *    try to read from it again, notice that the fd is
    655  *    closed and clean up from there.
    656  *****************/
    657 
    658 static void
    659 AbortClient(ClientPtr client)
    660 {
    661     OsCommPtr oc = client->osPrivate;
    662 
    663     if (oc->trans_conn) {
    664         CloseDownFileDescriptor(oc);
    665         mark_client_ready(client);
    666     }
    667 }
    668 
    669 /*****************
    670  * WriteToClient
    671  *    Copies buf into ClientPtr.buf if it fits (with padding), else
    672  *    flushes ClientPtr.buf and buf to client.  As of this writing,
    673  *    every use of WriteToClient is cast to void, and the result
    674  *    is ignored.  Potentially, this could be used by requests
    675  *    that are sending several chunks of data and want to break
    676  *    out of a loop on error.  Thus, we will leave the type of
    677  *    this routine as int.
    678  *****************/
    679 
    680 int
    681 WriteToClient(ClientPtr who, int count, const void *__buf)
    682 {
    683     OsCommPtr oc;
    684     ConnectionOutputPtr oco;
    685     int padBytes;
    686     const char *buf = __buf;
    687 
    688     BUG_RETURN_VAL_MSG(in_input_thread(), 0,
    689                        "******** %s called from input thread *********\n", __func__);
    690 
    691 #ifdef DEBUG_COMMUNICATION
    692     Bool multicount = FALSE;
    693 #endif
    694     if (!count || !who || who == serverClient || who->clientGone)
    695         return 0;
    696     oc = who->osPrivate;
    697     oco = oc->output;
    698 #ifdef DEBUG_COMMUNICATION
    699     {
    700         char info[128];
    701         xError *err;
    702         xGenericReply *rep;
    703         xEvent *ev;
    704 
    705         if (!who->replyBytesRemaining) {
    706             switch (buf[0]) {
    707             case X_Reply:
    708                 rep = (xGenericReply *) buf;
    709                 if (rep->sequenceNumber == who->sequence) {
    710                     snprintf(info, 127, "Xreply: type: 0x%x data: 0x%x "
    711                              "len: %i seq#: 0x%x", rep->type, rep->data1,
    712                              rep->length, rep->sequenceNumber);
    713                     multicount = TRUE;
    714                 }
    715                 break;
    716             case X_Error:
    717                 err = (xError *) buf;
    718                 snprintf(info, 127, "Xerror: Code: 0x%x resID: 0x%x maj: 0x%x "
    719                          "min: %x", err->errorCode, err->resourceID,
    720                          err->minorCode, err->majorCode);
    721                 break;
    722             default:
    723                 if ((buf[0] & 0x7f) == KeymapNotify)
    724                     snprintf(info, 127, "KeymapNotifyEvent: %i", buf[0]);
    725                 else {
    726                     ev = (xEvent *) buf;
    727                     snprintf(info, 127, "XEvent: type: 0x%x detail: 0x%x "
    728                              "seq#: 0x%x", ev->u.u.type, ev->u.u.detail,
    729                              ev->u.u.sequenceNumber);
    730                 }
    731             }
    732             ErrorF("REPLY: ClientIDX: %i %s\n", who->index, info);
    733         }
    734         else
    735             multicount = TRUE;
    736     }
    737 #endif
    738 
    739     if (!oco) {
    740         if ((oco = FreeOutputs)) {
    741             FreeOutputs = oco->next;
    742         }
    743         else if (!(oco = AllocateOutputBuffer())) {
    744             AbortClient(who);
    745             MarkClientException(who);
    746             return -1;
    747         }
    748         oc->output = oco;
    749     }
    750 
    751     padBytes = padding_for_int32(count);
    752 
    753     if (ReplyCallback) {
    754         ReplyInfoRec replyinfo;
    755 
    756         replyinfo.client = who;
    757         replyinfo.replyData = buf;
    758         replyinfo.dataLenBytes = count + padBytes;
    759         replyinfo.padBytes = padBytes;
    760         if (who->replyBytesRemaining) { /* still sending data of an earlier reply */
    761             who->replyBytesRemaining -= count + padBytes;
    762             replyinfo.startOfReply = FALSE;
    763             replyinfo.bytesRemaining = who->replyBytesRemaining;
    764             CallCallbacks((&ReplyCallback), (void *) &replyinfo);
    765         }
    766         else if (who->clientState == ClientStateRunning && buf[0] == X_Reply) { /* start of new reply */
    767             CARD32 replylen;
    768             unsigned long bytesleft;
    769 
    770             replylen = ((const xGenericReply *) buf)->length;
    771             if (who->swapped)
    772                 swapl(&replylen);
    773             bytesleft = (replylen * 4) + SIZEOF(xReply) - count - padBytes;
    774             replyinfo.startOfReply = TRUE;
    775             replyinfo.bytesRemaining = who->replyBytesRemaining = bytesleft;
    776             CallCallbacks((&ReplyCallback), (void *) &replyinfo);
    777         }
    778     }
    779 #ifdef DEBUG_COMMUNICATION
    780     else if (multicount) {
    781         if (who->replyBytesRemaining) {
    782             who->replyBytesRemaining -= (count + padBytes);
    783         }
    784         else {
    785             CARD32 replylen;
    786 
    787             replylen = ((xGenericReply *) buf)->length;
    788             who->replyBytesRemaining =
    789                 (replylen * 4) + SIZEOF(xReply) - count - padBytes;
    790         }
    791     }
    792 #endif
    793     if (oco->count == 0 || oco->count + count + padBytes > oco->size) {
    794         output_pending_clear(who);
    795         if (!any_output_pending()) {
    796             CriticalOutputPending = FALSE;
    797             NewOutputPending = FALSE;
    798         }
    799 
    800         return FlushClient(who, oc, buf, count);
    801     }
    802 
    803     NewOutputPending = TRUE;
    804     output_pending_mark(who);
    805     memmove((char *) oco->buf + oco->count, buf, count);
    806     oco->count += count;
    807     if (padBytes) {
    808         memset(oco->buf + oco->count, '\0', padBytes);
    809         oco->count += padBytes;
    810     }
    811     return count;
    812 }
    813 
    814  /********************
    815  * FlushClient()
    816  *    If the client isn't keeping up with us, then we try to continue
    817  *    buffering the data and set the appropriate bit in ClientsWritable
    818  *    (which is used by WaitFor in the select).  If the connection yields
    819  *    a permanent error, or we can't allocate any more space, we then
    820  *    close the connection.
    821  *
    822  **********************/
    823 
    824 int
    825 FlushClient(ClientPtr who, OsCommPtr oc, const void *__extraBuf, int extraCount)
    826 {
    827     ConnectionOutputPtr oco = oc->output;
    828     XtransConnInfo trans_conn = oc->trans_conn;
    829     struct iovec iov[3];
    830     static char padBuffer[3];
    831     const char *extraBuf = __extraBuf;
    832     long written;
    833     long padsize;
    834     long notWritten;
    835     long todo;
    836 
    837     if (!oco)
    838 	return 0;
    839     written = 0;
    840     padsize = padding_for_int32(extraCount);
    841     notWritten = oco->count + extraCount + padsize;
    842     if (!notWritten)
    843         return 0;
    844 
    845     if (FlushCallback)
    846         CallCallbacks(&FlushCallback, who);
    847 
    848     todo = notWritten;
    849     while (notWritten) {
    850         long before = written;  /* amount of whole thing written */
    851         long remain = todo;     /* amount to try this time, <= notWritten */
    852         int i = 0;
    853         long len;
    854 
    855         /* You could be very general here and have "in" and "out" iovecs
    856          * and write a loop without using a macro, but what the heck.  This
    857          * translates to:
    858          *
    859          *     how much of this piece is new?
    860          *     if more new then we are trying this time, clamp
    861          *     if nothing new
    862          *         then bump down amount already written, for next piece
    863          *         else put new stuff in iovec, will need all of next piece
    864          *
    865          * Note that todo had better be at least 1 or else we'll end up
    866          * writing 0 iovecs.
    867          */
    868 #define InsertIOV(pointer, length) \
    869 	len = (length) - before; \
    870 	if (len > remain) \
    871 	    len = remain; \
    872 	if (len <= 0) { \
    873 	    before = (-len); \
    874 	} else { \
    875 	    iov[i].iov_len = len; \
    876 	    iov[i].iov_base = (pointer) + before;	\
    877 	    i++; \
    878 	    remain -= len; \
    879 	    before = 0; \
    880 	}
    881 
    882         InsertIOV((char *) oco->buf, oco->count)
    883             InsertIOV((char *) extraBuf, extraCount)
    884             InsertIOV(padBuffer, padsize)
    885 
    886             errno = 0;
    887         if (trans_conn && (len = _XSERVTransWritev(trans_conn, iov, i)) >= 0) {
    888             written += len;
    889             notWritten -= len;
    890             todo = notWritten;
    891         }
    892         else if (ETEST(errno)
    893 #ifdef SUNSYSV                  /* check for another brain-damaged OS bug */
    894                  || (errno == 0)
    895 #endif
    896 #ifdef EMSGSIZE                 /* check for another brain-damaged OS bug */
    897                  || ((errno == EMSGSIZE) && (todo == 1))
    898 #endif
    899             ) {
    900             /* If we've arrived here, then the client is stuffed to the gills
    901                and not ready to accept more.  Make a note of it and buffer
    902                the rest. */
    903             output_pending_mark(who);
    904 
    905             if (written < oco->count) {
    906                 if (written > 0) {
    907                     oco->count -= written;
    908                     memmove((char *) oco->buf,
    909                             (char *) oco->buf + written, oco->count);
    910                     written = 0;
    911                 }
    912             }
    913             else {
    914                 written -= oco->count;
    915                 oco->count = 0;
    916             }
    917 
    918             if (notWritten > oco->size) {
    919                 unsigned char *obuf = NULL;
    920 
    921                 if (notWritten + BUFSIZE <= INT_MAX) {
    922                     obuf = realloc(oco->buf, notWritten + BUFSIZE);
    923                 }
    924                 if (!obuf) {
    925                     AbortClient(who);
    926                     MarkClientException(who);
    927                     oco->count = 0;
    928                     return -1;
    929                 }
    930                 oco->size = notWritten + BUFSIZE;
    931                 oco->buf = obuf;
    932             }
    933 
    934             /* If the amount written extended into the padBuffer, then the
    935                difference "extraCount - written" may be less than 0 */
    936             if ((len = extraCount - written) > 0)
    937                 memmove((char *) oco->buf + oco->count,
    938                         extraBuf + written, len);
    939 
    940             oco->count = notWritten;    /* this will include the pad */
    941             ospoll_listen(server_poll, oc->fd, X_NOTIFY_WRITE);
    942 
    943             /* return only the amount explicitly requested */
    944             return extraCount;
    945         }
    946 #ifdef EMSGSIZE                 /* check for another brain-damaged OS bug */
    947         else if (errno == EMSGSIZE) {
    948             todo >>= 1;
    949         }
    950 #endif
    951         else {
    952             AbortClient(who);
    953             MarkClientException(who);
    954             oco->count = 0;
    955             return -1;
    956         }
    957     }
    958 
    959     /* everything was flushed out */
    960     oco->count = 0;
    961     output_pending_clear(who);
    962 
    963     if (oco->size > BUFWATERMARK) {
    964         free(oco->buf);
    965         free(oco);
    966     }
    967     else {
    968         oco->next = FreeOutputs;
    969         FreeOutputs = oco;
    970     }
    971     oc->output = (ConnectionOutputPtr) NULL;
    972     return extraCount;          /* return only the amount explicitly requested */
    973 }
    974 
    975 static ConnectionInputPtr
    976 AllocateInputBuffer(void)
    977 {
    978     ConnectionInputPtr oci;
    979 
    980     oci = malloc(sizeof(ConnectionInput));
    981     if (!oci)
    982         return NULL;
    983     oci->buffer = malloc(BUFSIZE);
    984     if (!oci->buffer) {
    985         free(oci);
    986         return NULL;
    987     }
    988     oci->size = BUFSIZE;
    989     oci->bufptr = oci->buffer;
    990     oci->bufcnt = 0;
    991     oci->lenLastReq = 0;
    992     oci->ignoreBytes = 0;
    993     return oci;
    994 }
    995 
    996 static ConnectionOutputPtr
    997 AllocateOutputBuffer(void)
    998 {
    999     ConnectionOutputPtr oco;
   1000 
   1001     oco = malloc(sizeof(ConnectionOutput));
   1002     if (!oco)
   1003         return NULL;
   1004     oco->buf = calloc(1, BUFSIZE);
   1005     if (!oco->buf) {
   1006         free(oco);
   1007         return NULL;
   1008     }
   1009     oco->size = BUFSIZE;
   1010     oco->count = 0;
   1011     return oco;
   1012 }
   1013 
   1014 void
   1015 FreeOsBuffers(OsCommPtr oc)
   1016 {
   1017     ConnectionInputPtr oci;
   1018     ConnectionOutputPtr oco;
   1019 
   1020     if (AvailableInput == oc)
   1021         AvailableInput = (OsCommPtr) NULL;
   1022     if ((oci = oc->input)) {
   1023         if (FreeInputs) {
   1024             free(oci->buffer);
   1025             free(oci);
   1026         }
   1027         else {
   1028             FreeInputs = oci;
   1029             oci->next = (ConnectionInputPtr) NULL;
   1030             oci->bufptr = oci->buffer;
   1031             oci->bufcnt = 0;
   1032             oci->lenLastReq = 0;
   1033             oci->ignoreBytes = 0;
   1034         }
   1035     }
   1036     if ((oco = oc->output)) {
   1037         if (FreeOutputs) {
   1038             free(oco->buf);
   1039             free(oco);
   1040         }
   1041         else {
   1042             FreeOutputs = oco;
   1043             oco->next = (ConnectionOutputPtr) NULL;
   1044             oco->count = 0;
   1045         }
   1046     }
   1047 }
   1048 
   1049 void
   1050 ResetOsBuffers(void)
   1051 {
   1052     ConnectionInputPtr oci;
   1053     ConnectionOutputPtr oco;
   1054 
   1055     while ((oci = FreeInputs)) {
   1056         FreeInputs = oci->next;
   1057         free(oci->buffer);
   1058         free(oci);
   1059     }
   1060     while ((oco = FreeOutputs)) {
   1061         FreeOutputs = oco->next;
   1062         free(oco->buf);
   1063         free(oco);
   1064     }
   1065 }