|
|
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: connection.c,v 1.62 87/09/07 17:12:18 rws Exp $ */
25: /*****************************************************************
26: * Stuff to create connections --- OS dependent
27: *
28: * EstablishNewConnections, CreateWellKnownSockets,
29: * CloseDownConnection, CheckConnections, AddEnabledDevice,
30: * RemoveEnabledDevice, OnlyListToOneClient,
31: * ListenToAllClients,
32: *
33: * (WaitForSomething is in its own file)
34: *
35: * In this implementation, a client socket table is not kept.
36: * Instead, what would be the index into the table is just the
37: * file descriptor of the socket. This won't work for if the
38: * socket ids aren't small nums (0 - 2^8)
39: *
40: *****************************************************************/
41:
42: #include <dbm.h>
43: #undef NULL
44: #include "X.h"
45: #include "Xproto.h"
46: #include <sys/param.h>
47: #include <errno.h>
48: #include <sys/types.h>
49: #include <sys/socket.h>
50: #include <sys/time.h>
51: #include <signal.h>
52: #include <fcntl.h>
53: #include <setjmp.h>
54: #ifdef TCPCONN
55: #include <netinet/in.h>
56: #endif
57: #ifdef UNIXCONN
58: #include <sys/un.h>
59: #endif
60: #include <stdio.h>
61: #include <sys/uio.h>
62: #include <strings.h>
63: #include "osstruct.h"
64: #include "osdep.h"
65: #include "opaque.h"
66:
67: #include "dixstruct.h"
68:
69: #ifdef DNETCONN
70: #include <netdnet/dn.h>
71: #endif /* DNETCONN */
72:
73: typedef long CCID; /* mask of indices into client socket table */
74:
75: #define X_UNIX_PATH "/tmp/.X11-unix"
76:
77: char *display; /* The display number */
78: int lastfdesc; /* maximum file descriptor */
79:
80: long WellKnownConnections; /* Listener mask */
81: long EnabledDevices; /* mask for input devices that are on */
82: long AllSockets[mskcnt]; /* select on this */
83: long AllClients[mskcnt]; /* available clients */
84: long LastSelectMask[mskcnt]; /* mask returned from last select call */
85: long ClientsWithInput[mskcnt];
86: long MaxClients = MAXSOCKS ;
87: long NConnBitArrays = mskcnt;
88: long FirstClient;
89:
90: static Bool debug_conns = FALSE;
91:
92: static char whichByteIsFirst;
93:
94: static int SavedAllClients[mskcnt];
95: static int SavedAllSockets[mskcnt];
96: static int SavedClientsWithInput[mskcnt];
97: static Bool GrabDone = FALSE;
98:
99: ClientPtr ConnectionTranslation[MAXSOCKS];
100: extern ClientPtr NextAvailableClient();
101:
102: extern ConnectionInput inputBuffers[];
103:
104: int swappedClients[MAXSOCKS];
105:
106: extern int AutoResetServer();
107: extern int GiveUp();
108:
109: /*****************
110: * CreateWellKnownSockets
111: * At initialization, create the sockets to listen on for new clients.
112: * There are potentially 4: DECnet, UNIX Domain, TCP-IP with MSB first,
113: * with TCP-IP with LSB first.
114: *****************/
115:
116: void
117: CreateWellKnownSockets()
118: {
119: char fname[32];
120: int request, i;
121: int whichbyte; /* used to figure out whether this is
122: LSB or MSB */
123: #ifdef TCPCONN
124: struct sockaddr_in insock;
125: int tcpportReg; /* port with same byte order as server */
126:
127: #ifdef SO_LINGER
128: static int linger[2] = { 0, 0 };
129: #endif /* SO_LINGER */
130:
131: #endif /* TCPCONN */
132:
133: #ifdef UNIXCONN
134: struct sockaddr_un unsock;
135: int oldUmask;
136: #endif /* UNIXCONN */
137:
138: #ifdef DNETCONN
139: struct sockaddr_dn dnsock;
140: #endif /* DNETCONN */
141: int retry;
142:
143: CLEARBITS(AllSockets);
144: CLEARBITS(AllClients);
145: CLEARBITS(LastSelectMask);
146: CLEARBITS(ClientsWithInput);
147:
148: for (i=0; i<MAXSOCKS; i++) ConnectionTranslation[i] = (ClientPtr)NULL;
149:
150: lastfdesc = getdtablesize() - 1;
151: if (lastfdesc > MAXSOCKS)
152: {
153: lastfdesc = MAXSOCKS;
154: ErrorF( "GOT TO END OF SOCKETS %d\n", MAXSOCKS);
155: }
156:
157: /* hack test to decide where to log errors */
158:
159: if (write (2, fname, 0))
160: {
161: long t;
162:
163: char *ctime();
164: close(stdin);
165: close(stdout);
166: strcpy (fname, "/usr/adm/X");
167: strcat (fname, display);
168: strcat (fname, "msgs");
169: freopen (fname, "a+", stderr);
170: setlinebuf(stderr);
171: time (&t);
172: fprintf (stderr, "start %s", ctime(&t));
173: }
174: if (getpgrp (0) == 0)
175: setpgrp (0, getpid ());
176:
177: WellKnownConnections = 0;
178: whichbyte = 1;
179:
180: if (*(char *) &whichbyte)
181: whichByteIsFirst = 'l';
182: else
183: whichByteIsFirst = 'B';
184:
185:
186: #ifdef TCPCONN
187:
188: tcpportReg = atoi (display);
189: tcpportReg += X_TCP_PORT;
190:
191: if ((request = socket (AF_INET, SOCK_STREAM, 0)) < 0)
192: {
193: Notice ("Creating TCP socket");
194: }
195: else
196: {
197: bzero ((char *)&insock, sizeof (insock));
198: insock.sin_family = AF_INET;
199: insock.sin_port = htons (tcpportReg);
200: insock.sin_addr.s_addr = htonl(INADDR_ANY);
201: retry = 20;
202: while (i = bind(request, (struct sockaddr *) &insock, sizeof (insock)))
203: {
204: if (--retry == 0)
205: Error ("Binding MSB TCP socket");
206: sleep (10);
207: }
208: #ifdef SO_LINGER
209: if(setsockopt (request, SOL_SOCKET, SO_LINGER, linger, sizeof(linger)))
210: Notice ("Setting TCP SO_LINGER\n");
211: #endif /* SO_LINGER */
212: if (listen (request, 5))
213: Error ("Reg TCP Listening");
214: WellKnownConnections |= (1 << request);
215: DefineSelf (request);
216: }
217:
218: #endif /* TCPCONN */
219:
220: #ifdef UNIXCONN
221: unsock.sun_family = AF_UNIX;
222: oldUmask = umask (0);
223: mkdir (X_UNIX_PATH, 0777);
224: (void)umask(oldUmask);
225: strcpy (unsock.sun_path, X_UNIX_PATH);
226: strcat (unsock.sun_path, "/X");
227: strcat (unsock.sun_path, display);
228: unlink (unsock.sun_path);
229: if ((request = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
230: {
231: Notice ("Creating Unix socket");
232: }
233: else
234: {
235: if(bind(request,(struct sockaddr *)&unsock, strlen(unsock.sun_path)+2))
236: Error ("Binding Unix socket");
237: if (listen (request, 5)) Error ("Unix Listening");
238: WellKnownConnections |= (1 << request);
239: }
240: #endif /*UNIXCONN */
241:
242: #ifdef DNETCONN
243: if ((request = socket (AF_DECnet, SOCK_STREAM, 0)) < 0)
244: {
245: Notice ("Creating DECnet socket");
246: }
247: else
248: {
249: bzero ((char *)&dnsock, sizeof (dnsock));
250: dnsock.sdn_family = AF_DECnet;
251: sprintf(dnsock.sdn_objname, "X%d", atoi (display));
252: dnsock.sdn_objnamel = strlen(dnsock.sdn_objname);
253: if (bind (request, (struct sockaddr *) &dnsock, sizeof (dnsock)))
254: Error ("Binding DECnet socket");
255: if (listen (request, 5)) Error ("DECnet Listening");
256: WellKnownConnections |= (1 << request);
257: DefineSelf (request);
258: }
259: #endif /* DNETCONN */
260: if (WellKnownConnections == 0)
261: Error ("No Listeners, nothing to do");
262: signal (SIGPIPE, SIG_IGN);
263: signal (SIGHUP, AutoResetServer);
264: signal (SIGINT, GiveUp);
265: signal (SIGTERM, GiveUp);
266: FirstClient = request + 1;
267: AllSockets[0] = WellKnownConnections;
268: ResetHosts(display);
269:
270: for (i=0; i<MaxClients; i++)
271: {
272: inputBuffers[i].buffer = (char *) NULL;
273: inputBuffers[i].bufptr = (char *) NULL;
274: inputBuffers[i].bufcnt = 0;
275: inputBuffers[i].lenLastReq = 0;
276: inputBuffers[i].size = 0;
277: }
278: }
279:
280: /* We want to read the connection information. If the client doesn't
281: * send us enough data, however, we want to time out eventually.
282: * The scheme is to clear a flag, set an alarm, and keep doing non-blocking
283: * reads until we get all the data we want. If the alarm goes
284: * off, the handler will clear the flag. If we see that the flag is
285: * cleared, we know we've timed out and return with an error.
286: *
287: * there remains one problem with this code:
288: * there is a window of vulnerability in which we might get an alarm
289: * even though all the data has come in properly. This is because I
290: * can't atomically clear the alarm.
291: *
292: * Anyone who sees how to fix this problem should do so and
293: * submit a fix.
294: */
295:
296: jmp_buf env;
297: void TimeOut()
298: {
299: longjmp(env, 1);
300: }
301: static Bool
302: ReadBuffer(conn, buffer, charsWanted)
303: long conn;
304: char *buffer;
305: int charsWanted;
306: {
307: char *bptr = buffer;
308: int got, fTimeOut;
309: struct itimerval itv;
310:
311: signal(SIGALRM, TimeOut);
312: fTimeOut = FALSE;
313: /* only 1 alarm, please, not 1 per minute */
314: timerclear(&itv.it_interval);
315: itv.it_value.tv_sec = TimeOutValue;
316: itv.it_value.tv_usec = 0;
317: setitimer(ITIMER_REAL, &itv, NULL);
318: /* It better not take a full minute to get to the read call */
319:
320: while (charsWanted && (fTimeOut = setjmp(env)) == FALSE)
321: {
322: got = read(conn, bptr, charsWanted);
323: if (got <= 0)
324: return FALSE;
325: if(got > 0)
326: {
327: charsWanted -= got;
328: bptr += got;
329: /* Ok, we got something, reset the timer */
330: itv.it_value.tv_sec = TimeOutValue;
331: itv.it_value.tv_usec = 0;
332: setitimer(ITIMER_REAL, &itv, NULL);
333: }
334: }
335: /* disable the timer */
336: timerclear(&itv.it_value);
337: setitimer(ITIMER_REAL, &itv, NULL);
338: /* If we got here and we didn't time out, then return TRUE, because
339: * we must have read what we wanted. If we timed out, return FALSE */
340: if(fTimeOut && debug_conns)
341: ErrorF("Timed out on connection %d\n", conn);
342: return (!fTimeOut);
343: }
344:
345: /*****************************************************************
346: * ClientAuthorized
347: *
348: * Sent by the client at connection setup:
349: * typedef struct _xConnClientPrefix {
350: * CARD8 byteOrder;
351: * BYTE pad;
352: * CARD16 majorVersion, minorVersion;
353: * CARD16 nbytesAuthProto;
354: * CARD16 nbytesAuthString;
355: * } xConnClientPrefix;
356: *
357: * It is hoped that eventually one protocol will be agreed upon. In the
358: * mean time, a server that implements a different protocol than the
359: * client expects, or a server that only implements the host-based
360: * mechanism, will simply ignore this information.
361: *
362: *****************************************************************/
363:
364: int
365: ClientAuthorized(conn, pswapped, reason)
366: long conn;
367: int *pswapped;
368: char **reason; /* if authorization fails, put reason in here */
369: {
370: short slen;
371: union {
372: struct sockaddr sa;
373: #ifdef UNIXCONN
374: struct sockaddr_un un;
375: #endif /* UNIXCONN */
376: #ifdef TCPCONN
377: struct sockaddr_in in;
378: #endif /* TCPCONN */
379: #ifdef DNETCONN
380: struct sockaddr_dn dn;
381: #endif /* DNETCONN */
382: } from;
383: int fromlen;
384: xConnClientPrefix xccp;
385: char auth_proto[100];
386: char auth_string[100];
387:
388: if (!ReadBuffer(conn, &xccp, sizeof(xConnClientPrefix)))
389: {
390: /* If they can't even give us this much, just blow them off
391: * without an error message */
392: *reason = 0;
393: return 0;
394: }
395: if (xccp.byteOrder != whichByteIsFirst)
396: {
397: SwapConnClientPrefix(&xccp);
398: *pswapped = TRUE;
399: }
400: else
401: *pswapped = FALSE;
402: if ((xccp.majorVersion != X_PROTOCOL) ||
403: (xccp.minorVersion != X_PROTOCOL_REVISION))
404: {
405: #define STR "Protocol version mismatch"
406: *reason = (char *)Xalloc(strlen(STR) + 1);
407: strcpy(*reason, STR);
408: if (debug_conns)
409: ErrorF("%s\n", STR);
410: #undef STR
411: return 0;
412: }
413: fromlen = sizeof (from);
414: if (getpeername (conn, &from.sa, &fromlen) ||
415: InvalidHost (&from.sa, fromlen))
416: {
417: #define STR "Server is not authorized to connect to host"
418: *reason = (char *)Xalloc(strlen(STR));
419: strcpy(*reason, STR);
420: #undef STR
421: return 0;
422: }
423:
424: slen = (xccp.nbytesAuthProto + 3) & ~3;
425: if ( slen )
426: if (!ReadBuffer(conn, auth_proto, slen))
427: {
428: #define STR "Length error in xConnClientPrefix for protocol authorization "
429: *reason = (char *)Xalloc(strlen(STR));
430: strcpy(*reason, STR);
431: return 0;
432: #undef STR
433: }
434: auth_proto[slen] = '\0';
435:
436: slen = (xccp.nbytesAuthString + 3) & ~3;
437: if ( slen)
438: if (!ReadBuffer(conn, auth_string, slen))
439: {
440: #define STR "Length error in xConnClientPrefix for protocol string"
441: *reason = (char *)Xalloc(strlen(STR));
442: strcpy(*reason, STR);
443: return 0;
444: #undef STR
445: }
446: auth_string[slen] = '\0';
447:
448: /* At this point, if the client is authorized to change the access control
449: * list, we should getpeername() information, and add the client to
450: * the selfhosts list. It's not really the host machine, but the
451: * true purpose of the selfhosts list is to see who may change the
452: * access control list.
453: */
454: return(1);
455: }
456:
457: static int padlength[4] = {0, 3, 2, 1};
458:
459: /*****************
460: * EstablishNewConnections
461: * If anyone is waiting on listened sockets, accept them.
462: * Returns a mask with indices of new clients. Updates AllClients
463: * and AllSockets.
464: *****************/
465:
466: void
467: EstablishNewConnections(newclients, nnew)
468: ClientPtr *newclients;
469: int *nnew;
470: {
471: long readyconnections; /* mask of listeners that are ready */
472: long curconn; /* fd of listener that's ready */
473: long newconn; /* fd of new client */
474: int swapped; /* set by ClientAuthorized if connection is
475: * swapped */
476: char *reason;
477: struct iovec iov[2];
478: char p[3];
479:
480: #ifdef TCP_NODELAY
481: union {
482: struct sockaddr sa;
483: #ifdef UNIXCONN
484: struct sockaddr_un un;
485: #endif /* UNIXCONN */
486: #ifdef TCPCONN
487: struct sockaddr_in in;
488: #endif /* TCPCONN */
489: #ifdef DNETCONN
490: struct sockaddr_dn dn;
491: #endif /* DNETCONN */
492: } from;
493: int fromlen;
494: #endif TCP_NODELAY
495:
496: *nnew = 0;
497: if (readyconnections = (LastSelectMask[0] & WellKnownConnections))
498: {
499: while (readyconnections)
500: {
501: curconn = ffs (readyconnections) - 1;
502: if ((newconn = accept (curconn,
503: (struct sockaddr *) NULL,
504: (int *)NULL)) >= 0)
505: {
506: if (newconn >= lastfdesc)
507: {
508: if (debug_conns)
509: ErrorF("Didn't make connection: Out of file descriptors for connections\n");
510: close (newconn);
511: }
512: else
513: {
514: #ifdef TCP_NODELAY
515: fromlen = sizeof (from);
516: if (!getpeername (newconn, &from.sa, &fromlen))
517: {
518: if (fromlen && (from.sa.sa_family == AF_INET))
519: {
520: mi = 1;
521: setsockopt (newconn, IPPROTO_TCP, TCP_NODELAY,
522: &mi, sizeof (int));
523: }
524: }
525: #endif /* TCP_NODELAY */
526: if (ClientAuthorized(newconn, &swapped, &reason))
527: {
528: ClientPtr next;
529:
530: fcntl (newconn, F_SETFL, FNDELAY);
531: inputBuffers[newconn].used = 1;
532: if (! inputBuffers[newconn].size)
533: {
534: inputBuffers[newconn].buffer =
535: (char *)Xalloc(BUFSIZE);
536: inputBuffers[newconn].size = BUFSIZE;
537: inputBuffers[newconn].bufptr =
538: inputBuffers[newconn].buffer;
539: }
540: if (GrabDone)
541: {
542: BITSET(SavedAllClients, newconn);
543: BITSET(SavedAllSockets, newconn);
544: }
545: else
546: {
547: BITSET(AllClients, newconn);
548: BITSET(AllSockets, newconn);
549: }
550: next = NextAvailableClient();
551: if (next != (ClientPtr)NULL)
552: {
553: osPrivPtr priv;
554:
555: newclients[(*nnew)++] = next;
556: next->swapped = swapped;
557: ConnectionTranslation[newconn] = next;
558: priv = (osPrivPtr)Xalloc(sizeof(osPrivRec));
559: priv->fd = newconn;
560: next->osPrivate = (pointer)priv;
561: }
562: }
563: else
564: {
565: xConnSetupPrefix c;
566:
567: if(reason)
568: {
569: c.success = xFalse;
570: c.lengthReason = strlen(reason);
571: c.length = (c.lengthReason + 3) >> 2;
572: c.majorVersion = X_PROTOCOL;
573: c.minorVersion = X_PROTOCOL_REVISION;
574: if(swapped)
575: {
576: int n;
577:
578: swaps(&c.majorVersion, n);
579: swaps(&c.minorVersion, n);
580: swaps(&c.length, n);
581: }
582:
583: write(newconn, &c, sizeof(xConnSetupPrefix));
584: iov[0].iov_len = c.lengthReason;
585: iov[0].iov_base = reason;
586: iov[1].iov_len = padlength[c.lengthReason & 3];
587: iov[1].iov_base = p;
588: writev(newconn, iov, 2);
589: if (debug_conns)
590: ErrorF("Didn't make connection:%s\n", reason);
591: }
592: close(newconn);
593: Xfree(reason);
594: }
595:
596: }
597: }
598: readyconnections &= ~(1 << curconn);
599: }
600: }
601: }
602:
603: /************
604: * CloseDwonFileDescriptor:
605: * Remove this file descriptor and it's inputbuffers, etc.
606: ************/
607:
608: void
609: CloseDownFileDescriptor(connection)
610: long connection;
611: {
612: close(connection);
613:
614: inputBuffers[connection].bufptr = inputBuffers[connection].buffer;
615: inputBuffers[connection].bufcnt = 0;
616: inputBuffers[connection].lenLastReq = 0;
617: inputBuffers[connection].used = 0;
618:
619: BITCLEAR(AllSockets, connection);
620: BITCLEAR(AllClients, connection);
621: BITCLEAR(ClientsWithInput, connection);
622:
623: }
624:
625: /*****************
626: * CheckConections
627: * Some connection has died, go find which one and shut it down
628: * The file descriptor has been closed, but is still in AllClients.
629: * If would truly be wonderful if select() would put the bogus
630: * file descriptors in the exception mask, but nooooo. So we have
631: * to check each and every socket individually.
632: *****************/
633:
634: void
635: CheckConnections()
636: {
637: long mask[mskcnt];
638: long tmask[mskcnt];
639: register int curclient;
640: int i;
641: struct timeval notime;
642: ClientPtr bad;
643: int r;
644:
645: notime.tv_sec = 0;
646: notime.tv_usec = 0;
647:
648: COPYBITS(AllClients, mask);
649: for (i=0; i<mskcnt; i++)
650: {
651: while (mask[i])
652: {
653: curclient = ffs (mask[i]) - 1 + (i << 5);
654: CLEARBITS(tmask);
655: BITSET(tmask, curclient);
656: r = select (curclient + 1, tmask, (int *)NULL, (int *)NULL,
657: ¬ime);
658: if (r < 0)
659: {
660: if (bad = ConnectionTranslation[curclient])
661: CloseDownClient(bad);
662: else
663: CloseDownFileDescriptor(curclient);
664: }
665: BITCLEAR(mask, curclient);
666: }
667: }
668: }
669:
670:
671: /*****************
672: * CloseDownConnection
673: * Delete client from AllClients and free resources
674: *****************/
675:
676: CloseDownConnection(client)
677: ClientPtr client;
678: {
679: int connection = ((osPrivPtr)client->osPrivate)->fd;
680:
681: ConnectionTranslation[connection] = (ClientPtr)NULL;
682: CloseDownFileDescriptor(connection);
683: Xfree(client->osPrivate);
684: }
685:
686:
687: AddEnabledDevice(fd)
688: int fd;
689: {
690: EnabledDevices |= (1<<fd);
691: BITSET(AllSockets, fd);
692: }
693:
694:
695: RemoveEnabledDevice(fd)
696: int fd;
697: {
698: EnabledDevices &= ~(1<<fd);
699: BITCLEAR(AllSockets, fd);
700: }
701:
702: /*****************
703: * OnlyListenToOneClient:
704: * Only accept requests from one client. Continue to handle new
705: * connections, but don't take any protocol requests from the new
706: * ones. Note that if GrabDone is set, EstablishNewConnections
707: * needs to put new clients into SavedAllSockets and SavedAllClients.
708: * Note also that there is no timeout for this in the protocol.
709: * This routine is "undone" by ListenToAllClients()
710: *****************/
711:
712: OnlyListenToOneClient(client)
713: ClientPtr client;
714: {
715: int connection = ((osPrivPtr)client->osPrivate)->fd;
716:
717: if (! GrabDone)
718: {
719: COPYBITS (ClientsWithInput, SavedClientsWithInput);
720: BITCLEAR (SavedClientsWithInput, connection);
721: if (GETBIT(ClientsWithInput, connection))
722: {
723: CLEARBITS(ClientsWithInput);
724: BITSET(ClientsWithInput, connection);
725: }
726: else
727: {
728: CLEARBITS(ClientsWithInput);
729: }
730: COPYBITS(AllSockets, SavedAllSockets);
731: COPYBITS(AllClients, SavedAllClients);
732:
733: UNSETBITS(AllSockets, AllClients);
734: BITSET(AllSockets, connection);
735: CLEARBITS(AllClients);
736: BITSET(AllClients, connection);
737: GrabDone = TRUE;
738: }
739: }
740:
741: /****************
742: * ListenToAllClients:
743: * Undoes OnlyListentToOneClient()
744: ****************/
745:
746: ListenToAllClients()
747: {
748: if (GrabDone)
749: {
750: ORBITS(AllSockets, AllSockets, SavedAllSockets);
751: ORBITS(AllClients, AllClients, SavedAllClients);
752: ORBITS(ClientsWithInput, ClientsWithInput, SavedClientsWithInput);
753: GrabDone = FALSE;
754: }
755: }
756:
757:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.