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 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 *) FScalloc(1, count); 585 if (buf == NULL) 586 FatalError("WriteToClient couldn't create client buffer\n"); 587 } 588 write_to_client_internal(client, count, buf, padlength[count & 3]); 589 if (flag) 590 FSfree(buf); 591} 592 593static ConnectionInputPtr 594AllocateInputBuffer(void) 595{ 596 register ConnectionInputPtr oci; 597 598 oci = (ConnectionInputPtr) FSalloc(sizeof(ConnectionInput)); 599 if (!oci) 600 return (ConnectionInputPtr) NULL; 601 oci->buffer = (char *) FSalloc(BUFSIZE); 602 if (!oci->buffer) { 603 FSfree(oci); 604 return (ConnectionInputPtr) NULL; 605 } 606 oci->next = NULL; 607 oci->size = BUFSIZE; 608 oci->bufptr = oci->buffer; 609 oci->bufcnt = 0; 610 oci->lenLastReq = 0; 611 return oci; 612} 613 614static ConnectionOutputPtr 615AllocateOutputBuffer(void) 616{ 617 register ConnectionOutputPtr oco; 618 619 oco = (ConnectionOutputPtr) FSalloc(sizeof(ConnectionOutput)); 620 if (!oco) 621 return (ConnectionOutputPtr) NULL; 622 oco->buf = (unsigned char *) FSalloc(BUFSIZE); 623 if (!oco->buf) { 624 FSfree(oco); 625 return (ConnectionOutputPtr) NULL; 626 } 627 oco->size = BUFSIZE; 628 oco->count = 0; 629 return oco; 630} 631 632 633void 634FreeOsBuffers(OsCommPtr oc) 635{ 636 register ConnectionInputPtr oci; 637 register ConnectionOutputPtr oco; 638 639 if (AvailableInput == oc) 640 AvailableInput = (OsCommPtr) NULL; 641 if ((oci = oc->input) != (ConnectionInputPtr) 0) { 642 if (FreeInputs) { 643 FSfree(oci->buffer); 644 FSfree(oci); 645 } else { 646 FreeInputs = oci; 647 oci->next = (ConnectionInputPtr) NULL; 648 oci->bufptr = oci->buffer; 649 oci->bufcnt = 0; 650 oci->lenLastReq = 0; 651 } 652 } 653 if ((oco = oc->output) != (ConnectionOutputPtr) 0) { 654 if (FreeOutputs) { 655 FSfree(oco->buf); 656 FSfree(oco); 657 } else { 658 FreeOutputs = oco; 659 oco->next = (ConnectionOutputPtr) NULL; 660 oco->count = 0; 661 } 662 } 663} 664 665void 666ResetOsBuffers(void) 667{ 668 register ConnectionInputPtr oci; 669 register ConnectionOutputPtr oco; 670 671 while ((oci = FreeInputs) != (ConnectionInputPtr) 0) { 672 FreeInputs = oci->next; 673 FSfree(oci->buffer); 674 FSfree(oci); 675 } 676 while ((oco = FreeOutputs) != (ConnectionOutputPtr) 0) { 677 FreeOutputs = oco->next; 678 FSfree(oco->buf); 679 FSfree(oco); 680 } 681} 682