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