io.c revision 8f34cbf9
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 request = (fsReq *) oci->bufptr; 157 158 /* not enough for a request */ 159 if ((gotnow < SIZEOF(fsReq)) || 160 (gotnow < (needed = request_length(request, client)))) { 161 oci->lenLastReq = 0; 162 if ((gotnow < SIZEOF(fsReq)) || needed == 0) 163 needed = SIZEOF(fsReq); 164 else if (needed > MAXBUFSIZE) { 165 yield_control_death(); 166 return -1; 167 } 168 /* see if we need to shift up a partial request so the rest can fit */ 169 if ((gotnow == 0) || 170 ((oci->bufptr - oci->buffer + needed) > oci->size)) 171 { 172 if ((gotnow > 0) && (oci->bufptr != oci->buffer)) 173 memmove( oci->buffer, oci->bufptr, gotnow); 174 /* grow buffer if necessary */ 175 if (needed > oci->size) { 176 char *ibuf; 177 178 ibuf = (char *) fsrealloc(oci->buffer, needed); 179 if (!ibuf) { 180 yield_control_death(); 181 return -1; 182 } 183 oci->size = needed; 184 oci->buffer = ibuf; 185 } 186 oci->bufptr = oci->buffer; 187 oci->bufcnt = gotnow; 188 } 189 /* fill 'er up */ 190 if (oc->trans_conn == NULL) { 191 yield_control_death(); 192 return -1; 193 } 194 result = _FontTransRead(oc->trans_conn, oci->buffer + oci->bufcnt, 195 oci->size - oci->bufcnt); 196 if (result <= 0) { 197#if !(defined(SVR4) && defined(i386) && !defined(sun)) 198 if ((result < 0) && ETEST(errno)) { 199 yield_control_no_input(); 200 return 0; 201 } else 202#endif 203 { 204 205 yield_control_death(); 206 return -1; 207 } 208 } 209 oci->bufcnt += result; 210 gotnow += result; 211 212 /* free up space after huge requests */ 213 if ((oci->size > BUFWATERMARK) && 214 (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) { 215 char *ibuf; 216 217 ibuf = (char *) fsrealloc(oci->buffer, BUFSIZE); 218 if (ibuf) { 219 oci->size = BUFSIZE; 220 oci->buffer = ibuf; 221 oci->bufptr = ibuf + oci->bufcnt - gotnow; 222 } 223 } 224 request = (fsReq *) oci->bufptr; 225 if ((gotnow < SIZEOF(fsReq)) || 226 (gotnow < (needed = request_length(request, client)))) { 227 yield_control_no_input(); 228 return 0; 229 } 230 } 231 if (needed == 0) 232 needed = SIZEOF(fsReq); 233 oci->lenLastReq = needed; 234 /* 235 * Check to see if client has at least one whole request in the buffer. If 236 * there is only a partial request, treat like buffer is empty so that 237 * select() will be called again and other clients can get into the queue. 238 */ 239 240 if (gotnow >= needed + SIZEOF(fsReq)) { 241 request = (fsReq *) (oci->bufptr + needed); 242 if (gotnow >= needed + request_length(request, client)) 243 FD_SET(fd, &ClientsWithInput); 244 else 245 yield_control_no_input(); 246 } else { 247 if (gotnow == needed) 248 AvailableInput = oc; 249 yield_control_no_input(); 250 } 251 252 if (++timesThisConnection >= MAX_TIMES_PER) 253 yield_control(); 254 255 client->requestBuffer = (pointer) oci->bufptr; 256 return needed; 257} 258 259Bool 260InsertFakeRequest(ClientPtr client, char *data, int count) 261{ 262 OsCommPtr oc = (OsCommPtr) client->osPrivate; 263 ConnectionInputPtr oci = oc->input; 264 int fd = oc->fd; 265 fsReq *request; 266 int gotnow, 267 moveup; 268 269 if (AvailableInput) { 270 if (AvailableInput != oc) { 271 register ConnectionInputPtr aci = AvailableInput->input; 272 273 if (aci->size > BUFWATERMARK) { 274 fsfree(aci->buffer); 275 fsfree(aci); 276 } else { 277 aci->next = FreeInputs; 278 FreeInputs = aci; 279 } 280 AvailableInput->input = (ConnectionInputPtr) NULL; 281 } 282 AvailableInput = (OsCommPtr) NULL; 283 } 284 if (!oci) { 285 if ((oci = FreeInputs) != (ConnectionInputPtr) 0) 286 FreeInputs = oci->next; 287 else if (!(oci = AllocateInputBuffer())) 288 return FALSE; 289 oc->input = oci; 290 291 } 292 oci->bufptr += oci->lenLastReq; 293 oci->lenLastReq = 0; 294 gotnow = oci->bufcnt + oci->buffer - oci->bufptr; 295 if ((gotnow + count) > oci->size) { 296 char *ibuf; 297 298 ibuf = (char *) fsrealloc(oci->buffer, gotnow + count); 299 if (!ibuf) 300 return FALSE; 301 oci->size = gotnow + count; 302 oci->buffer = ibuf; 303 oci->bufptr = ibuf + oci->bufcnt - gotnow; 304 } 305 moveup = count - (oci->bufptr - oci->buffer); 306 if (moveup > 0) { 307 if (gotnow > 0) 308 memmove( oci->bufptr + moveup, oci->bufptr, gotnow); 309 oci->bufptr += moveup; 310 oci->bufcnt += moveup; 311 } 312 memmove( oci->bufptr - count, data, count); 313 oci->bufptr -= count; 314 request = (fsReq *) oci->bufptr; 315 gotnow += count; 316 if ((gotnow >= SIZEOF(fsReq)) && 317 (gotnow >= request_length(request, client))) 318 FD_SET(fd, &ClientsWithInput); 319 else 320 yield_control_no_input(); 321 return TRUE; 322} 323 324void 325ResetCurrentRequest(ClientPtr client) 326{ 327 OsCommPtr oc = (OsCommPtr) client->osPrivate; 328 ConnectionInputPtr oci = oc->input; 329 int fd = oc->fd; 330 fsReq *request; 331 int gotnow; 332 333 if (AvailableInput == oc) 334 AvailableInput = (OsCommPtr) NULL; 335 oci->lenLastReq = 0; 336 request = (fsReq *) oci->bufptr; 337 gotnow = oci->bufcnt + oci->buffer - oci->bufptr; 338 if ((gotnow >= SIZEOF(fsReq)) && 339 (gotnow >= request_length(request, client))) { 340 FD_SET(fd, &ClientsWithInput); 341 yield_control(); 342 } else { 343 yield_control_no_input(); 344 } 345} 346 347int 348FlushClient( 349 ClientPtr client, 350 OsCommPtr oc, 351 char *extraBuf, 352 int extraCount, 353 int padsize) 354{ 355 ConnectionOutputPtr oco = oc->output; 356 int fd = oc->fd; 357 struct iovec iov[3]; 358 char padBuffer[3]; 359 long written; 360 long notWritten; 361 long todo; 362 363 if (!oco) 364 return 0; 365 written = 0; 366 notWritten = oco->count + extraCount + padsize; 367 todo = notWritten; 368 while (notWritten) { 369 long before = written; 370 long remain = todo; 371 int i = 0; 372 long len; 373 374 /*- 375 * You could be very general here and have "in" and "out" iovecs and 376 * write a loop without using a macro, but what the heck. This 377 * translates to: 378 * 379 * how much of this piece is new? 380 * if more new then we are trying this time, clamp 381 * if nothing new 382 * then bump down amount already written, for next piece 383 * else put new stuff in iovec, will need all of next piece 384 * 385 * Note that todo had better be at least 1 or else we'll end up 386 * writing 0 iovecs. 387 */ 388 389#define InsertIOV(pointer, length) \ 390 len = (length) - before; \ 391 if (len > remain) \ 392 len = remain; \ 393 if (len <= 0) { \ 394 before = (-len); \ 395 } else { \ 396 iov[i].iov_len = len; \ 397 iov[i].iov_base = (pointer) + before; \ 398 i++; \ 399 remain -= len; \ 400 before = 0; \ 401 } 402 403 InsertIOV((char *) oco->buf, oco->count); 404 InsertIOV(extraBuf, extraCount); 405 InsertIOV(padBuffer, padsize); 406 407 errno = 0; 408 if (oc->trans_conn && (len = _FontTransWritev(oc->trans_conn, iov, i)) >= 0) { 409 written += len; 410 notWritten -= len; 411 todo = notWritten; 412 } else if (ETEST(errno) 413#ifdef SUNSYSV /* check for another brain-damaged OS bug */ 414 || (errno == 0) 415#endif 416#ifdef EMSGSIZE /* check for another brain-damaged OS bug */ 417 || ((errno == EMSGSIZE) && (todo == 1)) 418#endif 419 ) 420 { 421 FD_SET(fd, &ClientsWriteBlocked); 422 AnyClientsWriteBlocked = TRUE; 423 424 if (written < oco->count) { 425 if (written > 0) { 426 oco->count -= written; 427 memmove( (char *) oco->buf, (char *) oco->buf + written, 428 oco->count); 429 written = 0; 430 } 431 } else { 432 written -= oco->count; 433 oco->count = 0; 434 } 435 436 /* grow buffer if necessary */ 437 if (notWritten > oco->size) { 438 unsigned char *obuf; 439 440 obuf = (unsigned char *) fsrealloc(oco->buf, 441 notWritten + OutputBufferSize); 442 if (!obuf) { 443 if (oc->trans_conn) 444 _FontTransClose(oc->trans_conn); 445 oc->trans_conn = NULL; 446 MarkClientException(client); 447 oco->count = 0; 448 return -1; 449 } 450 oco->size = notWritten + OutputBufferSize; 451 oco->buf = obuf; 452 } 453 if ((len = extraCount - written) > 0) { 454 memmove( (char *) oco->buf + oco->count, 455 extraBuf + written, len); 456 } 457 oco->count = notWritten; 458 return extraCount; 459 } 460#ifdef EMSGSIZE /* check for another brain-damaged OS bug */ 461 else if (errno == EMSGSIZE) 462 { 463 todo >>= 1; 464 } 465#endif 466 else 467 { 468 if (oc->trans_conn) 469 _FontTransClose(oc->trans_conn); 470 oc->trans_conn = NULL; 471 MarkClientException(client); 472 oco->count = 0; 473 return -1; 474 } 475 } 476 477 /* everything was flushed */ 478 oco->count = 0; 479 480 /* clear the write block if it was set */ 481 if (AnyClientsWriteBlocked) { 482 FD_CLR(fd, &ClientsWriteBlocked); 483 if (!XFD_ANYSET(&ClientsWriteBlocked)) 484 AnyClientsWriteBlocked = FALSE; 485 } 486 if (oco->size > BUFWATERMARK) { 487 fsfree(oco->buf); 488 fsfree(oco); 489 } else { 490 oco->next = FreeOutputs; 491 FreeOutputs = oco; 492 } 493 oc->output = (ConnectionOutputPtr) NULL; 494 495 return extraCount; 496} 497 498void 499FlushAllOutput(void) 500{ 501 int index, base; 502 fd_mask mask; 503 OsCommPtr oc; 504 ClientPtr client; 505 506 if (!NewOutputPending) 507 return; 508 509 NewOutputPending = FALSE; 510 511 for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++) { 512 mask = OutputPending.fds_bits[base]; 513 OutputPending.fds_bits[base] = 0; 514 while (mask) { 515 index = xfd_ffs(mask) - 1; 516 mask &= ~lowbit(mask); 517 if ((index = ConnectionTranslation[(base * (sizeof(fd_mask) * 8)) + index]) == 0) 518 continue; 519 client = clients[index]; 520 if (client->clientGone == CLIENT_GONE) 521 continue; 522 oc = (OsCommPtr) client->osPrivate; 523 if (FD_ISSET(oc->fd, &ClientsWithInput)) { 524 FD_SET(oc->fd, &OutputPending); 525 NewOutputPending = TRUE; 526 } else { 527 (void) FlushClient(client, oc, (char *) NULL, 0, 0); 528 } 529 } 530 } 531} 532 533/* 534 * returns number of bytes written 535 */ 536static int 537write_to_client_internal(ClientPtr client, int count, char *buf, int padBytes) 538{ 539 OsCommPtr oc = (OsCommPtr) client->osPrivate; 540 ConnectionOutputPtr oco = oc->output; 541 542 if (!count) 543 return 0; 544 545 if (!oco) { 546 if ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) { 547 FreeOutputs = oco->next; 548 } else if (!(oco = AllocateOutputBuffer())) { 549 _FontTransClose(oc->trans_conn); 550 oc->trans_conn = NULL; 551 MarkClientException(client); 552 return -1; 553 } 554 oc->output = oco; 555 } 556 if (oco->count + count + padBytes > oco->size) { 557 FD_CLR(oc->fd, &OutputPending); 558 NewOutputPending = FALSE; 559 return FlushClient(client, oc, buf, count, padBytes); 560 } 561 NewOutputPending = TRUE; 562 FD_SET(oc->fd, &OutputPending); 563 memmove( (char *) oco->buf + oco->count, buf, count); 564 oco->count += count + padBytes; 565 566 return count; 567} 568 569void 570WriteToClientUnpadded(ClientPtr client, int count, char *buf) 571{ 572 write_to_client_internal(client, count, buf, 0); 573} 574 575static int padlength[4] = {0, 3, 2, 1}; 576 577void 578WriteToClient(ClientPtr client, int count, char *buf) 579{ 580 int flag = 0; 581 if (NULL == buf) { 582 flag = -1; 583 buf = (char *)fsalloc(count); memset(buf, 0, count); 584 } 585 write_to_client_internal(client, count, buf, padlength[count & 3]); 586 if (flag) 587 fsfree(buf); 588} 589 590static ConnectionInputPtr 591AllocateInputBuffer(void) 592{ 593 register ConnectionInputPtr oci; 594 595 oci = (ConnectionInputPtr) fsalloc(sizeof(ConnectionInput)); 596 if (!oci) 597 return (ConnectionInputPtr) NULL; 598 oci->buffer = (char *) fsalloc(BUFSIZE); 599 if (!oci->buffer) { 600 fsfree(oci); 601 return (ConnectionInputPtr) NULL; 602 } 603 oci->next = NULL; 604 oci->size = BUFSIZE; 605 oci->bufptr = oci->buffer; 606 oci->bufcnt = 0; 607 oci->lenLastReq = 0; 608 return oci; 609} 610 611static ConnectionOutputPtr 612AllocateOutputBuffer(void) 613{ 614 register ConnectionOutputPtr oco; 615 616 oco = (ConnectionOutputPtr) fsalloc(sizeof(ConnectionOutput)); 617 if (!oco) 618 return (ConnectionOutputPtr) NULL; 619 oco->buf = (unsigned char *) fsalloc(BUFSIZE); 620 if (!oco->buf) { 621 fsfree(oco); 622 return (ConnectionOutputPtr) NULL; 623 } 624 oco->size = BUFSIZE; 625 oco->count = 0; 626 return oco; 627} 628 629 630void 631FreeOsBuffers(OsCommPtr oc) 632{ 633 register ConnectionInputPtr oci; 634 register ConnectionOutputPtr oco; 635 636 if (AvailableInput == oc) 637 AvailableInput = (OsCommPtr) NULL; 638 if ((oci = oc->input) != (ConnectionInputPtr) 0) { 639 if (FreeInputs) { 640 fsfree(oci->buffer); 641 fsfree(oci); 642 } else { 643 FreeInputs = oci; 644 oci->next = (ConnectionInputPtr) NULL; 645 oci->bufptr = oci->buffer; 646 oci->bufcnt = 0; 647 oci->lenLastReq = 0; 648 } 649 } 650 if ((oco = oc->output) != (ConnectionOutputPtr) 0) { 651 if (FreeOutputs) { 652 fsfree(oco->buf); 653 fsfree(oco); 654 } else { 655 FreeOutputs = oco; 656 oco->next = (ConnectionOutputPtr) NULL; 657 oco->count = 0; 658 } 659 } 660} 661 662void 663ResetOsBuffers(void) 664{ 665 register ConnectionInputPtr oci; 666 register ConnectionOutputPtr oco; 667 668 while ((oci = FreeInputs) != (ConnectionInputPtr) 0) { 669 FreeInputs = oci->next; 670 fsfree(oci->buffer); 671 fsfree(oci); 672 } 673 while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) { 674 FreeOutputs = oco->next; 675 fsfree(oco->buf); 676 fsfree(oco); 677 } 678} 679