Commit | Line | Data |
---|---|---|
a09e091a JB |
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 | } |