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