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