Annotation of micropolis/src/tk/tktcp.c, revision 1.1.1.1

1.1       root        1: /* tkTCP.c --
                      2:  *
                      3:  *     This file provides basic capabilities to establish a server,
                      4:  * attached to a TCP/IP port, that attaches to a Tcl interpreter.
                      5:  * Such servers provide a remote-procedure-call mechanism for Tcl
                      6:  * without needing to resort to Tk's X-window-based complexities, and
                      7:  * also allow access to services that are not bound to any particular
                      8:  * display.
                      9:  */
                     10: 
                     11: static char RCSid [] =
                     12:   "$Header: /cluster21/kennykb/src/tclTCP.1.0beta/RCS/tclTCP.c,v 1.7 1992/05/05 18:31:13 kennykb Exp kennykb $";
                     13: /* $Source: /cluster21/kennykb/src/tclTCP.1.0beta/RCS/tclTCP.c,v $
                     14:  * $Log: tclTCP.c,v $
                     15:  * Revision 1.7  1992/05/05  18:31:13  kennykb
                     16:  * Changed the flow through the `delete server' code to make it work even
                     17:  * if a server is deleted while a client is active.
                     18:  * The change avoids aborts at termination time if the server delete code
                     19:  * is reached before the application exits.
                     20:  *
                     21:  * Revision 1.6  1992/03/04  20:04:00  kennykb
                     22:  * Modified source code to use the Tcl configurator and corresponding include
                     23:  * files.
                     24:  *
                     25:  * Revision 1.5  1992/02/25  15:21:30  kennykb
                     26:  * Modifications to quiet warnings from gcc
                     27:  * ,
                     28:  *
                     29:  * Revision 1.4  1992/02/24  19:30:30  kennykb
                     30:  * Merged branches (a) updated tcpTrustedHost and (b) broken-out event mgr.
                     31:  *
                     32:  * Revision 1.3  1992/02/20  16:22:53  kennykb
                     33:  * Event management code removed and broken out into a separate file,
                     34:  * simpleEvent.c
                     35:  *
                     36:  * Revision 1.2.1.1  1992/02/24  19:12:30  kennykb
                     37:  * Rewrote tcpTrustedHost to be more forgiving of unusual configurations.
                     38:  * It now looks through all aliases for the local host and the loopback
                     39:  * pseudo-host.
                     40:  *
                     41:  * Revision 1.2  1992/02/18  14:43:21  kennykb
                     42:  * Fix for bug 920218.1 in `History' file.
                     43:  *
                     44:  * Revision 1.1  1992/02/14  19:57:51  kennykb
                     45:  * Initial revision
                     46:  *
                     47:  */
                     48: 
                     49: static char copyright [] = 
                     50:   "Copyright (C) 1992 General Electric. All rights reserved."          ;
                     51: 
                     52: /*
                     53:  *   Permission to use, copy, modify, and distribute this
                     54:  *   software and its documentation for any purpose and without
                     55:  *   fee is hereby granted, provided that the above copyright
                     56:  *   notice appear in all copies and that both that copyright
                     57:  *   notice and this permission notice appear in supporting
                     58:  *   documentation, and that the name of General Electric not be used in
                     59:  *   advertising or publicity pertaining to distribution of the
                     60:  *   software without specific, written prior permission.
                     61:  *   General Electric makes no representations about the suitability of
                     62:  *   this software for any purpose.  It is provided "as is"
                     63:  *   without express or implied warranty.
                     64:  *
                     65:  *   This work was supported by the DARPA Initiative in Concurrent
                     66:  *   Engineering (DICE) through DARPA Contract MDA972-88-C-0047.
                     67:  */
                     68: 
                     69: #include <errno.h>
                     70: #include <sys/types.h>
                     71: #include <sys/ioctl.h>
                     72: #include <sys/socket.h>
                     73: #include <netinet/in.h>
                     74: #include <netdb.h>
                     75: #include <arpa/inet.h>
                     76: 
                     77: /* Only some copies of netinet/in.h have the following defined. */
                     78: 
                     79: #ifndef INADDR_LOOPBACK
                     80: #ifdef __STDC__
                     81: #define INADDR_LOOPBACK 0x7f000001UL
                     82: #else
                     83: #define INADDR_LOOPBACK (unsigned long) 0x7f000001L
                     84: #endif /* __STDC__ */
                     85: #endif /* INADDR_LOOPBACK */
                     86: 
                     87: #include <signal.h>
                     88: 
                     89: #include <tclInt.h>
                     90: #include <tclUnix.h>
                     91: #include <tk.h>
                     92: #include "tkTCP.h"
                     93: 
                     94: /* Kernel calls */
                     95: 
                     96: /* There doesn't seem to be any place to get these....
                     97:  * certainly not a portable one.
                     98:  */
                     99: 
                    100: extern int accept _ANSI_ARGS_((int, struct sockaddr *, int *));
                    101: extern int bind _ANSI_ARGS_((int, const struct sockaddr *, int));
                    102: extern int close _ANSI_ARGS_((int));
                    103: extern int connect _ANSI_ARGS_((int, const struct sockaddr *, int));
                    104: extern int gethostname _ANSI_ARGS_((char *, int));
                    105: extern int getsockname _ANSI_ARGS_((int, struct sockaddr *, int *));
                    106: extern int ioctl _ANSI_ARGS_((int, int, char *));
                    107: extern int listen _ANSI_ARGS_((int, int));
                    108: extern int read _ANSI_ARGS_((int, char *, int));
                    109: extern int select _ANSI_ARGS_((int, fd_set *, fd_set *, fd_set *,
                    110:                               struct timeval *));
                    111: extern int socket _ANSI_ARGS_((int, int, int));
                    112: extern int getdtablesize _ANSI_ARGS_((void));
                    113: 
                    114: 
                    115: /* Configuration parameters */
                    116: 
                    117: /*
                    118:  * TCP_LISTEN_BACKLOG gives the maximum backlog of connection requests
                    119:  * that may be queued for any server
                    120:  */
                    121: 
                    122: #define TCP_LISTEN_BACKLOG 3
                    123: 
                    124: /* Internal data structures */
                    125: 
                    126: /*
                    127:  * For each server that is established in any interpreter, there's a
                    128:  * record of the following type.  Note that only one server may be
                    129:  * running at a time in any interpreter, unless the Tk services are
                    130:  * available for event management.
                    131:  */
                    132: 
                    133: typedef struct tcp_ServerData {
                    134:   Tcl_Interp *         interp; /* Interpreter in which connections */
                    135:                                /* are processed. */
                    136:   char                 name[ 16 ];
                    137:                                /* Name of the server object. */
                    138:   int                  socketfd;
                    139:                                /* Filedescriptor of the socket at */
                    140:                                /* which the server listens for connections */
                    141:   char *               command;
                    142:                                /* Command to be executed (using */
                    143:                                /* Tcl_Eval) when a connection request */
                    144:                                /* arrives. */
                    145:   Tcl_FreeProc *       freeCommand;
                    146:                                /* Procedure to free the command when */
                    147:                                /* it's no longer needed. */
                    148:   int                  stopFlag;
                    149:                                /* Flag == TRUE if the server is trying */
                    150:                                /* to shut down. */
                    151:   int                  raw;    /* Flag == TRUE if for raw socket mode. */
                    152:   struct tcp_ClientData * firstClient;
                    153:                                /* First in the list of clients at this */
                    154:                                /* server */
                    155:   struct tcp_ServerData * next, * prev;
                    156:                                /* Linkage in the list of all active servers */
                    157: } Tcp_ServerData;
                    158: 
                    159: /*
                    160:  * Each client of a server will have a record of the following type.
                    161:  */
                    162: 
                    163: typedef struct tcp_ClientData {
                    164:   struct tcp_ServerData * server;
                    165:                                /* Server to which the client belongs */
                    166:   char                 name [16];
                    167:                                /* Name of the client */
                    168:   int                  socketfd;
                    169:                                /* Filedescriptor of the socket of the */
                    170:                                /* the client's connection. */
                    171:   struct sockaddr_in   peeraddr;
                    172:                                /* IP address from which the client */
                    173:                                /* established the connection. */
                    174:   char *               command;
                    175:                                /* Command to execute when the client */
                    176:                                /* sends a message */
                    177:   Tcl_FreeProc *       freeCommand;
                    178:                                /* Procedure to free the command when it's */
                    179:                                /* no longer needed */
                    180:   Tcl_CmdBuf           inputBuffer;
                    181:                                /* Buffer where client commands are stored */
                    182:   char *               resultString;
                    183:                                /* Result of executing a command on the */
                    184:                                /* client */
                    185:   char *               resultPointer;
                    186:                                /* Pointer to the portion of resultString */
                    187:                                /* that remains to be transmitted back */
                    188:                                /* to the client */
                    189:   Tcl_FreeProc *       freeResultString;
                    190:                                /* Procedure to free the result string when */
                    191:                                /* it's no longer needed. */
                    192:   int                  activeFlag;
                    193:                                /* Flag == 1 iff a command is pending on */
                    194:                                /* this client. */
                    195:   int                  closeFlag;
                    196:                                /* Flag == 1 if the client should be closed */
                    197:                                /* once its result has been returned. */
                    198:   struct tcp_ClientData *next, *prev;
                    199:                                /* Next and previous entries in the list of */
                    200:                                /* clients at this server */
                    201: } Tcp_ClientData;
                    202: 
                    203: /* Static variables in this file */
                    204: 
                    205: static char * tcpCurrentClient = NULL;
                    206:                                /* The name of the client for which a */
                    207:                                /* command is being processed. */
                    208: static Tcp_ServerData * tcpFirstServer = NULL;
                    209:                                /* Pointer to the first in a list of */
                    210:                                /* servers active in the current process. */
                    211: 
                    212: /* Declarations for static functions within this file. */
                    213: 
                    214: /* Static procedures in this file */
                    215: 
                    216: static void simpleDeleteFileHandler1 _ANSI_ARGS_((ClientData, int));
                    217: 
                    218: static void simpleDeleteFileHandler2 _ANSI_ARGS_((ClientData));
                    219: 
                    220: static int
                    221: tcpClientCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    222:                              int argc, char * * argv));
                    223: 
                    224: static int
                    225: tcpConnectCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    226:                              int argc, char * * argv));
                    227: 
                    228: static int
                    229: tcpEvalCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    230:                            int argc, char * * argv));
                    231: 
                    232: static int
                    233: tcpLoginCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    234:                             int argc, char * * argv));
                    235: 
                    236: static int
                    237: tcpMainLoopCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    238:                                int argc, char * * argv));
                    239: 
                    240: static int
                    241: tcpPollCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    242:                            int argc, char * * argv));
                    243: 
                    244: static int
                    245: tcpServerCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    246:                              int argc, char * * argv));
                    247: 
                    248: static int
                    249: tcpServersCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    250:                              int argc, char * * argv));
                    251: static int
                    252: tcpWaitCommand _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    253:                             int argc, char * * argv));
                    254: 
                    255: static int
                    256: tcpServerObjectCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    257:                                int argc, char * * argv));
                    258: static void
                    259: deleteTcpServerObjectCmd _ANSI_ARGS_((ClientData clientData));
                    260: 
                    261: static int
                    262: tcpServerObjectAcceptCmd _ANSI_ARGS_((ClientData clientData,
                    263:                                      Tcl_Interp * interp, int argc,
                    264:                                      char * * argv));
                    265: 
                    266: static int
                    267: tcpServerObjectClientsCmd _ANSI_ARGS_((ClientData clientData,
                    268:                                       Tcl_Interp * interp, int argc,
                    269:                                       char * * argv));
                    270: 
                    271: static int
                    272: tcpServerObjectConfigCmd _ANSI_ARGS_((ClientData clientData,
                    273:                                      Tcl_Interp * interp, int argc,
                    274:                                      char * * argv));
                    275: 
                    276: static int
                    277: tcpServerObjectStartCmd _ANSI_ARGS_((ClientData clientData,
                    278:                                     Tcl_Interp * interp, int argc,
                    279:                                     char * * argv));
                    280: 
                    281: static int
                    282: tcpServerObjectStopCmd _ANSI_ARGS_((ClientData clientData,
                    283:                                    Tcl_Interp * interp, int argc,
                    284:                                    char * * argv));
                    285: 
                    286: static void
                    287: tcpDeleteServer _ANSI_ARGS_((Tcp_ServerData * server));
                    288: 
                    289: static int
                    290: tcpServerObjectConfig _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    291:                                   int argc, char * * argv));
                    292: 
                    293: static int
                    294: tcpClientObjectCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    295:                                int argc, char * * argv));
                    296: 
                    297: static int
                    298: tcpClientObjectCloseCmd _ANSI_ARGS_((ClientData clientData,
                    299:                                     Tcl_Interp * interp,
                    300:                                     int argc, char * * argv));
                    301: 
                    302: static int
                    303: tcpClientObjectCommandCmd _ANSI_ARGS_((ClientData clientData,
                    304:                                       Tcl_Interp * interp,
                    305:                                       int argc, char * * argv));
                    306: 
                    307: static int
                    308: tcpClientObjectDoCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp * interp,
                    309:                                  int argc, char * * argv));
                    310: 
                    311: static int
                    312: tcpClientObjectHostnameCmd _ANSI_ARGS_((ClientData clientData,
                    313:                                        Tcl_Interp * interp,
                    314:                                        int argc, char * * argv));
                    315: 
                    316: static int
                    317: tcpClientObjectServerCmd _ANSI_ARGS_((ClientData clientData,
                    318:                                      Tcl_Interp * interp,
                    319:                                      int argc, char * * argv));
                    320: 
                    321: static void
                    322: deleteTcpClientObjectCmd _ANSI_ARGS_((ClientData clientData));
                    323: 
                    324: static int
                    325: tcpConnectionObjectCmd _ANSI_ARGS_((ClientData clientData,
                    326:                                    Tcl_Interp * interp,
                    327:                                    int argc, char * * argv));
                    328: 
                    329: static int
                    330: tcpConnectionObjectCloseCmd _ANSI_ARGS_((ClientData clientData,
                    331:                                         Tcl_Interp * interp,
                    332:                                         int argc, char * * argv));
                    333: 
                    334: static int
                    335: tcpConnectionObjectSendCmd _ANSI_ARGS_((ClientData clientData,
                    336:                                        Tcl_Interp * interp,
                    337:                                        int argc, char * * argv));
                    338: 
                    339: static void
                    340: deleteTcpConnectionObjectCmd _ANSI_ARGS_((ClientData clientData));
                    341: 
                    342: static void
                    343: tcpServerAcceptConnection _ANSI_ARGS_((ClientData clientData, int mask));
                    344: 
                    345: static void
                    346: tcpReturnResultToClient _ANSI_ARGS_((Tcp_ClientData * client,
                    347:                                     Tcl_Interp * interp,
                    348:                                     int status, int closeflag));
                    349: 
                    350: static void
                    351: tcpWriteResultToClient _ANSI_ARGS_((ClientData clientData, int mask));
                    352: 
                    353: static void
                    354: tcpClientReadError _ANSI_ARGS_((Tcp_ClientData * client));
                    355: 
                    356: static void
                    357: tcpClientWriteError _ANSI_ARGS_((Tcp_ClientData * client));
                    358: 
                    359: static void
                    360: tcpPrepareClientForInput _ANSI_ARGS_((Tcp_ClientData * client));
                    361: 
                    362: static void
                    363: tcpReceiveClientInput _ANSI_ARGS_((ClientData clientData, int mask));
                    364: 
                    365: static void
                    366: tcpCloseClient _ANSI_ARGS_((Tcp_ClientData * client));
                    367: 
                    368: static int
                    369: tcpTrustedHost _ANSI_ARGS_((char * hostname));
                    370: 
                    371: static int
                    372: tcpSendCmdToServer _ANSI_ARGS_((Tcl_Interp * interp, int s, char * message));
                    373: 
                    374: static int
                    375: tcpReceiveResultFromServer _ANSI_ARGS_((Tcl_Interp * interp, int s));
                    376: 
                    377: /*
                    378:  * simpleReportBackgroundError --
                    379:  *
                    380:  *     This procedure is invoked to report a Tcl error in the background,
                    381:  * when TCL_ERROR has been passed out to the outermost level.
                    382:  *
                    383:  *     It tries to run `bgerror' giving it  the error message.  If this
                    384:  * fails, it reports the problem on stderr.
                    385:  */
                    386: 
                    387: void
                    388: simpleReportBackgroundError (interp)
                    389:      Tcl_Interp * interp;
                    390: {
                    391: 
                    392:   char *argv[2];
                    393:   char *command;
                    394:   char *error;
                    395:   char *errorInfo, *tmp;
                    396:   int status;
                    397:   int unixStatus;
                    398: 
                    399:   /* Get the error message out of the interpreter. */
                    400: 
                    401:   error = (char *) ckalloc (strlen (interp -> result) + 1);
                    402:   strcpy (error, interp -> result);
                    403: 
                    404:   /* Get errorInfo, too */
                    405: 
                    406:   tmp = Tcl_GetVar (interp, "errorInfo", TCL_GLOBAL_ONLY);
                    407:   if (tmp == NULL) {
                    408:     errorInfo = error;
                    409:   } else {
                    410:     errorInfo = (char *) ckalloc (strlen (tmp) + 1);
                    411:     strcpy (errorInfo, tmp);
                    412:   }
                    413: 
                    414:   /* Build a `bgerror' command to report the error */
                    415: 
                    416:   argv[0] = "bgerror";
                    417:   argv[1] = error;
                    418:   command = Tcl_Merge (2, argv);
                    419: 
                    420:   /* Try to run the command */
                    421: 
                    422:   status = Tcl_Eval (interp, command, 0, (char **) NULL);
                    423: 
                    424:   if (status != TCL_OK) {
                    425: 
                    426:     /* Command failed.  Report the problem to stderr. */
                    427: 
                    428:     tmp = Tcl_GetVar (interp, "errorInfo", TCL_GLOBAL_ONLY);
                    429:     if (tmp == NULL) {
                    430:       tmp = interp -> result;
                    431:     }
                    432:     unixStatus = fprintf (stderr, "\n\
                    433: ------------------------------------------------------------------------\n\
                    434: Tcl interpreter detected a background error.\n\
                    435: Original error:\n\
                    436: %s\n\
                    437: \n\
                    438: User \"bgerror\" procedure failed to handle the background error.\n\
                    439: Error in bgerror:\n\
                    440: %s\n",
                    441:             errorInfo, tmp);
                    442:     if (unixStatus < 0) {
                    443:       abort ();
                    444:     }
                    445:   }
                    446: 
                    447:   Tcl_ResetResult (interp);
                    448: 
                    449:   free (command);
                    450: 
                    451:   ckfree (error);
                    452: 
                    453:   if (errorInfo != error) {
                    454:     ckfree (errorInfo);
                    455:   }
                    456: }
                    457: 
                    458: /*
                    459:  * simpleCreateFileHandler --
                    460:  *
                    461:  *     This procedure is invoked to create a handle to cause a callback
                    462:  *     whenever a condition (readable, writable, exception) is
                    463:  *     present on a given file.
                    464:  *
                    465:  *     In the Tk environment, the file handler is created using Tk's
                    466:  *     Tk_CreateFileHandler procedure, and the callback takes place
                    467:  *     from the Tk main loop.  In a non-Tk environment, a
                    468:  *     Tcp_FileHandler structure is created to describe the file, and
                    469:  *     this structure is linked to a chain of such structures
                    470:  *     processed by the server main loop.
                    471:  */
                    472: 
                    473: void
                    474: simpleCreateFileHandler (fd, mask, proc, clientData)
                    475:      int fd;
                    476:      int mask;
                    477:      Tk_FileProc * proc;
                    478:      ClientData clientData;
                    479: {
                    480:   Tk_CreateFileHandler (fd, mask, (Tk_FileProc *) proc, clientData);
                    481: 
                    482:   /* It is possible that we have a file handler scheduled for deletion.
                    483:    * This deletion has to be cancelled if we've requested creation of
                    484:    * another one.
                    485:    */
                    486: 
                    487:   Tk_CancelIdleCall ((Tk_IdleProc *) simpleDeleteFileHandler2,
                    488:                     (ClientData) fd);
                    489: }
                    490: 
                    491: /*
                    492:  * simpleDeleteFileHandler --
                    493:  *
                    494:  *     This function is invoked when the program is no longer interested in
                    495:  * handling events on a file.  It removes any outstanding handler on the file.
                    496:  *
                    497:  *     The function is a little tricky because a file handler on the file may
                    498:  * be active.  In a non-Tk environment, this is simple; the SIMPLE_DELETE flag
                    499:  * is set in the handler's mask, and the main loop deletes the handler once
                    500:  * it is quiescent.  In Tk, the event loop won't do that, so what we do
                    501:  * is set a DoWhenIdle to delete the handler and return.  The DoWhenIdle
                    502:  * gets called back from the Tk event loop at a time that the handler is
                    503:  * quiescent, and deletes the handler.
                    504:  */
                    505: 
                    506: void
                    507: simpleDeleteFileHandler (fd)
                    508:      int fd;
                    509: {
                    510:   /* First of all, we have to zero the file's mask to avoid calling the same
                    511:      handler over again if the file is still ready. */
                    512:   Tk_CreateFileHandler (fd, 0, (Tk_FileProc *) simpleDeleteFileHandler1,
                    513:                        (ClientData) NULL);
                    514:   Tk_DoWhenIdle ((Tk_IdleProc *) simpleDeleteFileHandler2,
                    515:                 (ClientData) fd);
                    516: }
                    517: 
                    518: 
                    519: /* ARGSUSED */
                    520: static void
                    521: simpleDeleteFileHandler1 (clientData, mask)
                    522:      ClientData clientData;
                    523:      int mask;
                    524: {
                    525:   (void) fprintf (stderr, "in simpleDeleteFileHandler1: bug in tkEvent.c");
                    526:   abort ();
                    527: }
                    528: 
                    529: static void
                    530: simpleDeleteFileHandler2 (clientData)
                    531:      ClientData clientData;
                    532: {
                    533:   int fd = (int) clientData;
                    534: 
                    535:   Tk_DeleteFileHandler (fd);
                    536: }
                    537: 
                    538: /*
                    539:  *----------------------------------------------------------------------
                    540:  * Tk_TcpCmd:
                    541:  *
                    542:  * This procedure implements a `tcp' command for Tcl.  It provides the
                    543:  * top-level actions for TCP/IP connections.
                    544:  *
                    545:  * This command is divided into variants, each with its own procedure:
                    546:  *
                    547:  *     tcp client
                    548:  *             Returns the current active client, or an error if there is
                    549:  *             none.
                    550:  *     tcp connect host port
                    551:  *             Establish a connection to a server running at `port' on
                    552:  *             `host.'
                    553:  *     tcp eval client command
                    554:  *             Do default command processing for command "$command",
                    555:  *             originating at client "$client".
                    556:  *     tcp login client
                    557:  *             Do default login processing for $client.
                    558:  *     tcp mainloop
                    559:  *             Start the main loop for a server or group of servers.
                    560:  *     tcp poll
                    561:  *             Poll for whether servers have work to do.
                    562:  *     tcp servers
                    563:  *             Returns a list of the currently active servers.
                    564:  *     tcp server ?args?
                    565:  *             Set up a server to run in the current interpreter.
                    566:  *     tcp wait
                    567:  *             Wait for a server to have work to do.
                    568:  *----------------------------------------------------------------------
                    569:  */
                    570: 
                    571: int
                    572: Tk_TcpCmd (clientData, interp, argc, argv)
                    573:      ClientData clientData;
                    574:      Tcl_Interp * interp;
                    575:      int argc;
                    576:      char * * argv;
                    577: {
                    578:   char c;
                    579:   unsigned length;
                    580: 
                    581:   if (argc < 2) {
                    582:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [0],
                    583:                      " command ?args?\"", (char *) NULL);
                    584:     return TCL_ERROR;
                    585:   }
                    586: 
                    587:   c = argv [1] [0];
                    588:   length = strlen (argv [1]);
                    589: 
                    590:   if ((c == 'c') && (length >= 2) &&
                    591:       (strncmp (argv [1], "client", length) == 0)) {
                    592:     return tcpClientCommand (clientData, interp, argc-1, argv+1);
                    593:   }
                    594:   if ((c == 'c') && (length >= 2) &&
                    595:       (strncmp (argv [1], "connect", length) == 0)) {
                    596:     return tcpConnectCommand (clientData, interp, argc-1, argv+1);
                    597:   }
                    598:   if ((c == 'e') && (strncmp (argv [1], "eval", length) == 0)) {
                    599:     return tcpEvalCommand (clientData, interp, argc-1, argv+1);
                    600:   }
                    601:   if ((c == 'l') && (strncmp (argv [1], "login", length) == 0)) {
                    602:     return tcpLoginCommand (clientData, interp, argc-1, argv+1);
                    603:   }
                    604:   if ((c == 'm') && (strncmp (argv [1], "mainloop", length) == 0)) {
                    605:     return tcpMainLoopCommand (clientData, interp, argc-1, argv+1);
                    606:   }
                    607:   if ((c == 'p') && (strncmp (argv [1], "poll", length) == 0)) {
                    608:     return tcpPollCommand (clientData, interp, argc-1, argv+1);
                    609:   }
                    610:   if ((c == 's') && (length >= 7)
                    611:       && (strncmp (argv [1], "servers", length) == 0)) {
                    612:     return tcpServersCommand (clientData, interp, argc-1, argv+1);
                    613:   }
                    614:   if ((c == 's') && (strncmp (argv [1], "server", length) == 0)) {
                    615:     return tcpServerCommand (clientData, interp, argc-1, argv+1);
                    616:   }
                    617:   if ((c == 'w') && (strncmp (argv [1], "wait", length) == 0)) {
                    618:     return tcpWaitCommand (clientData, interp, argc-1, argv+1);
                    619:   }
                    620:   Tcl_AppendResult (interp, "bad option \"", argv [1],
                    621:                    "\": should be client, eval, login,",
                    622:                    " mainloop, poll, servers, server or wait",
                    623:                    (char *) NULL);
                    624:   return TCL_ERROR;
                    625: 
                    626: }
                    627: 
                    628: /*
                    629:  * tcpClientCommand --
                    630:  *
                    631:  *     This procedure is invoked to process the "tcp client" Tcl command.
                    632:  * It returns the name of the currently-active client, or an error if there
                    633:  * is none.
                    634:  */
                    635: 
                    636: /* ARGSUSED */
                    637: static int
                    638: tcpClientCommand (clientData, interp, argc, argv)
                    639:      ClientData clientData;
                    640:      Tcl_Interp * interp;
                    641:      int argc;
                    642:      char * * argv;
                    643: {
                    644:   /* Check syntax */
                    645: 
                    646:   if (argc != 1) {
                    647:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1], " ",
                    648:                      argv [0], "\"", (char *) NULL);
                    649:     return TCL_ERROR;
                    650:   }
                    651: 
                    652:   /* Make sure there is a current client */
                    653: 
                    654:   if (tcpCurrentClient == NULL) {
                    655:     Tcl_SetResult (interp, "no current client", TCL_STATIC);
                    656:     return TCL_ERROR;
                    657:   }
                    658: 
                    659:   Tcl_SetResult (interp, tcpCurrentClient, TCL_VOLATILE);
                    660:   return TCL_OK;
                    661: }
                    662: 
                    663: /* tcpConnectCommand --
                    664:  *
                    665:  *     This procedure is invoked to process the "tcp connect" Tcl command.
                    666:  * It takes two arguments: a host name and a port.  It tries to establish a
                    667:  * connection to the specified port and host.
                    668:  */
                    669: 
                    670: /* ARGSUSED */
                    671: static int
                    672: tcpConnectCommand (clientData, interp, argc, argv)
                    673:      ClientData clientData;
                    674:      Tcl_Interp * interp;
                    675:      int argc;
                    676:      char * * argv;
                    677: {
                    678:   struct hostent * host;
                    679:   struct sockaddr_in sockaddr;
                    680:   int haddr;
                    681:   int port;
                    682:   int status;
                    683:   int f;
                    684:   char name [20];
                    685: 
                    686:   /* Check syntax */
                    687: 
                    688:   if (argc != 3) {
                    689:     Tcl_AppendResult (interp, "wrong # args, should be \"", argv [-1], " ",
                    690:                      argv [0], " hostname port#\"", (char *) NULL);
                    691:     return TCL_ERROR;
                    692:   }
                    693: 
                    694:   /* Decode the host name */
                    695: 
                    696:   sockaddr.sin_family = AF_INET;
                    697:   host = gethostbyname (argv [1]);
                    698:   if (host != NULL) {
                    699:     memcpy ((char *) &(sockaddr.sin_addr.s_addr),
                    700:            (char *) (host -> h_addr_list [0]),
                    701:            (size_t) (host -> h_length));
                    702:   } else {
                    703:     haddr = inet_addr (argv [1]);
                    704:     if (haddr == -1) {
                    705:       Tcl_AppendResult (interp, argv[1], ": host unknown", (char *) NULL);
                    706:       return TCL_ERROR;
                    707:     }
                    708:     sockaddr.sin_addr.s_addr = haddr;
                    709:   }
                    710: 
                    711:   /* Decode the port number */
                    712: 
                    713:   status = Tcl_GetInt (interp, argv [2], &port);
                    714:   if (status) return status;
                    715:   sockaddr.sin_port = htons (port);
                    716: 
                    717:   /* Make a socket to talk to the server */
                    718: 
                    719:   f = socket (AF_INET, SOCK_STREAM, 0);
                    720:   if (f < 0) {
                    721:     Tcl_AppendResult (interp, "can't create socket: ",
                    722:                      Tcl_UnixError (interp), (char *) NULL);
                    723:     return TCL_ERROR;
                    724:   }
                    725: 
                    726:   /* Connect to the server */
                    727: 
                    728:   status = connect (f, (struct sockaddr *) &sockaddr, sizeof sockaddr);
                    729:   if (status < 0) {
                    730:     Tcl_AppendResult (interp, "can't connect to server: ",
                    731:                      Tcl_UnixError (interp), (char *) NULL);
                    732:     (void) close (f);
                    733:     return TCL_ERROR;
                    734:   }
                    735: 
                    736:   /* Get the server's greeting message */
                    737: 
                    738:   status = tcpReceiveResultFromServer (interp, f);
                    739: 
                    740:   if (status == TCL_OK) {
                    741: 
                    742:     /* Stash the greeting, make the connection object and return it. */
                    743: 
                    744:     sprintf (name, "tcp_connection_%d", f);
                    745:     (void) Tcl_SetVar2 (interp, "tcp_greeting", name, interp -> result,
                    746:                        TCL_GLOBAL_ONLY);
                    747:     Tcl_CreateCommand (interp, name, (Tcl_CmdProc *) tcpConnectionObjectCmd,
                    748:                       (ClientData) f,
                    749:                       (Tcl_CmdDeleteProc *) deleteTcpConnectionObjectCmd);
                    750:     Tcl_SetResult (interp, name, TCL_VOLATILE);
                    751:     return TCL_OK;
                    752:   } else {
                    753: 
                    754:     /* Error reading greeting, quit */
                    755: 
                    756:     (void) close (f);
                    757:     return TCL_ERROR;
                    758:   }
                    759: }    
                    760: 
                    761: /*
                    762:  * tcpEvalCommand --
                    763:  *
                    764:  *     This procedure is invoked to process the "tcp eval" Tcl command.
                    765:  * "tcp eval" is the default command invoked to process connections once
                    766:  * a connection has been accepted by "tcp login".
                    767:  */
                    768: 
                    769: /* ARGSUSED */
                    770: static int
                    771: tcpEvalCommand (clientData, interp, argc, argv)
                    772:      ClientData clientData;
                    773:      Tcl_Interp * interp;
                    774:      int argc;
                    775:      char * * argv;
                    776: {
                    777:   int status;
                    778: 
                    779:   /* Argc == 2 means that we're logging out a client.  Default is to ignore
                    780:    * the logout.
                    781:    */
                    782: 
                    783:   if (argc == 2) {
                    784:     return TCL_OK;
                    785:   }
                    786: 
                    787:   /* Three-argument form is a command from a client.  Default is to eval
                    788:    * the command */
                    789: 
                    790:   if (argc != 3) {
                    791:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1],
                    792:                      " ", argv [0], " client command\"", (char *) NULL);
                    793:     return TCL_ERROR;
                    794:   }
                    795: 
                    796:   status = Tcl_Eval (interp, argv [2], 0, (char * *) NULL);
                    797:   return status;
                    798: }  
                    799: 
                    800: /*
                    801:  * tcpLoginCommand --
                    802:  *
                    803:  *     This procedure is invoked to process the "tcp login" Tcl command.
                    804:  * It is the default command procedure at initial connection to a server.
                    805:  * It is invoked with the name of a client.  It returns TCL_OK, together
                    806:  * with a greeting message, if the login succeeds, and TCL_ERROR, together
                    807:  * with a denial message, if it fails.
                    808:  *
                    809:  *     The authentication procedure is as follows:
                    810:  *
                    811:  * - If the client is on the local host, the connection is accepted.
                    812:  * - If the client's IP address is the same as the local host's IP address,
                    813:  *   the connection is accepted.
                    814:  * - Otherwise, the connection is refused.
                    815:  *
                    816:  *     Obviously, there are other authentication techniques.  The use can
                    817:  * replace this command with an arbitrary Tcl script.
                    818:  */
                    819: 
                    820: /*ARGSUSED*/
                    821: static int
                    822: tcpLoginCommand (clientData, interp, argc, argv)
                    823:      ClientData clientData;
                    824:      Tcl_Interp * interp;
                    825:      int argc;
                    826:      char * * argv;
                    827: {
                    828:   char * hostName;             /* Name of the client's host */
                    829:   int status;
                    830: 
                    831:   /* Check command syntax */
                    832: 
                    833:   if (argc != 2) {
                    834:     Tcl_AppendResult (interp, "wrong # args; should be \"", argv [-1], " ",
                    835:                      argv [0], " clientName\"", (char *) NULL);
                    836:     return TCL_ERROR;
                    837:   }
                    838: 
                    839:   /* Get the hostname by doing $client hostname */
                    840: 
                    841:   status = Tcl_VarEval (interp, argv [1], " hostname", (char *) NULL);
                    842:   if (status == TCL_OK) {
                    843:     hostName = (char *) ckalloc (strlen (interp -> result) + 1);
                    844:     strcpy (hostName, interp -> result);
                    845: 
                    846:     /* Check that the host is trusted */
                    847:     
                    848:     if (tcpTrustedHost (hostName)) {
                    849: 
                    850:       /* Change the command to `tcp eval' for next time */
                    851: 
                    852:       status = Tcl_VarEval (interp, argv [1], " command {tcp eval}",
                    853:                            (char *) NULL);
                    854: 
                    855: 
                    856:       if (status == TCL_OK) {
                    857: 
                    858:        /* Return a greeting message */
                    859: 
                    860:        Tcl_ResetResult (interp);
                    861:        Tcl_AppendResult (interp, "GE DICE TCP-based Tcl server\n", RCSid,
                    862:                          "\n", copyright, (char *) NULL);
                    863: 
                    864:        return TCL_OK;
                    865: 
                    866:       }
                    867: 
                    868:     }
                    869: 
                    870:     ckfree ((char *) hostName);
                    871:   }
                    872: 
                    873:   /* Host isn't trusted or one of the commands failed. */
                    874: 
                    875:   Tcl_SetResult (interp, "Permission denied", TCL_STATIC);
                    876:   return TCL_ERROR;
                    877: }
                    878: 
                    879: /*
                    880:  * tcpMainLoopCommand:
                    881:  *
                    882:  *     This procedure is invoked in a non-Tk environment when the server
                    883:  * implementor wishes to use a main loop built into the library.  It
                    884:  * repeatedly polls ofr work to be done, returning only when the last server
                    885:  * is closed.
                    886:  *
                    887:  *     In a Tk environment, the procedure returns immediately.
                    888:  */
                    889: 
                    890: /*ARGSUSED*/
                    891: static int
                    892: tcpMainLoopCommand (clientData, interp, argc, argv)
                    893:      ClientData clientData;
                    894:      Tcl_Interp * interp;
                    895:      int argc;
                    896:      char * * argv;
                    897: {
                    898: 
                    899:   int status;
                    900: 
                    901:   if (argc != 1) {
                    902:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1],
                    903:                      " ", argv [0], "\"", (char *) NULL);
                    904:     return TCL_ERROR;
                    905:   }
                    906: 
                    907:   errno = 0; status = -1;
                    908: 
                    909:   /* In a non-Tk environment, errno has a Unix error or 0 for no clients
                    910:    * or servers.  In a Tk environment, errno is zero at this point.
                    911:    */
                    912: 
                    913:   if (errno != 0) {
                    914:     Tcl_AppendResult (interp, "select: ", Tcl_UnixError (interp),
                    915:                      (char *) NULL);
                    916:     return TCL_ERROR;
                    917:   }
                    918: 
                    919:   return TCL_OK;
                    920: }
                    921: 
                    922: 
                    923: /*
                    924:  * tcpPollCommand:
                    925:  *
                    926:  *     This procedure is invoked to process the "tcp poll" Tcl
                    927:  * command.  It requests that pending events for the servers be processed.
                    928:  * It returns a count of events that were processed successfully.
                    929:  *
                    930:  *     In a Tk environment, the procedure reports that no servers are known
                    931:  * to the event handler.  This is correct -- servers register with Tk, not
                    932:  * with the simple event handler.
                    933:  */
                    934: 
                    935: /*ARGSUSED*/
                    936: static int
                    937: tcpPollCommand (clientData, interp, argc, argv)
                    938:      ClientData clientData;
                    939:      Tcl_Interp * interp;
                    940:      int argc;
                    941:      char * * argv;
                    942: {
                    943:   int status;
                    944: 
                    945:   if (argc != 1) {
                    946:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1],
                    947:                      " ", argv [0], "\"", (char *) NULL);
                    948:     return TCL_ERROR;
                    949:   }
                    950: 
                    951:   /* Do the poll */
                    952: 
                    953:   errno = 0; status = -1;
                    954: 
                    955:   /* Check for trouble */
                    956: 
                    957:   if (status < 0) {
                    958:     if (errno == 0) {
                    959:       Tcl_SetResult (interp, "no servers known to event handler", TCL_STATIC);
                    960:     } else {
                    961:       Tcl_AppendResult (interp, "select: ", Tcl_UnixError (interp),
                    962:                        (char *) NULL);
                    963:     }
                    964:     return TCL_ERROR;
                    965:   }
                    966: 
                    967:   /* Return the number of events processed. */
                    968: 
                    969:   sprintf (interp -> result, "%d", status);
                    970:   return TCL_OK;
                    971: }
                    972: 
                    973: /* tcpServerCommand:
                    974:  *
                    975:  *     This procedure is invoked to process the "tcp server" Tcl
                    976:  * command.  It requests that a server be created to listen at a
                    977:  * TCP/IP port, whose number may be assigned by the system or
                    978:  * specified by the user with the "-port" option.
                    979:  *
                    980:  *     A command string is supplied for use when the server begins to
                    981:  * accept connections.  See the documentation of tcpServerObjectCmd
                    982:  * for a description of the command string.
                    983:  *
                    984:  *     If the server is created successfully, the return value will
                    985:  * be the name of a "server object" that can be used for future
                    986:  * actions upon the server.  This object will be usable as a Tcl
                    987:  * command; the command is processed by the tcpServerObjectCmd function.
                    988:  *
                    989:  * Syntax:
                    990:  *     tcp server ?-port #? ?-command string?
                    991:  * 
                    992:  * Results:
                    993:  *     A standard Tcl result.  Return value is the name of the server
                    994:  *     object, which may be invoked as a Tcl command (see
                    995:  *     tcpServerObjectCmd for details).
                    996:  */
                    997: 
                    998: /* ARGSUSED */
                    999: static int
                   1000: tcpServerCommand (clientData, interp, argc, argv)
                   1001:      ClientData clientData;
                   1002:      Tcl_Interp * interp;
                   1003:      int argc;
                   1004:      char * * argv;
                   1005: {
                   1006:   int unixStatus;
                   1007:   int one;
                   1008:   char * message;
                   1009:   char * nargv [3];
                   1010:   int nargc;
                   1011: 
                   1012:   /* Create a structure to hold the tcp server's description. */
                   1013: 
                   1014:   Tcp_ServerData * server =
                   1015:     (Tcp_ServerData *) ckalloc (sizeof (Tcp_ServerData));
                   1016: 
                   1017:   /* Set up the interpreter and the default command. Clear the list of
                   1018:    * clients. */
                   1019: 
                   1020:   server -> interp = interp;
                   1021:   server -> command = "tcp login";
                   1022:   server -> freeCommand = TCL_STATIC;
                   1023:   server -> stopFlag = 0;
                   1024:   server -> raw = 0;
                   1025:   server -> firstClient = (Tcp_ClientData *) NULL;
                   1026: 
                   1027:   /* Create the socket at which the server will listen. */
                   1028: 
                   1029:   server -> socketfd = socket (AF_INET, SOCK_STREAM, 0);
                   1030:   if (server -> socketfd < 0) {
                   1031:     Tcl_AppendResult (interp, "can't create socket: ",
                   1032:                      Tcl_UnixError (interp), (char *) NULL);
                   1033:   } else {
                   1034: 
                   1035:     /* Set up the socket for non-blocking I/O. */
                   1036: 
                   1037:     one = 1;
                   1038:     unixStatus = ioctl (server -> socketfd, FIONBIO, (char *) &one);
                   1039:     if (unixStatus < 0) {
                   1040:       Tcl_AppendResult (interp, "can't set non-blocking I/O on socket: ",
                   1041:                        Tcl_UnixError (interp), (char *) NULL);
                   1042:     } else {
                   1043: 
                   1044:       /* Server structure has been created and socket has been opened.
                   1045:        * Now configure the server.
                   1046:        */
                   1047: 
                   1048:       if (tcpServerObjectConfig ((ClientData) server, interp, argc, argv)
                   1049:          == TCL_OK)
                   1050:        {
                   1051: 
                   1052:          /* Link the server on the list of active servers */
                   1053: 
                   1054:          if (tcpFirstServer)
                   1055:            tcpFirstServer -> prev = server;
                   1056:          server -> next = tcpFirstServer;
                   1057:          tcpFirstServer = server;
                   1058:          server -> prev = NULL;
                   1059: 
                   1060:          /* Add the server object command */
                   1061:          
                   1062:          sprintf (server -> name, "tcp_server_%d", server -> socketfd);
                   1063: 
                   1064:          Tcl_CreateCommand (interp, server -> name,
                   1065:                             (Tcl_CmdProc *) tcpServerObjectCmd,
                   1066:                             (ClientData) server,
                   1067:                             (Tcl_CmdDeleteProc *) deleteTcpServerObjectCmd);
                   1068: 
                   1069:          Tcl_SetResult (interp, server -> name, TCL_STATIC);
                   1070:          
                   1071:          return TCL_OK;
                   1072: 
                   1073:        }
                   1074:     }
                   1075: 
                   1076:     /* Error in configuring the server.  Trash the socket. */
                   1077:     
                   1078:     unixStatus = close (server -> socketfd);
                   1079:     if (unixStatus < 0) {
                   1080:       nargc = 3;
                   1081:       nargv [0] = "(also failed to close socket: ";
                   1082:       nargv [1] = Tcl_UnixError (interp);
                   1083:       nargv [2] = ")";
                   1084:       message = Tcl_Concat (nargc, nargv);
                   1085:       Tcl_AddErrorInfo (interp, message);
                   1086:       free (message);
                   1087:     }
                   1088:   }
                   1089:       
                   1090:   /* Error in creating the server -- get rid of the data structure */
                   1091: 
                   1092:   if (server -> freeCommand != NULL) {
                   1093:     (*(server -> freeCommand)) (server -> command);
                   1094:   }
                   1095:   ckfree ((char *) server);
                   1096:   return TCL_ERROR;
                   1097: }
                   1098: 
                   1099: /*
                   1100:  * tcpServersCommand:
                   1101:  *
                   1102:  *     The following procedure is invoked to process the `tcp servers' Tcl
                   1103:  * command.  It returns a list of the servers that are currently known.
                   1104:  */
                   1105: 
                   1106: /* ARGSUSED */
                   1107: static int
                   1108: tcpServersCommand (clientData, interp, argc, argv)
                   1109:      ClientData clientData;
                   1110:      Tcl_Interp * interp;
                   1111:      int argc;
                   1112:      char * * argv;
                   1113: {
                   1114:   Tcp_ServerData * server;
                   1115: 
                   1116:   /* Check syntax */
                   1117: 
                   1118:   if (argc != 1) {
                   1119:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1], " ",
                   1120:                      argv [0], "\"", (char *) NULL);
                   1121:     return TCL_ERROR;
                   1122:   }
                   1123: 
                   1124:   for (server = tcpFirstServer; server != NULL; server = server -> next) {
                   1125:     Tcl_AppendElement (interp, server -> name, 0);
                   1126:   }
                   1127: 
                   1128:   return TCL_OK;
                   1129: }
                   1130: 
                   1131: /*
                   1132:  * tcpWaitCommand:
                   1133:  *
                   1134:  *     This procedure is invoked to process the "tcp wait" Tcl
                   1135:  * command.  It requests that the process delay until an event is
                   1136:  * pending for a TCP server.
                   1137:  *
                   1138:  * It returns a count of pending events.
                   1139:  *
                   1140:  *     In a Tk environment, the procedure returns an error message stating
                   1141:  * that no servers are known to the event handler.  This is correct.  The
                   1142:  * servers register with Tk's event handler, and are not known to the simple
                   1143:  * event handler.
                   1144:  */
                   1145: 
                   1146: /*ARGSUSED*/
                   1147: static int
                   1148: tcpWaitCommand (clientData, interp, argc, argv)
                   1149:      ClientData clientData;
                   1150:      Tcl_Interp * interp;
                   1151:      int argc;
                   1152:      char * * argv;
                   1153: {
                   1154:   int status;
                   1155: 
                   1156:   if (argc != 1) {
                   1157:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1],
                   1158:                      " ", argv [0], "\"", (char *) NULL);
                   1159:     return TCL_ERROR;
                   1160:   }
                   1161: 
                   1162:   /* Do the poll */
                   1163: 
                   1164:   errno = 0; status = -1;
                   1165: 
                   1166:   /* Check for trouble */
                   1167: 
                   1168:   if (status < 0) {
                   1169:     if (errno == 0) {
                   1170:       Tcl_SetResult (interp, "no servers known to event handler", TCL_STATIC);
                   1171:     } else {
                   1172:       Tcl_AppendResult (interp, "select: ", Tcl_UnixError (interp),
                   1173:                        (char *) NULL);
                   1174:     }
                   1175:     return TCL_ERROR;
                   1176:   }
                   1177: 
                   1178:   /* Return the number of events pending. */
                   1179: 
                   1180:   sprintf (interp -> result, "%d", status);
                   1181:   return TCL_OK;
                   1182: }
                   1183: 
                   1184: /*
                   1185:  * tcpServerObjectCmd --
                   1186:  *
                   1187:  *     This procedure is invoked when a command is called on a server
                   1188:  *     object directly.  It dispatches to the appropriate command processing
                   1189:  *     procedure to handle the command.
                   1190:  *
                   1191:  * $server accept
                   1192:  *     [Internal call] - Accept a connection.
                   1193:  * $server clients
                   1194:  *     Return a list of all clients connected to a server.
                   1195:  * $server configure ?args?
                   1196:  *     Revise or query a server's configuration.
                   1197:  * $server start
                   1198:  *     Start a server running.
                   1199:  * $server stop
                   1200:  *     Terminate a server.
                   1201:  */
                   1202: 
                   1203: static int
                   1204: tcpServerObjectCmd (clientData, interp, argc, argv)
                   1205:      ClientData clientData;
                   1206:      Tcl_Interp * interp;
                   1207:      int argc;
                   1208:      char * * argv;
                   1209: {
                   1210:   int c;
                   1211:   unsigned length;
                   1212: 
                   1213:   if (argc < 2) {
                   1214:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [0],
                   1215:                      " command ?args?\"", (char *) NULL);
                   1216:     return TCL_ERROR;
                   1217:   }
                   1218: 
                   1219:   c = argv [1] [0];
                   1220:   length = strlen (argv [1]);
                   1221:   
                   1222:   if (c == 'a' && strncmp (argv [1], "accept", length) == 0) {
                   1223:     return tcpServerObjectAcceptCmd (clientData, interp, argc-1, argv+1);
                   1224:   }
                   1225:   if (c == 'c' && length >= 2 && strncmp (argv [1], "clients", length) == 0) {
                   1226:     return tcpServerObjectClientsCmd (clientData, interp, argc-1, argv+1);
                   1227:   }
                   1228:   if (c == 'c' && length >= 2
                   1229:       && strncmp (argv [1], "configure", length) == 0) {
                   1230:     return tcpServerObjectConfigCmd (clientData, interp, argc-1, argv+1);
                   1231:   }
                   1232:   if (c == 's' && length >= 3 && strncmp (argv [1], "start", length) == 0) {
                   1233:     return tcpServerObjectStartCmd (clientData, interp, argc-1, argv+1);
                   1234:   }
                   1235:   if (c == 's' && length >= 3 && strncmp (argv [1], "stop", length) == 0) {
                   1236:     return tcpServerObjectStopCmd (clientData, interp, argc-1, argv+1);
                   1237:   }
                   1238:   Tcl_AppendResult (interp, argv [0], ": ", "bad option \"", argv [1],
                   1239:                    "\": should be clients, configure, start, or stop",
                   1240:                    (char *) NULL);
                   1241:   return TCL_ERROR;
                   1242: }
                   1243: 
                   1244: /*
                   1245:  * tcpServerObjectAcceptCmd --
                   1246:  *
                   1247:  *     The following procedure handles the `accept' command on a
                   1248:  *     server object.  It is called in the background by
                   1249:  *     tcpServerAcceptConnection when a connection request appears on
                   1250:  *     a server.  It is responsible for creating the client and
                   1251:  *     accepting the connection request.
                   1252:  *
                   1253:  * Results:
                   1254:  *     Returns a standard TCL result.  The return value is the name
                   1255:  *     of the client if the call is successful.
                   1256:  *
                   1257:  * Side effects:
                   1258:  *     A Tcl command named after the client object is created.
                   1259:  */
                   1260: 
                   1261: static int
                   1262: tcpServerObjectAcceptCmd (clientData, interp, argc, argv)
                   1263:      ClientData clientData;
                   1264:      Tcl_Interp * interp;
                   1265:      int argc;
                   1266:      char * * argv;
                   1267: {
                   1268:   register Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   1269:   register Tcp_ClientData * client;
                   1270:   int rubbish;
                   1271:   int unixStatus;
                   1272:   int nargc;
                   1273:   char * nargv [3];
                   1274:   char * message;
                   1275: 
                   1276:   /* Check command syntax */
                   1277: 
                   1278:   if (argc != 1) {
                   1279:     Tcl_AppendResult (interp, "wrong # args, should be \"", argv [-1], " ",
                   1280:                      argv [0], "\"", (char *) NULL);
                   1281:     return TCL_ERROR;
                   1282:   }
                   1283: 
                   1284:   /* Create the client data structure */
                   1285: 
                   1286:   client = (Tcp_ClientData *) ckalloc (sizeof (Tcp_ClientData));
                   1287: 
                   1288:   /* Accept the client's connection request */
                   1289: 
                   1290:   rubbish = sizeof (struct sockaddr_in);
                   1291:   client -> socketfd = accept (server -> socketfd,
                   1292:                               (struct sockaddr *) &(client -> peeraddr),
                   1293:                               &rubbish);
                   1294:   if (client -> socketfd < 0) {
                   1295:     Tcl_AppendResult (interp, "can't accept connection: ",
                   1296:                      Tcl_UnixError (interp), (char *) NULL);
                   1297:     ckfree ((char *) client);
                   1298:     return TCL_ERROR;
                   1299:   }
                   1300: 
                   1301:   /* Set up the socket for non-blocking I/O */
                   1302: 
                   1303:   rubbish = 1;
                   1304:   unixStatus = ioctl (client -> socketfd, FIONBIO, (char *) &rubbish);
                   1305:   if (unixStatus < 0) {
                   1306:     Tcl_AppendResult (interp,
                   1307:                      "can't set non-blocking I/O on client's socket: ",
                   1308:                      Tcl_UnixError (interp), (char *) NULL);
                   1309:     unixStatus = close (client -> socketfd);
                   1310:     if (unixStatus < 0) {
                   1311:       nargc = 3;
                   1312:       nargv [0] = "(also failed to close socket: ";
                   1313:       nargv [1] = Tcl_UnixError (interp);
                   1314:       nargv [2] = ")";
                   1315:       message = Tcl_Concat (nargc, nargv);
                   1316:       Tcl_AddErrorInfo (interp, message);
                   1317:       free (message);
                   1318:     }
                   1319:     ckfree ((char *) client);
                   1320:     return TCL_ERROR;
                   1321:   }
                   1322: 
                   1323:   /* Set up the client's description */
                   1324: 
                   1325:   client -> server = server;
                   1326:   sprintf (client -> name, "tcp_client_%d", client -> socketfd);
                   1327:   client -> command = malloc (strlen (server -> command) + 1);
                   1328:   client -> freeCommand = (Tcl_FreeProc *) free;
                   1329:   strcpy (client -> command, server -> command);
                   1330:   client -> inputBuffer = Tcl_CreateCmdBuf ();
                   1331:   client -> resultString = client -> resultPointer = (char *) NULL;
                   1332:   client -> freeResultString = (Tcl_FreeProc *) NULL;
                   1333:   client -> activeFlag = 0;
                   1334:   client -> closeFlag = 0;
                   1335:   client -> next = server -> firstClient;
                   1336:   if (client -> next != NULL) {
                   1337:     client -> next -> prev = client;
                   1338:   }
                   1339:   client -> prev = NULL;
                   1340:   server -> firstClient = client;
                   1341: 
                   1342:   /* Create the Tcl command for the client */
                   1343:   
                   1344:   Tcl_CreateCommand (interp, client -> name,
                   1345:                     (Tcl_CmdProc *) tcpClientObjectCmd,
                   1346:                     (ClientData) client,
                   1347:                     (Tcl_CmdDeleteProc *) deleteTcpClientObjectCmd);
                   1348: 
                   1349:   /* Return the client's name */
                   1350: 
                   1351:   Tcl_SetResult (interp, client -> name, TCL_STATIC);
                   1352:   return TCL_OK;
                   1353: }
                   1354: 
                   1355: /*
                   1356:  * tcpServerObjectClientsCmd --
                   1357:  *
                   1358:  *     This procedure in invoked in response to the `clients' command
                   1359:  * on a TCP server object.  It returns a list of clients for the server.
                   1360:  */
                   1361: 
                   1362: static int
                   1363: tcpServerObjectClientsCmd (clientData, interp, argc, argv)
                   1364:      ClientData clientData;
                   1365:      Tcl_Interp * interp;
                   1366:      int argc;
                   1367:      char * * argv;
                   1368: {
                   1369:   Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   1370:   Tcp_ClientData * client;
                   1371: 
                   1372:   /* Check syntax */
                   1373: 
                   1374:   if (argc != 1) {
                   1375:     Tcl_AppendResult (interp, "wrong # args, should be\"", argv [-1], " ",
                   1376:                      argv [0], "\"", (char *) NULL);
                   1377:     return TCL_ERROR;
                   1378:   }
                   1379: 
                   1380:   for (client = server -> firstClient; client != NULL;
                   1381:        client = client -> next) {
                   1382:     Tcl_AppendElement (interp, client -> name, 0);
                   1383:   }
                   1384: 
                   1385:   return TCL_OK;
                   1386: }
                   1387: 
                   1388: /*
                   1389:  * tcpServerObjectConfigCmd --
                   1390:  *
                   1391:  *     This procedure is invoked in response to the `config' command
                   1392:  *     on a TCP server object.  With no arguments, it returns a list
                   1393:  *     of valid arguments.  With one argument, it returns the current
                   1394:  *     value of that option.  With multiple arguments, it attempts to
                   1395:  *     configure the server according to that argument list.
                   1396:  * Results:
                   1397:  *     Returns a standard Tcl result.
                   1398:  */
                   1399: 
                   1400: static int
                   1401: tcpServerObjectConfigCmd (clientData, interp, argc, argv)
                   1402:      ClientData clientData;
                   1403:      Tcl_Interp * interp;
                   1404:      int argc;
                   1405:      char * * argv;
                   1406: {
                   1407:   int unixStatus;
                   1408:   int c;
                   1409:   unsigned length;
                   1410: 
                   1411:   /* No arguments -- return a list of valid options. */
                   1412: 
                   1413:   if (argc <= 1) {
                   1414:     Tcl_SetResult (interp, "-command -port", TCL_STATIC);
                   1415:     return TCL_OK;
                   1416:   }
                   1417: 
                   1418:   /* One argument -- query a particular option */
                   1419: 
                   1420:   if (argc == 2) {
                   1421:     register Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   1422:     char * arg = argv [1];
                   1423: 
                   1424:     if (arg [0] != '-') {
                   1425:       Tcl_AppendResult (interp, argv [-1], " ", argv [0],
                   1426:                        ": bad option \"", arg,
                   1427:                        "\" -- each option must begin with a hyphen.",
                   1428:                        (char *) NULL);
                   1429:       return TCL_ERROR;
                   1430: 
                   1431:     }
                   1432: 
                   1433:     length = strlen (++arg);
                   1434:     c = arg [0];
                   1435: 
                   1436:     if (c == 'c' && strncmp (arg, "command", length) == 0) {
                   1437: 
                   1438:       /* Command option -- Get the command name */
                   1439: 
                   1440:       Tcl_SetResult (interp, server->name, TCL_STATIC);
                   1441:       return TCL_OK;
                   1442: 
                   1443:     }
                   1444: 
                   1445:     if (c == 'p' && strncmp (arg, "port", length) == 0) {
                   1446: 
                   1447:       /* Port option -- Get the port number */
                   1448: 
                   1449:       struct sockaddr_in portaddr;
                   1450:       int rubbish = sizeof (struct sockaddr_in);
                   1451: 
                   1452:       unixStatus = getsockname (server -> socketfd,
                   1453:                                (struct sockaddr *) &portaddr, &rubbish);
                   1454:       if (unixStatus < 0) {
                   1455:        Tcl_AppendResult (interp, argv [-1], ": can't read port #: ",
                   1456:                          Tcl_UnixError (interp), (char *) NULL);
                   1457:        return TCL_ERROR;
                   1458:       }
                   1459:       Tcl_ResetResult (interp);
                   1460:       sprintf (interp -> result, "%d", (int) ntohs (portaddr.sin_port));
                   1461:       return TCL_OK;
                   1462:     }
                   1463:        
                   1464:     /* Unknown option */
                   1465: 
                   1466:     Tcl_AppendResult (interp, argv [-1], ": unknown option \"", arg,
                   1467:                      "\" -- must be -command or -port", (char *) NULL);
                   1468:     return TCL_ERROR;
                   1469:   }
                   1470: 
                   1471:   return tcpServerObjectConfig (clientData, interp, argc, argv);
                   1472: }  
                   1473: 
                   1474: /*
                   1475:  * tcpServerObjectStartCmd --
                   1476:  *
                   1477:  *     This procedure is invoked to process the "start" command on a
                   1478:  *     TCP server object.  It sets the server up so that new
                   1479:  *     connection requests will create "server-client" objects and
                   1480:  *     invoke the server's command with them.
                   1481:  *
                   1482:  *     If Tk is available, the "start" command returns to the caller.
                   1483:  *     If Tk is not available, the "start" command immediately enters
                   1484:  *     a loop that attempts to process the connection events (and
                   1485:  *     other file events as well).  The loop may be exited by
                   1486:  *     executing a `stop' command on the server object.  (The `stop'
                   1487:  *     command also exists in the Tk environment, since there is more
                   1488:  *     to stopping a server than just breaking out of its event
                   1489:  *     loop.)
                   1490: */
                   1491: 
                   1492: static int
                   1493: tcpServerObjectStartCmd (clientData, interp, argc, argv)
                   1494:      ClientData clientData;
                   1495:      Tcl_Interp * interp;
                   1496:      int argc;
                   1497:      char * * argv;
                   1498: {
                   1499:   register Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   1500:   int unixStatus;
                   1501: 
                   1502:   /* Check command syntax */
                   1503: 
                   1504:   if (argc != 1) {
                   1505:     Tcl_AppendResult (interp, "wrong # args, should be \"", argv [-1], " ",
                   1506:                      argv [0], "\"", (char *) NULL);
                   1507:     return TCL_ERROR;
                   1508:   }
                   1509: 
                   1510:   /* Listen at the server's socket */
                   1511: 
                   1512:   unixStatus = listen (server -> socketfd, TCP_LISTEN_BACKLOG);
                   1513:   if (unixStatus < 0) {
                   1514:     Tcl_AppendResult (interp, argv [-1], ": can't listen at socket: ",
                   1515:                      Tcl_UnixError (interp), (char *) NULL);
                   1516:     return TCL_ERROR;
                   1517:   }
                   1518: 
                   1519:   /* Add a file handler to gain control at tcpServerAcceptConnection
                   1520:    * whenever a client attempts to connect.
                   1521:    */
                   1522: 
                   1523:   simpleCreateFileHandler (server -> socketfd, TK_READABLE,
                   1524:                           (Tk_FileProc *) tcpServerAcceptConnection,
                   1525:                           clientData);
                   1526:   return TCL_OK;
                   1527: }
                   1528: 
                   1529: /*
                   1530:  * tcpServerObjectStopCmd
                   1531:  *
                   1532:  *     This procedure is invoked in response to the `$server stop' Tcl
                   1533:  * command.  It destroys the server's object command.  Destroying the object
                   1534:  * command, in turn, attempts to shut down the server in question.  It closes
                   1535:  * the listen socket, closes all the clients, and sets the `stop' flag for
                   1536:  * the server itself.  It then calls `tcpServerClose' to try to get rid of
                   1537:  * the server.
                   1538:  *
                   1539:  *     If one or more clients are active, the server does not shut down
                   1540:  * until they can be closed properly.
                   1541:  */
                   1542: 
                   1543: static int
                   1544: tcpServerObjectStopCmd (clientData, interp, argc, argv)
                   1545:      ClientData clientData;
                   1546:      Tcl_Interp * interp;
                   1547:      int argc;
                   1548:      char * * argv;
                   1549: {
                   1550:   register Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   1551: 
                   1552:   if (argc != 1) {
                   1553:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1],
                   1554:                      " ", argv [0], "\"", (char *) NULL);
                   1555:     return TCL_ERROR;
                   1556:   }
                   1557: 
                   1558:   /* Delete the server command */
                   1559: 
                   1560:   Tcl_DeleteCommand (interp, server -> name);
                   1561: 
                   1562:   return TCL_OK;
                   1563: }
                   1564: 
                   1565: /*
                   1566:  * deleteTcpServerObjectCmd --
                   1567:  *
                   1568:  *     This procedure is called when a server's object command is deleted.
                   1569:  *
                   1570:  *     It is the first procedure called when a server is shut down.  It
                   1571:  * closes the listen socket and deletes its file handler.  It also attempts
                   1572:  * to close all the clients.
                   1573:  *
                   1574:  *     It may be that a client needs to be able to complete a data transfer
                   1575:  * before it can be closed.  In this case, the `close flag' for the client is
                   1576:  * set.  The client will be deleted when it reaches a quiescent point.
                   1577:  *
                   1578:  *     Once all the clients are gone, tcpDeleteServer removes the server's
                   1579:  * client data structure.
                   1580:  */
                   1581: 
                   1582: static void
                   1583: deleteTcpServerObjectCmd (clientData)
                   1584:      ClientData clientData;
                   1585: {
                   1586:   register Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   1587:   register Tcp_ClientData * client, * nextClient;
                   1588: 
                   1589:   /* Close the listen socket and delete its handler */
                   1590: 
                   1591:   simpleDeleteFileHandler (server -> socketfd);
                   1592:   (void) close (server -> socketfd);
                   1593:   server -> socketfd = -1;
                   1594: 
                   1595:   /* Close all clients */
                   1596: 
                   1597:   for (client = server -> firstClient; client != NULL; client = nextClient) {
                   1598:     nextClient = client -> next;
                   1599:     if (client -> activeFlag)
                   1600:       client -> closeFlag = 1;
                   1601:     else
                   1602:       tcpCloseClient (client);
                   1603:   }
                   1604: 
                   1605:   /* Remove the server from the list of servers. */
                   1606: 
                   1607:   if (server -> next != NULL)
                   1608:     server -> next -> prev = server -> prev;
                   1609:   if (server -> prev != NULL)
                   1610:     server -> prev -> next = server -> next;
                   1611:   else
                   1612:     tcpFirstServer = server -> next;
                   1613: 
                   1614:   /* If all clients are closed, get to tcpDeleteServer now.  Otherwise, set
                   1615:    * the server's stop flag and return.
                   1616:    */
                   1617: 
                   1618:   if (server -> firstClient == NULL) {
                   1619:     tcpDeleteServer (server);
                   1620:   } else {
                   1621:     server -> stopFlag = 1;
                   1622:   }
                   1623: }
                   1624: 
                   1625: /*
                   1626:  * tcpDeleteServer --
                   1627:  *
                   1628:  *     This procedure is invoked as the final phase of deleting a TCP server.
                   1629:  * When execution gets here, the server's listen socket has been closed and
                   1630:  * the handler has been removed.  The server's object command has been deleted.
                   1631:  * The server has been removed from the list of active servers.  All the
                   1632:  * server's clients have been closed.  The server's login command has been
                   1633:  * deleted.  All that remains is to deallocate the server's data structures.
                   1634:  */
                   1635: 
                   1636: static void
                   1637: tcpDeleteServer (server)
                   1638:      Tcp_ServerData * server;
                   1639: {
                   1640:   /* Get rid of the server's initial command */
                   1641: 
                   1642:   if (server -> command != NULL && server -> freeCommand != NULL) {
                   1643:     (*(server -> freeCommand)) (server -> command);
                   1644:   }
                   1645: 
                   1646:   /* Get rid of the server's own data structure */
                   1647: 
                   1648:   (void) ckfree ((char *) server);
                   1649: }
                   1650: 
                   1651: /*
                   1652:  * tcpServerObjectConfig --
                   1653:  *
                   1654:  *     This procedure is invoked to configure a TCP server object.
                   1655:  *     It may be called from tcpServerCommand when the server is
                   1656:  *     first being created, or else from tcpServerObjectCmd if the
                   1657:  *     server object is called with the "config" option.
                   1658:  *
                   1659:  *     In any case, the arguments are expected to contain zero or
                   1660:  *     more of the following:
                   1661:  *
                   1662:  *     -port <number>
                   1663:  *             Requests that the server listen at a specific port.
                   1664:  *             Default is whatever the system assigns.
                   1665:  *
                   1666:  *     -command <string>
                   1667:  *             Specifies the initial command used when a client
                   1668:  *             first connects to the server.  The command is
                   1669:  *             concatenated with the name of a "server-client" object
                   1670:  *             that identifies the client, and then called:
                   1671:  *                     command client
                   1672:  *             Default is "tcp login"
                   1673:  *
                   1674:  *     -raw
                   1675:  *             Puts the server in raw socket mode.
                   1676:  *
                   1677:  * Result:
                   1678:  *     A standard TCL result.
                   1679:  */
                   1680: 
                   1681: static int
                   1682: tcpServerObjectConfig (clientData, interp, argc, argv)
                   1683:      ClientData clientData;
                   1684:      Tcl_Interp * interp;
                   1685:      int argc;
                   1686:      char * * argv;
                   1687: {
                   1688: 
                   1689:   register Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   1690: 
                   1691:   int status;
                   1692:   int unixStatus;
                   1693: 
                   1694:   /* On entry, argc shows one plus the number of parameters.  Argv[-1] */
                   1695:   /* and argv[0] give the command that got us here: either "tcp */
                   1696:   /* server" or else "serverName config" */
                   1697: 
                   1698:   int a;
                   1699:   unsigned length;
                   1700:   int c;
                   1701: 
                   1702:   /* Step through the parameters */
                   1703: 
                   1704:   for (a = 1; a < argc; ++a) {
                   1705:     char * arg = argv [a];
                   1706: 
                   1707:     if (arg [0] != '-') {
                   1708:       Tcl_AppendResult (interp, argv [-1], ": bad option \"", arg,
                   1709:                        "\" -- each option must begin with a hyphen.",
                   1710:                        (char *) NULL);
                   1711:       return TCL_ERROR;
                   1712:     } else {
                   1713: 
                   1714:       length = strlen (++arg);
                   1715:       c = arg [0];
                   1716: 
                   1717:       if (c == 'c' && strncmp (arg, "command", length) == 0) {
                   1718: 
                   1719:        /* Command option -- Get the command name */
                   1720: 
                   1721:        ++a;
                   1722:        if (a >= argc) {
                   1723:          Tcl_AppendResult (interp, argv [-1],
                   1724:                            ": \"-command\" must be followed by a string.",
                   1725:                            (char *) NULL);
                   1726:          return TCL_ERROR;
                   1727:        }
                   1728: 
                   1729:        /* Free the old command name */
                   1730: 
                   1731:        if (server -> freeCommand != NULL) {
                   1732:          (*(server -> freeCommand)) (server -> command);
                   1733:        }
                   1734: 
                   1735:        /* Put in the new command name */
                   1736: 
                   1737:        server -> command = (char *) malloc (strlen (argv [a]) + 1);
                   1738:        strcpy (server -> command, argv [a]);
                   1739:        server -> freeCommand = (Tcl_FreeProc *) free;
                   1740: 
                   1741:       } else if (c == 'p' && strncmp (arg, "port", length) == 0) {
                   1742: 
                   1743:        /* Port option -- get the port number */
                   1744: 
                   1745:        char * portstr;
                   1746:        int portno;
                   1747:        struct sockaddr_in portaddr;
                   1748: 
                   1749:        ++a;
                   1750:        if (a >= argc) {
                   1751:          Tcl_AppendResult (interp, argv [-1],
                   1752:                            ": \"-port\" must be followed by a number.",
                   1753:                            (char *) NULL);
                   1754:          return TCL_ERROR;
                   1755:        }
                   1756:        portstr = argv [a];
                   1757:        status = Tcl_GetInt (interp, portstr, &portno);
                   1758:        if (status) return status;
                   1759: 
                   1760:        /* Set the port number */
                   1761: 
                   1762:        memset ((void *) & portaddr, 0, sizeof (struct sockaddr_in));
                   1763:        portaddr.sin_port = htons (portno);
                   1764:        unixStatus = bind (server -> socketfd,
                   1765:                           (struct sockaddr *) &portaddr,
                   1766:                           sizeof (struct sockaddr_in));
                   1767:        if (unixStatus < 0) {
                   1768:          Tcl_AppendResult (interp, argv [-1],
                   1769:                            ": can't set port number: ",
                   1770:                            Tcl_UnixError (interp), (char *) NULL);
                   1771:          return TCL_ERROR;
                   1772:        }
                   1773: 
                   1774:       } else if (c == 'r' && strncmp (arg, "raw", length) == 0) {
                   1775: 
                   1776:        /* raw option -- set raw socket mode */
                   1777: 
                   1778:        server -> raw = 1;
                   1779: 
                   1780:       } else {
                   1781: 
                   1782:        /* Unknown option */
                   1783: 
                   1784:        Tcl_AppendResult (interp, argv [-1],
                   1785:                          ": unknown option \"", arg - 1,
                   1786:                          "\" -- must be -command or -port", (char *) NULL);
                   1787:        return TCL_ERROR;
                   1788:       }
                   1789:     }
                   1790:   }
                   1791: 
                   1792:   Tcl_SetResult (interp, server -> name, TCL_STATIC);
                   1793:   return TCL_OK;
                   1794: }
                   1795: 
                   1796: /*
                   1797:  * tcpClientObjectCmd --
                   1798:  *
                   1799:  *     This procedure handles the object command for a Tcp client (on
                   1800:  *     the server side).  It takes several forms:
                   1801:  *             $client command ?command?
                   1802:  *                     With no arguments, returns the client's
                   1803:  *                     current command.  With arguments, replaces the
                   1804:  *                     client's command with the arguments
                   1805:  *             $client close
                   1806:  *                     Deletes the client.  If a command is being
                   1807:  *                     processed on the client's behalf, the client
                   1808:  *                     will not be deleted until the command's result
                   1809:  *                     is returned.
                   1810:  *             $client do ?args?
                   1811:  *                     Concatenate the client's command with ?args?,
                   1812:  *                     and execute the result.  Called in background
                   1813:  *                     when a command arrives and on initial
                   1814:  *                     connection.
                   1815:  *             $client hostname
                   1816:  *                     Returns the name of the host where the client
                   1817:  *                     is running.
                   1818:  *             $client server
                   1819:  *                     Returns the name of the server to which the client
                   1820:  *                     is connected.
                   1821:  */
                   1822: 
                   1823: static int
                   1824: tcpClientObjectCmd (clientData, interp, argc, argv)
                   1825:      ClientData clientData;
                   1826:      Tcl_Interp * interp;
                   1827:      int argc;
                   1828:      char * * argv;
                   1829: {
                   1830:   int c;
                   1831:   unsigned length;
                   1832: 
                   1833:   if (argc < 2) {
                   1834:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [0],
                   1835:                      " command ?args?\"", (char *) NULL);
                   1836:     return TCL_ERROR;
                   1837:   }
                   1838: 
                   1839:   c = argv [1] [0];
                   1840:   length = strlen (argv [1]);
                   1841:   
                   1842:   if (c == 'c' && length >= 2 && strncmp (argv [1], "close", length) == 0) {
                   1843:     return tcpClientObjectCloseCmd (clientData, interp, argc-1, argv+1);
                   1844:   }
                   1845:   if (c == 'c' && length >= 2 && strncmp (argv [1], "command", length) == 0) {
                   1846:     return tcpClientObjectCommandCmd (clientData, interp, argc-1, argv+1);
                   1847:   }
                   1848:   if (c == 'd' && strncmp (argv [1], "do", length) == 0) {
                   1849:     return tcpClientObjectDoCmd (clientData, interp, argc-1, argv+1);
                   1850:   }
                   1851:   if (c == 'h' && strncmp (argv [1], "hostname", length) == 0) {
                   1852:     return tcpClientObjectHostnameCmd (clientData, interp, argc-1, argv+1);
                   1853:   }
                   1854:   if (c == 's' && strncmp (argv [1], "server", length) == 0) {
                   1855:     return tcpClientObjectServerCmd (clientData, interp, argc-1, argv+1);
                   1856:   }
                   1857: 
                   1858:   Tcl_AppendResult (interp, "bad option \"", argv [1],
                   1859:                    "\": should be close, command, do, hostname or server",
                   1860:                    (char *) NULL);
                   1861:   return TCL_ERROR;
                   1862: }
                   1863: 
                   1864: /* 
                   1865:  * tcpClientObjectCloseCmd --
                   1866:  *
                   1867:  *     This procedure is called when the Tcl program wants to close a client.
                   1868:  * If the client is active, it sets a flag to close the client when it
                   1869:  * becomes quiescent.  Otherwise, it closes the client immediately.
                   1870:  */
                   1871: 
                   1872: static int
                   1873: tcpClientObjectCloseCmd (clientData, interp, argc, argv)
                   1874:      ClientData clientData;
                   1875:      Tcl_Interp * interp;
                   1876:      int argc;
                   1877:      char * * argv;
                   1878: {
                   1879:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   1880: 
                   1881:   if (argc != 1) {
                   1882:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1], " ",
                   1883:                      argv [0], "\"", (char *) NULL);
                   1884:     return TCL_ERROR;
                   1885:   }
                   1886: 
                   1887:   if (client -> activeFlag)
                   1888:     client -> closeFlag = 1;
                   1889:   else
                   1890:     tcpCloseClient (client);
                   1891: 
                   1892:   return TCL_OK;
                   1893: }
                   1894: 
                   1895: /* 
                   1896:  * tcpClientObjectCommandCmd --
                   1897:  *
                   1898:  *     Query/change the command associated with a client object
                   1899:  *
                   1900:  * Syntax:
                   1901:  *     $client command ?newcommand?
                   1902:  *
                   1903:  * Return:
                   1904:  *     A standard Tcl result containing the client's command.
                   1905:  */
                   1906: 
                   1907: static int
                   1908: tcpClientObjectCommandCmd (clientData, interp, argc, argv)
                   1909:      ClientData clientData;
                   1910:      Tcl_Interp * interp;
                   1911:      int argc;
                   1912:      char * * argv;
                   1913: {
                   1914:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   1915: 
                   1916:   /* Check syntax */
                   1917: 
                   1918:   if (argc > 2) {
                   1919:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1], " ",
                   1920:                      argv [0], " ?command?\"", (char *) NULL);
                   1921:     return TCL_ERROR;
                   1922:   }
                   1923: 
                   1924:   /* Set command if necessary */
                   1925: 
                   1926:   if (argc == 2) {
                   1927:     if (client -> freeCommand != (Tcl_FreeProc *) NULL) {
                   1928:       (*client -> freeCommand) (client -> command);
                   1929:     }
                   1930:     client -> command = malloc (strlen (argv [1]) + 1);
                   1931:     strcpy (client -> command, argv [1]);
                   1932:     client -> freeCommand = (Tcl_FreeProc *) free;
                   1933:   }
                   1934: 
                   1935:   /* Return command in any case */
                   1936: 
                   1937:   Tcl_SetResult (interp, client -> command, TCL_STATIC);
                   1938: 
                   1939:   return TCL_OK;
                   1940: }
                   1941: 
                   1942: /*
                   1943:  * tcpClientObjectDoCmd --
                   1944:  *
                   1945:  *     The following procedure handles the `do' command on a client
                   1946:  *     object.  It is called
                   1947:  *             (a) as "$client do", at login.
                   1948:  *             (b) as "$client do <command>", when the client sends a
                   1949:  *                     command. 
                   1950:  *             (c) as "$client do", with no further arguments, when
                   1951:  *                     the connection is closed.
                   1952:  *     It concatenates the client's saved command string with the
                   1953:  *     client's name, and then with the passed command, resulting in
                   1954:  *     a command:
                   1955:  *             saved_command client passed_command
                   1956:  *     which is then passed to Tcl_Eval for processing.
                   1957:  *      During the processing of the command, the `active' flag is set for
                   1958:  *     the client, to avoid having the client closed prematurely.
                   1959:  */
                   1960: static int
                   1961: tcpClientObjectDoCmd (clientData, interp, argc, argv)
                   1962:      ClientData clientData;
                   1963:      Tcl_Interp * interp;
                   1964:      int argc;
                   1965:      char * * argv;
                   1966: {
                   1967: 
                   1968:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   1969:   int status;
                   1970:   int closeflag;
                   1971:   char * prevClient;
                   1972:   char * excmd;
                   1973:   unsigned excmdl;
                   1974:   int scanflags;
                   1975: 
                   1976:   /* Check command syntax */
                   1977: 
                   1978:   if (argc > 2) {
                   1979:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1],
                   1980:                      " ", argv [0], " ?command?\"", (char *) NULL);
                   1981:     return TCL_ERROR;
                   1982:   }
                   1983: 
                   1984:   prevClient = tcpCurrentClient;
                   1985:   tcpCurrentClient = client -> name;
                   1986: 
                   1987:   /* Evaluate the client's command, passing the client name and message */
                   1988: 
                   1989:   closeflag = 0;
                   1990:   client -> activeFlag = 1;
                   1991: 
                   1992:   if (argc == 2) {
                   1993:     excmdl = Tcl_ScanElement (argv [1], &scanflags) + 1;
                   1994:     excmd = (char *) ckalloc (excmdl);
                   1995:     excmdl = Tcl_ConvertElement (argv [1], excmd, scanflags);
                   1996:     excmd [excmdl] = '\0';
                   1997:   } else {
                   1998:     excmd = (char *) NULL;
                   1999:   }
                   2000: 
                   2001:   status = Tcl_VarEval (interp, client -> command, " ", client -> name, " ",
                   2002:                        excmd, (char *) NULL);
                   2003: 
                   2004:   if (excmd)
                   2005:     ckfree (excmd);
                   2006:   
                   2007:   if (status != TCL_OK && argc < 2) {
                   2008:     closeflag = 1;
                   2009:   }
                   2010: 
                   2011:   client -> activeFlag = 0;
                   2012:   tcpCurrentClient = prevClient;
                   2013: 
                   2014:   /* If the client command throws an error on login or logout,
                   2015:    * the client should be disconnected.
                   2016:    * In any case, the result should be reported back to the client.
                   2017:    */
                   2018: 
                   2019:   if (! (client -> server -> raw)) {
                   2020:     tcpReturnResultToClient (client, interp, status, closeflag);
                   2021:   } else {
                   2022:     tcpPrepareClientForInput (client);
                   2023:   }
                   2024: 
                   2025:   /* The client may have been closed by the ReturnResult operation. DON'T
                   2026:    * USE IT AFTER THIS POINT.
                   2027:    */
                   2028: 
                   2029:   return TCL_OK;
                   2030: }
                   2031: 
                   2032: /*
                   2033:  * tcpClientObjectHostnameCmd --
                   2034:  *
                   2035:  *     This procedure is invoked in response to the `$client hostname'
                   2036:  * Tcl command.  It returns the name of the peer host on which the client
                   2037:  * runs.
                   2038:  */
                   2039: 
                   2040: static int
                   2041: tcpClientObjectHostnameCmd (clientData, interp, argc, argv)
                   2042:      ClientData clientData;
                   2043:      Tcl_Interp * interp;
                   2044:      int argc;
                   2045:      char * * argv;
                   2046: {
                   2047:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   2048: 
                   2049:   struct hostent * hostdesc;
                   2050: 
                   2051:   if (argc != 1) {
                   2052:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1], " ",
                   2053:                      argv [0], "\"", (char *) NULL);
                   2054:     return TCL_ERROR;
                   2055:   }
                   2056: 
                   2057:   hostdesc = gethostbyaddr ((char *) &(client -> peeraddr.sin_addr.s_addr),
                   2058:                            sizeof (client -> peeraddr.sin_addr.s_addr),
                   2059:                            AF_INET);
                   2060:   
                   2061:   if (hostdesc != (struct hostent *) NULL) {
                   2062:     Tcl_SetResult (interp, hostdesc -> h_name, TCL_VOLATILE);
                   2063:   } else {
                   2064:     Tcl_SetResult (interp, inet_ntoa (client -> peeraddr.sin_addr),
                   2065:                   TCL_VOLATILE);
                   2066:   }
                   2067: 
                   2068:   return TCL_OK;
                   2069: }
                   2070: 
                   2071: /*
                   2072:  * tcpClientObjectServerCmd --
                   2073:  *
                   2074:  *     This procedure is invoked in response to the `$client server'
                   2075:  * Tcl command.  It returns the name of the server to which the client
                   2076:  * is connected.
                   2077:  */
                   2078: 
                   2079: static int
                   2080: tcpClientObjectServerCmd (clientData, interp, argc, argv)
                   2081:      ClientData clientData;
                   2082:      Tcl_Interp * interp;
                   2083:      int argc;
                   2084:      char * * argv;
                   2085: {
                   2086:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   2087: 
                   2088:   if (argc != 1) {
                   2089:     Tcl_AppendResult (interp, "wrong # args: should be \"", argv [-1], " ",
                   2090:                      argv [0], "\"", (char *) NULL);
                   2091:     return TCL_ERROR;
                   2092:   }
                   2093: 
                   2094:   Tcl_SetResult (interp, client -> server -> name, TCL_STATIC);
                   2095: 
                   2096:   return TCL_OK;
                   2097: }
                   2098: 
                   2099: /*
                   2100:  * deleteTcpClientObjectCmd --
                   2101:  *
                   2102:  *     This procedure is invoked when a client object's command has
                   2103:  * been deleted.  WARNING -- deleting a client object command when the
                   2104:  * client is active is a FATAL error that cannot be reported through the
                   2105:  * Tcl interpreter.
                   2106:  *
                   2107:  *     This procedure does all the cleanup necessary to getting rid of the
                   2108:  * client.
                   2109:  */
                   2110: 
                   2111: static void
                   2112: deleteTcpClientObjectCmd (clientData)
                   2113:      ClientData clientData;
                   2114: {
                   2115:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   2116:   register Tcp_ServerData * server = client -> server;
                   2117: 
                   2118:   /* Make sure the client is really dead. */
                   2119: 
                   2120:   if (client -> activeFlag) {
                   2121:     fprintf (stderr, "attempt to delete an active TCP client!\n\n");
                   2122:     abort ();
                   2123:   }
                   2124: 
                   2125:   /* Remove any handler for data on the client's socket. */
                   2126: 
                   2127:   simpleDeleteFileHandler (client -> socketfd);
                   2128: 
                   2129:   /* Now it's safe to close the socket */
                   2130: 
                   2131:   (void) close (client -> socketfd);
                   2132: 
                   2133:   /* Get rid of the command */
                   2134: 
                   2135:   if (client -> command != NULL && client -> freeCommand != NULL) {
                   2136:     (*(client -> freeCommand)) (client -> command);
                   2137:   }
                   2138: 
                   2139:   /* Get rid of the input buffer */
                   2140: 
                   2141:   Tcl_DeleteCmdBuf (client -> inputBuffer);
                   2142: 
                   2143:   /* Get rid of any pending result */
                   2144: 
                   2145:   if (client -> resultString != NULL && client -> freeResultString != NULL) {
                   2146:     (*(client -> freeResultString)) (client -> resultString);
                   2147:   }
                   2148: 
                   2149:   /* Unlink the client from the list of active clients */
                   2150: 
                   2151:   if (client -> prev == NULL)
                   2152:     client -> server -> firstClient = client -> next;
                   2153:   else
                   2154:     client -> prev -> next = client -> next;
                   2155: 
                   2156:   if (client -> next != NULL)
                   2157:     client -> next -> prev = client -> prev;
                   2158: 
                   2159:   /* Now it's ok to destroy the client's data structure */
                   2160: 
                   2161:   ckfree ((char *) client);
                   2162: 
                   2163:   /* Handle a deferred close on the server if necessary */
                   2164: 
                   2165:   if (server -> stopFlag && server -> firstClient == NULL)
                   2166:     tcpDeleteServer (server);
                   2167: }
                   2168: 
                   2169: /*
                   2170:  * tcpConnectionObjectCmd --
                   2171:  *
                   2172:  *     This procedure is invoked to process the object command for a client-
                   2173:  * side connection object.  It takes a couple of diferent forms:
                   2174:  *
                   2175:  *     $connection close
                   2176:  *             Closes the connection.
                   2177:  *     $connection send arg ?arg....?
                   2178:  *             Catenates the arguments into a Tcl command, and sends them
                   2179:  *             to the server.
                   2180:  */
                   2181: 
                   2182: static int
                   2183: tcpConnectionObjectCmd (clientData, interp, argc, argv)
                   2184:      ClientData clientData;
                   2185:      Tcl_Interp * interp;
                   2186:      int argc;
                   2187:      char * * argv;
                   2188: {
                   2189:   unsigned length;
                   2190:   int c;
                   2191:   char * arg;
                   2192: 
                   2193:   if (argc < 2) {
                   2194:     Tcl_AppendResult (interp, "wrong # args, should be \"", argv [0], " ",
                   2195:                      "command ?args?\"", (char *) NULL);
                   2196:     return TCL_ERROR;
                   2197:   }
                   2198: 
                   2199:   arg = argv [1];
                   2200:   c = arg [0];
                   2201:   length = strlen (arg);
                   2202: 
                   2203:   if (c == 'c' && strncmp (arg, "close", length) == 0) {
                   2204:     return tcpConnectionObjectCloseCmd (clientData, interp, argc-1, argv+1);
                   2205:   }
                   2206:   if (c == 's' && strncmp (arg, "send", length) == 0) {
                   2207:     return tcpConnectionObjectSendCmd (clientData, interp, argc-1, argv+1);
                   2208:   }
                   2209: 
                   2210:   Tcl_AppendResult (interp, "unknown command \"", arg,
                   2211:                    "\": must be close or send", (char *) NULL);
                   2212:   return TCL_ERROR;
                   2213: }
                   2214: 
                   2215: /*
                   2216:  * tcpConnectionObjectCloseCmd --
                   2217:  *
                   2218:  *     This procedure is invoked in response to a `close' command on a
                   2219:  * client-side connection object.  It closes the socket and deletes the
                   2220:  * object command.
                   2221:  */
                   2222: 
                   2223: /* ARGSUSED */
                   2224: static int
                   2225: tcpConnectionObjectCloseCmd (clientData, interp, argc, argv)
                   2226:      ClientData clientData;
                   2227:      Tcl_Interp * interp;
                   2228:      int argc;
                   2229:      char * * argv;
                   2230: {
                   2231:   if (argc != 1) {
                   2232:     Tcl_AppendResult (interp, "wrong # args, should be \"", argv [-1], " ",
                   2233:                      argv [0], "\"", (char *) NULL);
                   2234:     return TCL_ERROR;
                   2235:   }
                   2236: 
                   2237:   Tcl_DeleteCommand (interp, argv [-1]);
                   2238:   return TCL_OK;
                   2239: }
                   2240: 
                   2241: /*
                   2242:  * tcpConnectionObjectSendCmd --
                   2243:  *
                   2244:  *     This procedure is invoked in response to a `send' command on a client-
                   2245:  * side connection object.  It catenates the `send' arguments into a single
                   2246:  * string, presents that string to the server as a command, and returns the
                   2247:  * server's reply.
                   2248:  */
                   2249: 
                   2250: static int
                   2251: tcpConnectionObjectSendCmd (clientData, interp, argc, argv)
                   2252:      ClientData clientData;
                   2253:      Tcl_Interp * interp;
                   2254:      int argc;
                   2255:      char * * argv;
                   2256: {
                   2257:   char * message;
                   2258:   int f = (int) clientData;
                   2259:   int status;
                   2260: 
                   2261:   if (argc < 2) {
                   2262:     Tcl_AppendResult (interp, "wrong # args, should be \"", argv [-1], " ",
                   2263:                      argv [0], " command\"", (char *) NULL);
                   2264:     return TCL_ERROR;
                   2265:   }
                   2266: 
                   2267:   /* Paste together the message */
                   2268: 
                   2269:   message = Tcl_Merge (argc-1, argv+1);
                   2270: 
                   2271:   /* Send the command to the server */
                   2272: 
                   2273:   status = tcpSendCmdToServer (interp, f, message);
                   2274:   if (status != TCL_OK)
                   2275:     return status;
                   2276: 
                   2277:   /* Get the server's reply */
                   2278: 
                   2279:   return tcpReceiveResultFromServer (interp, f);
                   2280: }
                   2281: 
                   2282: /*
                   2283:  * deleteTcpConnectionObjectCmd --
                   2284:  *
                   2285:  *     This procedure is called when a connection object is to be
                   2286:  * deleted.  It just has to close the socket that the object uses.
                   2287:  */
                   2288: 
                   2289: static void
                   2290: deleteTcpConnectionObjectCmd (clientData)
                   2291:      ClientData clientData;
                   2292: {
                   2293:   int f = (int) clientData;
                   2294:   (void) close (f);
                   2295: }
                   2296: 
                   2297: /*
                   2298:  * tcpCloseClient --
                   2299:  *
                   2300:  *     This procedure is called when the program is completely done with
                   2301:  * a client object.  If the `active' flag is set, there is still a reference
                   2302:  * to the dead client, but we shouldn't have come here in that case.
                   2303:  */
                   2304: 
                   2305: static void
                   2306: tcpCloseClient (client)
                   2307:      Tcp_ClientData * client;
                   2308: {
                   2309:   if (client -> activeFlag) 
                   2310:     abort ();
                   2311: 
                   2312:   /* Deleting the client command is all we need to do -- the delete
                   2313:    * procedure does everything else.
                   2314:    */
                   2315: 
                   2316:   Tcl_DeleteCommand (client -> server -> interp, client -> name);
                   2317: }
                   2318: 
                   2319: /*
                   2320:  * tcpServerAcceptConnection --
                   2321:  *
                   2322:  *     This procedure is invoked as a file handler whenever a server's
                   2323:  *     socket is ready for `reading' -- i.e., has a connection request
                   2324:  *     outstanding.
                   2325:  *
                   2326:  *     It calls the `accept' command on the server to create a client.
                   2327:  *     If the `accept' is successful, it then calls the `do'
                   2328:  *     command on the client.  If either call fails, a background error
                   2329:  *     is reported.
                   2330:  */
                   2331: 
                   2332: /* ARGSUSED */
                   2333: static void
                   2334: tcpServerAcceptConnection (clientData, mask)
                   2335:      ClientData clientData;
                   2336:      int mask;
                   2337: {
                   2338:   register Tcp_ServerData * server = (Tcp_ServerData *) clientData;
                   2339: 
                   2340:   int status;
                   2341: 
                   2342:   char * clientName;
                   2343: 
                   2344:   /* Accept the connection with `$server accept' */
                   2345: 
                   2346:   status = Tcl_VarEval (server -> interp, server -> name, " accept",
                   2347:                     (char *) NULL);
                   2348: 
                   2349:   /* On success, try to execute the client's command with `$client do' */
                   2350: 
                   2351:   if (status == TCL_OK) {
                   2352:     clientName = (char *) ckalloc (strlen (server -> interp -> result) + 1);
                   2353:     strcpy (clientName, server -> interp -> result);
                   2354:     status = Tcl_VarEval (server -> interp, clientName, " do", (char *) NULL);
                   2355: 
                   2356:     /* Client may have been closed at this point.  Don't refer to it again. */
                   2357:   }
                   2358: 
                   2359:   if (status != TCL_OK) {
                   2360:     simpleReportBackgroundError (server -> interp);
                   2361:   }
                   2362:   Tcl_ResetResult (server -> interp);
                   2363: }
                   2364: 
                   2365: /*
                   2366:  * tcpTrustedHost --
                   2367:  *
                   2368:  *     This procedure is invoked whenever the code must determine whether
                   2369:  * a host is trusted.  A host is considered trusted only if it is the local
                   2370:  * host.
                   2371:  *
                   2372:  * Results:
                   2373:  *     Returns a Boolean value that is TRUE iff the host is trusted.
                   2374:  */
                   2375: 
                   2376: /* The HOSTCMP macro is just strcmp, but puts its args on stderr if
                   2377:  * the DEBUG_TRUSTED_HOST flag is #define'd.  It's used because this
                   2378:  * code can be a little flaky; if `hostname' returns a name that is
                   2379:  * completely unknown in the database, this macro will trace what happened.
                   2380:  */
                   2381: 
                   2382: #ifdef DEBUG_TRUSTED_HOST
                   2383: #define HOSTCMP( name1, name2 )                                        \
                   2384:   (fprintf (stderr, "tcpTrustedHost: comparing %s with %s\n",  \
                   2385:            (name1), (name2)),                                  \
                   2386:    strcmp ((name1), (name2)))
                   2387: #else
                   2388: #define HOSTCMP( name1, name2 )                                        \
                   2389:   strcmp ((name1), (name2))
                   2390: #endif
                   2391: 
                   2392: static int
                   2393: tcpTrustedHost (hostName)
                   2394:      char * hostName;
                   2395: {
                   2396:   char localName [128];
                   2397:   struct hostent * hostEnt;
                   2398:   struct in_addr hostAddr;
                   2399:   int unixStatus;
                   2400:   int i;
                   2401: 
                   2402:   /* This procedure really has to do things the hard way.  The problem is
                   2403:    * that the hostname() kernel call returns the host name set by the system
                   2404:    * administrator, which may not be the host's primary name as known to
                   2405:    * the domain name system.  Furthermore, the host presented may be one
                   2406:    * of the names for the loopback port, 127.0.0.1, and this must be checked,
                   2407:    * too.
                   2408:    */
                   2409: 
                   2410:   /* Start assembling a list of possibilities for the host name.  First
                   2411:    * possibility is the name that the kernel returns as hostname ().
                   2412:    */
                   2413: 
                   2414:   unixStatus = gethostname (localName, 127);
                   2415:   if (unixStatus >= 0) {
                   2416: 
                   2417:     if (!HOSTCMP( hostName, localName )) return 1;
                   2418: 
                   2419:     /* Next possibility is a.b.c.d notation for all of the local addresses,
                   2420:      * plus all the nicknames for the host. 
                   2421:      */
                   2422: 
                   2423:     hostEnt = gethostbyname (localName);
                   2424:     if (hostEnt != (struct hostent *) NULL) {
                   2425:       if (!HOSTCMP( hostName, hostEnt -> h_name )) return 1;
                   2426:       if (hostEnt -> h_aliases != (char * *) NULL) {
                   2427:        for (i = 0; hostEnt -> h_aliases [i] != (char *) NULL; ++i) {
                   2428:          if (!HOSTCMP( hostName, hostEnt -> h_aliases [i] )) return 1;
                   2429:        }
                   2430:       }
                   2431:       if (hostEnt -> h_addr_list != (char * *) NULL) {
                   2432:        for (i = 0; hostEnt -> h_addr_list [i] != (char *) NULL; ++i) {
                   2433:          /* note that the address doesn't have to be word-aligned (!) */
                   2434:          memcpy ((char *) &hostAddr,
                   2435:                  hostEnt -> h_addr_list [i],
                   2436:                  hostEnt -> h_length);
                   2437:          if (!HOSTCMP( hostName, inet_ntoa (hostAddr) )) return 1;
                   2438:        }
                   2439:       }
                   2440:     }
                   2441:   }
                   2442: 
                   2443:   /* Finally, there's the possibility of the loopback address, and all of 
                   2444:    * its aliases.*/
                   2445: 
                   2446:   if (!HOSTCMP( hostName, "0.0.0.0" )) return 1;
                   2447:   if (!HOSTCMP( hostName, "127.0.0.1" )) return 1;
                   2448:   hostAddr.s_addr = htonl (INADDR_LOOPBACK);
                   2449:   hostEnt = gethostbyaddr ((char *) &hostAddr, sizeof hostAddr, AF_INET);
                   2450:   if (hostEnt != (struct hostent *) NULL) {
                   2451:     if (!HOSTCMP( hostName, hostEnt -> h_name )) return 1;
                   2452:     if (hostEnt -> h_aliases != (char * *) NULL) {
                   2453:       for (i = 0; hostEnt -> h_aliases [i] != (char *) NULL; ++i) {
                   2454:        if (!HOSTCMP( hostName, hostEnt -> h_aliases [i] )) return 1;
                   2455:       }
                   2456:     }
                   2457:     if (hostEnt -> h_addr_list != (char * *) NULL) {
                   2458:       for (i = 0; hostEnt -> h_addr_list [i] != (char *) NULL; ++i) {
                   2459:        /* note that the address doesn't have to be word-aligned (!) */
                   2460:        memcpy ((char *) &hostAddr,
                   2461:                hostEnt -> h_addr_list [i],
                   2462:                hostEnt -> h_length);
                   2463:        if (!HOSTCMP( hostName, inet_ntoa (hostAddr) )) return 1;
                   2464:       }
                   2465:     }
                   2466:   }
                   2467: 
                   2468:   return 0;
                   2469: }
                   2470: 
                   2471: /*
                   2472:  * tcpReturnResultToClient --
                   2473:  *
                   2474:  *     This procedure is invoked to return a result to a client.  It
                   2475:  * extracts the interpreter's result string, bundles it with the return
                   2476:  * status, and stores it in the client's `resultString' area.
                   2477:  *
                   2478:  *     It then calls tcpWriteResultToClient to try to start sending the
                   2479:  * result.
                   2480:  */
                   2481: 
                   2482: static void
                   2483: tcpReturnResultToClient (client, interp, status, closeflag)
                   2484:      Tcp_ClientData * client;
                   2485:      Tcl_Interp * interp;
                   2486:      int status;
                   2487:      int closeflag;
                   2488: {
                   2489:   char * argv [2];
                   2490:   char rint [16];
                   2491:   unsigned length;
                   2492:   char * result;
                   2493:   
                   2494:   /* Put together a message comprising the return status and the interpreter
                   2495:    * result */
                   2496: 
                   2497:   sprintf (rint, "%d", status);
                   2498:   argv [0] = rint;
                   2499:   argv [1] = interp -> result;
                   2500:   result = Tcl_Merge (2, argv);
                   2501:   length = strlen (result);
                   2502:   client -> resultString = (char *) malloc (length + 2);
                   2503:   strcpy (client -> resultString, result);
                   2504:   strcpy (client -> resultString + length, "\n");
                   2505:   free (result);
                   2506:   client -> resultPointer = client -> resultString;
                   2507:   client -> freeResultString = (Tcl_FreeProc *) free;
                   2508: 
                   2509:   Tcl_ResetResult (interp);
                   2510:   client -> closeFlag |= closeflag;
                   2511:   
                   2512:   /* Now try to send the reply. */
                   2513: 
                   2514:   tcpWriteResultToClient ((ClientData) client, TK_WRITABLE);
                   2515: 
                   2516:   /* tcpWriteResultToClient closes the client if it fails; don't depend on
                   2517:    * having the client still be usable. */
                   2518: }
                   2519: 
                   2520: /*
                   2521:  * tcpWriteResultToClient --
                   2522:  *
                   2523:  *     This procedure is invoked to issue a write on a client socket.
                   2524:  * It can be called directly by tcpReturnResultToClient, to attempt the
                   2525:  * initial write of results.  It can also be called as a file handler,
                   2526:  * to retry a write that was previously blocked.
                   2527:  */
                   2528: 
                   2529: /* ARGSUSED */
                   2530: static void
                   2531: tcpWriteResultToClient (clientData, mask)
                   2532:      ClientData clientData;
                   2533:      int mask;
                   2534: {
                   2535:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   2536: 
                   2537:   int unixStatus;
                   2538:   int length;
                   2539: 
                   2540:   length = strlen (client -> resultPointer);
                   2541: 
                   2542:   /* Issue the write */
                   2543: 
                   2544:   unixStatus = write (client -> socketfd, client -> resultPointer,
                   2545:                      length);
                   2546:   
                   2547:   /* Test for a total failure */
                   2548: 
                   2549:   if (unixStatus < 0) {
                   2550:     if (errno != EWOULDBLOCK) {
                   2551:       tcpClientWriteError (client);
                   2552:       /* tcpClientWriteError closes the client as a side effect.  Don't depend
                   2553:        * on the client still being there.
                   2554:        */
                   2555:       return;
                   2556:     } else {
                   2557:       unixStatus = 0;          /* Pretend that EWOULDBLOCK succeeded at
                   2558:                                 * writing zero characters. */
                   2559:     }
                   2560:   }
                   2561: 
                   2562:   /* Test for a partial success */
                   2563: 
                   2564:   if (unixStatus < length) {
                   2565:     client -> resultPointer += unixStatus;
                   2566:     simpleCreateFileHandler (client -> socketfd, TK_WRITABLE,
                   2567:                             (Tk_FileProc *) tcpWriteResultToClient,
                   2568:                             clientData);
                   2569:   }
                   2570: 
                   2571:   /* Total success -- prepare the client for the next input */
                   2572: 
                   2573:   else {
                   2574:     if (client -> freeResultString != NULL) {
                   2575:       (*(client -> freeResultString)) (client -> resultString);
                   2576:     }
                   2577:     client -> resultString = client -> resultPointer = (char *) NULL;
                   2578:     client -> freeResultString = (Tcl_FreeProc *) NULL;
                   2579:     simpleDeleteFileHandler (client -> socketfd);
                   2580:     if (client -> closeFlag) {
                   2581:       tcpCloseClient (client);
                   2582: 
                   2583:       /* After tcpCloseClient executes, the client goes away.  Don't depend
                   2584:         on it's still being there. */
                   2585: 
                   2586:     } else {
                   2587:       tcpPrepareClientForInput (client);
                   2588:     }
                   2589:   }
                   2590: }   
                   2591: 
                   2592: /*
                   2593:  * tcpPrepareClientForInput --
                   2594:  *
                   2595:  *     This procedure is invoked to prepare a client to accept command
                   2596:  * input.  It establishes a handler, tcpReceiveClientInput, that does the
                   2597:  * actual command buffering.
                   2598:  */
                   2599: 
                   2600: static void
                   2601: tcpPrepareClientForInput (client)
                   2602:      Tcp_ClientData * client;
                   2603: {
                   2604:   simpleCreateFileHandler (client -> socketfd, TK_READABLE,
                   2605:                           (Tk_FileProc *) tcpReceiveClientInput,
                   2606:                           (ClientData) client);
                   2607: }
                   2608: 
                   2609: /*
                   2610:  * tcpReceiveClientInput --
                   2611:  *
                   2612:  *     This procedure is called when a server is awaiting input from a client
                   2613:  * and the client socket tests to be `ready to read'.  It reads a bufferload
                   2614:  * of data from the client, and places it in the client's command buffer.  If
                   2615:  * the command is complete, it then tries to invoke the command.
                   2616:  */
                   2617: 
                   2618: /* ARGSUSED */
                   2619: static void
                   2620: tcpReceiveClientInput (clientData, mask)
                   2621:      ClientData clientData;
                   2622:      int mask;
                   2623: {
                   2624:   register Tcp_ClientData * client = (Tcp_ClientData *) clientData;
                   2625:   register Tcp_ServerData * server = client -> server;
                   2626:   register Tcl_Interp * interp = server -> interp;
                   2627: 
                   2628:   static char buffer [BUFSIZ+1];
                   2629:   int unixStatus;
                   2630:   char * command;
                   2631:   int status;
                   2632:   char * docmd;
                   2633:   char * argv [3];
                   2634:   int argc;
                   2635:   int i;
                   2636: 
                   2637:   /* Try to read from the client */
                   2638: 
                   2639:   errno = 0;
                   2640:   unixStatus = read (client -> socketfd, buffer, BUFSIZ);
                   2641:   if (unixStatus <= 0 && errno != EWOULDBLOCK)
                   2642:     tcpClientReadError (client);
                   2643: 
                   2644:   /* tcpClientReadError closes the client and reports the error.
                   2645:      In any case, if the read failed, we want to return. */
                   2646: 
                   2647:   if (unixStatus <= 0)
                   2648:     return;
                   2649: 
                   2650:   if (server -> raw) {
                   2651:     char buf[512];
                   2652: 
                   2653:     sprintf(buf, "RawInput %s %d %d", client -> name, buffer, unixStatus);
                   2654: printf("TCP executing: %s\n", buf);
                   2655:     status = Tcl_Eval (interp, buf, 0, (char * *) NULL);
                   2656: 
                   2657:     tcpPrepareClientForInput (client);
                   2658: 
                   2659:   } else {
                   2660: 
                   2661:     /* Assemble the received data into the buffer */
                   2662: 
                   2663:     buffer [unixStatus] = '\0';
                   2664:     command = Tcl_AssembleCmd (client -> inputBuffer, buffer);
                   2665:     if (command != (char *) NULL) {
                   2666: 
                   2667:       /* Process the received command. */
                   2668: 
                   2669:       simpleDeleteFileHandler (client -> socketfd);
                   2670:       argc = 3;
                   2671:       argv [0] = client -> name;
                   2672:       argv [1] = "do";
                   2673:       argv [2] = command;
                   2674:       docmd = Tcl_Merge (argc, argv);
                   2675:       status = Tcl_Eval (interp, docmd, 0, (char * *) NULL);
                   2676:       free (docmd);
                   2677: 
                   2678:       /* At this point, the client may have been closed.  Don't try to
                   2679:         refer to it. */
                   2680: 
                   2681:       if (status != TCL_OK) {
                   2682:        simpleReportBackgroundError (interp);
                   2683:       }
                   2684:     }
                   2685:   }
                   2686: }
                   2687: 
                   2688: /* tcpClientReadError --
                   2689:  *
                   2690:  *     This procedure is called when an attempt to read the command from a
                   2691:  * client fails.  There are two possibilities:
                   2692:  *
                   2693:  *     The first is that there really was a read error, originating in the
                   2694:  * socket system.  In this case, the error should be reported at background
                   2695:  * level, and the client should be closed.
                   2696:  *
                   2697:  *     The second is that the read reached the end-of-information marker in
                   2698:  * the client's stream.  In this case, the `do' command should be called on
                   2699:  * the client one last time, and then the client should be closed.
                   2700:  *
                   2701:  *     If the application needs to clean the client up after a read error,
                   2702:  * it must define the `tcperror' procedure and process the error.
                   2703:  */
                   2704: 
                   2705: static void
                   2706: tcpClientReadError (client)
                   2707:      Tcp_ClientData * client;
                   2708: {
                   2709:   Tcp_ServerData * server = client -> server;
                   2710:   Tcl_Interp * interp = server -> interp;
                   2711:   int status;
                   2712: 
                   2713:   if (errno != 0) {
                   2714: 
                   2715:     /* Read error */
                   2716: 
                   2717:     status = Tcl_VarEval (interp, "error {", client -> name, ": read error: ",
                   2718:                          Tcl_UnixError (interp), "}", (char *) NULL);
                   2719:     simpleReportBackgroundError (interp);
                   2720:     
                   2721:   } else {
                   2722: 
                   2723:     /* End of file */
                   2724: 
                   2725:     status = Tcl_VarEval (interp, client -> name, " do", (char *) NULL);
                   2726:     if (status != TCL_OK)
                   2727:       simpleReportBackgroundError (interp);
                   2728:   }
                   2729: 
                   2730:   tcpCloseClient (client);
                   2731: }
                   2732: 
                   2733: /* tcpClientWriteError --
                   2734:  *
                   2735:  *     This procedure is invoked when an attempt to return results to a client
                   2736:  * has failed.  It reports the error at background level and closes the client.
                   2737:  *
                   2738:  *     If the application needs to clean up the client after a write error,
                   2739:  * it must define the `tcperror' procedure to catch the error.
                   2740:  */
                   2741: 
                   2742: static void
                   2743: tcpClientWriteError (client)
                   2744:      Tcp_ClientData * client;
                   2745: {
                   2746:   Tcp_ServerData * server = client -> server;
                   2747:   Tcl_Interp * interp = server -> interp;
                   2748: 
                   2749:   (void) Tcl_VarEval (interp, "error {", client -> name, ": read error: ",
                   2750:                        Tcl_UnixError (interp), "}", (char *) NULL);
                   2751:   simpleReportBackgroundError (interp);
                   2752:   tcpCloseClient (client);
                   2753: }
                   2754: 
                   2755: /* tcpSendCmdToServer --
                   2756:  *
                   2757:  *     This procedure is invoked to send a command originated by a client
                   2758:  * using the `$connection send' Tcl command.
                   2759:  *
                   2760:  *     The message is passed without a newline appended.  The server requires
                   2761:  * a newline, which is sent in a separate call.
                   2762:  */
                   2763: 
                   2764: static int
                   2765: tcpSendCmdToServer (interp, s, message)
                   2766:      Tcl_Interp * interp;
                   2767:      int s;
                   2768:      char * message;
                   2769: {
                   2770:   int length;
                   2771:   int unixStatus;
                   2772:   int rubbish;
                   2773:   static char newline = '\n';
                   2774:   void (*oldPipeHandler) ();
                   2775: 
                   2776:   /* Set the socket for blocking I/O */
                   2777: 
                   2778:   rubbish = 0;
                   2779:   unixStatus = ioctl (s, FIONBIO, (char *) &rubbish);
                   2780:   if (unixStatus < 0) {
                   2781:     Tcl_AppendResult (interp, "can't set blocking I/O on socket: ",
                   2782:                      Tcl_UnixError (interp), (char *) NULL);
                   2783:     return TCL_ERROR;
                   2784:   }
                   2785: 
                   2786:   /* Keep a possible broken pipe from killing us silently */
                   2787: 
                   2788:   oldPipeHandler = signal (SIGPIPE, SIG_IGN);
                   2789: 
                   2790:   /* Write the message */
                   2791:   
                   2792:   length = strlen (message);
                   2793:   unixStatus = write (s, message, length);
                   2794:   if (unixStatus < length) {
                   2795:     (void) signal (SIGPIPE, oldPipeHandler);
                   2796:     Tcl_AppendResult (interp, "can't send message to server: ",
                   2797:                      Tcl_UnixError (interp), (char *) NULL);
                   2798:     return TCL_ERROR;
                   2799:   }
                   2800: 
                   2801:   /* Write the terminating newline */
                   2802: 
                   2803:   unixStatus = write (s, &newline, 1);
                   2804:   if (unixStatus < 1) {
                   2805:     (void) signal (SIGPIPE, oldPipeHandler);
                   2806:     Tcl_AppendResult (interp, "can't send newline to server: ",
                   2807:                      Tcl_UnixError (interp), (char *) NULL);
                   2808:     return TCL_ERROR;
                   2809:   }
                   2810: 
                   2811:   (void) signal (SIGPIPE, oldPipeHandler);
                   2812:   return TCL_OK;
                   2813: }
                   2814: 
                   2815: /*
                   2816:  * tcpReceiveResultFromServer --
                   2817:  *
                   2818:  *     This procedure is invoked to get the result transmitted from
                   2819:  * a remote server, either on establishing the connection or on processing
                   2820:  * a command.  It returns a standard Tcl result that is usually the result
                   2821:  * returned by the server.
                   2822:  */
                   2823: 
                   2824: static int
                   2825: tcpReceiveResultFromServer (interp, s)
                   2826:      Tcl_Interp * interp;
                   2827:      int s;
                   2828: {
                   2829:   int status;
                   2830:   int unixStatus;
                   2831:   int junk;
                   2832:   Tcl_CmdBuf cmdbuf;
                   2833:   struct timeval tick;
                   2834:   struct timeval * tickp;
                   2835:   fd_set readfds;
                   2836:   char buf [BUFSIZ+1];
                   2837:   char * reply;
                   2838:   int rargc;
                   2839:   char * * rargv;
                   2840:   int rstatus;
                   2841: 
                   2842:   /* Read the result using non-blocking I/O */
                   2843: 
                   2844:   junk = 1;
                   2845:   unixStatus = ioctl (s, FIONBIO, (char *) &junk);
                   2846:   if (unixStatus < 0) {
                   2847:     Tcl_AppendResult (interp, "can't set nonblocking I/O on socket: ",
                   2848:                      Tcl_UnixError (interp), (char *) NULL);
                   2849:     return TCL_ERROR;
                   2850:   }
                   2851: 
                   2852:   /* Make a buffer to receive the result */
                   2853: 
                   2854:   cmdbuf = Tcl_CreateCmdBuf ();
                   2855: 
                   2856:   /* Wait for the result to appear */
                   2857: 
                   2858:   tickp = (struct timeval *) 0;
                   2859:   FD_ZERO( &readfds );
                   2860:   FD_SET( s, &readfds );
                   2861:   for ( ; ; ) {
                   2862: 
                   2863:     unixStatus = select (s + 1, &readfds, (fd_set *) NULL, (fd_set *) NULL,
                   2864:                         tickp);
                   2865: 
                   2866:     if (unixStatus < 0) {
                   2867:       status = TCL_ERROR;
                   2868:       Tcl_AppendResult (interp, "error selecting socket for reply: ",
                   2869:                        Tcl_UnixError (interp), (char *) NULL);
                   2870:       break;
                   2871:     }
                   2872: 
                   2873:     if (unixStatus == 0) {
                   2874:       status = TCL_ERROR;
                   2875:       Tcl_SetResult (interp, "timed out waiting for server reply", TCL_STATIC);
                   2876:       break;
                   2877:     }
                   2878: 
                   2879:     /* Read the result */
                   2880: 
                   2881:     unixStatus = read (s, buf, BUFSIZ);
                   2882: 
                   2883:     if (unixStatus < 0) {
                   2884:       status = TCL_ERROR;
                   2885:       Tcl_AppendResult (interp, "error reading server reply: ",
                   2886:                        Tcl_UnixError (interp), (char *) NULL);
                   2887:       break;
                   2888:     }
                   2889: 
                   2890:     if (unixStatus == 0) {
                   2891:       status = TCL_ERROR;
                   2892:       Tcl_SetResult (interp, "Connection closed.", TCL_STATIC);
                   2893:       break;
                   2894:     }
                   2895: 
                   2896:     /* Parse the (partial) command */
                   2897:     
                   2898:     buf [unixStatus] = '\0';
                   2899:     reply = Tcl_AssembleCmd (cmdbuf, buf);
                   2900:     if (reply != NULL) {
                   2901:       status = TCL_OK;
                   2902:       break;
                   2903:     }
                   2904: 
                   2905:     /* Partial command not yet complete.  Set timeout for reading the
                   2906:      * rest of the result. */
                   2907: 
                   2908:     tick.tv_sec = 30;
                   2909:     tick.tv_usec = 0;
                   2910:     tickp = &tick;
                   2911:   }
                   2912: 
                   2913:   /* When we come here, either the status is TCL_ERROR and the error
                   2914:    * message is already set, or else the status is TCL_OK and `reply'
                   2915:    * contains the result that we have to return.  The first element of
                   2916:    * `reply' has the status, and the second has the result string. */
                   2917: 
                   2918:   /* Split the list elements */
                   2919: 
                   2920:   if (status == TCL_OK) {
                   2921:     status = Tcl_SplitList (interp, reply, &rargc, &rargv);
                   2922:     if (status != TCL_OK) {
                   2923:       Tcl_SetResult (interp, "server returned malformed list", TCL_STATIC);
                   2924:       status = TCL_ERROR;
                   2925:     }
                   2926:   }
                   2927: 
                   2928:   /* Verify the element count */
                   2929: 
                   2930:   if (status == TCL_OK) {
                   2931:     if (rargc != 2) {
                   2932:       Tcl_SetResult (interp, "server returned malformed list", TCL_STATIC);
                   2933:       status = TCL_ERROR;
                   2934:       free ((char *) rargv);
                   2935:     } else {
                   2936:       status = Tcl_GetInt (interp, rargv [0], &rstatus);
                   2937:       if (status != TCL_OK) {
                   2938:        Tcl_SetResult (interp, "server returned unrecognizable status",
                   2939:                       TCL_STATIC);
                   2940:        status = TCL_ERROR;
                   2941:        free ((char *) rargv);
                   2942:       }
                   2943:     }
                   2944:   }
                   2945: 
                   2946:   /* Return the result reported by the server */
                   2947: 
                   2948:   if (status == TCL_OK) {
                   2949:     Tcl_SetResult (interp, rargv [1], TCL_VOLATILE);
                   2950:     status = rstatus;
                   2951:     free ((char *) rargv);
                   2952:   }
                   2953: 
                   2954:   Tcl_DeleteCmdBuf (cmdbuf);
                   2955:   return status;
                   2956: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.