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