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