1 /*
2 * Copyright 1993 Network Computing Devices, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name Network Computing Devices, Inc. not be
9 * used in advertising or publicity pertaining to distribution of this
10 * software without specific, written prior permission.
11 *
12 * THIS SOFTWARE IS PROVIDED `AS-IS'. NETWORK COMPUTING DEVICES, INC.,
13 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT
14 * LIMITATION ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
15 * PARTICULAR PURPOSE, OR NONINFRINGEMENT. IN NO EVENT SHALL NETWORK
16 * COMPUTING DEVICES, INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING
17 * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA,
18 * OR PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS OF
19 * WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 *
22 * $NCDId: @(#)io.c,v 1.4 1994/05/27 03:10:44 greg Exp $
23 */
24 /***********************************************************
25 Some portions derived from:
26
27 Copyright 1987, 1989 by Digital Equipment Corporation, Maynard, Massachusetts,
28 and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
29
30 All Rights Reserved
31
32 Permission to use, copy, modify, and distribute this software and its
33 documentation for any purpose and without fee is hereby granted,
34 provided that the above copyright notice appear in all copies and that
35 both that copyright notice and this permission notice appear in
36 supporting documentation, and that the names of Digital or MIT not be
37 used in advertising or publicity pertaining to distribution of the
38 software without specific, written prior permission.
39
40 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
41 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
42 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
43 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
44 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
45 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
46 SOFTWARE.
47
48 ******************************************************************/
49 /*****************************************************************
50 * i/o functions
51 *
52 * WriteToClient, ReadRequestFromClient
53 * InsertFakeRequest, ResetCurrentRequest
54 *
55 *****************************************************************/
56
57 #include <stdio.h>
58 #include <audio/audio.h>
59 #include <audio/Aproto.h>
60 #include <audio/Aos.h>
61 #include <errno.h>
62 #if !defined(AMOEBA) && !defined(_MINIX)
63 #include <sys/param.h>
64 #include <sys/uio.h>
65 #endif
66 #include "os.h"
67 #include "osdep.h"
68 #include "opaque.h"
69 #include "dixstruct.h"
70 #include "misc.h"
71
72 #ifndef SCO
73 #define _OSWriteV writev
74 #endif /* SCO */
75
76 #if !defined(AMOEBA) && !defined(_MINIX)
77 /* check for both EAGAIN and EWOULDBLOCK, because some supposedly POSIX
78 * systems are broken and return EWOULDBLOCK when they should return EAGAIN
79 */
80 #if defined(EAGAIN) && defined(EWOULDBLOCK)
81 #define ETEST(err) (err == EAGAIN || err == EWOULDBLOCK)
82 #else
83 #ifdef EAGAIN
84 #define ETEST(err) (err == EAGAIN)
85 #else
86 #define ETEST(err) (err == EWOULDBLOCK)
87 #endif
88 #endif
89
90 extern void MarkClientException();
91 extern long ClientsWithInput[];
92 extern long ClientsWriteBlocked[];
93 extern long OutputPending[];
94 #endif /* AMOEBA || _MINIX */
95 extern int ConnectionTranslation[];
96 extern Bool NewOutputPending;
97 extern Bool AnyClientsWriteBlocked;
98 static Bool CriticalOutputPending;
99 static int timesThisConnection = 0;
100 #ifndef AMOEBA
101 static ConnectionInputPtr FreeInputs = (ConnectionInputPtr) NULL;
102 static ConnectionOutputPtr FreeOutputs = (ConnectionOutputPtr) NULL;
103 static OsCommPtr AvailableInput = (OsCommPtr) NULL;
104
105 static ConnectionInputPtr AllocateInputBuffer();
106 static ConnectionOutputPtr AllocateOutputBuffer();
107 #endif /* AMOEBA */
108
109 #define get_req_len(req,cli) ((cli)->swapped ? \
110 lswaps((req)->length) : (req)->length)
111
112 #define MAX_TIMES_PER 10
113
114 /*****************************************************************
115 * ReadRequestFromClient
116 * Returns one request in client->requestBuffer. Return status is:
117 *
118 * > 0 if successful, specifies length in bytes of the request
119 * = 0 if entire request is not yet available
120 * < 0 if client should be terminated
121 *
122 * The request returned must be contiguous so that it can be
123 * cast in the dispatcher to the correct request type. Because requests
124 * are variable length, ReadRequestFromClient() must look at the first 4
125 * bytes of a request to determine the length (the request length is
126 * always the 3rd and 4th bytes of the request).
127 *
128 * Note: in order to make the server scheduler (WaitForSomething())
129 * "fair", the ClientsWithInput mask is used. This mask tells which
130 * clients have FULL requests left in their buffers. Clients with
131 * partial requests require a read. Basically, client buffers
132 * are drained before select() is called again. But, we can't keep
133 * reading from a client that is sending buckets of data (or has
134 * a partial request) because others clients need to be scheduled.
135 *****************************************************************/
136
137 #define YieldControl() \
138 { isItTimeToYield = TRUE; \
139 timesThisConnection = 0; }
140 #ifndef _MINIX
141 #define YieldControlNoInput() \
142 { YieldControl(); \
143 BITCLEAR(ClientsWithInput, fd); }
144 #else
145 #define YieldControlNoInput() \
146 { YieldControl(); \
147 ASIO_FD_CLR(fd, ASIO_READ, &CompletedFdSet); }
148 #endif
149 #define YieldControlDeath() \
150 { timesThisConnection = 0; }
151
152 /* lookup table for adding padding bytes to data that is read from
153 or written to the audio connection. */
154 static int padlength[4] = { 0, 3, 2, 1 };
155
156 #if !defined(AMOEBA) && !defined(_MINIX)
157 int
ReadRequestFromClient(client)158 ReadRequestFromClient(client)
159 ClientPtr client;
160 {
161 OsCommPtr oc = (OsCommPtr) client->osPrivate;
162 ConnectionInputPtr oci = oc->input;
163 int fd = oc->fd;
164 int gotnow, needed;
165 int result;
166 auReq *request;
167 Bool need_header;
168
169 if (AvailableInput) {
170 if (AvailableInput != oc) {
171 ConnectionInputPtr aci = AvailableInput->input;
172 if (aci->size > BUFWATERMARK) {
173 xfree(aci->buffer);
174 xfree(aci);
175 } else {
176 aci->next = FreeInputs;
177 FreeInputs = aci;
178 }
179 AvailableInput->input = (ConnectionInputPtr) NULL;
180 }
181 AvailableInput = (OsCommPtr) NULL;
182 }
183 if (!oci) {
184 if (oci = FreeInputs) {
185 FreeInputs = oci->next;
186 } else if (!(oci = AllocateInputBuffer())) {
187 YieldControlDeath();
188 return -1;
189 }
190 oc->input = oci;
191 }
192 oci->bufptr += oci->lenLastReq;
193
194 need_header = FALSE;
195 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
196 if (gotnow < sizeof(auReq)) {
197 needed = sizeof(auReq);
198 need_header = TRUE;
199 } else {
200 request = (auReq *) oci->bufptr;
201 needed = get_req_len(request, client);
202 client->req_len = needed;
203 needed <<= 2;
204 }
205 if (gotnow < needed) {
206 oci->lenLastReq = 0;
207 if (needed > MAXBUFSIZE) {
208 YieldControlDeath();
209 return -1;
210 }
211 if ((gotnow == 0) ||
212 ((oci->bufptr - oci->buffer + needed) > oci->size)) {
213 if ((gotnow > 0) && (oci->bufptr != oci->buffer))
214 bcopy(oci->bufptr, oci->buffer, gotnow);
215 if (needed > oci->size) {
216 char *ibuf;
217
218 ibuf = (char *) xrealloc(oci->buffer, needed);
219 if (!ibuf) {
220 YieldControlDeath();
221 return -1;
222 }
223 oci->size = needed;
224 oci->buffer = ibuf;
225 }
226 oci->bufptr = oci->buffer;
227 oci->bufcnt = gotnow;
228 }
229 result = read(fd, oci->buffer + oci->bufcnt,
230 oci->size - oci->bufcnt);
231 if (result <= 0) {
232 #if !defined(SYSV386) || !defined(SVR4)
233 if ((result < 0) && ETEST(errno)) {
234 YieldControlNoInput();
235 return 0;
236 }
237 #endif
238 YieldControlDeath();
239 return -1;
240 }
241 oci->bufcnt += result;
242 gotnow += result;
243 /* free up some space after huge requests */
244 if ((oci->size > BUFWATERMARK) &&
245 (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
246 char *ibuf;
247
248 ibuf = (char *) xrealloc(oci->buffer, BUFSIZE);
249 if (ibuf) {
250 oci->size = BUFSIZE;
251 oci->buffer = ibuf;
252 oci->bufptr = ibuf + oci->bufcnt - gotnow;
253 }
254 }
255 if (need_header && gotnow >= needed) {
256 request = (auReq *) oci->bufptr;
257 needed = get_req_len(request, client);
258 client->req_len = needed;
259 needed <<= 2;
260 }
261 if (gotnow < needed) {
262 YieldControlNoInput();
263 return 0;
264 }
265 }
266 if (needed == 0) {
267 needed = sizeof(auReq);
268 }
269 oci->lenLastReq = needed;
270
271 /*
272 * Check to see if client has at least one whole request in the
273 * buffer. If there is only a partial request, treat like buffer
274 * is empty so that select() will be called again and other clients
275 * can get into the queue.
276 */
277
278 gotnow -= needed;
279 if (gotnow >= sizeof(auReq)) {
280 request = (auReq *) (oci->bufptr + needed);
281 if (gotnow >= (result = (get_req_len(request, client) << 2)))
282 BITSET(ClientsWithInput, fd);
283 else
284 YieldControlNoInput();
285 } else {
286 if (!gotnow)
287 AvailableInput = oc;
288 YieldControlNoInput();
289 }
290 if (++timesThisConnection >= MAX_TIMES_PER)
291 YieldControl();
292 client->requestBuffer = (pointer) oci->bufptr;
293 return needed;
294 }
295
296 /*****************************************************************
297 * InsertFakeRequest
298 * Splice a consed up (possibly partial) request in as the next request.
299 *
300 **********************/
301
302 Bool
InsertFakeRequest(client,data,count)303 InsertFakeRequest(client, data, count)
304 ClientPtr client;
305 char *data;
306 int count;
307 {
308 OsCommPtr oc = (OsCommPtr) client->osPrivate;
309 ConnectionInputPtr oci = oc->input;
310 int fd = oc->fd;
311 auReq *request;
312 int gotnow, moveup;
313
314 if (AvailableInput) {
315 if (AvailableInput != oc) {
316 ConnectionInputPtr aci = AvailableInput->input;
317 if (aci->size > BUFWATERMARK) {
318 xfree(aci->buffer);
319 xfree(aci);
320 } else {
321 aci->next = FreeInputs;
322 FreeInputs = aci;
323 }
324 AvailableInput->input = (ConnectionInputPtr) NULL;
325 }
326 AvailableInput = (OsCommPtr) NULL;
327 }
328 if (!oci) {
329 if (oci = FreeInputs)
330 FreeInputs = oci->next;
331 else if (!(oci = AllocateInputBuffer()))
332 return FALSE;
333 oc->input = oci;
334 }
335 oci->bufptr += oci->lenLastReq;
336 oci->lenLastReq = 0;
337 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
338 if ((gotnow + count) > oci->size) {
339 char *ibuf;
340
341 ibuf = (char *) xrealloc(oci->buffer, gotnow + count);
342 if (!ibuf)
343 return (FALSE);
344 oci->size = gotnow + count;
345 oci->buffer = ibuf;
346 oci->bufptr = ibuf + oci->bufcnt - gotnow;
347 }
348 moveup = count - (oci->bufptr - oci->buffer);
349 if (moveup > 0) {
350 if (gotnow > 0)
351 bcopy(oci->bufptr, oci->bufptr + moveup, gotnow);
352 oci->bufptr += moveup;
353 oci->bufcnt += moveup;
354 }
355 bcopy(data, oci->bufptr - count, count);
356 oci->bufptr -= count;
357 request = (auReq *) oci->bufptr;
358 gotnow += count;
359 if ((gotnow >= sizeof(auReq)) &&
360 (gotnow >= (int) (get_req_len(request, client) << 2)))
361 BITSET(ClientsWithInput, fd);
362 else
363 YieldControlNoInput();
364 return (TRUE);
365 }
366
367 /*****************************************************************
368 * ResetRequestFromClient
369 * Reset to reexecute the current request, and yield.
370 *
371 **********************/
372
ResetCurrentRequest(client)373 ResetCurrentRequest(client)
374 ClientPtr client;
375 {
376 OsCommPtr oc = (OsCommPtr) client->osPrivate;
377 ConnectionInputPtr oci = oc->input;
378 int fd = oc->fd;
379 auReq *request;
380 int gotnow, needed;
381
382 if (AvailableInput == oc)
383 AvailableInput = (OsCommPtr) NULL;
384 oci->lenLastReq = 0;
385 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
386 if (gotnow < sizeof(auReq)) {
387 YieldControlNoInput();
388 } else {
389 request = (auReq *) oci->bufptr;
390 needed = get_req_len(request, client);
391 if (gotnow >= (needed << 2)) {
392 BITSET(ClientsWithInput, fd);
393 YieldControl();
394 } else
395 YieldControlNoInput();
396 }
397 }
398
399 /********************
400 * FlushClient()
401 * If the client isn't keeping up with us, then we try to continue
402 * buffering the data and set the apropriate bit in ClientsWritable
403 * (which is used by WaitFor in the select). If the connection yields
404 * a permanent error, or we can't allocate any more space, we then
405 * close the connection.
406 *
407 **********************/
408
409 int
FlushClient(who,oc,extraBuf,extraCount)410 FlushClient(who, oc, extraBuf, extraCount)
411 ClientPtr who;
412 OsCommPtr oc;
413 char *extraBuf;
414 int extraCount; /* do not modify... returned below */
415 {
416 ConnectionOutputPtr oco = oc->output;
417 int connection = oc->fd;
418 struct iovec iov[3];
419 char padBuffer[3];
420 long written;
421 long padsize;
422 long notWritten;
423 long todo;
424
425 if (!oco)
426 return 0;
427 written = 0;
428 padsize = padlength[extraCount & 3];
429 notWritten = oco->count + extraCount + padsize;
430 todo = notWritten;
431 while (notWritten) {
432 long before = written; /* amount of whole thing written */
433 long remain = todo; /* amount to try this time, <= notWritten */
434 int i = 0;
435 long len;
436
437 /* You could be very general here and have "in" and "out" iovecs
438 * and write a loop without using a macro, but what the heck. This
439 * translates to:
440 *
441 * how much of this piece is new?
442 * if more new then we are trying this time, clamp
443 * if nothing new
444 * then bump down amount already written, for next piece
445 * else put new stuff in iovec, will need all of next piece
446 *
447 * Note that todo had better be at least 1 or else we'll end up
448 * writing 0 iovecs.
449 */
450 #define InsertIOV(pointer, length) \
451 len = (length) - before; \
452 if (len > remain) \
453 len = remain; \
454 if (len <= 0) { \
455 before = (-len); \
456 } else { \
457 iov[i].iov_len = len; \
458 iov[i].iov_base = (pointer) + before; \
459 i++; \
460 remain -= len; \
461 before = 0; \
462 }
463
464 InsertIOV((char *) oco->buf, oco->count)
465 InsertIOV(extraBuf, extraCount)
466 InsertIOV(padBuffer, padsize)
467
468 errno = 0;
469 if ((len = _OSWriteV(connection, iov, i)) >= 0) {
470 written += len;
471 notWritten -= len;
472 todo = notWritten;
473 } else if (ETEST(errno)
474 #ifdef SUNSYSV /* check for another brain-damaged OS bug */
475 || (errno == 0)
476 #endif
477 #ifdef EMSGSIZE /* check for another brain-damaged OS bug */
478 || ((errno == EMSGSIZE) && (todo == 1))
479 #endif
480 #ifdef SERVER_LOCALCONN /* STREAMS ... */
481 || ((errno == ERANGE) && (todo == 1))
482 #endif
483 ) {
484 /* If we've arrived here, then the client is stuffed to the gills
485 and not ready to accept more. Make a note of it and buffer
486 the rest. */
487 BITSET(ClientsWriteBlocked, connection);
488 AnyClientsWriteBlocked = TRUE;
489
490 if (written < oco->count) {
491 if (written > 0) {
492 oco->count -= written;
493 bcopy((char *) oco->buf + written,
494 (char *) oco->buf, oco->count);
495 written = 0;
496 }
497 } else {
498 written -= oco->count;
499 oco->count = 0;
500 }
501
502 if (notWritten > oco->size) {
503 unsigned char *obuf;
504
505 obuf = (unsigned char *) xrealloc(oco->buf,
506 notWritten + BUFSIZE);
507 if (!obuf) {
508 close(connection);
509 MarkClientException(who);
510 oco->count = 0;
511 return (-1);
512 }
513 oco->size = notWritten + BUFSIZE;
514 oco->buf = obuf;
515 }
516
517 /* If the amount written extended into the padBuffer, then the
518 difference "extraCount - written" may be less than 0 */
519 if ((len = extraCount - written) > 0)
520 bcopy(extraBuf + written,
521 (char *) oco->buf + oco->count, len);
522
523 oco->count = notWritten; /* this will include the pad */
524 /* return only the amount explicitly requested */
525 return extraCount;
526 }
527 #ifdef EMSGSIZE /* check for another brain-damaged OS bug */
528 else if (errno == EMSGSIZE) {
529 todo >>= 1;
530 }
531 #endif
532 #ifdef SERVER_LOCALCONN /* STREAMS ... */
533 else if (errno == ERANGE) {
534 todo >>= 1;
535 }
536 #endif
537 else {
538 close(connection);
539 MarkClientException(who);
540 oco->count = 0;
541 return (-1);
542 }
543 }
544
545 /* everything was flushed out */
546 oco->count = 0;
547 /* check to see if this client was write blocked */
548 if (AnyClientsWriteBlocked) {
549 BITCLEAR(ClientsWriteBlocked, oc->fd);
550 if (!ANYSET(ClientsWriteBlocked))
551 AnyClientsWriteBlocked = FALSE;
552 }
553 if (oco->size > BUFWATERMARK) {
554 xfree(oco->buf);
555 xfree(oco);
556 } else {
557 oco->next = FreeOutputs;
558 FreeOutputs = oco;
559 }
560 oc->output = (ConnectionOutputPtr) NULL;
561 return extraCount; /* return only the amount explicitly requested */
562 }
563
564 /********************
565 * FlushAllOutput()
566 * Flush all clients with output. However, if some client still
567 * has input in the queue (more requests), then don't flush. This
568 * will prevent the output queue from being flushed every time around
569 * the round robin queue. Now, some say that it SHOULD be flushed
570 * every time around, but...
571 *
572 **********************/
573
574 void
FlushAllOutput()575 FlushAllOutput()
576 {
577 int index, base, mask;
578 OsCommPtr oc;
579 ClientPtr client;
580
581 if (!NewOutputPending)
582 return;
583
584 /*
585 * It may be that some client still has critical output pending,
586 * but he is not yet ready to receive it anyway, so we will
587 * simply wait for the select to tell us when he's ready to receive.
588 */
589 CriticalOutputPending = FALSE;
590 NewOutputPending = FALSE;
591
592 for (base = 0; base < mskcnt; base++) {
593 mask = OutputPending[base];
594 OutputPending[base] = 0;
595 while (mask) {
596 index = ffs(mask) - 1;
597 mask &= ~lowbit(mask);
598 if ((index = ConnectionTranslation[(base << 5) + index]) == 0)
599 continue;
600 client = clients[index];
601 if (client->clientGone)
602 continue;
603 oc = (OsCommPtr) client->osPrivate;
604 if (GETBIT(ClientsWithInput, oc->fd)) {
605 BITSET(OutputPending, oc->fd); /* set the bit again */
606 NewOutputPending = TRUE;
607 } else
608 (void) FlushClient(client, oc, (char *) NULL, 0);
609 }
610 }
611
612 }
613 #endif /* !AMOEBA && !_MINIX */
614
615 void
FlushIfCriticalOutputPending()616 FlushIfCriticalOutputPending()
617 {
618 if (CriticalOutputPending)
619 FlushAllOutput();
620 }
621
622 void
SetCriticalOutputPending()623 SetCriticalOutputPending()
624 {
625 CriticalOutputPending = TRUE;
626 }
627
628 /*****************
629 * WriteToClient
630 * Copies buf into ClientPtr.buf if it fits (with padding), else
631 * flushes ClientPtr.buf and buf to client. As of this writing,
632 * every use of WriteToClient is cast to void, and the result
633 * is ignored. Potentially, this could be used by requests
634 * that are sending several chunks of data and want to break
635 * out of a loop on error. Thus, we will leave the type of
636 * this routine as int.
637 *****************/
638
639 #if !defined(AMOEBA) && !defined(_MINIX)
640 int
WriteToClient(who,count,buf)641 WriteToClient(who, count, buf)
642 ClientPtr who;
643 char *buf;
644 int count;
645 {
646 OsCommPtr oc = (OsCommPtr) who->osPrivate;
647 ConnectionOutputPtr oco = oc->output;
648 int padBytes;
649
650 if (!count)
651 return (0);
652
653 if (!oco) {
654 if (oco = FreeOutputs) {
655 FreeOutputs = oco->next;
656 } else if (!(oco = AllocateOutputBuffer())) {
657 close(oc->fd);
658 MarkClientException(who);
659 return -1;
660 }
661 oc->output = oco;
662 }
663
664 padBytes = padlength[count & 3];
665
666 if (oco->count + count + padBytes > oco->size) {
667 BITCLEAR(OutputPending, oc->fd);
668 CriticalOutputPending = FALSE;
669 NewOutputPending = FALSE;
670 return FlushClient(who, oc, buf, count);
671 }
672
673 NewOutputPending = TRUE;
674 BITSET(OutputPending, oc->fd);
675 bcopy(buf, (char *) oco->buf + oco->count, count);
676 oco->count += count + padBytes;
677
678 return (count);
679 }
680 #endif /* !AMOEBA && !_MINIX */
681
682 #ifndef AMOEBA
683 static ConnectionInputPtr
AllocateInputBuffer(void)684 AllocateInputBuffer(void)
685 {
686 ConnectionInputPtr oci;
687
688 oci = (ConnectionInputPtr) xalloc(sizeof(ConnectionInput));
689 if (!oci)
690 return (ConnectionInputPtr) NULL;
691 oci->buffer = (char *) xalloc(BUFSIZE);
692 if (!oci->buffer) {
693 xfree(oci);
694 return (ConnectionInputPtr) NULL;
695 }
696 oci->size = BUFSIZE;
697 oci->bufptr = oci->buffer;
698 oci->bufcnt = 0;
699 oci->lenLastReq = 0;
700 return oci;
701 }
702
703 static ConnectionOutputPtr
AllocateOutputBuffer(void)704 AllocateOutputBuffer(void)
705 {
706 ConnectionOutputPtr oco;
707
708 oco = (ConnectionOutputPtr) xalloc(sizeof(ConnectionOutput));
709 if (!oco)
710 return (ConnectionOutputPtr) NULL;
711 oco->buf = (unsigned char *) xalloc(BUFSIZE);
712 if (!oco->buf) {
713 xfree(oco);
714 return (ConnectionOutputPtr) NULL;
715 }
716 oco->size = BUFSIZE;
717 oco->count = 0;
718 return oco;
719 }
720
721 void
FreeOsBuffers(oc)722 FreeOsBuffers(oc)
723 OsCommPtr oc;
724 {
725 ConnectionInputPtr oci;
726 ConnectionOutputPtr oco;
727
728 #ifndef _MINIX
729 if (AvailableInput == oc)
730 AvailableInput = (OsCommPtr) NULL;
731 #else
732 if (oci = oc->inputFake) {
733 if (FreeInputs) {
734 xfree(oci->buffer);
735 xfree(oci);
736 } else {
737 FreeInputs = oci;
738 oci->next = (ConnectionInputPtr) NULL;
739 oci->bufptr = oci->buffer;
740 oci->bufcnt = 0;
741 oci->lenLastReq = 0;
742 }
743 }
744 #endif
745 if (oci = oc->input) {
746 if (FreeInputs) {
747 xfree(oci->buffer);
748 xfree(oci);
749 } else {
750 FreeInputs = oci;
751 oci->next = (ConnectionInputPtr) NULL;
752 oci->bufptr = oci->buffer;
753 oci->bufcnt = 0;
754 oci->lenLastReq = 0;
755 }
756 }
757 if (oco = oc->output) {
758 if (FreeOutputs) {
759 xfree(oco->buf);
760 xfree(oco);
761 } else {
762 FreeOutputs = oco;
763 oco->next = (ConnectionOutputPtr) NULL;
764 oco->count = 0;
765 }
766 }
767 #ifdef _MINIX
768 if (oco = oc->outputNext) {
769 if (FreeOutputs) {
770 xfree(oco->buf);
771 xfree(oco);
772 } else {
773 FreeOutputs = oco;
774 oco->next = (ConnectionOutputPtr) NULL;
775 oco->count = 0;
776 }
777 }
778 #endif
779 }
780
781 void
ResetOsBuffers()782 ResetOsBuffers()
783 {
784 ConnectionInputPtr oci;
785 ConnectionOutputPtr oco;
786
787 while (oci = FreeInputs) {
788 FreeInputs = oci->next;
789 xfree(oci->buffer);
790 xfree(oci);
791 }
792 while (oco = FreeOutputs) {
793 FreeOutputs = oco->next;
794 xfree(oco->buf);
795 xfree(oco);
796 }
797 }
798 #endif /* AMOEBA */
799
800 /*
801 * The rest of this file contains the Amoeba and Minix implementations.
802 */
803
804 #ifdef AMOEBA
805 #include <amoeba.h>
806 #include <cmdreg.h>
807 #include <stdcom.h>
808 #include <stderr.h>
809 #include <ampolicy.h>
810 #include <server/ip/hton.h>
811 #include <server/ip/types.h>
812 #include <server/ip/tcpip.h>
813 #include <server/ip/tcp_io.h>
814 #include <server/ip/gen/in.h>
815 #include <server/ip/gen/tcp.h>
816 #include <server/ip/gen/tcp_io.h>
817
818 /*
819 * Philip's TCP/IP server silently assumes a
820 * maximum buffer size of 30000 bytes.
821 */
822 #define TCPIP_BUFSIZE 16384
823
824 int
ReadRequestFromClient(client)825 ReadRequestFromClient(client)
826 ClientPtr client;
827 {
828 OsCommPtr oc = (OsCommPtr) client->osPrivate;
829 xReq *request;
830 int havenow, needed;
831 char *p;
832 int stat, n, rv;
833
834 oc->status &= ~REQ_PUSHBACK;
835 if (oc->size == 0) {
836 if (oc->buffer) {
837 Xfree(oc->buffer);
838 oc->buffer = NULL;
839 }
840 if ((rv = am_avail(oc, VC_IN)) >= (SIZEOF(xReq))) {
841 /*
842 * Enough available to read first portion of
843 * the request.
844 */
845 oc->buffer = (char *) xalloc(SIZEOF(xReq));
846 if (oc->buffer == NULL) {
847 ErrorF("ReadRequestFromClient: out of memory\n");
848 return -1;
849 }
850 oc->size = SIZEOF(xReq);
851 n = am_read(oc, oc->buffer, SIZEOF(xReq));
852 if (n != SIZEOF(xReq)) {
853 ErrorF("ReadRequestFromClient: got %d wanted %d\n",
854 n, SIZEOF(xReq));
855 return -1;
856 }
857 } else if (rv < 0) {
858 ErrorF("ReadRequestFromClient: read failed (connection %d)\n",
859 oc->number);
860 oc->status |= CONN_KILLED;
861 return -1;
862 } else {
863 isItTimeToYield = TRUE;
864 return 0;
865 }
866 }
867
868 /*
869 * See if we have enough in the local buffer,
870 * plus what is still in the virtual circuit.
871 */
872 if ((stat = am_avail(oc, VC_IN)) < 0) { /* oc closed */
873 ErrorF("ReadRequestFromClient: read failed (connection %d)\n",
874 oc->number);
875 oc->status |= CONN_KILLED;
876 return -1;
877 }
878
879 havenow = oc->size + stat;
880 request = (xReq *) oc->buffer;
881 if (havenow < SIZEOF(xReq)) {
882 NotAllHereYet:
883 /* not a whole message yet; return */
884 isItTimeToYield = TRUE;
885 return 0;
886 }
887
888 /*
889 * Everything is ok, see how much we need to read
890 */
891 if (request != 0)
892 needed = request_length(request, client);
893 else
894 needed = -1;
895 if (needed <= 0)
896 needed = sizeof(xReq);
897 if (needed > havenow) {
898 if (havenow > oc->size) {
899 if ((oc->buffer = (char *) xrealloc(oc->buffer, havenow)) == 0) {
900 ErrorF("ReadRequestFromClient: out of memory\n");
901 return -1;
902 }
903 n = am_read(oc, oc->buffer + oc->size, havenow - oc->size);
904 if (n != havenow - oc->size) {
905 ErrorF("ReadRequestFromClient: got %d wanted %d\n",
906 n, havenow - oc->size);
907 return -1;
908 }
909 oc->size = havenow;
910 }
911 goto NotAllHereYet;
912 }
913 if (needed > oc->size) {
914 if ((oc->buffer = (char *) Xrealloc(oc->buffer, needed)) == 0) {
915 ErrorF("ReadRequestFromClient: out of memory\n");
916 return -1;
917 }
918 request = (xReq *) oc->buffer;
919 n = am_read(oc, oc->buffer + oc->size, needed - oc->size);
920 if (n != needed - oc->size) {
921 ErrorF("ReadRequestFromClient: got %d wanted %d\n",
922 n, needed - oc->size);
923 return -1;
924 }
925 }
926 oc->size = 0;
927 if (++timesThisConnection >= MAX_TIMES_PER || isItTimeToYield) {
928 isItTimeToYield = TRUE;
929 timesThisConnection = 0;
930 }
931 client->requestBuffer = (pointer) oc->buffer;
932 return needed;
933 }
934
935 Bool
InsertFakeRequest(client,data,count)936 InsertFakeRequest(client, data, count)
937 ClientPtr client;
938 char *data;
939 int count;
940 {
941 OsCommPtr oc = (OsCommPtr) client->osPrivate;
942
943 oc->status |= REQ_PUSHBACK;
944 WakeUpMainThread();
945 if (oc->size) {
946 ErrorF("Warning: InsertFakeRequest(%d): %d already\n", count,
947 oc->size);
948 oc->buffer = (char *) xrealloc(oc->buffer, oc->size + count);
949 if (oc->buffer == NULL) {
950 ErrorF("InsertFakeClient: out of memory\n");
951 oc->size = 0;
952 oc->buffer = 0;
953 return FALSE;
954 }
955 } else {
956 oc->buffer = (char *) xalloc(count);
957 if (oc->buffer == NULL) {
958 ErrorF("InsertFakeRequest: out of memory\n");
959 oc->size = 0;
960 oc->buffer = 0;
961 return FALSE;
962 }
963 }
964 bcopy(data, oc->buffer + oc->size, count);
965 oc->size += count;
966 return TRUE;
967 }
968
ResetCurrentRequest(client)969 ResetCurrentRequest(client)
970 ClientPtr client;
971 {
972 OsCommPtr oc = (OsCommPtr) client->osPrivate;
973 xReq *request;
974
975 oc->status |= REQ_PUSHBACK;
976 WakeUpMainThread();
977 if (oc->size) {
978 ErrorF("ResetCurrentRequest: partial request\n");
979 return;
980 }
981 if (oc->buffer == NULL) {
982 ErrorF("ResetCurrentRequest: no request\n");
983 return;
984 }
985 request = (xReq *) oc->buffer;
986 oc->size = request_length(request, client);
987 }
988
989 int
FlushClient(who,oc,extraBuf,extraCount)990 FlushClient(who, oc, extraBuf, extraCount)
991 ClientPtr who;
992 OsCommPtr oc;
993 char *extraBuf;
994 int extraCount;
995 {
996 return 0;
997 }
998
999 void
FlushAllOutput()1000 FlushAllOutput()
1001 {
1002 }
1003
1004 int
WriteToClient(who,count,buf)1005 WriteToClient(who, count, buf)
1006 ClientPtr who;
1007 char *buf;
1008 int count;
1009 {
1010 OsCommPtr oc = (OsCommPtr) who->osPrivate;
1011 int padBytes;
1012
1013 if (count == 0)
1014 return 0;
1015 if (count < 0) {
1016 ErrorF("WriteToClient: count %d < 0?\n", count);
1017 return 0; /* silly request */
1018 }
1019
1020 padBytes = padlength[count & 3];
1021 if ((count = am_write(oc, buf, count)) < 0)
1022 oc->status |= CONN_KILLED;
1023 if (count > 0 && count & 3) {
1024 if (am_write(oc, "\0\0\0\0", padlength[count & 3]) < 0)
1025 oc->status |= CONN_KILLED;
1026 }
1027 return count;
1028 }
1029
1030 int
am_avail(oc,which)1031 am_avail(oc, which)
1032 OsCommPtr oc;
1033 int which;
1034 {
1035 if (oc->family == FamilyAmoeba)
1036 return vc_avail(oc->conn.vc, which);
1037 if (oc->family == FamilyInternet)
1038 return cb_full(oc->conn.tcp.cb);
1039 return -1;
1040 }
1041
1042 int
am_read(oc,buffer,size)1043 am_read(oc, buffer, size)
1044 OsCommPtr oc;
1045 char *buffer;
1046 int size;
1047 {
1048 if (oc->family == FamilyAmoeba)
1049 return vc_readall(oc->conn.vc, buffer, size);
1050 if (oc->family == FamilyInternet)
1051 return cb_gets(oc->conn.tcp.cb, buffer, size, size);
1052 return -1;
1053 }
1054
1055 int
am_write(oc,buffer,size)1056 am_write(oc, buffer, size)
1057 OsCommPtr oc;
1058 char *buffer;
1059 int size;
1060 {
1061 if (oc->family == FamilyAmoeba)
1062 return vc_write(oc->conn.vc, buffer, size);
1063 if (oc->family == FamilyInternet) {
1064 bufsize bsize;
1065 int count, wrcnt;
1066
1067 for (count = 0; size > 0;) {
1068 wrcnt = size > TCPIP_BUFSIZE ? TCPIP_BUFSIZE : size;
1069 bsize = tcpip_write(&oc->conn.tcp.cap, buffer, wrcnt);
1070 if (ERR_STATUS(bsize)) {
1071 ErrorF("TCP/IP write failed: %s\n",
1072 tcpip_why(ERR_CONVERT(bsize)));
1073 return -1;
1074 }
1075 if (bsize != wrcnt) {
1076 ErrorF("TCP/IP write failed (expected %d, wrote %d)\n",
1077 (int) bsize, wrcnt);
1078 return -1;
1079 }
1080 buffer += bsize;
1081 size -= bsize;
1082 count += bsize;
1083 }
1084
1085 return size;
1086 }
1087 return -1;
1088 }
1089
1090 void
am_close(oc,which)1091 am_close(oc, which)
1092 OsCommPtr oc;
1093 int which;
1094 {
1095 if (amDebug)
1096 ErrorF("am_close() %s, %d\n",
1097 oc->family == FamilyAmoeba ? "Amoeba" : "TCP/IP",
1098 oc->number);
1099
1100 if (oc->family == FamilyAmoeba)
1101 vc_close(oc->conn.vc, which);
1102 if (oc->family == FamilyInternet) {
1103 if (oc->conn.tcp.signal != -1)
1104 sig_raise(oc->conn.tcp.signal);
1105 std_destroy(&oc->conn.tcp.cap);
1106 cb_close(oc->conn.tcp.cb);
1107 cb_free(oc->conn.tcp.cb);
1108 oc->conn.tcp.cb = NULL;
1109 }
1110 }
1111 #endif /* AMOEBA */
1112
1113 #ifdef _MINIX
1114 extern asio_fd_set_t InprogressFdSet;
1115 extern asio_fd_set_t ListenFdSet;
1116 extern asio_fd_set_t ClientFdSet;
1117 extern asio_fd_set_t CompletedFdSet;
1118 extern asio_fd_set_t IgnoreFdSet;
1119 extern asio_fd_set_t GrabFdSet;
1120
1121 extern Bool AnyClientsWithInput;
1122 extern int lastfdesc; /* maximum file descriptor */
1123 extern int GrabInProgress;
1124
1125 int
ReadRequestFromClient(client)1126 ReadRequestFromClient(client)
1127 ClientPtr client;
1128 {
1129 OsCommPtr oc = (OsCommPtr) client->osPrivate;
1130 ConnectionInputPtr oci, oci_r;
1131 int fd = oc->fd;
1132 int gotnow, gotnow_r, needed;
1133 int result;
1134 xReq *request;
1135
1136 if (GrabInProgress && !ASIO_FD_ISSET(fd, ASIO_READ, &GrabFdSet)) {
1137 YieldControl();
1138 return 0;
1139 }
1140 if (ASIO_FD_ISSET(fd, ASIO_READ, &IgnoreFdSet)) {
1141 YieldControl();
1142 return 0;
1143 }
1144
1145 ASIO_FD_CLR(fd, ASIO_READ, &CompletedFdSet);
1146 oci = oc->inputFake;
1147 if (oci) {
1148 oci->bufptr += oci->lenLastReq;
1149 oci->lenLastReq = 0;
1150 }
1151 oci = oc->input;
1152 if (oci) {
1153 oci->bufptr += oci->lenLastReq;
1154 oci->lenLastReq = 0;
1155 }
1156 for (;;) {
1157 /* Let's check Fake requests first */
1158
1159 oci = oc->inputFake;
1160 if (oci) {
1161 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
1162 if (gotnow == 0) {
1163 /* End of fake request */
1164 xfree(oci->buffer);
1165 xfree(oci);
1166 oc->inputFake = NULL;
1167
1168 continue;
1169 }
1170
1171 /* Let's move the data down, if necessary */
1172 if (oci->bufptr != oci->buffer) {
1173 bcopy(oci->bufptr, oci->buffer, gotnow);
1174 oci->bufptr = oci->buffer;
1175 }
1176
1177 if (gotnow < sizeof(xReq))
1178 FatalError("Fake request is too small\n");
1179 request = (xReq *) oci->bufptr;
1180 needed = request_length(request, client);
1181 if (needed < sizeof(xReq))
1182 needed = sizeof(xReq);
1183 else if (needed > MAXBUFSIZE)
1184 FatalError("Fake request is too large\n");
1185 if (needed > oci->size) {
1186 char *ibuf;
1187
1188 ibuf = (char *) xrealloc(oci->buffer, needed);
1189 if (!ibuf) {
1190 YieldControlDeath();
1191 ErrorF("ReadRequestFromClient: cannot reallocate buffer\n");
1192 return -1;
1193 }
1194 oci->size = needed;
1195 oci->buffer = ibuf;
1196 oci->bufptr = ibuf;
1197 }
1198 if (gotnow >= needed) {
1199 if (++timesThisConnection >= MAX_TIMES_PER)
1200 YieldControl();
1201 ASIO_FD_SET(fd, ASIO_READ, &CompletedFdSet);
1202 AnyClientsWithInput = TRUE;
1203
1204 client->requestBuffer = (pointer) oci->bufptr;
1205 oci->lenLastReq = needed;
1206 ErrorF("ReadRequestFromClient: gotnow = %d; needed = %d\n",
1207 gotnow, needed);
1208 return needed;
1209 }
1210
1211 /* Do we have something in the input buffer, we can use? */
1212 oci_r = oc->input;
1213 if (oci_r == NULL) {
1214 /* No input buffer, we can make the fake buffer the input
1215 * buffer */
1216 if (ASIO_FD_ISSET(fd, ASIO_READ, &InprogressFdSet))
1217 FatalError("no input buffer but in progress\n");
1218 oc->input = oci;
1219 oc->inputFake = NULL;
1220 continue;
1221 }
1222 gotnow_r = oci_r->bufcnt + oci_r->buffer - oci_r->bufptr;
1223 if (gotnow_r == 0) {
1224 /* No input buffer, do we have a read in progress? */
1225 if (ASIO_FD_ISSET(fd, ASIO_READ, &InprogressFdSet)) {
1226 YieldControlNoInput();
1227 ErrorF("ReadRequestFromClient: no read in progress\n");
1228 return 0;
1229 }
1230
1231 xfree(oci_r->buffer);
1232 xfree(oci_r);
1233 oc->input = oci;
1234 oc->inputFake = NULL;
1235 continue;
1236 }
1237 if (gotnow_r > needed - gotnow)
1238 gotnow_r = needed - gotnow;
1239 bcopy(oci_r->bufptr, oci->buffer + gotnow, gotnow_r);
1240 oci_r->bufptr += gotnow_r;
1241 continue;
1242 }
1243
1244 /* No fake input */
1245 oci = oc->input;
1246 if (!oci) {
1247 if (oci = FreeInputs) {
1248 FreeInputs = oci->next;
1249 } else if (!(oci = AllocateInputBuffer())) {
1250 YieldControlDeath();
1251 ErrorF("ReadRequestFromClient: cannot allocate buffer\n");
1252 return -1;
1253 }
1254 oc->input = oci;
1255 }
1256
1257 request = (xReq *) oci->bufptr;
1258 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
1259 if ((gotnow < sizeof(xReq)) ||
1260 (gotnow < (needed = request_length(request, client)))) {
1261 if ((gotnow < sizeof(xReq)) || (needed < sizeof(xReq)))
1262 needed = sizeof(xReq);
1263 else if (needed > MAXBUFSIZE) {
1264 YieldControlDeath();
1265 ErrorF("ReadRequestFromClient: request too big(%d)\n",
1266 needed);
1267 return -1;
1268 }
1269
1270 /* Let's check for a read in progress */
1271 if (ASIO_FD_ISSET(fd, ASIO_READ, &InprogressFdSet)) {
1272 YieldControlNoInput();
1273 return 0;
1274 }
1275 if ((gotnow == 0) ||
1276 ((oci->bufptr - oci->buffer + needed) > oci->size)) {
1277 if ((gotnow > 0) && (oci->bufptr != oci->buffer))
1278 bcopy(oci->bufptr, oci->buffer, gotnow);
1279 if (needed > oci->size) {
1280 char *ibuf;
1281
1282 ibuf = (char *) xrealloc(oci->buffer, needed);
1283 if (!ibuf) {
1284 YieldControlDeath();
1285 return -1;
1286 }
1287 oci->size = needed;
1288 oci->buffer = ibuf;
1289 }
1290 oci->bufptr = oci->buffer;
1291 oci->bufcnt = gotnow;
1292 }
1293 result = read(fd, oci->buffer + oci->bufcnt,
1294 oci->size - oci->bufcnt);
1295 if (result <= 0) {
1296 if ((result < 0) && errno == EINPROGRESS) {
1297 ASIO_FD_SET(fd, ASIO_READ, &InprogressFdSet);
1298 ASIO_FD_SET(fd, ASIO_READ, &ClientFdSet);
1299 YieldControlNoInput();
1300 return 0;
1301 }
1302 YieldControlDeath();
1303 ErrorF("ReadRequestFromClient: read failed\n");
1304 ErrorF("result = %d; errno = %d; size-bufcnt = %d\n",
1305 result, errno, oci->size - oci->bufcnt);
1306 return -1;
1307 }
1308 oci->bufcnt += result;
1309 gotnow += result;
1310
1311 /* free up some space after huge requests */
1312 if ((oci->size > BUFWATERMARK) &&
1313 (oci->bufcnt < BUFSIZE) && (needed < BUFSIZE)) {
1314 char *ibuf;
1315
1316 ibuf = (char *) xrealloc(oci->buffer, BUFSIZE);
1317 if (ibuf) {
1318 oci->size = BUFSIZE;
1319 oci->buffer = ibuf;
1320 oci->bufptr = ibuf + oci->bufcnt - gotnow;
1321 }
1322 }
1323 continue;
1324 } else
1325 break;
1326 }
1327
1328 if (needed < sizeof(xReq))
1329 needed = sizeof(xReq);
1330
1331 if (++timesThisConnection >= MAX_TIMES_PER)
1332 YieldControl();
1333
1334 client->requestBuffer = (pointer) oci->bufptr;
1335 oci->lenLastReq = needed;
1336 ASIO_FD_SET(fd, ASIO_READ, &CompletedFdSet);
1337 ASIO_FD_SET(fd, ASIO_READ, &ClientFdSet);
1338 AnyClientsWithInput = TRUE;
1339 return needed;
1340 }
1341
1342 Bool
InsertFakeRequest(client,data,count)1343 InsertFakeRequest(client, data, count)
1344 ClientPtr client;
1345 char *data;
1346 int count;
1347 {
1348 OsCommPtr oc = (OsCommPtr) client->osPrivate;
1349 ConnectionInputPtr oci;
1350 int fd = oc->fd;
1351 int gotnow, moveup;
1352
1353 oci = oc->input;
1354 if (oci) {
1355 oci->bufptr += oci->lenLastReq;
1356 oci->lenLastReq = 0;
1357 }
1358 oci = oc->inputFake;
1359 if (!oci) {
1360 if (oci = FreeInputs)
1361 FreeInputs = oci->next;
1362 else if (!(oci = AllocateInputBuffer()))
1363 return FALSE;
1364 oc->inputFake = oci;
1365 }
1366 oci->bufptr += oci->lenLastReq;
1367 oci->lenLastReq = 0;
1368 gotnow = oci->bufcnt + oci->buffer - oci->bufptr;
1369 if ((gotnow + count) > oci->size) {
1370 char *ibuf;
1371
1372 ibuf = (char *) xrealloc(oci->buffer, gotnow + count);
1373 if (!ibuf)
1374 return (FALSE);
1375 oci->size = gotnow + count;
1376 oci->buffer = ibuf;
1377 oci->bufptr = ibuf + oci->bufcnt - gotnow;
1378 }
1379 moveup = count - (oci->bufptr - oci->buffer);
1380 if (moveup > 0) {
1381 if (gotnow > 0)
1382 bcopy(oci->bufptr, oci->bufptr + moveup, gotnow);
1383 oci->bufptr += moveup;
1384 oci->bufcnt += moveup;
1385 }
1386 bcopy(data, oci->bufptr - count, count);
1387 oci->bufptr -= count;
1388 ASIO_FD_SET(fd, ASIO_READ, &CompletedFdSet);
1389 AnyClientsWithInput = TRUE;
1390 return (TRUE);
1391 }
1392
ResetCurrentRequest(client)1393 ResetCurrentRequest(client)
1394 ClientPtr client;
1395 {
1396 OsCommPtr oc = (OsCommPtr) client->osPrivate;
1397 ConnectionInputPtr oci;
1398 int fd = oc->fd;
1399
1400 oci = oc->inputFake;
1401 if (oci)
1402 oci->lenLastReq = 0;
1403 oci = oc->input;
1404 if (oci)
1405 oci->lenLastReq = 0;
1406 ASIO_FD_SET(fd, ASIO_READ, &CompletedFdSet);
1407 AnyClientsWithInput = TRUE;
1408 YieldControl();
1409 }
1410
1411 int
FlushClient(who,oc,extraBuf,extraCount)1412 FlushClient(who, oc, extraBuf, extraCount)
1413 ClientPtr who;
1414 OsCommPtr oc;
1415 char *extraBuf;
1416 int extraCount; /* do not modify... returned below */
1417 {
1418 ConnectionOutputPtr oco;
1419 int fd = oc->fd;
1420 long padsize;
1421 char padBuffer[3];
1422 int newsize, r;
1423
1424 ASIO_FD_CLR(fd, ASIO_WRITE, &CompletedFdSet);
1425 padsize = padlength[extraCount & 3];
1426
1427 /* Insert new data in outputNext */
1428 if (extraCount + padsize != 0) {
1429 oco = oc->outputNext;
1430 if (!oco) {
1431 if (oco = FreeOutputs) {
1432 FreeOutputs = oco->next;
1433 } else if (!(oco = AllocateOutputBuffer())) {
1434 close(oc->fd);
1435 CheckListeners();
1436 MarkClientException(who);
1437 return -1;
1438 }
1439 oc->outputNext = oco;
1440 }
1441 newsize = oco->count + extraCount + padsize;
1442 if (newsize > oco->size) {
1443 unsigned char *obuf;
1444
1445 obuf = (unsigned char *) xrealloc(oco->buf, newsize + BUFSIZE);
1446 if (obuf == NULL) {
1447 close(oc->fd);
1448 CheckListeners();
1449 MarkClientException(who);
1450 oco->count = 0;
1451 return -1;
1452 }
1453 oco->buf = obuf;
1454 oco->size = newsize + BUFSIZE;
1455 }
1456 bcopy(extraBuf, oco->buf + oco->count, extraCount);
1457 oco->count += extraCount;
1458 bcopy(padBuffer, oco->buf + oco->count, padsize);
1459 oco->count += padsize;
1460 }
1461
1462 for (;;) {
1463 /* If we have a write in progress we can quit imediately */
1464 if (ASIO_FD_ISSET(fd, ASIO_WRITE, &InprogressFdSet))
1465 return extraCount;
1466
1467 oco = oc->output;
1468 if (oco == NULL) {
1469 /* If we have no output buffer, but do have an outputNext,
1470 * move outputNext to output and retry */
1471
1472 if (oc->outputNext) {
1473 oc->output = oc->outputNext;
1474 oc->outputNext = NULL;
1475 continue;
1476 }
1477
1478 /* We are done */
1479 return extraCount;
1480 }
1481
1482 /* If the write buffer exists but is empty we can remove that buffer */
1483 if (oco->count == 0) {
1484 if (oco->size > BUFWATERMARK) {
1485 xfree(oco->buf);
1486 xfree(oco);
1487 } else {
1488 oco->next = FreeOutputs;
1489 FreeOutputs = oco;
1490 }
1491 oc->output = (ConnectionOutputPtr) NULL;
1492 continue;
1493 }
1494
1495 /* We have some work to do */
1496 r = write(fd, (char *) oco->buf, oco->count);
1497 if (r > 0) {
1498 if (r == oco->count) {
1499 /* The normal case we assume */
1500 oco->count = 0;
1501 continue;
1502 }
1503 oco->count -= r;
1504 bcopy(oco->buf + r, oco->buf, oco->count);
1505 continue;
1506 }
1507 if (r == -1 && errno == EINPROGRESS) {
1508 ASIO_FD_SET(fd, ASIO_WRITE, &InprogressFdSet);
1509 ASIO_FD_SET(fd, ASIO_WRITE, &ClientFdSet);
1510 continue;
1511 }
1512
1513 /* Now we got an error */
1514 close(fd);
1515 CheckListeners();
1516 MarkClientException(who);
1517 oco->count = 0;
1518 return (-1);
1519 }
1520 }
1521
1522 void
FlushAllOutput()1523 FlushAllOutput()
1524 {
1525 int i, index;
1526 ClientPtr client;
1527 OsCommPtr oc;
1528
1529 if (!NewOutputPending)
1530 return;
1531
1532 /*
1533 * It may be that some client still has critical output pending,
1534 * but he is not yet ready to receive it anyway, so we will
1535 * simply wait for the select to tell us when he's ready to receive.
1536 */
1537 CriticalOutputPending = FALSE;
1538 NewOutputPending = FALSE;
1539
1540 for (i = 0; i <= lastfdesc; i++) {
1541 if (ASIO_FD_ISSET(i, ASIO_WRITE, &CompletedFdSet) &&
1542 ASIO_FD_ISSET(i, ASIO_WRITE, &ClientFdSet)) {
1543 ASIO_FD_CLR(i, ASIO_WRITE, &CompletedFdSet);
1544 if ((index = ConnectionTranslation[i]) == 0)
1545 continue;
1546 client = clients[index];
1547 if (client->clientGone)
1548 continue;
1549 oc = (OsCommPtr) client->osPrivate;
1550 if (ASIO_FD_ISSET(i, ASIO_READ, &CompletedFdSet)) {
1551 ASIO_FD_SET(i, ASIO_WRITE, &CompletedFdSet);
1552 NewOutputPending = TRUE;
1553 } else
1554 (void) FlushClient(client, oc, (char *) NULL, 0);
1555 }
1556 }
1557 }
1558
WriteToClient(who,count,buf)1559 WriteToClient(who, count, buf)
1560 ClientPtr who;
1561 int count;
1562 char *buf;
1563 {
1564 OsCommPtr oc = (OsCommPtr) who->osPrivate;
1565 ConnectionOutputPtr oco;
1566 int padBytes;
1567
1568 if (!count)
1569 return (0);
1570
1571 oco = oc->output;
1572 if (!oco) {
1573 if (oco = FreeOutputs) {
1574 FreeOutputs = oco->next;
1575 } else if (!(oco = AllocateOutputBuffer())) {
1576 close(oc->fd);
1577 CheckListeners();
1578 MarkClientException(who);
1579 return -1;
1580 }
1581 oc->output = oco;
1582 }
1583 if (oc->outputNext)
1584 oco = oc->outputNext;
1585
1586 padBytes = padlength[count & 3];
1587
1588 if (oco->count + count + padBytes > oco->size) {
1589 CriticalOutputPending = FALSE;
1590 NewOutputPending = FALSE;
1591 return FlushClient(who, oc, buf, count);
1592 }
1593
1594 NewOutputPending = TRUE;
1595 ASIO_FD_SET(oc->fd, ASIO_WRITE, &CompletedFdSet);
1596 ASIO_FD_SET(oc->fd, ASIO_WRITE, &ClientFdSet);
1597 bcopy(buf, (char *) oco->buf + oco->count, count);
1598 oco->count += count + padBytes;
1599
1600 return (count);
1601 }
1602
1603 void
UpdateClientIOStatus(fd,operation,result,error)1604 UpdateClientIOStatus(fd, operation, result, error)
1605 int fd;
1606 int operation;
1607 int result;
1608 int error;
1609 {
1610 int client_no;
1611 ClientPtr client;
1612 OsCommPtr oc;
1613 ConnectionInputPtr oci;
1614 ConnectionOutputPtr oco;
1615
1616 ASIO_FD_CLR(fd, operation, &InprogressFdSet);
1617 client_no = ConnectionTranslation[fd];
1618 client = clients[client_no];
1619 oc = (OsCommPtr) client->osPrivate;
1620
1621 switch (operation) {
1622 case ASIO_READ:
1623 AnyClientsWithInput = TRUE;
1624 ASIO_FD_SET(fd, ASIO_READ, &CompletedFdSet);
1625 if (result == -1) {
1626 /* Assume the error will happen again on the next read */
1627 ErrorF("(warning) read error: %s\n", strerror(error));
1628 return;
1629 }
1630 oci = oc->input;
1631 oci->bufcnt += result;
1632 break;
1633 case ASIO_WRITE:
1634 if (result == -1) {
1635 /* Assume the error will happen again on the next write */
1636 ErrorF("(warning) write error: %s\n", strerror(error));
1637 return;
1638 }
1639 oco = oc->output;
1640 oco->count -= result;
1641 if (oco->count != 0)
1642 bcopy(oco->buf + result, oco->buf, oco->count);
1643 FlushClient(client, oc, NULL, 0);
1644 break;
1645 default:
1646 FatalError("UpdateClientIOStatus: oper %d not implemented\n",
1647 operation);
1648 }
1649 }
1650 #endif /* _MINIX */
1651