|
|
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: /* $Header: io.c,v 1.36 87/09/09 23:04:35 rws Exp $ */ ! 25: /***************************************************************** ! 26: * i/o functions ! 27: * ! 28: * WriteToClient, ReadRequestFromClient ! 29: * ! 30: *****************************************************************/ ! 31: ! 32: #include <stdio.h> ! 33: #include "Xmd.h" ! 34: #include <errno.h> ! 35: #include <sys/param.h> ! 36: #include <sys/types.h> ! 37: #include <sys/uio.h> ! 38: #include <sys/time.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: ! 46: extern long ClientsWithInput[]; ! 47: extern long PartialRequest; ! 48: extern ClientPtr ConnectionTranslation[]; ! 49: static int timesThisConnection = 0; ! 50: ! 51: extern int errno; ! 52: ! 53: #define request_length(req, cli) ((cli->swapped ? \ ! 54: lswaps((req)->length) : (req)->length) << 2) ! 55: #define MAX_TIMES_PER 10 ! 56: ! 57: /***************************************************************** ! 58: * ReadRequestFromClient ! 59: * Returns one request from client. If the client misbehaves, ! 60: * returns NULL. The dispatcher closes down all misbehaving clients. ! 61: * ! 62: * client: index into bit array returned from WaitForSomething() ! 63: * ! 64: * status: status is set to ! 65: * > 0 the number of bytes in the request if the read is sucessful ! 66: * = 0 if action would block (entire request not ready) ! 67: * < 0 indicates an error (probably client died) ! 68: * ! 69: * oldbuf: ! 70: * To facilitate buffer management (e.g. on multi-processor ! 71: * systems), the diX layer must tell the OS layer when it is ! 72: * done with a request, so the parameter oldbuf is a pointer ! 73: * to a request that diX is finished with. In the ! 74: * sample implementation, which is single threaded, ! 75: * oldbuf is ignored. We assume that when diX calls ! 76: * ReadRequestFromClient(), the previous buffer is finished with. ! 77: * ! 78: * The returned string returned must be contiguous so that it can be ! 79: * cast in the dispatcher to the correct request type. Because requests ! 80: * are variable length, ReadRequestFromClient() must look at the first 4 ! 81: * bytes of a request to determine the length (the request length is ! 82: * always the 3rd byte in the request). ! 83: * ! 84: * Note: in order to make the server scheduler (WaitForSomething()) ! 85: * "fair", the ClientsWithInput mask is used. This mask tells which ! 86: * clients have FULL requests left in their buffers. Clients with ! 87: * partial requests require a read. Basically, client buffers ! 88: * are drained before select() is called again. But, we can't keep ! 89: * reading from a client that is sending buckets of data (or has ! 90: * a partial request) because others clients need to be scheduled. ! 91: *****************************************************************/ ! 92: ! 93: ConnectionInput inputBuffers[MAXSOCKS]; /* buffers for clients */ ! 94: ! 95: char * ! 96: ReadRequestFromClient(who, status, oldbuf) ! 97: ClientPtr who; ! 98: int *status; /* read at least n from client */ ! 99: char *oldbuf; ! 100: { ! 101: int client = ((osPrivPtr)who->osPrivate)->fd; ! 102: int result, gotnow, needed; ! 103: register ConnectionInput *pBuff; ! 104: register xReq *request; ! 105: ! 106: /* ignore oldbuf, just assume we're done with prev. buffer */ ! 107: ! 108: if (client == -1) ! 109: { ! 110: ErrorF( "OH NO, %d translates to -1\n", who); ! 111: return((char *)NULL); ! 112: } ! 113: ! 114: pBuff = &inputBuffers[client]; ! 115: pBuff->bufptr += pBuff->lenLastReq; ! 116: pBuff->lenLastReq = 0; ! 117: ! 118: /* handle buffer empty or full case first */ ! 119: ! 120: if ((pBuff->bufptr - pBuff->buffer >= pBuff->bufcnt) || (!pBuff->bufcnt)) ! 121: { ! 122: result = read(client, pBuff->buffer, pBuff->size); ! 123: if (result < 0) ! 124: { ! 125: if (errno == EWOULDBLOCK) ! 126: *status = 0; ! 127: else ! 128: *status = -1; ! 129: BITCLEAR(ClientsWithInput, client); ! 130: isItTimeToYield = TRUE; ! 131: return((char *)NULL); ! 132: } ! 133: else if (result == 0) ! 134: { ! 135: BITCLEAR(ClientsWithInput, client); ! 136: isItTimeToYield = TRUE; ! 137: *status = -1; ! 138: return((char *) NULL); ! 139: } ! 140: else ! 141: { ! 142: pBuff->bufcnt = result; ! 143: pBuff->bufptr = pBuff->buffer; ! 144: } ! 145: } ! 146: /* now look if there is enough in the buffer */ ! 147: ! 148: request = (xReq *)pBuff->bufptr; ! 149: gotnow = pBuff->bufcnt + pBuff->buffer - pBuff->bufptr; ! 150: ! 151: if (gotnow < sizeof(xReq)) ! 152: needed = sizeof(xReq) - gotnow; ! 153: else ! 154: { ! 155: needed = request_length(request, who); ! 156: if (needed > MAXBUFSIZE) ! 157: { ! 158: *status = -1; ! 159: BITCLEAR(ClientsWithInput, client); ! 160: isItTimeToYield = TRUE; ! 161: return((char *)NULL); ! 162: } ! 163: if (needed <= 0) ! 164: needed = sizeof(xReq); ! 165: } ! 166: /* if the needed amount won't fit in what's remaining, ! 167: move everything to the front of the buffer. If the ! 168: entire header isn't available, move what's there too */ ! 169: if ((pBuff->bufptr + needed - pBuff->buffer > pBuff->size) || ! 170: (gotnow < sizeof(xReq))) ! 171: { ! 172: bcopy(pBuff->bufptr, pBuff->buffer, gotnow); ! 173: pBuff->bufcnt = gotnow; ! 174: if (needed > pBuff->size) ! 175: { ! 176: pBuff->size = needed; ! 177: pBuff->buffer = (char *)Xrealloc(pBuff->buffer, needed); ! 178: } ! 179: pBuff->bufptr = pBuff->buffer; ! 180: } ! 181: /* don't have a full header */ ! 182: if (gotnow < sizeof(xReq)) ! 183: { ! 184: while (pBuff->bufcnt + pBuff->buffer - pBuff->bufptr < sizeof(xReq)) ! 185: { ! 186: result = read(client, pBuff->buffer + pBuff->bufcnt, ! 187: pBuff->size - pBuff->bufcnt); ! 188: if (result < 0) { ! 189: if (errno == EWOULDBLOCK) ! 190: *status = 0; ! 191: else ! 192: *status = -1; ! 193: BITCLEAR(ClientsWithInput, client); ! 194: isItTimeToYield = TRUE; ! 195: return((char *)NULL); ! 196: } ! 197: if (result == 0) { ! 198: *status = -1; ! 199: BITCLEAR(ClientsWithInput, client); ! 200: isItTimeToYield = TRUE; ! 201: return((char *)NULL); ! 202: } ! 203: pBuff->bufcnt += result; ! 204: } ! 205: request = (xReq *)pBuff->bufptr; ! 206: gotnow = pBuff->bufcnt + pBuff->buffer - pBuff->bufptr; ! 207: needed = request_length(request, who); ! 208: if (needed <= 0) ! 209: needed = sizeof(xReq); ! 210: if (needed > pBuff->size) ! 211: { ! 212: pBuff->size = needed; ! 213: pBuff->buffer = (char *)Xrealloc(pBuff->buffer, needed); ! 214: } ! 215: pBuff->bufptr = pBuff->buffer; ! 216: } ! 217: ! 218: if (gotnow < needed ) ! 219: { ! 220: int i, wanted; ! 221: ! 222: wanted = needed - gotnow; ! 223: i = 0; ! 224: while (i < wanted) ! 225: { ! 226: result = read(client, pBuff->buffer + pBuff->bufcnt, ! 227: pBuff->size - pBuff->bufcnt); ! 228: if (result < 0) ! 229: { ! 230: if (errno == EWOULDBLOCK) ! 231: *status = 0; ! 232: else ! 233: *status = -1; ! 234: BITCLEAR(ClientsWithInput, client); ! 235: isItTimeToYield = TRUE; ! 236: return((char *)NULL); ! 237: } ! 238: else if (result == 0) ! 239: { ! 240: *status = -1; ! 241: BITCLEAR(ClientsWithInput, client); ! 242: isItTimeToYield = TRUE; ! 243: return((char *)NULL); ! 244: } ! 245: i += result; ! 246: pBuff->bufcnt += result; ! 247: } ! 248: } ! 249: *status = needed; ! 250: pBuff->lenLastReq = needed; ! 251: ! 252: /* ! 253: * Check to see if client has at least one whole request in the ! 254: * buffer. If there is only a partial request, treat like buffer ! 255: * is empty so that select() will be called again and other clients ! 256: * can get into the queue. ! 257: */ ! 258: ! 259: if (pBuff->bufcnt + pBuff->buffer >= pBuff->bufptr + needed + sizeof(xReq)) ! 260: { ! 261: request = (xReq *)(pBuff->bufptr + needed); ! 262: if ((pBuff->bufcnt + pBuff->buffer) >= ! 263: ((char *)request + request_length(request, who))) ! 264: BITSET(ClientsWithInput, client); ! 265: else ! 266: { ! 267: BITCLEAR(ClientsWithInput, client); ! 268: isItTimeToYield = TRUE; ! 269: } ! 270: } ! 271: else ! 272: { ! 273: BITCLEAR(ClientsWithInput, client); ! 274: isItTimeToYield = TRUE; ! 275: } ! 276: if ((++timesThisConnection == MAX_TIMES_PER) || (isItTimeToYield)) ! 277: { ! 278: isItTimeToYield = TRUE; ! 279: timesThisConnection = 0; ! 280: } ! 281: return((char *)pBuff->bufptr); ! 282: } ! 283: ! 284: ! 285: /***************** ! 286: * WriteToClient ! 287: * We might have to wait, if the client isn't keeping up with us. We ! 288: * wait for a short time, then close the connection. This isn't a ! 289: * wonderful solution, ! 290: * but it rarely seems to be a problem right now, and buffering output for ! 291: * asynchronous delivery sounds complicated and expensive. ! 292: * Long word aligns all data. ! 293: *****************/ ! 294: ! 295: /* lookup table for adding padding bytes to data that is read from ! 296: or written to the X socket. */ ! 297: static int padlength[4] = {0, 3, 2, 1}; ! 298: ! 299: int ! 300: WriteToClient (who, count, buf) ! 301: ClientPtr who; ! 302: char *buf; ! 303: int count; ! 304: { ! 305: #define OUTTIME 2 ! 306: int connection = ((osPrivPtr)who->osPrivate)->fd; ! 307: int total; ! 308: register int n; ! 309: int mask[mskcnt]; ! 310: struct timeval outtime; ! 311: struct iovec iov[2]; ! 312: char pad[3]; ! 313: int secondTime = 0; ! 314: ! 315: outtime.tv_sec = (long) OUTTIME; ! 316: outtime.tv_usec = 0; ! 317: ! 318: if (connection == -1) ! 319: { ! 320: ErrorF( "OH NO, %d translates to -1\n", connection); ! 321: return(-1); ! 322: } ! 323: ! 324: if (connection == -2) ! 325: { ! 326: #ifdef notdef ! 327: ErrorF( "CONNECTION %d ON ITS WAY OUT\n", connection); ! 328: #endif ! 329: return(-1); ! 330: } ! 331: ! 332: iov[0].iov_len = count; ! 333: iov[0].iov_base = buf; ! 334: iov[1].iov_len = padlength[count & 3]; ! 335: iov[1].iov_base = pad; ! 336: ! 337: total = iov[0].iov_len + iov[1].iov_len; ! 338: while ((n = writev (connection, iov, 2)) != total) ! 339: { ! 340: if (n > 0) ! 341: { ! 342: total -= n; ! 343: if ((iov[0].iov_len -= n) < 0) ! 344: { ! 345: iov[1].iov_len += iov[0].iov_len; ! 346: iov[1].iov_base -= iov[0].iov_len; ! 347: iov[0].iov_len = 0; ! 348: } ! 349: else ! 350: iov[0].iov_base += n; ! 351: continue; ! 352: } ! 353: else if (errno != EWOULDBLOCK) ! 354: { ! 355: #ifdef notdef ! 356: if (errno != EBADF) ! 357: ErrorF("Closing connection %d because write failed\n", ! 358: connection); ! 359: /* this close will cause the select in WaitForSomething ! 360: to return that the connection is dead, so we can actually ! 361: clean up after the client. We can't clean up here, ! 362: because the we're in the middle of doing something ! 363: and will probably screw up some data strucutres */ ! 364: #endif ! 365: close(connection); ! 366: MarkClientException(who); ! 367: return(-1); ! 368: } ! 369: /* blocked => be willing to try him once more */ ! 370: #ifdef notdef ! 371: ErrorF("Connection %d blocked, be willing to try write once more:\n", ! 372: connection); ! 373: ErrorF("need to write: %d, have written: %d, errno: %d\n", ! 374: total, n, errno); ! 375: #endif ! 376: CLEARBITS(mask); ! 377: BITSET(mask, connection); ! 378: n = select (connection + 1, (int *) NULL, mask, (int *) NULL, ! 379: &outtime); ! 380: if ((n != 1) && (secondTime == 3)) ! 381: { ! 382: #ifdef notdef ! 383: ErrorF("Connection %d write failed after partial\n", connection); ! 384: #endif ! 385: /* this close will cause the select in WaitForSomething ! 386: to return that the connection is dead, so we can actually ! 387: clean up after the client. We can't clean up here, ! 388: because the we're in the middle of doing something ! 389: and will probably screw up some data strucutres */ ! 390: close(connection); ! 391: MarkClientException(who); ! 392: return(-1); ! 393: } ! 394: else ! 395: secondTime++; ! 396: } ! 397: return(count); ! 398: } ! 399:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.