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