io.c revision bbe1b32b
1/* $Xorg: io.c,v 1.5 2001/02/09 02:05:44 xorgcvs Exp $ */ 2/* 3 * i/o functions 4 */ 5/* 6 7Copyright 1990, 1991, 1998 The Open Group 8 9Permission to use, copy, modify, distribute, and sell this software and its 10documentation for any purpose is hereby granted without fee, provided that 11the above copyright notice appear in all copies and that both that 12copyright notice and this permission notice appear in supporting 13documentation. 14 15The above copyright notice and this permission notice shall be included in 16all copies or substantial portions of the Software. 17 18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25Except as contained in this notice, the name of The Open Group shall not be 26used in advertising or otherwise to promote the sale, use or other dealings 27in this Software without prior written authorization from The Open Group. 28 29 * Copyright 1990, 1991 Network Computing Devices; 30 * Portions Copyright 1987 by Digital Equipment Corporation 31 * 32 * Permission to use, copy, modify, distribute, and sell this software and 33 * its documentation for any purpose is hereby granted without fee, provided 34 * that the above copyright notice appear in all copies and that both that 35 * copyright notice and this permission notice appear in supporting 36 * documentation, and that the names of Network Computing Devices, or Digital 37 * not be used in advertising or publicity pertaining to distribution 38 * of the software without specific, written prior permission. 39 * 40 * NETWORK COMPUTING DEVICES, AND DIGITAL DISCLAIM ALL WARRANTIES WITH 41 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF 42 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES, 43 * OR DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 44 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 45 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 46 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 47 * THIS SOFTWARE. 48 */ 49/* $XFree86: xc/programs/xfs/os/io.c,v 3.18 2001/12/14 20:01:41 dawes Exp $ */ 50 51#include <X11/Xtrans/Xtrans.h> 52#include <stdio.h> 53#include <errno.h> 54#include <sys/types.h> 55#ifndef Lynx 56#include <sys/param.h> 57#ifndef __UNIXOS2__ 58#include <sys/uio.h> 59#endif 60#else 61#include <uio.h> 62#endif 63 64#include <X11/fonts/FSproto.h> 65#include "clientstr.h" 66#include "X11/Xpoll.h" 67#include "osdep.h" 68#include "globals.h" 69#include "dispatch.h" 70 71 72/* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX 73 * systems are broken and return EWOULDBLOCK when they should return EAGAIN 74 */ 75 76#if defined(EAGAIN) && defined(EWOULDBLOCK) 77#define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK) 78#else 79 80#ifdef EAGAIN 81#define ETEST(err) (err == EAGAIN) 82#else 83#define ETEST(err) (err == EWOULDBLOCK) 84#endif 85 86#endif 87 88 89extern fd_set ClientsWithInput; 90extern fd_set ClientsWriteBlocked; 91extern fd_set OutputPending; 92 93extern long OutputBufferSize; 94 95extern int ConnectionTranslation[]; 96 97extern Bool AnyClientsWriteBlocked; 98extern Bool NewOutputPending; 99 100static int timesThisConnection = 0; 101static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL; 102static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL; 103static OsCommPtr AvailableInput = (OsCommPtr) NULL; 104 105static ConnectionInputPtr AllocateInputBuffer(void); 106static ConnectionOutputPtr AllocateOutputBuffer(void); 107 108 109#define MAX_TIMES_PER 10 110 111#define yield_control() \ 112 { isItTimeToYield = TRUE; \ 113 timesThisConnection = 0; } 114 115#define yield_control_no_input() \ 116 { yield_control(); \ 117 FD_CLR(fd, &ClientsWithInput); } 118 119#define yield_control_death() \ 120 { timesThisConnection = 0; } 121 122#define request_length(req, client) \ 123 ((int)((client)->swapped ? lswaps((req)->length) : (req)->length) << 2) 124 125int 126ReadRequest(ClientPtr client) 127{ 128 OsCommPtr oc; 129 ConnectionInputPtr oci; 130 fsReq *request; 131 int fd, 132 result, 133 gotnow, 134 needed = 0; 135 136 if (client == NULL) 137 return -1; 138 oc = (OsCommPtr) client->osPrivate; 139 if (oc == NULL) 140 return -1; 141 oci = oc->input; 142 fd = oc->fd; 143 if (oci != NULL && fd < 0) 144 return -1; 145 146 if (AvailableInput) { 147 if (AvailableInput != oc) { 148 ConnectionInputPtr aci = AvailableInput->input; 149 150 if (aci->size > BUFWATERMARK) { 151 fsfree(aci->buffer); 152 fsfree(aci); 153 } else { 154 aci->next = FreeInputs; 155 FreeInputs = aci; 156 } 157 AvailableInput->input = (ConnectionInputPtr) NULL; 158 } 159 AvailableInput = (OsCommPtr) NULL; 160 } 161 if (!oci) { 162 if ((oci = FreeInputs ) != (ConnectionInputPtr) 0) { 163 FreeInputs = oci->next; 164 } else if (!(oci = AllocateInputBuffer())) { 165 yield_control_death(); 166 return -1; 167 } 168 oc->input = oci; 169 } 170 oci->bufptr += oci->lenLastReq; 171 172 gotnow = oci->bufcnt + oci->buffer - oci->bufptr; 173 174#ifdef WORD64 175 /* need 8-byte alignment */ 176 if ((oci->bufptr - oci->buffer) & 7 && gotnow > 0) 177 { 178 memmove( oci->buffer, oci->bufptr, gotnow); 179 oci->bufptr = oci->buffer; 180 oci->bufcnt = gotnow; 181 } 182#endif 183 184 request = (fsReq *) oci->bufptr; 185 186 /* not enough for a request */ 187 if ((gotnow < SIZEOF(fsReq)) || 188 (gotnow < (needed = request_length(request, client)))) { 189 oci->lenLastReq = 0; 190 if ((gotnow < SIZEOF(fsReq)) || needed == 0) 191 needed = SIZEOF(fsReq); 192 else if (needed > MAXBUFSIZE) { 193 yield_control_death(); 194 return -1; 195 } 196 /* see if we need to shift up a partial request so the rest can fit */ 197 if ((gotnow == 0) || 198 ((oci->bufptr - oci->buffer + needed) > oci->size)) 199 { 200 if ((gotnow > 0) && (oci->bufptr != oci->buffer)) 201 memmove( oci->buffer, oci->bufptr, gotnow); 202 /* grow buffer if necessary */ 203 if (needed > oci->size) { 204 char *ibuf; 205 206 ibuf = (char *) fsrealloc(oci->buffer, needed); 207 if (!ibuf) { 208 yield_control_death(); 209 return -1; 210 } 211 oci->size = needed; 212 oci->buffer = ibuf; 213 } 214 oci->bufptr = oci->buffer; 215 oci->bufcnt = gotnow; 216 } 217 /* fill 'er up */ 218 if (oc->trans_conn == NULL) { 219 yield_control_death(); 220 return -1; 221 } 222 result = _FontTransRead(oc->trans_conn, oci->buffer + oci->bufcnt, 223 oci->size - oci->bufcnt); 224 if (result <= 0) { 225#if !(defined(SVR4) && defined(i386) && !defined(sun)) 226 if ((result < 0) && ETEST(errno)) { 227 yield_control_no_input(); 228 return 0; 229 } else 230#endif 231 { 232 233 yield_control_death(); 234 return -1; 235 } 236 } 237 oci->bufcnt += result; 238 gotnow += result; 239 240 /* free up space after huge requests */ 241 if ((oci->size > BUFWATERMARK) && 242 (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) { 243 char *ibuf; 244 245 ibuf = (char *) fsrealloc(oci->buffer, BUFSIZE); 246 if (ibuf) { 247 oci->size = BUFSIZE; 248 oci->buffer = ibuf; 249 oci->bufptr = ibuf + oci->bufcnt - gotnow; 250 } 251 } 252 request = (fsReq *) oci->bufptr; 253 if ((gotnow < SIZEOF(fsReq)) || 254 (gotnow < (needed = request_length(request, client)))) { 255 yield_control_no_input(); 256 return 0; 257 } 258 } 259 if (needed == 0) 260 needed = SIZEOF(fsReq); 261 oci->lenLastReq = needed; 262 /* 263 * Check to see if client has at least one whole request in the buffer. If 264 * there is only a partial request, treat like buffer is empty so that 265 * select() will be called again and other clients can get into the queue. 266 */ 267 268 if (gotnow >= needed + SIZEOF(fsReq)) { 269 request = (fsReq *) (oci->bufptr + needed); 270 if (gotnow >= needed + request_length(request, client)) 271 FD_SET(fd, &ClientsWithInput); 272 else 273 yield_control_no_input(); 274 } else { 275 if (gotnow == needed) 276 AvailableInput = oc; 277 yield_control_no_input(); 278 } 279 280 if (++timesThisConnection >= MAX_TIMES_PER) 281 yield_control(); 282 283 client->requestBuffer = (pointer) oci->bufptr; 284 return needed; 285} 286 287Bool 288InsertFakeRequest(ClientPtr client, char *data, int count) 289{ 290 OsCommPtr oc = (OsCommPtr) client->osPrivate; 291 ConnectionInputPtr oci = oc->input; 292 int fd = oc->fd; 293 fsReq *request; 294 int gotnow, 295 moveup; 296 297 if (AvailableInput) { 298 if (AvailableInput != oc) { 299 register ConnectionInputPtr aci = AvailableInput->input; 300 301 if (aci->size > BUFWATERMARK) { 302 fsfree(aci->buffer); 303 fsfree(aci); 304 } else { 305 aci->next = FreeInputs; 306 FreeInputs = aci; 307 } 308 AvailableInput->input = (ConnectionInputPtr) NULL; 309 } 310 AvailableInput = (OsCommPtr) NULL; 311 } 312 if (!oci) { 313 if ((oci = FreeInputs) != (ConnectionInputPtr) 0) 314 FreeInputs = oci->next; 315 else if (!(oci = AllocateInputBuffer())) 316 return FALSE; 317 oc->input = oci; 318 319 } 320 oci->bufptr += oci->lenLastReq; 321 oci->lenLastReq = 0; 322 gotnow = oci->bufcnt + oci->buffer - oci->bufptr; 323 if ((gotnow + count) > oci->size) { 324 char *ibuf; 325 326 ibuf = (char *) fsrealloc(oci->buffer, gotnow + count); 327 if (!ibuf) 328 return FALSE; 329 oci->size = gotnow + count; 330 oci->buffer = ibuf; 331 oci->bufptr = ibuf + oci->bufcnt - gotnow; 332 } 333 moveup = count - (oci->bufptr - oci->buffer); 334 if (moveup > 0) { 335 if (gotnow > 0) 336 memmove( oci->bufptr + moveup, oci->bufptr, gotnow); 337 oci->bufptr += moveup; 338 oci->bufcnt += moveup; 339 } 340 memmove( oci->bufptr - count, data, count); 341 oci->bufptr -= count; 342 request = (fsReq *) oci->bufptr; 343 gotnow += count; 344 if ((gotnow >= SIZEOF(fsReq)) && 345 (gotnow >= request_length(request, client))) 346 FD_SET(fd, &ClientsWithInput); 347 else 348 yield_control_no_input(); 349 return TRUE; 350} 351 352void 353ResetCurrentRequest(ClientPtr client) 354{ 355 OsCommPtr oc = (OsCommPtr) client->osPrivate; 356 ConnectionInputPtr oci = oc->input; 357 int fd = oc->fd; 358 fsReq *request; 359 int gotnow; 360 361 if (AvailableInput == oc) 362 AvailableInput = (OsCommPtr) NULL; 363 oci->lenLastReq = 0; 364 request = (fsReq *) oci->bufptr; 365 gotnow = oci->bufcnt + oci->buffer - oci->bufptr; 366 if ((gotnow >= SIZEOF(fsReq)) && 367 (gotnow >= request_length(request, client))) { 368 FD_SET(fd, &ClientsWithInput); 369 yield_control(); 370 } else { 371 yield_control_no_input(); 372 } 373} 374 375int 376FlushClient( 377 ClientPtr client, 378 OsCommPtr oc, 379 char *extraBuf, 380 int extraCount, 381 int padsize) 382{ 383 ConnectionOutputPtr oco = oc->output; 384 int fd = oc->fd; 385 struct iovec iov[3]; 386 char padBuffer[3]; 387 long written; 388 long notWritten; 389 long todo; 390 391 if (!oco) 392 return 0; 393 written = 0; 394 notWritten = oco->count + extraCount + padsize; 395 todo = notWritten; 396 while (notWritten) { 397 long before = written; 398 long remain = todo; 399 int i = 0; 400 long len; 401 402 /*- 403 * You could be very general here and have "in" and "out" iovecs and 404 * write a loop without using a macro, but what the heck. This 405 * translates to: 406 * 407 * how much of this piece is new? 408 * if more new then we are trying this time, clamp 409 * if nothing new 410 * then bump down amount already written, for next piece 411 * else put new stuff in iovec, will need all of next piece 412 * 413 * Note that todo had better be at least 1 or else we'll end up 414 * writing 0 iovecs. 415 */ 416 417#define InsertIOV(pointer, length) \ 418 len = (length) - before; \ 419 if (len > remain) \ 420 len = remain; \ 421 if (len <= 0) { \ 422 before = (-len); \ 423 } else { \ 424 iov[i].iov_len = len; \ 425 iov[i].iov_base = (pointer) + before; \ 426 i++; \ 427 remain -= len; \ 428 before = 0; \ 429 } 430 431 InsertIOV((char *) oco->buf, oco->count); 432 InsertIOV(extraBuf, extraCount); 433 InsertIOV(padBuffer, padsize); 434 435 errno = 0; 436 if (oc->trans_conn && (len = _FontTransWritev(oc->trans_conn, iov, i)) >= 0) { 437 written += len; 438 notWritten -= len; 439 todo = notWritten; 440 } else if (ETEST(errno) 441#ifdef SUNSYSV /* check for another brain-damaged OS bug */ 442 || (errno == 0) 443#endif 444#ifdef EMSGSIZE /* check for another brain-damaged OS bug */ 445 || ((errno == EMSGSIZE) && (todo == 1)) 446#endif 447 ) 448 { 449 FD_SET(fd, &ClientsWriteBlocked); 450 AnyClientsWriteBlocked = TRUE; 451 452 if (written < oco->count) { 453 if (written > 0) { 454 oco->count -= written; 455 memmove( (char *) oco->buf, (char *) oco->buf + written, 456 oco->count); 457 written = 0; 458 } 459 } else { 460 written -= oco->count; 461 oco->count = 0; 462 } 463 464 /* grow buffer if necessary */ 465 if (notWritten > oco->size) { 466 unsigned char *obuf; 467 468 obuf = (unsigned char *) fsrealloc(oco->buf, 469 notWritten + OutputBufferSize); 470 if (!obuf) { 471 if (oc->trans_conn) 472 _FontTransClose(oc->trans_conn); 473 oc->trans_conn = NULL; 474 MarkClientException(client); 475 oco->count = 0; 476 return -1; 477 } 478 oco->size = notWritten + OutputBufferSize; 479 oco->buf = obuf; 480 } 481 if ((len = extraCount - written) > 0) { 482 memmove( (char *) oco->buf + oco->count, 483 extraBuf + written, len); 484 } 485 oco->count = notWritten; 486 return extraCount; 487 } 488#ifdef EMSGSIZE /* check for another brain-damaged OS bug */ 489 else if (errno == EMSGSIZE) 490 { 491 todo >>= 1; 492 } 493#endif 494 else 495 { 496 if (oc->trans_conn) 497 _FontTransClose(oc->trans_conn); 498 oc->trans_conn = NULL; 499 MarkClientException(client); 500 oco->count = 0; 501 return -1; 502 } 503 } 504 505 /* everything was flushed */ 506 oco->count = 0; 507 508 /* clear the write block if it was set */ 509 if (AnyClientsWriteBlocked) { 510 FD_CLR(fd, &ClientsWriteBlocked); 511 if (!XFD_ANYSET(&ClientsWriteBlocked)) 512 AnyClientsWriteBlocked = FALSE; 513 } 514 if (oco->size > BUFWATERMARK) { 515 fsfree(oco->buf); 516 fsfree(oco); 517 } else { 518 oco->next = FreeOutputs; 519 FreeOutputs = oco; 520 } 521 oc->output = (ConnectionOutputPtr) NULL; 522 523 return extraCount; 524} 525 526void 527FlushAllOutput(void) 528{ 529 int index, base; 530 fd_mask mask; 531 OsCommPtr oc; 532 ClientPtr client; 533 534 if (!NewOutputPending) 535 return; 536 537 NewOutputPending = FALSE; 538 539 for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++) { 540 mask = OutputPending.fds_bits[base]; 541 OutputPending.fds_bits[base] = 0; 542 while (mask) { 543 index = ffs(mask) - 1; 544 mask &= ~lowbit(mask); 545 if ((index = ConnectionTranslation[(base << 5) + index]) == 0) 546 continue; 547 client = clients[index]; 548 if (client->clientGone == CLIENT_GONE) 549 continue; 550 oc = (OsCommPtr) client->osPrivate; 551 if (FD_ISSET(oc->fd, &ClientsWithInput)) { 552 FD_SET(oc->fd, &OutputPending); 553 NewOutputPending = TRUE; 554 } else { 555 (void) FlushClient(client, oc, (char *) NULL, 0, 0); 556 } 557 } 558 } 559} 560 561/* 562 * returns number of bytes written 563 */ 564static int 565write_to_client_internal(ClientPtr client, int count, char *buf, int padBytes) 566{ 567 OsCommPtr oc = (OsCommPtr) client->osPrivate; 568 ConnectionOutputPtr oco = oc->output; 569 570 if (!count) 571 return 0; 572 573 if (!oco) { 574 if ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) { 575 FreeOutputs = oco->next; 576 } else if (!(oco = AllocateOutputBuffer())) { 577 _FontTransClose(oc->trans_conn); 578 oc->trans_conn = NULL; 579 MarkClientException(client); 580 return -1; 581 } 582 oc->output = oco; 583 } 584 if (oco->count + count + padBytes > oco->size) { 585 FD_CLR(oc->fd, &OutputPending); 586 NewOutputPending = FALSE; 587 return FlushClient(client, oc, buf, count, padBytes); 588 } 589 NewOutputPending = TRUE; 590 FD_SET(oc->fd, &OutputPending); 591 memmove( (char *) oco->buf + oco->count, buf, count); 592 oco->count += count + padBytes; 593 594 return count; 595} 596 597void 598WriteToClientUnpadded(ClientPtr client, int count, char *buf) 599{ 600 write_to_client_internal(client, count, buf, 0); 601} 602 603static int padlength[4] = {0, 3, 2, 1}; 604 605void 606WriteToClient(ClientPtr client, int count, char *buf) 607{ 608 int flag = 0; 609 if (NULL == buf) { 610 flag = -1; 611 buf = (char *)fsalloc(count); memset(buf, 0, count); 612 } 613 write_to_client_internal(client, count, buf, padlength[count & 3]); 614 if (flag) 615 fsfree(buf); 616} 617 618static ConnectionInputPtr 619AllocateInputBuffer(void) 620{ 621 register ConnectionInputPtr oci; 622 623 oci = (ConnectionInputPtr) fsalloc(sizeof(ConnectionInput)); 624 if (!oci) 625 return (ConnectionInputPtr) NULL; 626 oci->buffer = (char *) fsalloc(BUFSIZE); 627 if (!oci->buffer) { 628 fsfree(oci); 629 return (ConnectionInputPtr) NULL; 630 } 631 oci->next = 0; 632 oci->size = BUFSIZE; 633 oci->bufptr = oci->buffer; 634 oci->bufcnt = 0; 635 oci->lenLastReq = 0; 636 return oci; 637} 638 639static ConnectionOutputPtr 640AllocateOutputBuffer(void) 641{ 642 register ConnectionOutputPtr oco; 643 644 oco = (ConnectionOutputPtr) fsalloc(sizeof(ConnectionOutput)); 645 if (!oco) 646 return (ConnectionOutputPtr) NULL; 647 oco->buf = (unsigned char *) fsalloc(BUFSIZE); 648 if (!oco->buf) { 649 fsfree(oco); 650 return (ConnectionOutputPtr) NULL; 651 } 652 oco->size = BUFSIZE; 653 oco->count = 0; 654 return oco; 655} 656 657 658void 659FreeOsBuffers(OsCommPtr oc) 660{ 661 register ConnectionInputPtr oci; 662 register ConnectionOutputPtr oco; 663 664 if (AvailableInput == oc) 665 AvailableInput = (OsCommPtr) NULL; 666 if ((oci = oc->input) != (ConnectionInputPtr) 0) { 667 if (FreeInputs) { 668 fsfree(oci->buffer); 669 fsfree(oci); 670 } else { 671 FreeInputs = oci; 672 oci->next = (ConnectionInputPtr) NULL; 673 oci->bufptr = oci->buffer; 674 oci->bufcnt = 0; 675 oci->lenLastReq = 0; 676 } 677 } 678 if ((oco = oc->output) != (ConnectionOutputPtr) 0) { 679 if (FreeOutputs) { 680 fsfree(oco->buf); 681 fsfree(oco); 682 } else { 683 FreeOutputs = oco; 684 oco->next = (ConnectionOutputPtr) NULL; 685 oco->count = 0; 686 } 687 } 688} 689 690void 691ResetOsBuffers(void) 692{ 693 register ConnectionInputPtr oci; 694 register ConnectionOutputPtr oco; 695 696 while ((oci = FreeInputs) != (ConnectionInputPtr) 0) { 697 FreeInputs = oci->next; 698 fsfree(oci->buffer); 699 fsfree(oci); 700 } 701 while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) { 702 FreeOutputs = oco->next; 703 fsfree(oco->buf); 704 fsfree(oco); 705 } 706} 707