| 1 | /*********************************************************** |
| 2 | |
| 3 | Copyright 1987, 1989, 1998 The Open Group |
| 4 | |
| 5 | Permission to use, copy, modify, distribute, and sell this software and its |
| 6 | documentation for any purpose is hereby granted without fee, provided that |
| 7 | the above copyright notice appear in all copies and that both that |
| 8 | copyright notice and this permission notice appear in supporting |
| 9 | documentation. |
| 10 | |
| 11 | The above copyright notice and this permission notice shall be included in |
| 12 | all copies or substantial portions of the Software. |
| 13 | |
| 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 17 | OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
| 18 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 20 | |
| 21 | Except as contained in this notice, the name of The Open Group shall not be |
| 22 | used in advertising or otherwise to promote the sale, use or other dealings |
| 23 | in this Software without prior written authorization from The Open Group. |
| 24 | |
| 25 | Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts. |
| 26 | |
| 27 | All Rights Reserved |
| 28 | |
| 29 | Permission to use, copy, modify, and distribute this software and its |
| 30 | documentation for any purpose and without fee is hereby granted, |
| 31 | provided that the above copyright notice appear in all copies and that |
| 32 | both that copyright notice and this permission notice appear in |
| 33 | supporting documentation, and that the name of Digital not be |
| 34 | used in advertising or publicity pertaining to distribution of the |
| 35 | software without specific, written prior permission. |
| 36 | |
| 37 | DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
| 38 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
| 39 | DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
| 40 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| 41 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| 42 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| 43 | SOFTWARE. |
| 44 | |
| 45 | ******************************************************************/ |
| 46 | /***************************************************************** |
| 47 | * i/o functions |
| 48 | * |
| 49 | * WriteToClient, ReadRequestFromClient |
| 50 | * InsertFakeRequest, ResetCurrentRequest |
| 51 | * |
| 52 | *****************************************************************/ |
| 53 | |
| 54 | #ifdef HAVE_DIX_CONFIG_H |
| 55 | #include <dix-config.h> |
| 56 | #endif |
| 57 | |
| 58 | #undef DEBUG_COMMUNICATION |
| 59 | |
| 60 | #ifdef WIN32 |
| 61 | #include <X11/Xwinsock.h> |
| 62 | #endif |
| 63 | #include <stdio.h> |
| 64 | #define XSERV_t |
| 65 | #define TRANS_SERVER |
| 66 | #define TRANS_REOPEN |
| 67 | #include <X11/Xtrans/Xtrans.h> |
| 68 | #include <X11/Xmd.h> |
| 69 | #include <errno.h> |
| 70 | #if !defined(WIN32) |
| 71 | #include <sys/uio.h> |
| 72 | #endif |
| 73 | #include <X11/X.h> |
| 74 | #include <X11/Xproto.h> |
| 75 | #include "os.h" |
| 76 | #include "osdep.h" |
| 77 | #include <X11/Xpoll.h> |
| 78 | #include "opaque.h" |
| 79 | #include "dixstruct.h" |
| 80 | #include "misc.h" |
| 81 | |
| 82 | CallbackListPtr ReplyCallback; |
| 83 | CallbackListPtr FlushCallback; |
| 84 | |
| 85 | typedef struct _connectionInput { |
| 86 | struct _connectionInput *next; |
| 87 | char *buffer; /* contains current client input */ |
| 88 | char *bufptr; /* pointer to current start of data */ |
| 89 | int bufcnt; /* count of bytes in buffer */ |
| 90 | int lenLastReq; |
| 91 | int size; |
| 92 | unsigned int ignoreBytes; /* bytes to ignore before the next request */ |
| 93 | } ConnectionInput; |
| 94 | |
| 95 | typedef struct _connectionOutput { |
| 96 | struct _connectionOutput *next; |
| 97 | unsigned char *buf; |
| 98 | int size; |
| 99 | int count; |
| 100 | } ConnectionOutput; |
| 101 | |
| 102 | static ConnectionInputPtr AllocateInputBuffer(void); |
| 103 | static ConnectionOutputPtr AllocateOutputBuffer(void); |
| 104 | |
| 105 | /* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX |
| 106 | * systems are broken and return EWOULDBLOCK when they should return EAGAIN |
| 107 | */ |
| 108 | #ifndef WIN32 |
| 109 | #define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK) |
| 110 | #else /* WIN32 The socket errorcodes differ from the normal errors */ |
| 111 | #define ETEST(err) (err == EAGAIN || err == WSAEWOULDBLOCK) |
| 112 | #endif |
| 113 | |
| 114 | static Bool CriticalOutputPending; |
| 115 | static int timesThisConnection = 0; |
| 116 | static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL; |
| 117 | static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL; |
| 118 | static OsCommPtr AvailableInput = (OsCommPtr) NULL; |
| 119 | |
| 120 | #define get_req_len(req,cli) ((cli)->swapped ? \ |
| 121 | lswaps((req)->length) : (req)->length) |
| 122 | |
| 123 | #include <X11/extensions/bigreqsproto.h> |
| 124 | |
| 125 | #define get_big_req_len(req,cli) ((cli)->swapped ? \ |
| 126 | lswapl(((xBigReq *)(req))->length) : \ |
| 127 | ((xBigReq *)(req))->length) |
| 128 | |
| 129 | #define MAX_TIMES_PER 10 |
| 130 | #define BUFSIZE 4096 |
| 131 | #define BUFWATERMARK 8192 |
| 132 | |
| 133 | /* |
| 134 | * A lot of the code in this file manipulates a ConnectionInputPtr: |
| 135 | * |
| 136 | * ----------------------------------------------- |
| 137 | * |------- bufcnt ------->| | | |
| 138 | * | |- gotnow ->| | | |
| 139 | * | |-------- needed ------>| | |
| 140 | * |-----------+--------- size --------+---------->| |
| 141 | * ----------------------------------------------- |
| 142 | * ^ ^ |
| 143 | * | | |
| 144 | * buffer bufptr |
| 145 | * |
| 146 | * buffer is a pointer to the start of the buffer. |
| 147 | * bufptr points to the start of the current request. |
| 148 | * bufcnt counts how many bytes are in the buffer. |
| 149 | * size is the size of the buffer in bytes. |
| 150 | * |
| 151 | * In several of the functions, gotnow and needed are local variables |
| 152 | * that do the following: |
| 153 | * |
| 154 | * gotnow is the number of bytes of the request that we're |
| 155 | * trying to read that are currently in the buffer. |
| 156 | * Typically, gotnow = (buffer + bufcnt) - bufptr |
| 157 | * |
| 158 | * needed = the length of the request that we're trying to |
| 159 | * read. Watch out: needed sometimes counts bytes and sometimes |
| 160 | * counts CARD32's. |
| 161 | */ |
| 162 | |
| 163 | /***************************************************************** |
| 164 | * ReadRequestFromClient |
| 165 | * Returns one request in client->requestBuffer. The request |
| 166 | * length will be in client->req_len. Return status is: |
| 167 | * |
| 168 | * > 0 if successful, specifies length in bytes of the request |
| 169 | * = 0 if entire request is not yet available |
| 170 | * < 0 if client should be terminated |
| 171 | * |
| 172 | * The request returned must be contiguous so that it can be |
| 173 | * cast in the dispatcher to the correct request type. Because requests |
| 174 | * are variable length, ReadRequestFromClient() must look at the first 4 |
| 175 | * or 8 bytes of a request to determine the length (the request length is |
| 176 | * in the 3rd and 4th bytes of the request unless it is a Big Request |
| 177 | * (see the Big Request Extension), in which case the 3rd and 4th bytes |
| 178 | * are zero and the following 4 bytes are the request length. |
| 179 | * |
| 180 | * Note: in order to make the server scheduler (WaitForSomething()) |
| 181 | * "fair", the ClientsWithInput mask is used. This mask tells which |
| 182 | * clients have FULL requests left in their buffers. Clients with |
| 183 | * partial requests require a read. Basically, client buffers |
| 184 | * are drained before select() is called again. But, we can't keep |
| 185 | * reading from a client that is sending buckets of data (or has |
| 186 | * a partial request) because others clients need to be scheduled. |
| 187 | *****************************************************************/ |
| 188 | |
| 189 | static void |
| 190 | YieldControl(void) |
| 191 | { |
| 192 | isItTimeToYield = TRUE; |
| 193 | timesThisConnection = 0; |
| 194 | } |
| 195 | |
| 196 | static void |
| 197 | YieldControlNoInput(int fd) |
| 198 | { |
| 199 | YieldControl(); |
| 200 | FD_CLR(fd, &ClientsWithInput); |
| 201 | } |
| 202 | |
| 203 | static void |
| 204 | YieldControlDeath(void) |
| 205 | { |
| 206 | timesThisConnection = 0; |
| 207 | } |
| 208 | |
| 209 | /* If an input buffer was empty, either free it if it is too big or link it |
| 210 | * into our list of free input buffers. This means that different clients can |
| 211 | * share the same input buffer (at different times). This was done to save |
| 212 | * memory. |
| 213 | */ |
| 214 | static void |
| 215 | NextAvailableInput(OsCommPtr oc) |
| 216 | { |
| 217 | if (AvailableInput) { |
| 218 | if (AvailableInput != oc) { |
| 219 | ConnectionInputPtr aci = AvailableInput->input; |
| 220 | |
| 221 | if (aci->size > BUFWATERMARK) { |
| 222 | free(aci->buffer); |
| 223 | free(aci); |
| 224 | } |
| 225 | else { |
| 226 | aci->next = FreeInputs; |
| 227 | FreeInputs = aci; |
| 228 | } |
| 229 | AvailableInput->input = NULL; |
| 230 | } |
| 231 | AvailableInput = NULL; |
| 232 | } |
| 233 | } |
| 234 | |
| 235 | int |
| 236 | ReadRequestFromClient(ClientPtr client) |
| 237 | { |
| 238 | OsCommPtr oc = (OsCommPtr) client->osPrivate; |
| 239 | ConnectionInputPtr oci = oc->input; |
| 240 | int fd = oc->fd; |
| 241 | unsigned int gotnow, needed; |
| 242 | int result; |
| 243 | register xReq *request; |
| 244 | Bool need_header; |
| 245 | Bool move_header; |
| 246 | |
| 247 | NextAvailableInput(oc); |
| 248 | |
| 249 | /* make sure we have an input buffer */ |
| 250 | |
| 251 | if (!oci) { |
| 252 | if ((oci = FreeInputs)) { |
| 253 | FreeInputs = oci->next; |
| 254 | } |
| 255 | else if (!(oci = AllocateInputBuffer())) { |
| 256 | YieldControlDeath(); |
| 257 | return -1; |
| 258 | } |
| 259 | oc->input = oci; |
| 260 | } |
| 261 | |
| 262 | #if XTRANS_SEND_FDS |
| 263 | /* Discard any unused file descriptors */ |
| 264 | while (client->req_fds > 0) { |
| 265 | int req_fd = ReadFdFromClient(client); |
| 266 | if (req_fd >= 0) |
| 267 | close(req_fd); |
| 268 | } |
| 269 | #endif |
| 270 | /* advance to start of next request */ |
| 271 | |
| 272 | oci->bufptr += oci->lenLastReq; |
| 273 | |
| 274 | need_header = FALSE; |
| 275 | move_header = FALSE; |
| 276 | gotnow = oci->bufcnt + oci->buffer - oci->bufptr; |
| 277 | |
| 278 | if (oci->ignoreBytes > 0) { |
| 279 | if (oci->ignoreBytes > oci->size) |
| 280 | needed = oci->size; |
| 281 | else |
| 282 | needed = oci->ignoreBytes; |
| 283 | } |
| 284 | else if (gotnow < sizeof(xReq)) { |
| 285 | /* We don't have an entire xReq yet. Can't tell how big |
| 286 | * the request will be until we get the whole xReq. |
| 287 | */ |
| 288 | needed = sizeof(xReq); |
| 289 | need_header = TRUE; |
| 290 | } |
| 291 | else { |
| 292 | /* We have a whole xReq. We can tell how big the whole |
| 293 | * request will be unless it is a Big Request. |
| 294 | */ |
| 295 | request = (xReq *) oci->bufptr; |
| 296 | needed = get_req_len(request, client); |
| 297 | if (!needed && client->big_requests) { |
| 298 | /* It's a Big Request. */ |
| 299 | move_header = TRUE; |
| 300 | if (gotnow < sizeof(xBigReq)) { |
| 301 | /* Still need more data to tell just how big. */ |
| 302 | needed = bytes_to_int32(sizeof(xBigReq)); /* needed is in CARD32s now */ |
| 303 | need_header = TRUE; |
| 304 | } |
| 305 | else |
| 306 | needed = get_big_req_len(request, client); |
| 307 | } |
| 308 | client->req_len = needed; |
| 309 | needed <<= 2; /* needed is in bytes now */ |
| 310 | } |
| 311 | if (gotnow < needed) { |
| 312 | /* Need to read more data, either so that we can get a |
| 313 | * complete xReq (if need_header is TRUE), a complete |
| 314 | * xBigReq (if move_header is TRUE), or the rest of the |
| 315 | * request (if need_header and move_header are both FALSE). |
| 316 | */ |
| 317 | |
| 318 | oci->lenLastReq = 0; |
| 319 | if (needed > maxBigRequestSize << 2) { |
| 320 | /* request is too big for us to handle */ |
| 321 | /* |
| 322 | * Mark the rest of it as needing to be ignored, and then return |
| 323 | * the full size. Dispatch() will turn it into a BadLength error. |
| 324 | */ |
| 325 | oci->ignoreBytes = needed - gotnow; |
| 326 | oci->lenLastReq = gotnow; |
| 327 | return needed; |
| 328 | } |
| 329 | if ((gotnow == 0) || ((oci->bufptr - oci->buffer + needed) > oci->size)) { |
| 330 | /* no data, or the request is too big to fit in the buffer */ |
| 331 | |
| 332 | if ((gotnow > 0) && (oci->bufptr != oci->buffer)) |
| 333 | /* save the data we've already read */ |
| 334 | memmove(oci->buffer, oci->bufptr, gotnow); |
| 335 | if (needed > oci->size) { |
| 336 | /* make buffer bigger to accomodate request */ |
| 337 | char *ibuf; |
| 338 | |
| 339 | ibuf = (char *) realloc(oci->buffer, needed); |
| 340 | if (!ibuf) { |
| 341 | YieldControlDeath(); |
| 342 | return -1; |
| 343 | } |
| 344 | oci->size = needed; |
| 345 | oci->buffer = ibuf; |
| 346 | } |
| 347 | oci->bufptr = oci->buffer; |
| 348 | oci->bufcnt = gotnow; |
| 349 | } |
| 350 | /* XXX this is a workaround. This function is sometimes called |
| 351 | * after the trans_conn has been freed. In this case trans_conn |
| 352 | * will be null. Really ought to restructure things so that we |
| 353 | * never get here in those circumstances. |
| 354 | */ |
| 355 | if (!oc->trans_conn) { |
| 356 | /* treat as if an error occured on the read, which is what |
| 357 | * used to happen |
| 358 | */ |
| 359 | YieldControlDeath(); |
| 360 | return -1; |
| 361 | } |
| 362 | result = _XSERVTransRead(oc->trans_conn, oci->buffer + oci->bufcnt, |
| 363 | oci->size - oci->bufcnt); |
| 364 | if (result <= 0) { |
| 365 | if ((result < 0) && ETEST(errno)) { |
| 366 | #if defined(SVR4) && defined(__i386__) && !defined(sun) |
| 367 | if (0) |
| 368 | #endif |
| 369 | { |
| 370 | YieldControlNoInput(fd); |
| 371 | return 0; |
| 372 | } |
| 373 | } |
| 374 | YieldControlDeath(); |
| 375 | return -1; |
| 376 | } |
| 377 | oci->bufcnt += result; |
| 378 | gotnow += result; |
| 379 | /* free up some space after huge requests */ |
| 380 | if ((oci->size > BUFWATERMARK) && |
| 381 | (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) { |
| 382 | char *ibuf; |
| 383 | |
| 384 | ibuf = (char *) realloc(oci->buffer, BUFSIZE); |
| 385 | if (ibuf) { |
| 386 | oci->size = BUFSIZE; |
| 387 | oci->buffer = ibuf; |
| 388 | oci->bufptr = ibuf + oci->bufcnt - gotnow; |
| 389 | } |
| 390 | } |
| 391 | if (need_header && gotnow >= needed) { |
| 392 | /* We wanted an xReq, now we've gotten it. */ |
| 393 | request = (xReq *) oci->bufptr; |
| 394 | needed = get_req_len(request, client); |
| 395 | if (!needed && client->big_requests) { |
| 396 | move_header = TRUE; |
| 397 | if (gotnow < sizeof(xBigReq)) |
| 398 | needed = bytes_to_int32(sizeof(xBigReq)); |
| 399 | else |
| 400 | needed = get_big_req_len(request, client); |
| 401 | } |
| 402 | client->req_len = needed; |
| 403 | needed <<= 2; |
| 404 | } |
| 405 | if (gotnow < needed) { |
| 406 | /* Still don't have enough; punt. */ |
| 407 | YieldControlNoInput(fd); |
| 408 | return 0; |
| 409 | } |
| 410 | } |
| 411 | if (needed == 0) { |
| 412 | if (client->big_requests) |
| 413 | needed = sizeof(xBigReq); |
| 414 | else |
| 415 | needed = sizeof(xReq); |
| 416 | } |
| 417 | |
| 418 | /* If there are bytes to ignore, ignore them now. */ |
| 419 | |
| 420 | if (oci->ignoreBytes > 0) { |
| 421 | assert(needed == oci->ignoreBytes || needed == oci->size); |
| 422 | /* |
| 423 | * The _XSERVTransRead call above may return more or fewer bytes than we |
| 424 | * want to ignore. Ignore the smaller of the two sizes. |
| 425 | */ |
| 426 | if (gotnow < needed) { |
| 427 | oci->ignoreBytes -= gotnow; |
| 428 | oci->bufptr += gotnow; |
| 429 | gotnow = 0; |
| 430 | } |
| 431 | else { |
| 432 | oci->ignoreBytes -= needed; |
| 433 | oci->bufptr += needed; |
| 434 | gotnow -= needed; |
| 435 | } |
| 436 | needed = 0; |
| 437 | } |
| 438 | |
| 439 | oci->lenLastReq = needed; |
| 440 | |
| 441 | /* |
| 442 | * Check to see if client has at least one whole request in the |
| 443 | * buffer beyond the request we're returning to the caller. |
| 444 | * If there is only a partial request, treat like buffer |
| 445 | * is empty so that select() will be called again and other clients |
| 446 | * can get into the queue. |
| 447 | */ |
| 448 | |
| 449 | gotnow -= needed; |
| 450 | if (gotnow >= sizeof(xReq)) { |
| 451 | request = (xReq *) (oci->bufptr + needed); |
| 452 | if (gotnow >= (result = (get_req_len(request, client) << 2)) |
| 453 | && (result || |
| 454 | (client->big_requests && |
| 455 | (gotnow >= sizeof(xBigReq) && |
| 456 | gotnow >= (get_big_req_len(request, client) << 2)))) |
| 457 | ) |
| 458 | FD_SET(fd, &ClientsWithInput); |
| 459 | else { |
| 460 | if (!SmartScheduleDisable) |
| 461 | FD_CLR(fd, &ClientsWithInput); |
| 462 | else |
| 463 | YieldControlNoInput(fd); |
| 464 | } |
| 465 | } |
| 466 | else { |
| 467 | if (!gotnow) |
| 468 | AvailableInput = oc; |
| 469 | if (!SmartScheduleDisable) |
| 470 | FD_CLR(fd, &ClientsWithInput); |
| 471 | else |
| 472 | YieldControlNoInput(fd); |
| 473 | } |
| 474 | if (SmartScheduleDisable) |
| 475 | if (++timesThisConnection >= MAX_TIMES_PER) |
| 476 | YieldControl(); |
| 477 | if (move_header) { |
| 478 | request = (xReq *) oci->bufptr; |
| 479 | oci->bufptr += (sizeof(xBigReq) - sizeof(xReq)); |
| 480 | *(xReq *) oci->bufptr = *request; |
| 481 | oci->lenLastReq -= (sizeof(xBigReq) - sizeof(xReq)); |
| 482 | client->req_len -= bytes_to_int32(sizeof(xBigReq) - sizeof(xReq)); |
| 483 | } |
| 484 | client->requestBuffer = (pointer) oci->bufptr; |
| 485 | #ifdef DEBUG_COMMUNICATION |
| 486 | { |
| 487 | xReq *req = client->requestBuffer; |
| 488 | |
| 489 | ErrorF("REQUEST: ClientIDX: %i, type: 0x%x data: 0x%x len: %i\n", |
| 490 | client->index, req->reqType, req->data, req->length); |
| 491 | } |
| 492 | #endif |
| 493 | return needed; |
| 494 | } |
| 495 | |
| 496 | #if XTRANS_SEND_FDS |
| 497 | int |
| 498 | ReadFdFromClient(ClientPtr client) |
| 499 | { |
| 500 | int fd = -1; |
| 501 | |
| 502 | if (client->req_fds > 0) { |
| 503 | OsCommPtr oc = (OsCommPtr) client->osPrivate; |
| 504 | |
| 505 | --client->req_fds; |
| 506 | fd = _XSERVTransRecvFd(oc->trans_conn); |
| 507 | } else |
| 508 | LogMessage(X_ERROR, "Request asks for FD without setting req_fds\n"); |
| 509 | return fd; |
| 510 | } |
| 511 | |
| 512 | int |
| 513 | WriteFdToClient(ClientPtr client, int fd, Bool do_close) |
| 514 | { |
| 515 | OsCommPtr oc = (OsCommPtr) client->osPrivate; |
| 516 | |
| 517 | return _XSERVTransSendFd(oc->trans_conn, fd, do_close); |
| 518 | } |
| 519 | #endif |
| 520 | |
| 521 | /***************************************************************** |
| 522 | * InsertFakeRequest |
| 523 | * Splice a consed up (possibly partial) request in as the next request. |
| 524 | * |
| 525 | **********************/ |
| 526 | |
| 527 | Bool |
| 528 | InsertFakeRequest(ClientPtr client, char *data, int count) |
| 529 | { |
| 530 | OsCommPtr oc = (OsCommPtr) client->osPrivate; |
| 531 | ConnectionInputPtr oci = oc->input; |
| 532 | int fd = oc->fd; |
| 533 | int gotnow, moveup; |
| 534 | |
| 535 | NextAvailableInput(oc); |
| 536 | |
| 537 | if (!oci) { |
| 538 | if ((oci = FreeInputs)) |
| 539 | FreeInputs = oci->next; |
| 540 | else if (!(oci = AllocateInputBuffer())) |
| 541 | return FALSE; |
| 542 | oc->input = oci; |
| 543 | } |
| 544 | oci->bufptr += oci->lenLastReq; |
| 545 | oci->lenLastReq = 0; |
| 546 | gotnow = oci->bufcnt + oci->buffer - oci->bufptr; |
| 547 | if ((gotnow + count) > oci->size) { |
| 548 | char *ibuf; |
| 549 | |
| 550 | ibuf = (char *) realloc(oci->buffer, gotnow + count); |
| 551 | if (!ibuf) |
| 552 | return FALSE; |
| 553 | oci->size = gotnow + count; |
| 554 | oci->buffer = ibuf; |
| 555 | oci->bufptr = ibuf + oci->bufcnt - gotnow; |
| 556 | } |
| 557 | moveup = count - (oci->bufptr - oci->buffer); |
| 558 | if (moveup > 0) { |
| 559 | if (gotnow > 0) |
| 560 | memmove(oci->bufptr + moveup, oci->bufptr, gotnow); |
| 561 | oci->bufptr += moveup; |
| 562 | oci->bufcnt += moveup; |
| 563 | } |
| 564 | memmove(oci->bufptr - count, data, count); |
| 565 | oci->bufptr -= count; |
| 566 | gotnow += count; |
| 567 | if ((gotnow >= sizeof(xReq)) && |
| 568 | (gotnow >= (int) (get_req_len((xReq *) oci->bufptr, client) << 2))) |
| 569 | FD_SET(fd, &ClientsWithInput); |
| 570 | else |
| 571 | YieldControlNoInput(fd); |
| 572 | return TRUE; |
| 573 | } |
| 574 | |
| 575 | /***************************************************************** |
| 576 | * ResetRequestFromClient |
| 577 | * Reset to reexecute the current request, and yield. |
| 578 | * |
| 579 | **********************/ |
| 580 | |
| 581 | void |
| 582 | ResetCurrentRequest(ClientPtr client) |
| 583 | { |
| 584 | OsCommPtr oc = (OsCommPtr) client->osPrivate; |
| 585 | register ConnectionInputPtr oci = oc->input; |
| 586 | int fd = oc->fd; |
| 587 | register xReq *request; |
| 588 | int gotnow, needed; |
| 589 | |
| 590 | if (AvailableInput == oc) |
| 591 | AvailableInput = (OsCommPtr) NULL; |
| 592 | oci->lenLastReq = 0; |
| 593 | gotnow = oci->bufcnt + oci->buffer - oci->bufptr; |
| 594 | if (gotnow < sizeof(xReq)) { |
| 595 | YieldControlNoInput(fd); |
| 596 | } |
| 597 | else { |
| 598 | request = (xReq *) oci->bufptr; |
| 599 | needed = get_req_len(request, client); |
| 600 | if (!needed && client->big_requests) { |
| 601 | oci->bufptr -= sizeof(xBigReq) - sizeof(xReq); |
| 602 | *(xReq *) oci->bufptr = *request; |
| 603 | ((xBigReq *) oci->bufptr)->length = client->req_len; |
| 604 | if (client->swapped) { |
| 605 | swapl(&((xBigReq *) oci->bufptr)->length); |
| 606 | } |
| 607 | } |
| 608 | if (gotnow >= (needed << 2)) { |
| 609 | if (FD_ISSET(fd, &AllClients)) { |
| 610 | FD_SET(fd, &ClientsWithInput); |
| 611 | } |
| 612 | else { |
| 613 | FD_SET(fd, &IgnoredClientsWithInput); |
| 614 | } |
| 615 | YieldControl(); |
| 616 | } |
| 617 | else |
| 618 | YieldControlNoInput(fd); |
| 619 | } |
| 620 | } |
| 621 | |
| 622 | /******************** |
| 623 | * FlushAllOutput() |
| 624 | * Flush all clients with output. However, if some client still |
| 625 | * has input in the queue (more requests), then don't flush. This |
| 626 | * will prevent the output queue from being flushed every time around |
| 627 | * the round robin queue. Now, some say that it SHOULD be flushed |
| 628 | * every time around, but... |
| 629 | * |
| 630 | **********************/ |
| 631 | |
| 632 | void |
| 633 | FlushAllOutput(void) |
| 634 | { |
| 635 | register int index, base; |
| 636 | register fd_mask mask; /* raphael */ |
| 637 | OsCommPtr oc; |
| 638 | register ClientPtr client; |
| 639 | Bool newoutput = NewOutputPending; |
| 640 | |
| 641 | #if defined(WIN32) |
| 642 | fd_set newOutputPending; |
| 643 | #endif |
| 644 | |
| 645 | if (FlushCallback) |
| 646 | CallCallbacks(&FlushCallback, NULL); |
| 647 | |
| 648 | if (!newoutput) |
| 649 | return; |
| 650 | |
| 651 | /* |
| 652 | * It may be that some client still has critical output pending, |
| 653 | * but he is not yet ready to receive it anyway, so we will |
| 654 | * simply wait for the select to tell us when he's ready to receive. |
| 655 | */ |
| 656 | CriticalOutputPending = FALSE; |
| 657 | NewOutputPending = FALSE; |
| 658 | |
| 659 | #ifndef WIN32 |
| 660 | for (base = 0; base < howmany(XFD_SETSIZE, NFDBITS); base++) { |
| 661 | mask = OutputPending.fds_bits[base]; |
| 662 | OutputPending.fds_bits[base] = 0; |
| 663 | while (mask) { |
| 664 | index = ffs(mask) - 1; |
| 665 | mask &= ~lowbit(mask); |
| 666 | if ((index = |
| 667 | ConnectionTranslation[(base * (sizeof(fd_mask) * 8)) + |
| 668 | index]) == 0) |
| 669 | continue; |
| 670 | client = clients[index]; |
| 671 | if (client->clientGone) |
| 672 | continue; |
| 673 | oc = (OsCommPtr) client->osPrivate; |
| 674 | if (FD_ISSET(oc->fd, &ClientsWithInput)) { |
| 675 | FD_SET(oc->fd, &OutputPending); /* set the bit again */ |
| 676 | NewOutputPending = TRUE; |
| 677 | } |
| 678 | else |
| 679 | (void) FlushClient(client, oc, (char *) NULL, 0); |
| 680 | } |
| 681 | } |
| 682 | #else /* WIN32 */ |
| 683 | FD_ZERO(&newOutputPending); |
| 684 | for (base = 0; base < XFD_SETCOUNT(&OutputPending); base++) { |
| 685 | index = XFD_FD(&OutputPending, base); |
| 686 | if ((index = GetConnectionTranslation(index)) == 0) |
| 687 | continue; |
| 688 | client = clients[index]; |
| 689 | if (client->clientGone) |
| 690 | continue; |
| 691 | oc = (OsCommPtr) client->osPrivate; |
| 692 | if (FD_ISSET(oc->fd, &ClientsWithInput)) { |
| 693 | FD_SET(oc->fd, &newOutputPending); /* set the bit again */ |
| 694 | NewOutputPending = TRUE; |
| 695 | } |
| 696 | else |
| 697 | (void) FlushClient(client, oc, (char *) NULL, 0); |
| 698 | } |
| 699 | XFD_COPYSET(&newOutputPending, &OutputPending); |
| 700 | #endif /* WIN32 */ |
| 701 | } |
| 702 | |
| 703 | void |
| 704 | FlushIfCriticalOutputPending(void) |
| 705 | { |
| 706 | if (CriticalOutputPending) |
| 707 | FlushAllOutput(); |
| 708 | } |
| 709 | |
| 710 | void |
| 711 | SetCriticalOutputPending(void) |
| 712 | { |
| 713 | CriticalOutputPending = TRUE; |
| 714 | } |
| 715 | |
| 716 | /***************** |
| 717 | * WriteToClient |
| 718 | * Copies buf into ClientPtr.buf if it fits (with padding), else |
| 719 | * flushes ClientPtr.buf and buf to client. As of this writing, |
| 720 | * every use of WriteToClient is cast to void, and the result |
| 721 | * is ignored. Potentially, this could be used by requests |
| 722 | * that are sending several chunks of data and want to break |
| 723 | * out of a loop on error. Thus, we will leave the type of |
| 724 | * this routine as int. |
| 725 | *****************/ |
| 726 | |
| 727 | int |
| 728 | WriteToClient(ClientPtr who, int count, const void *__buf) |
| 729 | { |
| 730 | OsCommPtr oc; |
| 731 | ConnectionOutputPtr oco; |
| 732 | int padBytes; |
| 733 | const char *buf = __buf; |
| 734 | |
| 735 | #ifdef DEBUG_COMMUNICATION |
| 736 | Bool multicount = FALSE; |
| 737 | #endif |
| 738 | if (!count || !who || who == serverClient || who->clientGone) |
| 739 | return 0; |
| 740 | oc = who->osPrivate; |
| 741 | oco = oc->output; |
| 742 | #ifdef DEBUG_COMMUNICATION |
| 743 | { |
| 744 | char info[128]; |
| 745 | xError *err; |
| 746 | xGenericReply *rep; |
| 747 | xEvent *ev; |
| 748 | |
| 749 | if (!who->replyBytesRemaining) { |
| 750 | switch (buf[0]) { |
| 751 | case X_Reply: |
| 752 | rep = (xGenericReply *) buf; |
| 753 | if (rep->sequenceNumber == who->sequence) { |
| 754 | snprintf(info, 127, "Xreply: type: 0x%x data: 0x%x " |
| 755 | "len: %i seq#: 0x%x", rep->type, rep->data1, |
| 756 | rep->length, rep->sequenceNumber); |
| 757 | multicount = TRUE; |
| 758 | } |
| 759 | break; |
| 760 | case X_Error: |
| 761 | err = (xError *) buf; |
| 762 | snprintf(info, 127, "Xerror: Code: 0x%x resID: 0x%x maj: 0x%x " |
| 763 | "min: %x", err->errorCode, err->resourceID, |
| 764 | err->minorCode, err->majorCode); |
| 765 | break; |
| 766 | default: |
| 767 | if ((buf[0] & 0x7f) == KeymapNotify) |
| 768 | snprintf(info, 127, "KeymapNotifyEvent: %i", buf[0]); |
| 769 | else { |
| 770 | ev = (xEvent *) buf; |
| 771 | snprintf(info, 127, "XEvent: type: 0x%x detail: 0x%x " |
| 772 | "seq#: 0x%x", ev->u.u.type, ev->u.u.detail, |
| 773 | ev->u.u.sequenceNumber); |
| 774 | } |
| 775 | } |
| 776 | ErrorF("REPLY: ClientIDX: %i %s\n", who->index, info); |
| 777 | } |
| 778 | else |
| 779 | multicount = TRUE; |
| 780 | } |
| 781 | #endif |
| 782 | |
| 783 | if (!oco) { |
| 784 | if ((oco = FreeOutputs)) { |
| 785 | FreeOutputs = oco->next; |
| 786 | } |
| 787 | else if (!(oco = AllocateOutputBuffer())) { |
| 788 | if (oc->trans_conn) { |
| 789 | _XSERVTransDisconnect(oc->trans_conn); |
| 790 | _XSERVTransClose(oc->trans_conn); |
| 791 | oc->trans_conn = NULL; |
| 792 | } |
| 793 | MarkClientException(who); |
| 794 | return -1; |
| 795 | } |
| 796 | oc->output = oco; |
| 797 | } |
| 798 | |
| 799 | padBytes = padding_for_int32(count); |
| 800 | |
| 801 | if (ReplyCallback) { |
| 802 | ReplyInfoRec replyinfo; |
| 803 | |
| 804 | replyinfo.client = who; |
| 805 | replyinfo.replyData = buf; |
| 806 | replyinfo.dataLenBytes = count + padBytes; |
| 807 | replyinfo.padBytes = padBytes; |
| 808 | if (who->replyBytesRemaining) { /* still sending data of an earlier reply */ |
| 809 | who->replyBytesRemaining -= count + padBytes; |
| 810 | replyinfo.startOfReply = FALSE; |
| 811 | replyinfo.bytesRemaining = who->replyBytesRemaining; |
| 812 | CallCallbacks((&ReplyCallback), (pointer) &replyinfo); |
| 813 | } |
| 814 | else if (who->clientState == ClientStateRunning && buf[0] == X_Reply) { /* start of new reply */ |
| 815 | CARD32 replylen; |
| 816 | unsigned long bytesleft; |
| 817 | |
| 818 | replylen = ((const xGenericReply *) buf)->length; |
| 819 | if (who->swapped) |
| 820 | swapl(&replylen); |
| 821 | bytesleft = (replylen * 4) + SIZEOF(xReply) - count - padBytes; |
| 822 | replyinfo.startOfReply = TRUE; |
| 823 | replyinfo.bytesRemaining = who->replyBytesRemaining = bytesleft; |
| 824 | CallCallbacks((&ReplyCallback), (pointer) &replyinfo); |
| 825 | } |
| 826 | } |
| 827 | #ifdef DEBUG_COMMUNICATION |
| 828 | else if (multicount) { |
| 829 | if (who->replyBytesRemaining) { |
| 830 | who->replyBytesRemaining -= (count + padBytes); |
| 831 | } |
| 832 | else { |
| 833 | CARD32 replylen; |
| 834 | |
| 835 | replylen = ((xGenericReply *) buf)->length; |
| 836 | who->replyBytesRemaining = |
| 837 | (replylen * 4) + SIZEOF(xReply) - count - padBytes; |
| 838 | } |
| 839 | } |
| 840 | #endif |
| 841 | if (oco->count == 0 || oco->count + count + padBytes > oco->size) { |
| 842 | FD_CLR(oc->fd, &OutputPending); |
| 843 | if (!XFD_ANYSET(&OutputPending)) { |
| 844 | CriticalOutputPending = FALSE; |
| 845 | NewOutputPending = FALSE; |
| 846 | } |
| 847 | |
| 848 | if (FlushCallback) |
| 849 | CallCallbacks(&FlushCallback, NULL); |
| 850 | |
| 851 | return FlushClient(who, oc, buf, count); |
| 852 | } |
| 853 | |
| 854 | NewOutputPending = TRUE; |
| 855 | FD_SET(oc->fd, &OutputPending); |
| 856 | memmove((char *) oco->buf + oco->count, buf, count); |
| 857 | oco->count += count; |
| 858 | if (padBytes) { |
| 859 | memset(oco->buf + oco->count, '\0', padBytes); |
| 860 | oco->count += padBytes; |
| 861 | } |
| 862 | return count; |
| 863 | } |
| 864 | |
| 865 | /******************** |
| 866 | * FlushClient() |
| 867 | * If the client isn't keeping up with us, then we try to continue |
| 868 | * buffering the data and set the apropriate bit in ClientsWritable |
| 869 | * (which is used by WaitFor in the select). If the connection yields |
| 870 | * a permanent error, or we can't allocate any more space, we then |
| 871 | * close the connection. |
| 872 | * |
| 873 | **********************/ |
| 874 | |
| 875 | int |
| 876 | FlushClient(ClientPtr who, OsCommPtr oc, const void *__extraBuf, int extraCount) |
| 877 | { |
| 878 | ConnectionOutputPtr oco = oc->output; |
| 879 | int connection = oc->fd; |
| 880 | XtransConnInfo trans_conn = oc->trans_conn; |
| 881 | struct iovec iov[3]; |
| 882 | static char padBuffer[3]; |
| 883 | const char *extraBuf = __extraBuf; |
| 884 | long written; |
| 885 | long padsize; |
| 886 | long notWritten; |
| 887 | long todo; |
| 888 | |
| 889 | if (!oco) |
| 890 | return 0; |
| 891 | written = 0; |
| 892 | padsize = padding_for_int32(extraCount); |
| 893 | notWritten = oco->count + extraCount + padsize; |
| 894 | if (!notWritten) |
| 895 | return 0; |
| 896 | |
| 897 | todo = notWritten; |
| 898 | while (notWritten) { |
| 899 | long before = written; /* amount of whole thing written */ |
| 900 | long remain = todo; /* amount to try this time, <= notWritten */ |
| 901 | int i = 0; |
| 902 | long len; |
| 903 | |
| 904 | /* You could be very general here and have "in" and "out" iovecs |
| 905 | * and write a loop without using a macro, but what the heck. This |
| 906 | * translates to: |
| 907 | * |
| 908 | * how much of this piece is new? |
| 909 | * if more new then we are trying this time, clamp |
| 910 | * if nothing new |
| 911 | * then bump down amount already written, for next piece |
| 912 | * else put new stuff in iovec, will need all of next piece |
| 913 | * |
| 914 | * Note that todo had better be at least 1 or else we'll end up |
| 915 | * writing 0 iovecs. |
| 916 | */ |
| 917 | #define InsertIOV(pointer, length) \ |
| 918 | len = (length) - before; \ |
| 919 | if (len > remain) \ |
| 920 | len = remain; \ |
| 921 | if (len <= 0) { \ |
| 922 | before = (-len); \ |
| 923 | } else { \ |
| 924 | iov[i].iov_len = len; \ |
| 925 | iov[i].iov_base = (pointer) + before; \ |
| 926 | i++; \ |
| 927 | remain -= len; \ |
| 928 | before = 0; \ |
| 929 | } |
| 930 | |
| 931 | InsertIOV((char *) oco->buf, oco->count) |
| 932 | InsertIOV((char *) extraBuf, extraCount) |
| 933 | InsertIOV(padBuffer, padsize) |
| 934 | |
| 935 | errno = 0; |
| 936 | if (trans_conn && (len = _XSERVTransWritev(trans_conn, iov, i)) >= 0) { |
| 937 | written += len; |
| 938 | notWritten -= len; |
| 939 | todo = notWritten; |
| 940 | } |
| 941 | else if (ETEST(errno) |
| 942 | #ifdef SUNSYSV /* check for another brain-damaged OS bug */ |
| 943 | || (errno == 0) |
| 944 | #endif |
| 945 | #ifdef EMSGSIZE /* check for another brain-damaged OS bug */ |
| 946 | || ((errno == EMSGSIZE) && (todo == 1)) |
| 947 | #endif |
| 948 | ) { |
| 949 | /* If we've arrived here, then the client is stuffed to the gills |
| 950 | and not ready to accept more. Make a note of it and buffer |
| 951 | the rest. */ |
| 952 | FD_SET(connection, &ClientsWriteBlocked); |
| 953 | AnyClientsWriteBlocked = TRUE; |
| 954 | |
| 955 | if (written < oco->count) { |
| 956 | if (written > 0) { |
| 957 | oco->count -= written; |
| 958 | memmove((char *) oco->buf, |
| 959 | (char *) oco->buf + written, oco->count); |
| 960 | written = 0; |
| 961 | } |
| 962 | } |
| 963 | else { |
| 964 | written -= oco->count; |
| 965 | oco->count = 0; |
| 966 | } |
| 967 | |
| 968 | if (notWritten > oco->size) { |
| 969 | unsigned char *obuf; |
| 970 | |
| 971 | obuf = (unsigned char *) realloc(oco->buf, |
| 972 | notWritten + BUFSIZE); |
| 973 | if (!obuf) { |
| 974 | _XSERVTransDisconnect(oc->trans_conn); |
| 975 | _XSERVTransClose(oc->trans_conn); |
| 976 | oc->trans_conn = NULL; |
| 977 | MarkClientException(who); |
| 978 | oco->count = 0; |
| 979 | return -1; |
| 980 | } |
| 981 | oco->size = notWritten + BUFSIZE; |
| 982 | oco->buf = obuf; |
| 983 | } |
| 984 | |
| 985 | /* If the amount written extended into the padBuffer, then the |
| 986 | difference "extraCount - written" may be less than 0 */ |
| 987 | if ((len = extraCount - written) > 0) |
| 988 | memmove((char *) oco->buf + oco->count, |
| 989 | extraBuf + written, len); |
| 990 | |
| 991 | oco->count = notWritten; /* this will include the pad */ |
| 992 | /* return only the amount explicitly requested */ |
| 993 | return extraCount; |
| 994 | } |
| 995 | #ifdef EMSGSIZE /* check for another brain-damaged OS bug */ |
| 996 | else if (errno == EMSGSIZE) { |
| 997 | todo >>= 1; |
| 998 | } |
| 999 | #endif |
| 1000 | else { |
| 1001 | if (oc->trans_conn) { |
| 1002 | _XSERVTransDisconnect(oc->trans_conn); |
| 1003 | _XSERVTransClose(oc->trans_conn); |
| 1004 | oc->trans_conn = NULL; |
| 1005 | } |
| 1006 | MarkClientException(who); |
| 1007 | oco->count = 0; |
| 1008 | return -1; |
| 1009 | } |
| 1010 | } |
| 1011 | |
| 1012 | /* everything was flushed out */ |
| 1013 | oco->count = 0; |
| 1014 | /* check to see if this client was write blocked */ |
| 1015 | if (AnyClientsWriteBlocked) { |
| 1016 | FD_CLR(oc->fd, &ClientsWriteBlocked); |
| 1017 | if (!XFD_ANYSET(&ClientsWriteBlocked)) |
| 1018 | AnyClientsWriteBlocked = FALSE; |
| 1019 | } |
| 1020 | if (oco->size > BUFWATERMARK) { |
| 1021 | free(oco->buf); |
| 1022 | free(oco); |
| 1023 | } |
| 1024 | else { |
| 1025 | oco->next = FreeOutputs; |
| 1026 | FreeOutputs = oco; |
| 1027 | } |
| 1028 | oc->output = (ConnectionOutputPtr) NULL; |
| 1029 | return extraCount; /* return only the amount explicitly requested */ |
| 1030 | } |
| 1031 | |
| 1032 | static ConnectionInputPtr |
| 1033 | AllocateInputBuffer(void) |
| 1034 | { |
| 1035 | ConnectionInputPtr oci; |
| 1036 | |
| 1037 | oci = malloc(sizeof(ConnectionInput)); |
| 1038 | if (!oci) |
| 1039 | return NULL; |
| 1040 | oci->buffer = malloc(BUFSIZE); |
| 1041 | if (!oci->buffer) { |
| 1042 | free(oci); |
| 1043 | return NULL; |
| 1044 | } |
| 1045 | oci->size = BUFSIZE; |
| 1046 | oci->bufptr = oci->buffer; |
| 1047 | oci->bufcnt = 0; |
| 1048 | oci->lenLastReq = 0; |
| 1049 | oci->ignoreBytes = 0; |
| 1050 | return oci; |
| 1051 | } |
| 1052 | |
| 1053 | static ConnectionOutputPtr |
| 1054 | AllocateOutputBuffer(void) |
| 1055 | { |
| 1056 | ConnectionOutputPtr oco; |
| 1057 | |
| 1058 | oco = malloc(sizeof(ConnectionOutput)); |
| 1059 | if (!oco) |
| 1060 | return NULL; |
| 1061 | oco->buf = calloc(1, BUFSIZE); |
| 1062 | if (!oco->buf) { |
| 1063 | free(oco); |
| 1064 | return NULL; |
| 1065 | } |
| 1066 | oco->size = BUFSIZE; |
| 1067 | oco->count = 0; |
| 1068 | return oco; |
| 1069 | } |
| 1070 | |
| 1071 | void |
| 1072 | FreeOsBuffers(OsCommPtr oc) |
| 1073 | { |
| 1074 | ConnectionInputPtr oci; |
| 1075 | ConnectionOutputPtr oco; |
| 1076 | |
| 1077 | if (AvailableInput == oc) |
| 1078 | AvailableInput = (OsCommPtr) NULL; |
| 1079 | if ((oci = oc->input)) { |
| 1080 | if (FreeInputs) { |
| 1081 | free(oci->buffer); |
| 1082 | free(oci); |
| 1083 | } |
| 1084 | else { |
| 1085 | FreeInputs = oci; |
| 1086 | oci->next = (ConnectionInputPtr) NULL; |
| 1087 | oci->bufptr = oci->buffer; |
| 1088 | oci->bufcnt = 0; |
| 1089 | oci->lenLastReq = 0; |
| 1090 | oci->ignoreBytes = 0; |
| 1091 | } |
| 1092 | } |
| 1093 | if ((oco = oc->output)) { |
| 1094 | if (FreeOutputs) { |
| 1095 | free(oco->buf); |
| 1096 | free(oco); |
| 1097 | } |
| 1098 | else { |
| 1099 | FreeOutputs = oco; |
| 1100 | oco->next = (ConnectionOutputPtr) NULL; |
| 1101 | oco->count = 0; |
| 1102 | } |
| 1103 | } |
| 1104 | } |
| 1105 | |
| 1106 | void |
| 1107 | ResetOsBuffers(void) |
| 1108 | { |
| 1109 | ConnectionInputPtr oci; |
| 1110 | ConnectionOutputPtr oco; |
| 1111 | |
| 1112 | while ((oci = FreeInputs)) { |
| 1113 | FreeInputs = oci->next; |
| 1114 | free(oci->buffer); |
| 1115 | free(oci); |
| 1116 | } |
| 1117 | while ((oco = FreeOutputs)) { |
| 1118 | FreeOutputs = oco->next; |
| 1119 | free(oco->buf); |
| 1120 | free(oco); |
| 1121 | } |
| 1122 | } |