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