ci: Check for DDXen to be built
[xserver.git] / os / io.c
blobb0c314a4de55d0dc63fd7ff053eae2b843c8faf2
1 /***********************************************************
3 Copyright 1987, 1989, 1998 The Open Group
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.
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
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.
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.
25 Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
27 All Rights Reserved
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.
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.
45 ******************************************************************/
46 /*****************************************************************
47 * i/o functions
49 * WriteToClient, ReadRequestFromClient
50 * InsertFakeRequest, ResetCurrentRequest
52 *****************************************************************/
54 #include <dix-config.h>
56 #undef DEBUG_COMMUNICATION
58 #include "dixstruct_priv.h"
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>
76 #include "dix/dix_priv.h"
78 #include "os.h"
79 #include "osdep.h"
80 #include "opaque.h"
81 #include "dixstruct.h"
82 #include "misc.h"
84 CallbackListPtr ReplyCallback;
85 CallbackListPtr FlushCallback;
87 typedef struct _connectionInput {
88 struct _connectionInput *next;
89 char *buffer; /* contains current client input */
90 char *bufptr; /* pointer to current start of data */
91 int bufcnt; /* count of bytes in buffer */
92 int lenLastReq;
93 int size;
94 unsigned int ignoreBytes; /* bytes to ignore before the next request */
95 } ConnectionInput;
97 typedef struct _connectionOutput {
98 struct _connectionOutput *next;
99 unsigned char *buf;
100 int size;
101 int count;
102 } ConnectionOutput;
104 static ConnectionInputPtr AllocateInputBuffer(void);
105 static ConnectionOutputPtr AllocateOutputBuffer(void);
107 static Bool CriticalOutputPending;
108 static int timesThisConnection = 0;
109 static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL;
110 static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL;
111 static OsCommPtr AvailableInput = (OsCommPtr) NULL;
113 #define get_req_len(req,cli) ((cli)->swapped ? \
114 bswap_16((req)->length) : (req)->length)
116 #include <X11/extensions/bigreqsproto.h>
118 #define get_big_req_len(req,cli) ((cli)->swapped ? \
119 bswap_32(((xBigReq *)(req))->length) : \
120 ((xBigReq *)(req))->length)
122 #define BUFSIZE 16384
123 #define BUFWATERMARK 32768
126 * A lot of the code in this file manipulates a ConnectionInputPtr:
128 * -----------------------------------------------
129 * |------- bufcnt ------->| | |
130 * | |- gotnow ->| | |
131 * | |-------- needed ------>| |
132 * |-----------+--------- size --------+---------->|
133 * -----------------------------------------------
134 * ^ ^
135 * | |
136 * buffer bufptr
138 * buffer is a pointer to the start of the buffer.
139 * bufptr points to the start of the current request.
140 * bufcnt counts how many bytes are in the buffer.
141 * size is the size of the buffer in bytes.
143 * In several of the functions, gotnow and needed are local variables
144 * that do the following:
146 * gotnow is the number of bytes of the request that we're
147 * trying to read that are currently in the buffer.
148 * Typically, gotnow = (buffer + bufcnt) - bufptr
150 * needed = the length of the request that we're trying to
151 * read. Watch out: needed sometimes counts bytes and sometimes
152 * counts CARD32's.
155 /*****************************************************************
156 * ReadRequestFromClient
157 * Returns one request in client->requestBuffer. The request
158 * length will be in client->req_len. Return status is:
160 * > 0 if successful, specifies length in bytes of the request
161 * = 0 if entire request is not yet available
162 * < 0 if client should be terminated
164 * The request returned must be contiguous so that it can be
165 * cast in the dispatcher to the correct request type. Because requests
166 * are variable length, ReadRequestFromClient() must look at the first 4
167 * or 8 bytes of a request to determine the length (the request length is
168 * in the 3rd and 4th bytes of the request unless it is a Big Request
169 * (see the Big Request Extension), in which case the 3rd and 4th bytes
170 * are zero and the following 4 bytes are the request length.
172 * Note: in order to make the server scheduler (WaitForSomething())
173 * "fair", the ClientsWithInput mask is used. This mask tells which
174 * clients have FULL requests left in their buffers. Clients with
175 * partial requests require a read. Basically, client buffers
176 * are drained before select() is called again. But, we can't keep
177 * reading from a client that is sending buckets of data (or has
178 * a partial request) because others clients need to be scheduled.
179 *****************************************************************/
181 static void
182 YieldControl(void)
184 isItTimeToYield = TRUE;
185 timesThisConnection = 0;
188 static void
189 YieldControlNoInput(ClientPtr client)
191 OsCommPtr oc = client->osPrivate;
192 YieldControl();
193 if (oc->trans_conn)
194 ospoll_reset_events(server_poll, oc->fd);
197 static void
198 YieldControlDeath(void)
200 timesThisConnection = 0;
203 /* If an input buffer was empty, either free it if it is too big or link it
204 * into our list of free input buffers. This means that different clients can
205 * share the same input buffer (at different times). This was done to save
206 * memory.
208 static void
209 NextAvailableInput(OsCommPtr oc)
211 if (AvailableInput) {
212 if (AvailableInput != oc) {
213 ConnectionInputPtr aci = AvailableInput->input;
215 if (aci->size > BUFWATERMARK) {
216 free(aci->buffer);
217 free(aci);
219 else {
220 aci->next = FreeInputs;
221 FreeInputs = aci;
223 AvailableInput->input = NULL;
225 AvailableInput = NULL;
230 ReadRequestFromClient(ClientPtr client)
232 OsCommPtr oc = (OsCommPtr) client->osPrivate;
233 ConnectionInputPtr oci = oc->input;
234 unsigned int gotnow, needed;
235 int result;
236 register xReq *request;
237 Bool need_header;
238 Bool move_header;
240 NextAvailableInput(oc);
242 /* make sure we have an input buffer */
244 if (!oci) {
245 if ((oci = FreeInputs)) {
246 FreeInputs = oci->next;
248 else if (!(oci = AllocateInputBuffer())) {
249 YieldControlDeath();
250 return -1;
252 oc->input = oci;
255 #if XTRANS_SEND_FDS
256 /* Discard any unused file descriptors */
257 while (client->req_fds > 0) {
258 int req_fd = ReadFdFromClient(client);
259 if (req_fd >= 0)
260 close(req_fd);
262 #endif
263 /* advance to start of next request */
265 oci->bufptr += oci->lenLastReq;
267 need_header = FALSE;
268 move_header = FALSE;
269 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
271 if (oci->ignoreBytes > 0) {
272 if (oci->ignoreBytes > oci->size)
273 needed = oci->size;
274 else
275 needed = oci->ignoreBytes;
277 else if (gotnow < sizeof(xReq)) {
278 /* We don't have an entire xReq yet. Can't tell how big
279 * the request will be until we get the whole xReq.
281 needed = sizeof(xReq);
282 need_header = TRUE;
284 else {
285 /* We have a whole xReq. We can tell how big the whole
286 * request will be unless it is a Big Request.
288 request = (xReq *) oci->bufptr;
289 needed = get_req_len(request, client);
290 if (!needed && client->big_requests) {
291 /* It's a Big Request. */
292 move_header = TRUE;
293 if (gotnow < sizeof(xBigReq)) {
294 /* Still need more data to tell just how big. */
295 needed = bytes_to_int32(sizeof(xBigReq)); /* needed is in CARD32s now */
296 need_header = TRUE;
298 else
299 needed = get_big_req_len(request, client);
301 client->req_len = needed;
302 needed <<= 2; /* needed is in bytes now */
304 if (gotnow < needed) {
305 /* Need to read more data, either so that we can get a
306 * complete xReq (if need_header is TRUE), a complete
307 * xBigReq (if move_header is TRUE), or the rest of the
308 * request (if need_header and move_header are both FALSE).
311 oci->lenLastReq = 0;
312 if (needed > maxBigRequestSize << 2) {
313 /* request is too big for us to handle */
315 * Mark the rest of it as needing to be ignored, and then return
316 * the full size. Dispatch() will turn it into a BadLength error.
318 oci->ignoreBytes = needed - gotnow;
319 oci->lenLastReq = gotnow;
320 return needed;
322 if ((gotnow == 0) || ((oci->bufptr - oci->buffer + needed) > oci->size)) {
323 /* no data, or the request is too big to fit in the buffer */
325 if ((gotnow > 0) && (oci->bufptr != oci->buffer))
326 /* save the data we've already read */
327 memmove(oci->buffer, oci->bufptr, gotnow);
328 if (needed > oci->size) {
329 /* make buffer bigger to accommodate request */
330 char *ibuf;
332 ibuf = (char *) realloc(oci->buffer, needed);
333 if (!ibuf) {
334 YieldControlDeath();
335 return -1;
337 oci->size = needed;
338 oci->buffer = ibuf;
340 oci->bufptr = oci->buffer;
341 oci->bufcnt = gotnow;
343 /* XXX this is a workaround. This function is sometimes called
344 * after the trans_conn has been freed. In this case trans_conn
345 * will be null. Really ought to restructure things so that we
346 * never get here in those circumstances.
348 if (!oc->trans_conn) {
349 /* treat as if an error occurred on the read, which is what
350 * used to happen
352 YieldControlDeath();
353 return -1;
355 result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt,
356 oci->size - oci->bufcnt);
357 if (result <= 0) {
358 if ((result < 0) && ETEST(errno)) {
359 mark_client_not_ready(client);
360 YieldControlNoInput(client);
361 return 0;
363 YieldControlDeath();
364 return -1;
366 oci->bufcnt += result;
367 gotnow += result;
368 /* free up some space after huge requests */
369 if ((oci->size > BUFWATERMARK) &&
370 (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
371 char *ibuf;
373 ibuf = (char *) realloc(oci->buffer, BUFSIZE);
374 if (ibuf) {
375 oci->size = BUFSIZE;
376 oci->buffer = ibuf;
377 oci->bufptr = ibuf + oci->bufcnt - gotnow;
380 if (need_header && gotnow >= needed) {
381 /* We wanted an xReq, now we've gotten it. */
382 request = (xReq *) oci->bufptr;
383 needed = get_req_len(request, client);
384 if (!needed && client->big_requests) {
385 move_header = TRUE;
386 if (gotnow < sizeof(xBigReq))
387 needed = bytes_to_int32(sizeof(xBigReq));
388 else
389 needed = get_big_req_len(request, client);
391 client->req_len = needed;
392 needed <<= 2;
394 if (gotnow < needed) {
395 /* Still don't have enough; punt. */
396 YieldControlNoInput(client);
397 return 0;
400 if (needed == 0) {
401 if (client->big_requests)
402 needed = sizeof(xBigReq);
403 else
404 needed = sizeof(xReq);
407 /* If there are bytes to ignore, ignore them now. */
409 if (oci->ignoreBytes > 0) {
410 assert(needed == oci->ignoreBytes || needed == oci->size);
412 * The _XSERVTransRead call above may return more or fewer bytes than we
413 * want to ignore. Ignore the smaller of the two sizes.
415 if (gotnow < needed) {
416 oci->ignoreBytes -= gotnow;
417 oci->bufptr += gotnow;
418 gotnow = 0;
420 else {
421 oci->ignoreBytes -= needed;
422 oci->bufptr += needed;
423 gotnow -= needed;
425 needed = 0;
428 oci->lenLastReq = needed;
431 * Check to see if client has at least one whole request in the
432 * buffer beyond the request we're returning to the caller.
433 * If there is only a partial request, treat like buffer
434 * is empty so that select() will be called again and other clients
435 * can get into the queue.
438 gotnow -= needed;
439 if (!gotnow)
440 AvailableInput = oc;
441 if (move_header) {
442 if (client->req_len < bytes_to_int32(sizeof(xBigReq) - sizeof(xReq))) {
443 YieldControlDeath();
444 return -1;
447 request = (xReq *) oci->bufptr;
448 oci->bufptr += (sizeof(xBigReq) - sizeof(xReq));
449 *(xReq *) oci->bufptr = *request;
450 oci->lenLastReq -= (sizeof(xBigReq) - sizeof(xReq));
451 client->req_len -= bytes_to_int32(sizeof(xBigReq) - sizeof(xReq));
453 client->requestBuffer = (void *) oci->bufptr;
454 #ifdef DEBUG_COMMUNICATION
456 xReq *req = client->requestBuffer;
458 ErrorF("REQUEST: ClientIDX: %i, type: 0x%x data: 0x%x len: %i\n",
459 client->index, req->reqType, req->data, req->length);
461 #endif
462 return needed;
466 ReadFdFromClient(ClientPtr client)
468 int fd = -1;
470 #if XTRANS_SEND_FDS
471 if (client->req_fds > 0) {
472 OsCommPtr oc = (OsCommPtr) client->osPrivate;
474 --client->req_fds;
475 fd = _XSERVTransRecvFd(oc->trans_conn);
476 } else
477 LogMessage(X_ERROR, "Request asks for FD without setting req_fds\n");
478 #endif
480 return fd;
484 WriteFdToClient(ClientPtr client, int fd, Bool do_close)
486 #if XTRANS_SEND_FDS
487 OsCommPtr oc = (OsCommPtr) client->osPrivate;
489 return _XSERVTransSendFd(oc->trans_conn, fd, do_close);
490 #else
491 return -1;
492 #endif
495 /*****************************************************************
496 * InsertFakeRequest
497 * Splice a consed up (possibly partial) request in as the next request.
499 **********************/
501 Bool
502 InsertFakeRequest(ClientPtr client, char *data, int count)
504 OsCommPtr oc = (OsCommPtr) client->osPrivate;
505 ConnectionInputPtr oci = oc->input;
506 int gotnow, moveup;
508 NextAvailableInput(oc);
510 if (!oci) {
511 if ((oci = FreeInputs))
512 FreeInputs = oci->next;
513 else if (!(oci = AllocateInputBuffer()))
514 return FALSE;
515 oc->input = oci;
517 oci->bufptr += oci->lenLastReq;
518 oci->lenLastReq = 0;
519 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
520 if ((gotnow + count) > oci->size) {
521 char *ibuf;
523 ibuf = (char *) realloc(oci->buffer, gotnow + count);
524 if (!ibuf)
525 return FALSE;
526 oci->size = gotnow + count;
527 oci->buffer = ibuf;
528 oci->bufptr = ibuf + oci->bufcnt - gotnow;
530 moveup = count - (oci->bufptr - oci->buffer);
531 if (moveup > 0) {
532 if (gotnow > 0)
533 memmove(oci->bufptr + moveup, oci->bufptr, gotnow);
534 oci->bufptr += moveup;
535 oci->bufcnt += moveup;
537 memmove(oci->bufptr - count, data, count);
538 oci->bufptr -= count;
539 gotnow += count;
540 if ((gotnow >= sizeof(xReq)) &&
541 (gotnow >= (int) (get_req_len((xReq *) oci->bufptr, client) << 2)))
542 mark_client_ready(client);
543 else
544 YieldControlNoInput(client);
545 return TRUE;
548 /*****************************************************************
549 * ResetRequestFromClient
550 * Reset to reexecute the current request, and yield.
552 **********************/
554 void
555 ResetCurrentRequest(ClientPtr client)
557 OsCommPtr oc = (OsCommPtr) client->osPrivate;
559 /* ignore dying clients */
560 if (!oc)
561 return;
563 register ConnectionInputPtr oci = oc->input;
564 register xReq *request;
565 int gotnow, needed;
567 if (AvailableInput == oc)
568 AvailableInput = (OsCommPtr) NULL;
569 oci->lenLastReq = 0;
570 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
571 if (gotnow < sizeof(xReq)) {
572 YieldControlNoInput(client);
574 else {
575 request = (xReq *) oci->bufptr;
576 needed = get_req_len(request, client);
577 if (!needed && client->big_requests) {
578 oci->bufptr -= sizeof(xBigReq) - sizeof(xReq);
579 *(xReq *) oci->bufptr = *request;
580 ((xBigReq *) oci->bufptr)->length = client->req_len;
581 if (client->swapped) {
582 swapl(&((xBigReq *) oci->bufptr)->length);
585 if (gotnow >= (needed << 2)) {
586 if (listen_to_client(client))
587 mark_client_ready(client);
588 YieldControl();
590 else
591 YieldControlNoInput(client);
595 /********************
596 * FlushAllOutput()
597 * Flush all clients with output. However, if some client still
598 * has input in the queue (more requests), then don't flush. This
599 * will prevent the output queue from being flushed every time around
600 * the round robin queue. Now, some say that it SHOULD be flushed
601 * every time around, but...
603 **********************/
605 void
606 FlushAllOutput(void)
608 OsCommPtr oc;
609 register ClientPtr client, tmp;
610 Bool newoutput = NewOutputPending;
612 if (!newoutput)
613 return;
616 * It may be that some client still has critical output pending,
617 * but he is not yet ready to receive it anyway, so we will
618 * simply wait for the select to tell us when he's ready to receive.
620 CriticalOutputPending = FALSE;
621 NewOutputPending = FALSE;
623 xorg_list_for_each_entry_safe(client, tmp, &output_pending_clients, output_pending) {
624 if (client->clientGone)
625 continue;
626 if (!client_is_ready(client)) {
627 oc = (OsCommPtr) client->osPrivate;
628 (void) FlushClient(client, oc, (char *) NULL, 0);
629 } else
630 NewOutputPending = TRUE;
634 void
635 FlushIfCriticalOutputPending(void)
637 if (CriticalOutputPending)
638 FlushAllOutput();
641 void
642 SetCriticalOutputPending(void)
644 CriticalOutputPending = TRUE;
647 /*****************
648 * AbortClient:
649 * When a write error occurs to a client, close
650 * the connection and clean things up. Mark
651 * the client as 'ready' so that the server will
652 * try to read from it again, notice that the fd is
653 * closed and clean up from there.
654 *****************/
656 static void
657 AbortClient(ClientPtr client)
659 OsCommPtr oc = client->osPrivate;
661 if (oc->trans_conn) {
662 CloseDownFileDescriptor(oc);
663 mark_client_ready(client);
667 /*****************
668 * WriteToClient
669 * Copies buf into ClientPtr.buf if it fits (with padding), else
670 * flushes ClientPtr.buf and buf to client. As of this writing,
671 * every use of WriteToClient is cast to void, and the result
672 * is ignored. Potentially, this could be used by requests
673 * that are sending several chunks of data and want to break
674 * out of a loop on error. Thus, we will leave the type of
675 * this routine as int.
676 *****************/
679 WriteToClient(ClientPtr who, int count, const void *__buf)
681 OsCommPtr oc;
682 ConnectionOutputPtr oco;
683 int padBytes;
684 const char *buf = __buf;
686 BUG_RETURN_VAL_MSG(in_input_thread(), 0,
687 "******** %s called from input thread *********\n", __func__);
689 #ifdef DEBUG_COMMUNICATION
690 Bool multicount = FALSE;
691 #endif
692 if (!count || !who || who == serverClient || who->clientGone)
693 return 0;
694 oc = who->osPrivate;
695 oco = oc->output;
696 #ifdef DEBUG_COMMUNICATION
698 char info[128];
699 xError *err;
700 xGenericReply *rep;
701 xEvent *ev;
703 if (!who->replyBytesRemaining) {
704 switch (buf[0]) {
705 case X_Reply:
706 rep = (xGenericReply *) buf;
707 if (rep->sequenceNumber == who->sequence) {
708 snprintf(info, 127, "Xreply: type: 0x%x data: 0x%x "
709 "len: %i seq#: 0x%x", rep->type, rep->data1,
710 rep->length, rep->sequenceNumber);
711 multicount = TRUE;
713 break;
714 case X_Error:
715 err = (xError *) buf;
716 snprintf(info, 127, "Xerror: Code: 0x%x resID: 0x%x maj: 0x%x "
717 "min: %x", err->errorCode, err->resourceID,
718 err->minorCode, err->majorCode);
719 break;
720 default:
721 if ((buf[0] & 0x7f) == KeymapNotify)
722 snprintf(info, 127, "KeymapNotifyEvent: %i", buf[0]);
723 else {
724 ev = (xEvent *) buf;
725 snprintf(info, 127, "XEvent: type: 0x%x detail: 0x%x "
726 "seq#: 0x%x", ev->u.u.type, ev->u.u.detail,
727 ev->u.u.sequenceNumber);
730 ErrorF("REPLY: ClientIDX: %i %s\n", who->index, info);
732 else
733 multicount = TRUE;
735 #endif
737 if (!oco) {
738 if ((oco = FreeOutputs)) {
739 FreeOutputs = oco->next;
741 else if (!(oco = AllocateOutputBuffer())) {
742 AbortClient(who);
743 MarkClientException(who);
744 return -1;
746 oc->output = oco;
749 padBytes = padding_for_int32(count);
751 if (ReplyCallback) {
752 ReplyInfoRec replyinfo;
754 replyinfo.client = who;
755 replyinfo.replyData = buf;
756 replyinfo.dataLenBytes = count + padBytes;
757 replyinfo.padBytes = padBytes;
758 if (who->replyBytesRemaining) { /* still sending data of an earlier reply */
759 who->replyBytesRemaining -= count + padBytes;
760 replyinfo.startOfReply = FALSE;
761 replyinfo.bytesRemaining = who->replyBytesRemaining;
762 CallCallbacks((&ReplyCallback), (void *) &replyinfo);
764 else if (who->clientState == ClientStateRunning && buf[0] == X_Reply) { /* start of new reply */
765 CARD32 replylen;
766 unsigned long bytesleft;
768 replylen = ((const xGenericReply *) buf)->length;
769 if (who->swapped)
770 swapl(&replylen);
771 bytesleft = (replylen * 4) + SIZEOF(xReply) - count - padBytes;
772 replyinfo.startOfReply = TRUE;
773 replyinfo.bytesRemaining = who->replyBytesRemaining = bytesleft;
774 CallCallbacks((&ReplyCallback), (void *) &replyinfo);
777 #ifdef DEBUG_COMMUNICATION
778 else if (multicount) {
779 if (who->replyBytesRemaining) {
780 who->replyBytesRemaining -= (count + padBytes);
782 else {
783 CARD32 replylen;
785 replylen = ((xGenericReply *) buf)->length;
786 who->replyBytesRemaining =
787 (replylen * 4) + SIZEOF(xReply) - count - padBytes;
790 #endif
791 if ((oco->count == 0 && who->local) || oco->count + count + padBytes > oco->size) {
792 output_pending_clear(who);
793 if (!any_output_pending()) {
794 CriticalOutputPending = FALSE;
795 NewOutputPending = FALSE;
798 return FlushClient(who, oc, buf, count);
801 NewOutputPending = TRUE;
802 output_pending_mark(who);
803 memmove((char *) oco->buf + oco->count, buf, count);
804 oco->count += count;
805 if (padBytes) {
806 memset(oco->buf + oco->count, '\0', padBytes);
807 oco->count += padBytes;
809 return count;
812 /********************
813 * FlushClient()
814 * If the client isn't keeping up with us, then we try to continue
815 * buffering the data and set the appropriate bit in ClientsWritable
816 * (which is used by WaitFor in the select). If the connection yields
817 * a permanent error, or we can't allocate any more space, we then
818 * close the connection.
820 **********************/
823 FlushClient(ClientPtr who, OsCommPtr oc, const void *__extraBuf, int extraCount)
825 ConnectionOutputPtr oco = oc->output;
826 XtransConnInfo trans_conn = oc->trans_conn;
827 struct iovec iov[3];
828 static char padBuffer[3];
829 const char *extraBuf = __extraBuf;
830 long written;
831 long padsize;
832 long notWritten;
833 long todo;
835 if (!oco)
836 return 0;
837 written = 0;
838 padsize = padding_for_int32(extraCount);
839 notWritten = oco->count + extraCount + padsize;
840 if (!notWritten)
841 return 0;
843 if (FlushCallback)
844 CallCallbacks(&FlushCallback, who);
846 todo = notWritten;
847 while (notWritten) {
848 long before = written; /* amount of whole thing written */
849 long remain = todo; /* amount to try this time, <= notWritten */
850 int i = 0;
851 long len;
853 /* You could be very general here and have "in" and "out" iovecs
854 * and write a loop without using a macro, but what the heck. This
855 * translates to:
857 * how much of this piece is new?
858 * if more new then we are trying this time, clamp
859 * if nothing new
860 * then bump down amount already written, for next piece
861 * else put new stuff in iovec, will need all of next piece
863 * Note that todo had better be at least 1 or else we'll end up
864 * writing 0 iovecs.
866 #define InsertIOV(pointer, length) \
867 len = (length) - before; \
868 if (len > remain) \
869 len = remain; \
870 if (len <= 0) { \
871 before = (-len); \
872 } else { \
873 iov[i].iov_len = len; \
874 iov[i].iov_base = (pointer) + before; \
875 i++; \
876 remain -= len; \
877 before = 0; \
880 InsertIOV((char *) oco->buf, oco->count)
881 InsertIOV((char *) extraBuf, extraCount)
882 InsertIOV(padBuffer, padsize)
884 errno = 0;
885 if (trans_conn && (len = _XSERVTransWritev(trans_conn, iov, i)) >= 0) {
886 written += len;
887 notWritten -= len;
888 todo = notWritten;
890 else if (ETEST(errno)
891 #ifdef EMSGSIZE /* check for another brain-damaged OS bug */
892 || ((errno == EMSGSIZE) && (todo == 1))
893 #endif
895 /* If we've arrived here, then the client is stuffed to the gills
896 and not ready to accept more. Make a note of it and buffer
897 the rest. */
898 output_pending_mark(who);
900 if (written < oco->count) {
901 if (written > 0) {
902 oco->count -= written;
903 memmove((char *) oco->buf,
904 (char *) oco->buf + written, oco->count);
905 written = 0;
908 else {
909 written -= oco->count;
910 oco->count = 0;
913 if (notWritten > oco->size) {
914 unsigned char *obuf = NULL;
916 if (notWritten + BUFSIZE <= INT_MAX) {
917 obuf = realloc(oco->buf, notWritten + BUFSIZE);
919 if (!obuf) {
920 AbortClient(who);
921 MarkClientException(who);
922 oco->count = 0;
923 return -1;
925 oco->size = notWritten + BUFSIZE;
926 oco->buf = obuf;
929 /* If the amount written extended into the padBuffer, then the
930 difference "extraCount - written" may be less than 0 */
931 if ((len = extraCount - written) > 0)
932 memmove((char *) oco->buf + oco->count,
933 extraBuf + written, len);
935 oco->count = notWritten; /* this will include the pad */
936 ospoll_listen(server_poll, oc->fd, X_NOTIFY_WRITE);
938 /* return only the amount explicitly requested */
939 return extraCount;
941 #ifdef EMSGSIZE /* check for another brain-damaged OS bug */
942 else if (errno == EMSGSIZE) {
943 todo >>= 1;
945 #endif
946 else {
947 AbortClient(who);
948 MarkClientException(who);
949 oco->count = 0;
950 return -1;
954 /* everything was flushed out */
955 oco->count = 0;
956 output_pending_clear(who);
958 if (oco->size > BUFWATERMARK) {
959 free(oco->buf);
960 free(oco);
962 else {
963 oco->next = FreeOutputs;
964 FreeOutputs = oco;
966 oc->output = (ConnectionOutputPtr) NULL;
967 return extraCount; /* return only the amount explicitly requested */
970 static ConnectionInputPtr
971 AllocateInputBuffer(void)
973 ConnectionInputPtr oci;
975 oci = malloc(sizeof(ConnectionInput));
976 if (!oci)
977 return NULL;
978 oci->buffer = malloc(BUFSIZE);
979 if (!oci->buffer) {
980 free(oci);
981 return NULL;
983 oci->size = BUFSIZE;
984 oci->bufptr = oci->buffer;
985 oci->bufcnt = 0;
986 oci->lenLastReq = 0;
987 oci->ignoreBytes = 0;
988 return oci;
991 static ConnectionOutputPtr
992 AllocateOutputBuffer(void)
994 ConnectionOutputPtr oco;
996 oco = malloc(sizeof(ConnectionOutput));
997 if (!oco)
998 return NULL;
999 oco->buf = calloc(1, BUFSIZE);
1000 if (!oco->buf) {
1001 free(oco);
1002 return NULL;
1004 oco->size = BUFSIZE;
1005 oco->count = 0;
1006 return oco;
1009 void
1010 FreeOsBuffers(OsCommPtr oc)
1012 ConnectionInputPtr oci;
1013 ConnectionOutputPtr oco;
1015 if (AvailableInput == oc)
1016 AvailableInput = (OsCommPtr) NULL;
1017 if ((oci = oc->input)) {
1018 if (FreeInputs) {
1019 free(oci->buffer);
1020 free(oci);
1022 else {
1023 FreeInputs = oci;
1024 oci->next = (ConnectionInputPtr) NULL;
1025 oci->bufptr = oci->buffer;
1026 oci->bufcnt = 0;
1027 oci->lenLastReq = 0;
1028 oci->ignoreBytes = 0;
1031 if ((oco = oc->output)) {
1032 if (FreeOutputs) {
1033 free(oco->buf);
1034 free(oco);
1036 else {
1037 FreeOutputs = oco;
1038 oco->next = (ConnectionOutputPtr) NULL;
1039 oco->count = 0;
1044 void
1045 ResetOsBuffers(void)
1047 ConnectionInputPtr oci;
1048 ConnectionOutputPtr oco;
1050 while ((oci = FreeInputs)) {
1051 FreeInputs = oci->next;
1052 free(oci->buffer);
1053 free(oci);
1055 while ((oco = FreeOutputs)) {
1056 FreeOutputs = oco->next;
1057 free(oco->buf);
1058 free(oco);