|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.