|
|
1.1 root 1: /***********************************************************
2: Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
3: and the Massachusetts Institute of Technology, Cambridge, Massachusetts.
4:
5: All Rights Reserved
6:
7: Permission to use, copy, modify, and distribute this software and its
8: documentation for any purpose and without fee is hereby granted,
9: provided that the above copyright notice appear in all copies and that
10: both that copyright notice and this permission notice appear in
11: supporting documentation, and that the names of Digital or MIT not be
12: used in advertising or publicity pertaining to distribution of the
13: software without specific, written prior permission.
14:
15: DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
16: ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
17: DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
18: ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
19: WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
20: ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
21: SOFTWARE.
22:
23: ******************************************************************/
24: /* $XConsortium: io.c,v 1.49 88/09/06 15:50:44 jim Exp $ */
25: /*****************************************************************
26: * i/o functions
27: *
28: * WriteToClient, ReadRequestFromClient
29: *
30: *****************************************************************/
31:
32: #include <stdio.h>
33: #include "Xos.h"
34: #include "Xmd.h"
35: #include <errno.h>
36: #include <sys/param.h>
37: #include <sys/types.h>
38: #include <sys/uio.h>
39: #include "X.h"
40: #include "Xproto.h"
41: #include "os.h"
42: #include "osdep.h"
43: #include "opaque.h"
44: #include "dixstruct.h"
45: #include "misc.h"
46:
47: #ifdef ISOCONN
48: #include <isode/tsap.h>
49: #endif /* ISOCONN */
50:
51: extern long ClientsWithInput[];
52: extern long ClientsWriteBlocked[];
53: extern long OutputPending[];
54: extern long OutputBufferSize;
55: extern ClientPtr ConnectionTranslation[];
56: extern Bool NewOutputPending;
57: extern Bool AnyClientsWriteBlocked;
58: static Bool CriticalOutputPending;
59: static int timesThisConnection = 0;
60:
61: extern int errno;
62:
63: #define request_length(req, cli) ((cli->swapped ? \
64: lswaps((req)->length) : (req)->length) << 2)
65: #define MAX_TIMES_PER 10
66:
67: #ifdef ISOCONN
68: /*
69: * Convenience Routines
70: */
71: TWriteToClient(sd, buf, len)
72: int sd, len;
73: char *buf;
74: {
75: struct TSAPdisconnect tds;
76: struct TSAPdisconnect *td = &tds;
77:
78: if (TDataRequest(sd, buf, len, td) == NOTOK){
79: if (errno != EWOULDBLOCK)
80: fprintf(stderr, "TWriteToClient: %s\n", td->td_reason);
81: }
82: }
83: /*
84: * sd = transport descriptor
85: * iov is iovec of iovcnt buffers
86: */
87: TWritevToClient(sd, iov, iovcnt)
88: int sd, iovcnt;
89: struct iovec *iov;
90: {
91: int i, ret, tot = 0;
92: struct udvec uv[64], *uvp = uv;
93: struct TSAPdisconnect tds;
94: struct TSAPdisconnect *td = &tds;
95:
96: /*
97: * Grotty hack
98: */
99: if (iovcnt >= 64) {
100: fprintf(stderr, "Oh Spaghettio\n");
101: return -1;
102: }
103: for(i=0; i<iovcnt; i++, uvp++, iov++) {
104: uvp->uv_base = iov->iov_base;
105: uvp->uv_len = iov->iov_len;
106: tot += iov->iov_len;
107: }
108: uvp->uv_base = NULL;
109: uvp->uv_len = 0;
110:
111: ret = TWriteRequest (sd, uv, td);
112: if (ret == NOTOK) {
113: #ifdef ISODEBUG
114: if (errno != EWOULDBLOCK)
115: if (isodexbug)
116: fprintf(stderr, "TWritevToCl: %s\n", TErrString(td->td_reason));
117: #endif /* ISODEBUG */
118: return ret;
119: } else {
120: #ifdef ISODEBUG
121: if (isodexbug)
122: fprintf(stderr, "TWritevToCl to %d: %d\n", sd, tot);
123: #endif /* ISODEBUG */
124: return tot;
125: }
126: }
127:
128: TAcceptFromClient(fd, vecp, vec)
129: int fd;
130: int vecp;
131: char **vec;
132: {
133: struct TSAPdisconnect tds;
134: struct TSAPdisconnect *td = &tds;
135: struct TSAPstart tsts;
136: struct TSAPstart *tst = &tsts;
137:
138: if (TInit(vecp, vec, tst, td) == NOTOK) {
139: Error(TErrString(td->td_reason));
140: Error("TInit");
141: return -1;
142: }
143:
144: if (TConnResponse(tst->ts_sd, NULLTA, tst->ts_expedited, NULL, 0,
145: &(tst->ts_qos), td) == NOTOK) {
146: Error(TErrString(td->td_reason));
147: Error("TConnResponse");
148: return -1;
149: }
150: return tst->ts_sd;
151: }
152:
153: TDiscFromClient(fd)
154: int fd;
155: {
156: struct TSAPdisconnect tds;
157:
158: if (TDiscRequest(fd, NULLCP, 0, &tds)==NOTOK)
159: fprintf(stderr, "TDisc Failed %s\n",
160: TErrString(tds.td_reason));
161: }
162: #endif /* ISOCONN */
163:
164: /*****************************************************************
165: * ReadRequestFromClient
166: * Returns one request from client. If the client misbehaves,
167: * returns NULL. The dispatcher closes down all misbehaving clients.
168: *
169: * client: index into bit array returned from WaitForSomething()
170: *
171: * status: status is set to
172: * > 0 the number of bytes in the request if the read is sucessful
173: * = 0 if action would block (entire request not ready)
174: * < 0 indicates an error (probably client died)
175: *
176: * oldbuf:
177: * To facilitate buffer management (e.g. on multi-processor
178: * systems), the diX layer must tell the OS layer when it is
179: * done with a request, so the parameter oldbuf is a pointer
180: * to a request that diX is finished with. In the
181: * sample implementation, which is single threaded,
182: * oldbuf is ignored. We assume that when diX calls
183: * ReadRequestFromClient(), the previous buffer is finished with.
184: *
185: * The returned string returned must be contiguous so that it can be
186: * cast in the dispatcher to the correct request type. Because requests
187: * are variable length, ReadRequestFromClient() must look at the first 4
188: * bytes of a request to determine the length (the request length is
189: * always the 3rd byte in the request).
190: *
191: * Note: in order to make the server scheduler (WaitForSomething())
192: * "fair", the ClientsWithInput mask is used. This mask tells which
193: * clients have FULL requests left in their buffers. Clients with
194: * partial requests require a read. Basically, client buffers
195: * are drained before select() is called again. But, we can't keep
196: * reading from a client that is sending buckets of data (or has
197: * a partial request) because others clients need to be scheduled.
198: *****************************************************************/
199:
200: ConnectionInput inputBuffers[MAXSOCKS]; /* buffers for clients */
201:
202: /*ARGSUSED*/
203: char *
204: ReadRequestFromClient(who, status, oldbuf)
205: ClientPtr who;
206: int *status; /* read at least n from client */
207: char *oldbuf;
208: {
209: #define YieldControl() \
210: { isItTimeToYield = TRUE; \
211: timesThisConnection = 0; }
212: #define YieldControlNoInput() \
213: { YieldControl(); \
214: BITCLEAR(ClientsWithInput, client); }
215: #define YieldControlAndReturnNull() \
216: { YieldControlNoInput(); \
217: return((char *) NULL ); }
218:
219: OsCommPtr oc = (OsCommPtr)who->osPrivate;
220: int client = oc->fd;
221: int result, gotnow, needed;
222: register ConnectionInput *pBuff;
223: register xReq *request;
224:
225: /* ignore oldbuf, just assume we're done with prev. buffer */
226:
227: if (client == -1)
228: {
229: ErrorF( "OH NO, %d translates to -1\n", who);
230: return((char *)NULL);
231: }
232:
233: pBuff = &inputBuffers[client];
234: pBuff->bufptr += pBuff->lenLastReq;
235: pBuff->lenLastReq = 0;
236:
237: /* handle buffer empty or full case first */
238:
239: if ((pBuff->bufptr - pBuff->buffer) >= pBuff->bufcnt)
240: {
241: #ifdef ISOCONN
242: result = SRead(client, pBuff->buffer, pBuff->size, OK);
243: #else /* ISOCONN */
244: result = read(client, pBuff->buffer, pBuff->size);
245: #endif /* ISOCONN */
246: if (result < 0)
247: {
248: if (errno == EWOULDBLOCK)
249: *status = 0;
250: else
251: *status = -1;
252: YieldControlAndReturnNull();
253: }
254: else if (result == 0)
255: {
256: *status = -1;
257: YieldControlAndReturnNull();
258: }
259: else
260: {
261: pBuff->bufcnt = result;
262: /* free up some space after huge requests */
263: if ((pBuff->size > BUFWATERMARK) && (result < BUFSIZE))
264: {
265: pBuff->size = BUFSIZE;
266: pBuff->buffer = (char *)xrealloc(pBuff->buffer, pBuff->size);
267: }
268: pBuff->bufptr = pBuff->buffer;
269: }
270: }
271: /* now look if there is enough in the buffer */
272:
273: request = (xReq *)pBuff->bufptr;
274: gotnow = pBuff->bufcnt + pBuff->buffer - pBuff->bufptr;
275:
276: if (gotnow < sizeof(xReq))
277: needed = sizeof(xReq) - gotnow;
278: else
279: {
280: needed = request_length(request, who);
281: if (needed > MAXBUFSIZE)
282: {
283: *status = -1;
284: YieldControlAndReturnNull();
285: }
286: if (needed <= 0)
287: needed = sizeof(xReq);
288: }
289: /* if the needed amount won't fit in what's remaining,
290: move everything to the front of the buffer. If the
291: entire header isn't available, move what's there too */
292: if ((pBuff->bufptr + needed - pBuff->buffer > pBuff->size) ||
293: (gotnow < sizeof(xReq)))
294: {
295: bcopy(pBuff->bufptr, pBuff->buffer, gotnow);
296: pBuff->bufcnt = gotnow;
297: if (needed > pBuff->size)
298: {
299: pBuff->size = needed;
300: pBuff->buffer = (char *)xrealloc(pBuff->buffer, needed);
301: }
302: pBuff->bufptr = pBuff->buffer;
303: }
304: /* don't have a full header */
305: if (gotnow < sizeof(xReq))
306: {
307: while (pBuff->bufcnt + pBuff->buffer - pBuff->bufptr < sizeof(xReq))
308: {
309: #ifdef ISOCONN
310: result = SRead(client,
311: pBuff->buffer + pBuff->bufcnt,
312: pBuff->size - pBuff->bufcnt,
313: OK);
314: #else /* ISOCONN */
315: result = read(client, pBuff->buffer + pBuff->bufcnt,
316: pBuff->size - pBuff->bufcnt);
317: #endif /* ISOCONN */
318: if (result < 0)
319: {
320: if (errno == EWOULDBLOCK)
321: *status = 0;
322: else
323: *status = -1;
324: YieldControlAndReturnNull();
325: }
326: if (result == 0)
327: {
328: *status = -1;
329: YieldControlAndReturnNull();
330: }
331: pBuff->bufcnt += result;
332: }
333: request = (xReq *)pBuff->bufptr;
334: gotnow = pBuff->bufcnt + pBuff->buffer - pBuff->bufptr;
335: needed = request_length(request, who);
336: if (needed <= 0)
337: needed = sizeof(xReq);
338: if (needed > pBuff->size)
339: {
340: pBuff->size = needed;
341: pBuff->buffer = (char *)xrealloc(pBuff->buffer, needed);
342: }
343: pBuff->bufptr = pBuff->buffer;
344: }
345:
346: if (gotnow < needed )
347: {
348: int i, wanted;
349:
350: wanted = needed - gotnow;
351: i = 0;
352: while (i < wanted)
353: {
354: #ifdef ISOCONN
355: result = SRead(client,
356: pBuff->buffer + pBuff->bufcnt,
357: pBuff->size - pBuff->bufcnt,
358: OK);
359: #else /* ISOCONN */
360: result = read(client, pBuff->buffer + pBuff->bufcnt,
361: pBuff->size - pBuff->bufcnt);
362: #endif /* ISOCONN */
363: if (result < 0)
364: {
365: if (errno == EWOULDBLOCK)
366: *status = 0;
367: else
368: *status = -1;
369: YieldControlAndReturnNull();
370: }
371: else if (result == 0)
372: {
373: *status = -1;
374: YieldControlAndReturnNull();
375: }
376: i += result;
377: pBuff->bufcnt += result;
378: }
379: }
380: *status = needed;
381: pBuff->lenLastReq = needed;
382:
383: /*
384: * Check to see if client has at least one whole request in the
385: * buffer. If there is only a partial request, treat like buffer
386: * is empty so that select() will be called again and other clients
387: * can get into the queue.
388: */
389:
390: timesThisConnection++;
391: if (pBuff->bufcnt + pBuff->buffer >= pBuff->bufptr + needed + sizeof(xReq))
392: {
393: request = (xReq *)(pBuff->bufptr + needed);
394: if ((pBuff->bufcnt + pBuff->buffer) >=
395: ((char *)request + request_length(request, who)))
396: BITSET(ClientsWithInput, client);
397: else
398: YieldControlNoInput();
399: }
400: else
401: YieldControlNoInput();
402: if (timesThisConnection == MAX_TIMES_PER)
403: YieldControl();
404:
405: return((char *)pBuff->bufptr);
406:
407: #undef YieldControlAndReturnNull
408: #undef YieldControlNoInput
409: #undef YieldControl
410: }
411:
412:
413: /* lookup table for adding padding bytes to data that is read from
414: or written to the X socket. */
415: static int padlength[4] = {0, 3, 2, 1};
416:
417: /********************
418: * FlushClient()
419: * If the client isn't keeping up with us, then we try to continue
420: * buffering the data and set the apropriate bit in ClientsWritable
421: * (which is used by WaitFor in the select). If the connection yields
422: * a permanent error, or we can't allocate any more space, we then
423: * close the connection.
424: *
425: **********************/
426:
427: static int
428: FlushClient(who, oc, extraBuf, extraCount)
429: ClientPtr who;
430: OsCommPtr oc;
431: char *extraBuf;
432: int extraCount; /* do not modify... returned below */
433: {
434: int connection = oc->fd,
435: total, n, i, notWritten, written,
436: iovCnt = 0;
437: struct iovec iov[3];
438: char padBuffer[3];
439: #ifdef ISOCONN
440: struct TSAPdisconnect tds;
441: #endif /* ISOCONN */
442:
443: total = 0;
444: if (oc->count)
445: {
446: total += iov[iovCnt].iov_len = oc->count;
447: iov[iovCnt++].iov_base = (caddr_t)oc->buf;
448: /* Notice that padding isn't needed for oc->buf since
449: it is alreay padded by WriteToClient */
450: }
451: if (extraCount)
452: {
453: total += iov[iovCnt].iov_len = extraCount;
454: iov[iovCnt++].iov_base = extraBuf;
455: if (extraCount & 3)
456: {
457: total += iov[iovCnt].iov_len = padlength[extraCount & 3];
458: iov[iovCnt++].iov_base = padBuffer;
459: }
460: }
461:
462: notWritten = total;
463:
464: #ifdef ISOCONN
465: while ((n = SWritev (connection, iov, iovCnt)) != notWritten)
466: #else /* ISOCONN */
467: while ((n = writev (connection, iov, iovCnt)) != notWritten)
468: #endif /* ISOCONN */
469: {
470: #ifdef hpux
471: if (n == -1 && errno == EMSGSIZE)
472: n = swWritev (connection, iov, 2);
473: #endif
474: if (n > 0)
475: {
476: notWritten -= n;
477: for (i = 0; i < iovCnt; i++)
478: {
479: if (n > iov[i].iov_len)
480: {
481: n -= iov[i].iov_len;
482: iov[i].iov_len = 0;
483: }
484: else
485: {
486: iov[i].iov_len -= n;
487: iov[i].iov_base += n;
488: break;
489: }
490: }
491: continue;
492: }
493: else if (errno != EWOULDBLOCK)
494: {
495: #ifdef notdef
496: if (errno != EBADF)
497: ErrorF("Closing connection %d because write failed\n",
498: connection);
499: /* this close will cause the select in WaitForSomething
500: to return that the connection is dead, so we can actually
501: clean up after the client. We can't clean up here,
502: because the we're in the middle of doing something
503: and will probably screw up some data strucutres */
504: #endif
505: #ifdef ISOCONN
506: SClose(connection);
507: #else /* ISOCONN */
508: close(connection);
509: #endif /* ISOCONN */
510: MarkClientException(who);
511: return(-1);
512: }
513:
514: /* If we've arrived here, then the client is stuffed to the gills
515: and not ready to accept more. Make a note of it and buffer
516: the rest. */
517: BITSET(ClientsWriteBlocked, connection);
518: AnyClientsWriteBlocked = TRUE;
519:
520: written = total - notWritten;
521: if (written < oc->count)
522: {
523: if (written > 0)
524: {
525: oc->count -= written;
526: bcopy((char *)oc->buf + written, (char *)oc->buf, oc->count);
527: written = 0;
528: }
529: }
530: else
531: {
532: written -= oc->count;
533: oc->count = 0;
534: }
535:
536: if (notWritten > oc->bufsize)
537: {
538: /* allocate at least enough to contain it plus one
539: OutputBufferSize */
540: oc->bufsize = notWritten + OutputBufferSize;
541: oc->buf = (unsigned char *)xrealloc(oc->buf, oc->bufsize);
542: if (oc->buf == NULL)
543: {
544: outOfMem:
545: #ifdef notdef
546: ErrorF("Closing connection %d because out of memory\n",
547: connection);
548: /* this close will cause the select in WaitForSomething
549: to return that the connection is dead, so we can actually
550: clean up after the client. We can't clean up here,
551: because the we're in the middle of doing something
552: and will probably screw up some data strucutres */
553: #endif
554: #ifdef ISOCONN
555: #ifdef ISODEBUG
556: fprintf(stderr, "out of mem: closing connection %d\n",
557: connection);
558: #endif /* ISODEBUG */
559: SClose(connection);
560: #else /* ISOCONN */
561: close(connection);
562: #endif /* ISOCONN */
563: MarkClientException(who);
564: oc->count = 0;
565: oc->bufsize = 0;
566: return(-1);
567: }
568: }
569:
570: /* If the amount written extended into the padBuffer, then the
571: difference "extraCount - written" may be less than 0 */
572: if ((n = extraCount - written) > 0)
573: bcopy (extraBuf + written, (char *)oc->buf + oc->count, n);
574:
575: oc->count = notWritten; /* this will include the pad */
576:
577: return extraCount; /* return only the amount explicitly requested */
578: }
579:
580: /* everything was flushed out */
581: oc->count = 0;
582: if (oc->bufsize > OutputBufferSize)
583: {
584: oc->bufsize = OutputBufferSize;
585: oc->buf = (unsigned char *)xrealloc(oc->buf, OutputBufferSize);
586: if (oc->buf == NULL) /* nearly impossible */
587: goto outOfMem;
588: }
589: return extraCount; /* return only the amount explicitly requested */
590: }
591:
592: /********************
593: * FlushAllOutput()
594: * Flush all clients with output. However, if some client still
595: * has input in the queue (more requests), then don't flush. This
596: * will prevent the output queue from being flushed every time around
597: * the round robin queue. Now, some say that it SHOULD be flushed
598: * every time around, but...
599: *
600: **********************/
601:
602: void
603: FlushAllOutput()
604: {
605: register int index, base, mask;
606: OsCommPtr oc;
607: register ClientPtr client;
608:
609: if (! NewOutputPending)
610: return;
611:
612: /*
613: * It may be that some client still has critical output pending,
614: * but he is not yet ready to receive it anyway, so we will
615: * simply wait for the select to tell us when he's ready to receive.
616: */
617: CriticalOutputPending = FALSE;
618: NewOutputPending = FALSE;
619:
620: for (base = 0; base < mskcnt; base++)
621: {
622: mask = OutputPending[ base ];
623: OutputPending[ base ] = 0;
624: while (mask)
625: {
626: index = ffs(mask) - 1;
627: mask &= ~lowbit(mask);
628: if ((client = ConnectionTranslation[(32 * base) + index ]) == NULL)
629: continue;
630: if (client->clientGone)
631: continue;
632: oc = (OsCommPtr)client->osPrivate;
633: if (GETBIT(ClientsWithInput, client->index))
634: {
635: BITSET(OutputPending, oc->fd); /* set the bit again */
636: NewOutputPending = TRUE;
637: }
638: else
639: FlushClient(client, oc, (char *)NULL, 0);
640: }
641: }
642:
643: }
644:
645: void
646: FlushIfCriticalOutputPending()
647: {
648: if (CriticalOutputPending)
649: FlushAllOutput();
650: }
651:
652: void
653: SetCriticalOutputPending()
654: {
655: CriticalOutputPending = TRUE;
656: }
657:
658: /*****************
659: * WriteToClient
660: * Copies buf into ClientPtr.buf if it fits (with padding), else
661: * flushes ClientPtr.buf and buf to client. As of this writing,
662: * every use of WriteToClient is cast to void, and the result
663: * is ignored. Potentially, this could be used by requests
664: * that are sending several chunks of data and want to break
665: * out of a loop on error. Thus, we will leave the type of
666: * this routine as int.
667: *****************/
668:
669: int
670: WriteToClient (who, count, buf)
671: ClientPtr who;
672: char *buf;
673: int count;
674: {
675: OsCommPtr oc = (OsCommPtr)who->osPrivate;
676: int padBytes;
677:
678: if (oc->fd == -1)
679: {
680: ErrorF( "OH NO, %d translates to -1\n", oc->fd);
681: return(-1);
682: }
683:
684: if (oc->fd == -2)
685: {
686: #ifdef notdef
687: ErrorF( "CONNECTION %d ON ITS WAY OUT\n", oc->fd);
688: #endif
689: return(-1);
690: }
691:
692: padBytes = padlength[count & 3];
693:
694: if (oc->count + count + padBytes > oc->bufsize)
695: {
696: BITCLEAR(OutputPending, oc->fd);
697: CriticalOutputPending = FALSE;
698: NewOutputPending = FALSE;
699: return FlushClient(who, oc, buf, count);
700: }
701:
702: NewOutputPending = TRUE;
703: BITSET(OutputPending, oc->fd);
704: bcopy(buf, (char *)oc->buf + oc->count, count);
705: oc->count += count + padBytes;
706:
707: return(count);
708: }
709:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.