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
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 $
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.
21 * Revision 1.6 1992/03/04 20:04:00 kennykb
22 * Modified source code to use the Tcl configurator and corresponding include
25 * Revision 1.5 1992/02/25 15:21:30 kennykb
26 * Modifications to quiet warnings from gcc
29 * Revision 1.4 1992/02/24 19:30:30 kennykb
30 * Merged branches (a) updated tcpTrustedHost and (b) broken-out event mgr.
32 * Revision 1.3 1992/02/20 16:22:53 kennykb
33 * Event management code removed and broken out into a separate file,
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
41 * Revision 1.2 1992/02/18 14:43:21 kennykb
42 * Fix for bug 920218.1 in `History' file.
44 * Revision 1.1 1992/02/14 19:57:51 kennykb
49 static char copyright
[] =
50 "Copyright (C) 1992 General Electric. All rights reserved." ;
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.
65 * This work was supported by the DARPA Initiative in Concurrent
66 * Engineering (DICE) through DARPA Contract MDA972-88-C-0047.
70 #include <sys/types.h>
71 #include <sys/ioctl.h>
72 #include <sys/socket.h>
73 #include <netinet/in.h>
75 #include <arpa/inet.h>
77 /* Only some copies of netinet/in.h have the following defined. */
79 #ifndef INADDR_LOOPBACK
81 #define INADDR_LOOPBACK 0x7f000001UL
83 #define INADDR_LOOPBACK (unsigned long) 0x7f000001L
85 #endif /* INADDR_LOOPBACK */
96 /* There doesn't seem to be any place to get these....
97 * certainly not a portable one.
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
*,
111 extern int socket
_ANSI_ARGS_((int, int, int));
112 extern int getdtablesize
_ANSI_ARGS_((void));
115 /* Configuration parameters */
118 * TCP_LISTEN_BACKLOG gives the maximum backlog of connection requests
119 * that may be queued for any server
122 #define TCP_LISTEN_BACKLOG 3
124 /* Internal data structures */
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.
133 typedef struct tcp_ServerData
{
134 Tcl_Interp
* interp
; /* Interpreter in which connections */
137 /* Name of the server object. */
139 /* Filedescriptor of the socket at */
140 /* which the server listens for connections */
142 /* Command to be executed (using */
143 /* Tcl_Eval) when a connection request */
145 Tcl_FreeProc
* freeCommand
;
146 /* Procedure to free the command when */
147 /* it's no longer needed. */
149 /* Flag == TRUE if the server is trying */
151 int raw
; /* Flag == TRUE if for raw socket mode. */
152 struct tcp_ClientData
* firstClient
;
153 /* First in the list of clients at this */
155 struct tcp_ServerData
* next
, * prev
;
156 /* Linkage in the list of all active servers */
160 * Each client of a server will have a record of the following type.
163 typedef struct tcp_ClientData
{
164 struct tcp_ServerData
* server
;
165 /* Server to which the client belongs */
167 /* Name of the client */
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. */
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 */
183 /* Result of executing a command on the */
185 char * resultPointer
;
186 /* Pointer to the portion of resultString */
187 /* that remains to be transmitted back */
189 Tcl_FreeProc
* freeResultString
;
190 /* Procedure to free the result string when */
191 /* it's no longer needed. */
193 /* Flag == 1 iff a command is pending on */
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 */
203 /* Static variables in this file */
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. */
212 /* Declarations for static functions within this file. */
214 /* Static procedures in this file */
216 static void simpleDeleteFileHandler1
_ANSI_ARGS_((ClientData
, int));
218 static void simpleDeleteFileHandler2
_ANSI_ARGS_((ClientData
));
221 tcpClientCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
222 int argc
, char * * argv
));
225 tcpConnectCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
226 int argc
, char * * argv
));
229 tcpEvalCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
230 int argc
, char * * argv
));
233 tcpLoginCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
234 int argc
, char * * argv
));
237 tcpMainLoopCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
238 int argc
, char * * argv
));
241 tcpPollCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
242 int argc
, char * * argv
));
245 tcpServerCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
246 int argc
, char * * argv
));
249 tcpServersCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
250 int argc
, char * * argv
));
252 tcpWaitCommand
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
253 int argc
, char * * argv
));
256 tcpServerObjectCmd
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
257 int argc
, char * * argv
));
259 deleteTcpServerObjectCmd
_ANSI_ARGS_((ClientData clientData
));
262 tcpServerObjectAcceptCmd
_ANSI_ARGS_((ClientData clientData
,
263 Tcl_Interp
* interp
, int argc
,
267 tcpServerObjectClientsCmd
_ANSI_ARGS_((ClientData clientData
,
268 Tcl_Interp
* interp
, int argc
,
272 tcpServerObjectConfigCmd
_ANSI_ARGS_((ClientData clientData
,
273 Tcl_Interp
* interp
, int argc
,
277 tcpServerObjectStartCmd
_ANSI_ARGS_((ClientData clientData
,
278 Tcl_Interp
* interp
, int argc
,
282 tcpServerObjectStopCmd
_ANSI_ARGS_((ClientData clientData
,
283 Tcl_Interp
* interp
, int argc
,
287 tcpDeleteServer
_ANSI_ARGS_((Tcp_ServerData
* server
));
290 tcpServerObjectConfig
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
291 int argc
, char * * argv
));
294 tcpClientObjectCmd
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
295 int argc
, char * * argv
));
298 tcpClientObjectCloseCmd
_ANSI_ARGS_((ClientData clientData
,
300 int argc
, char * * argv
));
303 tcpClientObjectCommandCmd
_ANSI_ARGS_((ClientData clientData
,
305 int argc
, char * * argv
));
308 tcpClientObjectDoCmd
_ANSI_ARGS_((ClientData clientData
, Tcl_Interp
* interp
,
309 int argc
, char * * argv
));
312 tcpClientObjectHostnameCmd
_ANSI_ARGS_((ClientData clientData
,
314 int argc
, char * * argv
));
317 tcpClientObjectServerCmd
_ANSI_ARGS_((ClientData clientData
,
319 int argc
, char * * argv
));
322 deleteTcpClientObjectCmd
_ANSI_ARGS_((ClientData clientData
));
325 tcpConnectionObjectCmd
_ANSI_ARGS_((ClientData clientData
,
327 int argc
, char * * argv
));
330 tcpConnectionObjectCloseCmd
_ANSI_ARGS_((ClientData clientData
,
332 int argc
, char * * argv
));
335 tcpConnectionObjectSendCmd
_ANSI_ARGS_((ClientData clientData
,
337 int argc
, char * * argv
));
340 deleteTcpConnectionObjectCmd
_ANSI_ARGS_((ClientData clientData
));
343 tcpServerAcceptConnection
_ANSI_ARGS_((ClientData clientData
, int mask
));
346 tcpReturnResultToClient
_ANSI_ARGS_((Tcp_ClientData
* client
,
348 int status
, int closeflag
));
351 tcpWriteResultToClient
_ANSI_ARGS_((ClientData clientData
, int mask
));
354 tcpClientReadError
_ANSI_ARGS_((Tcp_ClientData
* client
));
357 tcpClientWriteError
_ANSI_ARGS_((Tcp_ClientData
* client
));
360 tcpPrepareClientForInput
_ANSI_ARGS_((Tcp_ClientData
* client
));
363 tcpReceiveClientInput
_ANSI_ARGS_((ClientData clientData
, int mask
));
366 tcpCloseClient
_ANSI_ARGS_((Tcp_ClientData
* client
));
369 tcpTrustedHost
_ANSI_ARGS_((char * hostname
));
372 tcpSendCmdToServer
_ANSI_ARGS_((Tcl_Interp
* interp
, int s
, char * message
));
375 tcpReceiveResultFromServer
_ANSI_ARGS_((Tcl_Interp
* interp
, int s
));
378 * simpleReportBackgroundError --
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.
383 * It tries to run `bgerror' giving it the error message. If this
384 * fails, it reports the problem on stderr.
388 simpleReportBackgroundError (interp
)
395 char *errorInfo
, *tmp
;
399 /* Get the error message out of the interpreter. */
401 error
= (char *) ckalloc (strlen (interp
-> result
) + 1);
402 strcpy (error
, interp
-> result
);
404 /* Get errorInfo, too */
406 tmp
= Tcl_GetVar (interp
, "errorInfo", TCL_GLOBAL_ONLY
);
410 errorInfo
= (char *) ckalloc (strlen (tmp
) + 1);
411 strcpy (errorInfo
, tmp
);
414 /* Build a `bgerror' command to report the error */
418 command
= Tcl_Merge (2, argv
);
420 /* Try to run the command */
422 status
= Tcl_Eval (interp
, command
, 0, (char **) NULL
);
424 if (status
!= TCL_OK
) {
426 /* Command failed. Report the problem to stderr. */
428 tmp
= Tcl_GetVar (interp
, "errorInfo", TCL_GLOBAL_ONLY
);
430 tmp
= interp
-> result
;
432 unixStatus
= fprintf (stderr
, "\n\
433 ------------------------------------------------------------------------\n\
434 Tcl interpreter detected a background error.\n\
438 User \"bgerror\" procedure failed to handle the background error.\n\
442 if (unixStatus
< 0) {
447 Tcl_ResetResult (interp
);
453 if (errorInfo
!= error
) {
459 * simpleCreateFileHandler --
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.
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.
474 simpleCreateFileHandler (fd
, mask
, proc
, clientData
)
478 ClientData clientData
;
480 Tk_CreateFileHandler (fd
, mask
, (Tk_FileProc
*) proc
, clientData
);
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
487 Tk_CancelIdleCall ((Tk_IdleProc
*) simpleDeleteFileHandler2
,
492 * simpleDeleteFileHandler --
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.
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.
507 simpleDeleteFileHandler (int fd
)
509 /* First of all, we have to zero the file's mask to avoid calling the same
510 handler over again if the file is still ready. */
511 Tk_CreateFileHandler (fd
, 0, (Tk_FileProc
*) simpleDeleteFileHandler1
,
513 Tk_DoWhenIdle ((Tk_IdleProc
*) simpleDeleteFileHandler2
,
520 simpleDeleteFileHandler1 (clientData
, mask
)
521 ClientData clientData
;
524 (void) fprintf (stderr
, "in simpleDeleteFileHandler1: bug in tkEvent.c");
529 simpleDeleteFileHandler2 (clientData
)
530 ClientData clientData
;
532 int fd
= (int) clientData
;
534 Tk_DeleteFileHandler (fd
);
538 *----------------------------------------------------------------------
541 * This procedure implements a `tcp' command for Tcl. It provides the
542 * top-level actions for TCP/IP connections.
544 * This command is divided into variants, each with its own procedure:
547 * Returns the current active client, or an error if there is
549 * tcp connect host port
550 * Establish a connection to a server running at `port' on
552 * tcp eval client command
553 * Do default command processing for command "$command",
554 * originating at client "$client".
556 * Do default login processing for $client.
558 * Start the main loop for a server or group of servers.
560 * Poll for whether servers have work to do.
562 * Returns a list of the currently active servers.
564 * Set up a server to run in the current interpreter.
566 * Wait for a server to have work to do.
567 *----------------------------------------------------------------------
571 Tk_TcpCmd (clientData
, interp
, argc
, argv
)
572 ClientData clientData
;
581 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[0],
582 " command ?args?\"", (char *) NULL
);
587 length
= strlen (argv
[1]);
589 if ((c
== 'c') && (length
>= 2) &&
590 (strncmp (argv
[1], "client", length
) == 0)) {
591 return tcpClientCommand (clientData
, interp
, argc
-1, argv
+1);
593 if ((c
== 'c') && (length
>= 2) &&
594 (strncmp (argv
[1], "connect", length
) == 0)) {
595 return tcpConnectCommand (clientData
, interp
, argc
-1, argv
+1);
597 if ((c
== 'e') && (strncmp (argv
[1], "eval", length
) == 0)) {
598 return tcpEvalCommand (clientData
, interp
, argc
-1, argv
+1);
600 if ((c
== 'l') && (strncmp (argv
[1], "login", length
) == 0)) {
601 return tcpLoginCommand (clientData
, interp
, argc
-1, argv
+1);
603 if ((c
== 'm') && (strncmp (argv
[1], "mainloop", length
) == 0)) {
604 return tcpMainLoopCommand (clientData
, interp
, argc
-1, argv
+1);
606 if ((c
== 'p') && (strncmp (argv
[1], "poll", length
) == 0)) {
607 return tcpPollCommand (clientData
, interp
, argc
-1, argv
+1);
609 if ((c
== 's') && (length
>= 7)
610 && (strncmp (argv
[1], "servers", length
) == 0)) {
611 return tcpServersCommand (clientData
, interp
, argc
-1, argv
+1);
613 if ((c
== 's') && (strncmp (argv
[1], "server", length
) == 0)) {
614 return tcpServerCommand (clientData
, interp
, argc
-1, argv
+1);
616 if ((c
== 'w') && (strncmp (argv
[1], "wait", length
) == 0)) {
617 return tcpWaitCommand (clientData
, interp
, argc
-1, argv
+1);
619 Tcl_AppendResult (interp
, "bad option \"", argv
[1],
620 "\": should be client, eval, login,",
621 " mainloop, poll, servers, server or wait",
628 * tcpClientCommand --
630 * This procedure is invoked to process the "tcp client" Tcl command.
631 * It returns the name of the currently-active client, or an error if there
637 tcpClientCommand (clientData
, interp
, argc
, argv
)
638 ClientData clientData
;
646 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
647 argv
[0], "\"", (char *) NULL
);
651 /* Make sure there is a current client */
653 if (tcpCurrentClient
== NULL
) {
654 Tcl_SetResult (interp
, "no current client", TCL_STATIC
);
658 Tcl_SetResult (interp
, tcpCurrentClient
, TCL_VOLATILE
);
662 /* tcpConnectCommand --
664 * This procedure is invoked to process the "tcp connect" Tcl command.
665 * It takes two arguments: a host name and a port. It tries to establish a
666 * connection to the specified port and host.
671 tcpConnectCommand (clientData
, interp
, argc
, argv
)
672 ClientData clientData
;
677 struct hostent
* host
;
678 struct sockaddr_in sockaddr
;
688 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
689 argv
[0], " hostname port#\"", (char *) NULL
);
693 /* Decode the host name */
695 sockaddr
.sin_family
= AF_INET
;
696 host
= gethostbyname (argv
[1]);
698 memcpy ((char *) &(sockaddr
.sin_addr
.s_addr
),
699 (char *) (host
-> h_addr_list
[0]),
700 (size_t) (host
-> h_length
));
702 haddr
= inet_addr (argv
[1]);
704 Tcl_AppendResult (interp
, argv
[1], ": host unknown", (char *) NULL
);
707 sockaddr
.sin_addr
.s_addr
= haddr
;
710 /* Decode the port number */
712 status
= Tcl_GetInt (interp
, argv
[2], &port
);
713 if (status
) return status
;
714 sockaddr
.sin_port
= htons (port
);
716 /* Make a socket to talk to the server */
718 f
= socket (AF_INET
, SOCK_STREAM
, 0);
720 Tcl_AppendResult (interp
, "can't create socket: ",
721 Tcl_UnixError (interp
), (char *) NULL
);
725 /* Connect to the server */
727 status
= connect (f
, (struct sockaddr
*) &sockaddr
, sizeof sockaddr
);
729 Tcl_AppendResult (interp
, "can't connect to server: ",
730 Tcl_UnixError (interp
), (char *) NULL
);
735 /* Get the server's greeting message */
737 status
= tcpReceiveResultFromServer (interp
, f
);
739 if (status
== TCL_OK
) {
741 /* Stash the greeting, make the connection object and return it. */
743 sprintf (name
, "tcp_connection_%d", f
);
744 (void) Tcl_SetVar2 (interp
, "tcp_greeting", name
, interp
-> result
,
746 Tcl_CreateCommand (interp
, name
, (Tcl_CmdProc
*) tcpConnectionObjectCmd
,
748 (Tcl_CmdDeleteProc
*) deleteTcpConnectionObjectCmd
);
749 Tcl_SetResult (interp
, name
, TCL_VOLATILE
);
753 /* Error reading greeting, quit */
763 * This procedure is invoked to process the "tcp eval" Tcl command.
764 * "tcp eval" is the default command invoked to process connections once
765 * a connection has been accepted by "tcp login".
770 tcpEvalCommand (clientData
, interp
, argc
, argv
)
771 ClientData clientData
;
778 /* Argc == 2 means that we're logging out a client. Default is to ignore
786 /* Three-argument form is a command from a client. Default is to eval
790 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
791 " ", argv
[0], " client command\"", (char *) NULL
);
795 status
= Tcl_Eval (interp
, argv
[2], 0, (char * *) NULL
);
802 * This procedure is invoked to process the "tcp login" Tcl command.
803 * It is the default command procedure at initial connection to a server.
804 * It is invoked with the name of a client. It returns TCL_OK, together
805 * with a greeting message, if the login succeeds, and TCL_ERROR, together
806 * with a denial message, if it fails.
808 * The authentication procedure is as follows:
810 * - If the client is on the local host, the connection is accepted.
811 * - If the client's IP address is the same as the local host's IP address,
812 * the connection is accepted.
813 * - Otherwise, the connection is refused.
815 * Obviously, there are other authentication techniques. The use can
816 * replace this command with an arbitrary Tcl script.
821 tcpLoginCommand (clientData
, interp
, argc
, argv
)
822 ClientData clientData
;
827 char * hostName
; /* Name of the client's host */
830 /* Check command syntax */
833 Tcl_AppendResult (interp
, "wrong # args; should be \"", argv
[-1], " ",
834 argv
[0], " clientName\"", (char *) NULL
);
838 /* Get the hostname by doing $client hostname */
840 status
= Tcl_VarEval (interp
, argv
[1], " hostname", (char *) NULL
);
841 if (status
== TCL_OK
) {
842 hostName
= (char *) ckalloc (strlen (interp
-> result
) + 1);
843 strcpy (hostName
, interp
-> result
);
845 /* Check that the host is trusted */
847 if (tcpTrustedHost (hostName
)) {
849 /* Change the command to `tcp eval' for next time */
851 status
= Tcl_VarEval (interp
, argv
[1], " command {tcp eval}",
855 if (status
== TCL_OK
) {
857 /* Return a greeting message */
859 Tcl_ResetResult (interp
);
860 Tcl_AppendResult (interp
, "GE DICE TCP-based Tcl server\n", RCSid
,
861 "\n", copyright
, (char *) NULL
);
869 ckfree ((char *) hostName
);
872 /* Host isn't trusted or one of the commands failed. */
874 Tcl_SetResult (interp
, "Permission denied", TCL_STATIC
);
879 * tcpMainLoopCommand:
881 * This procedure is invoked in a non-Tk environment when the server
882 * implementor wishes to use a main loop built into the library. It
883 * repeatedly polls ofr work to be done, returning only when the last server
886 * In a Tk environment, the procedure returns immediately.
891 tcpMainLoopCommand (clientData
, interp
, argc
, argv
)
892 ClientData clientData
;
901 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
902 " ", argv
[0], "\"", (char *) NULL
);
906 errno
= 0; status
= -1;
908 /* In a non-Tk environment, errno has a Unix error or 0 for no clients
909 * or servers. In a Tk environment, errno is zero at this point.
913 Tcl_AppendResult (interp
, "select: ", Tcl_UnixError (interp
),
925 * This procedure is invoked to process the "tcp poll" Tcl
926 * command. It requests that pending events for the servers be processed.
927 * It returns a count of events that were processed successfully.
929 * In a Tk environment, the procedure reports that no servers are known
930 * to the event handler. This is correct -- servers register with Tk, not
931 * with the simple event handler.
936 tcpPollCommand (clientData
, interp
, argc
, argv
)
937 ClientData clientData
;
945 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
946 " ", argv
[0], "\"", (char *) NULL
);
952 errno
= 0; status
= -1;
954 /* Check for trouble */
958 Tcl_SetResult (interp
, "no servers known to event handler", TCL_STATIC
);
960 Tcl_AppendResult (interp
, "select: ", Tcl_UnixError (interp
),
966 /* Return the number of events processed. */
968 sprintf (interp
-> result
, "%d", status
);
974 * This procedure is invoked to process the "tcp server" Tcl
975 * command. It requests that a server be created to listen at a
976 * TCP/IP port, whose number may be assigned by the system or
977 * specified by the user with the "-port" option.
979 * A command string is supplied for use when the server begins to
980 * accept connections. See the documentation of tcpServerObjectCmd
981 * for a description of the command string.
983 * If the server is created successfully, the return value will
984 * be the name of a "server object" that can be used for future
985 * actions upon the server. This object will be usable as a Tcl
986 * command; the command is processed by the tcpServerObjectCmd function.
989 * tcp server ?-port #? ?-command string?
992 * A standard Tcl result. Return value is the name of the server
993 * object, which may be invoked as a Tcl command (see
994 * tcpServerObjectCmd for details).
999 tcpServerCommand (clientData
, interp
, argc
, argv
)
1000 ClientData clientData
;
1001 Tcl_Interp
* interp
;
1011 /* Create a structure to hold the tcp server's description. */
1013 Tcp_ServerData
* server
=
1014 (Tcp_ServerData
*) ckalloc (sizeof (Tcp_ServerData
));
1016 /* Set up the interpreter and the default command. Clear the list of
1019 server
-> interp
= interp
;
1020 server
-> command
= "tcp login";
1021 server
-> freeCommand
= TCL_STATIC
;
1022 server
-> stopFlag
= 0;
1024 server
-> firstClient
= (Tcp_ClientData
*) NULL
;
1026 /* Create the socket at which the server will listen. */
1028 server
-> socketfd
= socket (AF_INET
, SOCK_STREAM
, 0);
1029 if (server
-> socketfd
< 0) {
1030 Tcl_AppendResult (interp
, "can't create socket: ",
1031 Tcl_UnixError (interp
), (char *) NULL
);
1034 /* Set up the socket for non-blocking I/O. */
1037 unixStatus
= ioctl (server
-> socketfd
, FIONBIO
, (char *) &one
);
1038 if (unixStatus
< 0) {
1039 Tcl_AppendResult (interp
, "can't set non-blocking I/O on socket: ",
1040 Tcl_UnixError (interp
), (char *) NULL
);
1043 /* Server structure has been created and socket has been opened.
1044 * Now configure the server.
1047 if (tcpServerObjectConfig ((ClientData
) server
, interp
, argc
, argv
)
1051 /* Link the server on the list of active servers */
1054 tcpFirstServer
-> prev
= server
;
1055 server
-> next
= tcpFirstServer
;
1056 tcpFirstServer
= server
;
1057 server
-> prev
= NULL
;
1059 /* Add the server object command */
1061 sprintf (server
-> name
, "tcp_server_%d", server
-> socketfd
);
1063 Tcl_CreateCommand (interp
, server
-> name
,
1064 (Tcl_CmdProc
*) tcpServerObjectCmd
,
1065 (ClientData
) server
,
1066 (Tcl_CmdDeleteProc
*) deleteTcpServerObjectCmd
);
1068 Tcl_SetResult (interp
, server
-> name
, TCL_STATIC
);
1075 /* Error in configuring the server. Trash the socket. */
1077 unixStatus
= close (server
-> socketfd
);
1078 if (unixStatus
< 0) {
1080 nargv
[0] = "(also failed to close socket: ";
1081 nargv
[1] = Tcl_UnixError (interp
);
1083 message
= Tcl_Concat (nargc
, nargv
);
1084 Tcl_AddErrorInfo (interp
, message
);
1089 /* Error in creating the server -- get rid of the data structure */
1091 if (server
-> freeCommand
!= NULL
) {
1092 (*(server
-> freeCommand
)) (server
-> command
);
1094 ckfree ((char *) server
);
1099 * tcpServersCommand:
1101 * The following procedure is invoked to process the `tcp servers' Tcl
1102 * command. It returns a list of the servers that are currently known.
1107 tcpServersCommand (clientData
, interp
, argc
, argv
)
1108 ClientData clientData
;
1109 Tcl_Interp
* interp
;
1113 Tcp_ServerData
* server
;
1118 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
1119 argv
[0], "\"", (char *) NULL
);
1123 for (server
= tcpFirstServer
; server
!= NULL
; server
= server
-> next
) {
1124 Tcl_AppendElement (interp
, server
-> name
, 0);
1133 * This procedure is invoked to process the "tcp wait" Tcl
1134 * command. It requests that the process delay until an event is
1135 * pending for a TCP server.
1137 * It returns a count of pending events.
1139 * In a Tk environment, the procedure returns an error message stating
1140 * that no servers are known to the event handler. This is correct. The
1141 * servers register with Tk's event handler, and are not known to the simple
1147 tcpWaitCommand (clientData
, interp
, argc
, argv
)
1148 ClientData clientData
;
1149 Tcl_Interp
* interp
;
1156 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
1157 " ", argv
[0], "\"", (char *) NULL
);
1163 errno
= 0; status
= -1;
1165 /* Check for trouble */
1169 Tcl_SetResult (interp
, "no servers known to event handler", TCL_STATIC
);
1171 Tcl_AppendResult (interp
, "select: ", Tcl_UnixError (interp
),
1177 /* Return the number of events pending. */
1179 sprintf (interp
-> result
, "%d", status
);
1184 * tcpServerObjectCmd --
1186 * This procedure is invoked when a command is called on a server
1187 * object directly. It dispatches to the appropriate command processing
1188 * procedure to handle the command.
1191 * [Internal call] - Accept a connection.
1193 * Return a list of all clients connected to a server.
1194 * $server configure ?args?
1195 * Revise or query a server's configuration.
1197 * Start a server running.
1199 * Terminate a server.
1203 tcpServerObjectCmd (clientData
, interp
, argc
, argv
)
1204 ClientData clientData
;
1205 Tcl_Interp
* interp
;
1213 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[0],
1214 " command ?args?\"", (char *) NULL
);
1219 length
= strlen (argv
[1]);
1221 if (c
== 'a' && strncmp (argv
[1], "accept", length
) == 0) {
1222 return tcpServerObjectAcceptCmd (clientData
, interp
, argc
-1, argv
+1);
1224 if (c
== 'c' && length
>= 2 && strncmp (argv
[1], "clients", length
) == 0) {
1225 return tcpServerObjectClientsCmd (clientData
, interp
, argc
-1, argv
+1);
1227 if (c
== 'c' && length
>= 2
1228 && strncmp (argv
[1], "configure", length
) == 0) {
1229 return tcpServerObjectConfigCmd (clientData
, interp
, argc
-1, argv
+1);
1231 if (c
== 's' && length
>= 3 && strncmp (argv
[1], "start", length
) == 0) {
1232 return tcpServerObjectStartCmd (clientData
, interp
, argc
-1, argv
+1);
1234 if (c
== 's' && length
>= 3 && strncmp (argv
[1], "stop", length
) == 0) {
1235 return tcpServerObjectStopCmd (clientData
, interp
, argc
-1, argv
+1);
1237 Tcl_AppendResult (interp
, argv
[0], ": ", "bad option \"", argv
[1],
1238 "\": should be clients, configure, start, or stop",
1244 * tcpServerObjectAcceptCmd --
1246 * The following procedure handles the `accept' command on a
1247 * server object. It is called in the background by
1248 * tcpServerAcceptConnection when a connection request appears on
1249 * a server. It is responsible for creating the client and
1250 * accepting the connection request.
1253 * Returns a standard TCL result. The return value is the name
1254 * of the client if the call is successful.
1257 * A Tcl command named after the client object is created.
1261 tcpServerObjectAcceptCmd (clientData
, interp
, argc
, argv
)
1262 ClientData clientData
;
1263 Tcl_Interp
* interp
;
1267 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1268 register Tcp_ClientData
* client
;
1275 /* Check command syntax */
1278 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
1279 argv
[0], "\"", (char *) NULL
);
1283 /* Create the client data structure */
1285 client
= (Tcp_ClientData
*) ckalloc (sizeof (Tcp_ClientData
));
1287 /* Accept the client's connection request */
1289 rubbish
= sizeof (struct sockaddr_in
);
1290 client
-> socketfd
= accept (server
-> socketfd
,
1291 (struct sockaddr
*) &(client
-> peeraddr
),
1293 if (client
-> socketfd
< 0) {
1294 Tcl_AppendResult (interp
, "can't accept connection: ",
1295 Tcl_UnixError (interp
), (char *) NULL
);
1296 ckfree ((char *) client
);
1300 /* Set up the socket for non-blocking I/O */
1303 unixStatus
= ioctl (client
-> socketfd
, FIONBIO
, (char *) &rubbish
);
1304 if (unixStatus
< 0) {
1305 Tcl_AppendResult (interp
,
1306 "can't set non-blocking I/O on client's socket: ",
1307 Tcl_UnixError (interp
), (char *) NULL
);
1308 unixStatus
= close (client
-> socketfd
);
1309 if (unixStatus
< 0) {
1311 nargv
[0] = "(also failed to close socket: ";
1312 nargv
[1] = Tcl_UnixError (interp
);
1314 message
= Tcl_Concat (nargc
, nargv
);
1315 Tcl_AddErrorInfo (interp
, message
);
1318 ckfree ((char *) client
);
1322 /* Set up the client's description */
1324 client
-> server
= server
;
1325 sprintf (client
-> name
, "tcp_client_%d", client
-> socketfd
);
1326 client
-> command
= malloc (strlen (server
-> command
) + 1);
1327 client
-> freeCommand
= (Tcl_FreeProc
*) free
;
1328 strcpy (client
-> command
, server
-> command
);
1329 client
-> inputBuffer
= Tcl_CreateCmdBuf ();
1330 client
-> resultString
= client
-> resultPointer
= (char *) NULL
;
1331 client
-> freeResultString
= (Tcl_FreeProc
*) NULL
;
1332 client
-> activeFlag
= 0;
1333 client
-> closeFlag
= 0;
1334 client
-> next
= server
-> firstClient
;
1335 if (client
-> next
!= NULL
) {
1336 client
-> next
-> prev
= client
;
1338 client
-> prev
= NULL
;
1339 server
-> firstClient
= client
;
1341 /* Create the Tcl command for the client */
1343 Tcl_CreateCommand (interp
, client
-> name
,
1344 (Tcl_CmdProc
*) tcpClientObjectCmd
,
1345 (ClientData
) client
,
1346 (Tcl_CmdDeleteProc
*) deleteTcpClientObjectCmd
);
1348 /* Return the client's name */
1350 Tcl_SetResult (interp
, client
-> name
, TCL_STATIC
);
1355 * tcpServerObjectClientsCmd --
1357 * This procedure in invoked in response to the `clients' command
1358 * on a TCP server object. It returns a list of clients for the server.
1362 tcpServerObjectClientsCmd (clientData
, interp
, argc
, argv
)
1363 ClientData clientData
;
1364 Tcl_Interp
* interp
;
1368 Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1369 Tcp_ClientData
* client
;
1374 Tcl_AppendResult (interp
, "wrong # args, should be\"", argv
[-1], " ",
1375 argv
[0], "\"", (char *) NULL
);
1379 for (client
= server
-> firstClient
; client
!= NULL
;
1380 client
= client
-> next
) {
1381 Tcl_AppendElement (interp
, client
-> name
, 0);
1388 * tcpServerObjectConfigCmd --
1390 * This procedure is invoked in response to the `config' command
1391 * on a TCP server object. With no arguments, it returns a list
1392 * of valid arguments. With one argument, it returns the current
1393 * value of that option. With multiple arguments, it attempts to
1394 * configure the server according to that argument list.
1396 * Returns a standard Tcl result.
1400 tcpServerObjectConfigCmd (clientData
, interp
, argc
, argv
)
1401 ClientData clientData
;
1402 Tcl_Interp
* interp
;
1410 /* No arguments -- return a list of valid options. */
1413 Tcl_SetResult (interp
, "-command -port", TCL_STATIC
);
1417 /* One argument -- query a particular option */
1420 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1421 char * arg
= argv
[1];
1423 if (arg
[0] != '-') {
1424 Tcl_AppendResult (interp
, argv
[-1], " ", argv
[0],
1425 ": bad option \"", arg
,
1426 "\" -- each option must begin with a hyphen.",
1432 length
= strlen (++arg
);
1435 if (c
== 'c' && strncmp (arg
, "command", length
) == 0) {
1437 /* Command option -- Get the command name */
1439 Tcl_SetResult (interp
, server
->name
, TCL_STATIC
);
1444 if (c
== 'p' && strncmp (arg
, "port", length
) == 0) {
1446 /* Port option -- Get the port number */
1448 struct sockaddr_in portaddr
;
1449 int rubbish
= sizeof (struct sockaddr_in
);
1451 unixStatus
= getsockname (server
-> socketfd
,
1452 (struct sockaddr
*) &portaddr
, &rubbish
);
1453 if (unixStatus
< 0) {
1454 Tcl_AppendResult (interp
, argv
[-1], ": can't read port #: ",
1455 Tcl_UnixError (interp
), (char *) NULL
);
1458 Tcl_ResetResult (interp
);
1459 sprintf (interp
-> result
, "%d", (int) ntohs (portaddr
.sin_port
));
1463 /* Unknown option */
1465 Tcl_AppendResult (interp
, argv
[-1], ": unknown option \"", arg
,
1466 "\" -- must be -command or -port", (char *) NULL
);
1470 return tcpServerObjectConfig (clientData
, interp
, argc
, argv
);
1474 * tcpServerObjectStartCmd --
1476 * This procedure is invoked to process the "start" command on a
1477 * TCP server object. It sets the server up so that new
1478 * connection requests will create "server-client" objects and
1479 * invoke the server's command with them.
1481 * If Tk is available, the "start" command returns to the caller.
1482 * If Tk is not available, the "start" command immediately enters
1483 * a loop that attempts to process the connection events (and
1484 * other file events as well). The loop may be exited by
1485 * executing a `stop' command on the server object. (The `stop'
1486 * command also exists in the Tk environment, since there is more
1487 * to stopping a server than just breaking out of its event
1492 tcpServerObjectStartCmd (clientData
, interp
, argc
, argv
)
1493 ClientData clientData
;
1494 Tcl_Interp
* interp
;
1498 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1501 /* Check command syntax */
1504 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
1505 argv
[0], "\"", (char *) NULL
);
1509 /* Listen at the server's socket */
1511 unixStatus
= listen (server
-> socketfd
, TCP_LISTEN_BACKLOG
);
1512 if (unixStatus
< 0) {
1513 Tcl_AppendResult (interp
, argv
[-1], ": can't listen at socket: ",
1514 Tcl_UnixError (interp
), (char *) NULL
);
1518 /* Add a file handler to gain control at tcpServerAcceptConnection
1519 * whenever a client attempts to connect.
1522 simpleCreateFileHandler (server
-> socketfd
, TK_READABLE
,
1523 (Tk_FileProc
*) tcpServerAcceptConnection
,
1529 * tcpServerObjectStopCmd
1531 * This procedure is invoked in response to the `$server stop' Tcl
1532 * command. It destroys the server's object command. Destroying the object
1533 * command, in turn, attempts to shut down the server in question. It closes
1534 * the listen socket, closes all the clients, and sets the `stop' flag for
1535 * the server itself. It then calls `tcpServerClose' to try to get rid of
1538 * If one or more clients are active, the server does not shut down
1539 * until they can be closed properly.
1543 tcpServerObjectStopCmd (clientData
, interp
, argc
, argv
)
1544 ClientData clientData
;
1545 Tcl_Interp
* interp
;
1549 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1552 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
1553 " ", argv
[0], "\"", (char *) NULL
);
1557 /* Delete the server command */
1559 Tcl_DeleteCommand (interp
, server
-> name
);
1565 * deleteTcpServerObjectCmd --
1567 * This procedure is called when a server's object command is deleted.
1569 * It is the first procedure called when a server is shut down. It
1570 * closes the listen socket and deletes its file handler. It also attempts
1571 * to close all the clients.
1573 * It may be that a client needs to be able to complete a data transfer
1574 * before it can be closed. In this case, the `close flag' for the client is
1575 * set. The client will be deleted when it reaches a quiescent point.
1577 * Once all the clients are gone, tcpDeleteServer removes the server's
1578 * client data structure.
1582 deleteTcpServerObjectCmd (clientData
)
1583 ClientData clientData
;
1585 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1586 register Tcp_ClientData
* client
, * nextClient
;
1588 /* Close the listen socket and delete its handler */
1590 simpleDeleteFileHandler (server
-> socketfd
);
1591 (void) close (server
-> socketfd
);
1592 server
-> socketfd
= -1;
1594 /* Close all clients */
1596 for (client
= server
-> firstClient
; client
!= NULL
; client
= nextClient
) {
1597 nextClient
= client
-> next
;
1598 if (client
-> activeFlag
)
1599 client
-> closeFlag
= 1;
1601 tcpCloseClient (client
);
1604 /* Remove the server from the list of servers. */
1606 if (server
-> next
!= NULL
)
1607 server
-> next
-> prev
= server
-> prev
;
1608 if (server
-> prev
!= NULL
)
1609 server
-> prev
-> next
= server
-> next
;
1611 tcpFirstServer
= server
-> next
;
1613 /* If all clients are closed, get to tcpDeleteServer now. Otherwise, set
1614 * the server's stop flag and return.
1617 if (server
-> firstClient
== NULL
) {
1618 tcpDeleteServer (server
);
1620 server
-> stopFlag
= 1;
1625 * tcpDeleteServer --
1627 * This procedure is invoked as the final phase of deleting a TCP server.
1628 * When execution gets here, the server's listen socket has been closed and
1629 * the handler has been removed. The server's object command has been deleted.
1630 * The server has been removed from the list of active servers. All the
1631 * server's clients have been closed. The server's login command has been
1632 * deleted. All that remains is to deallocate the server's data structures.
1636 tcpDeleteServer (Tcp_ServerData
*server
)
1638 /* Get rid of the server's initial command */
1640 if (server
-> command
!= NULL
&& server
-> freeCommand
!= NULL
) {
1641 (*(server
-> freeCommand
)) (server
-> command
);
1644 /* Get rid of the server's own data structure */
1646 (void) ckfree ((char *) server
);
1650 * tcpServerObjectConfig --
1652 * This procedure is invoked to configure a TCP server object.
1653 * It may be called from tcpServerCommand when the server is
1654 * first being created, or else from tcpServerObjectCmd if the
1655 * server object is called with the "config" option.
1657 * In any case, the arguments are expected to contain zero or
1658 * more of the following:
1661 * Requests that the server listen at a specific port.
1662 * Default is whatever the system assigns.
1665 * Specifies the initial command used when a client
1666 * first connects to the server. The command is
1667 * concatenated with the name of a "server-client" object
1668 * that identifies the client, and then called:
1670 * Default is "tcp login"
1673 * Puts the server in raw socket mode.
1676 * A standard TCL result.
1680 tcpServerObjectConfig (clientData
, interp
, argc
, argv
)
1681 ClientData clientData
;
1682 Tcl_Interp
* interp
;
1687 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1692 /* On entry, argc shows one plus the number of parameters. Argv[-1] */
1693 /* and argv[0] give the command that got us here: either "tcp */
1694 /* server" or else "serverName config" */
1700 /* Step through the parameters */
1702 for (a
= 1; a
< argc
; ++a
) {
1703 char * arg
= argv
[a
];
1705 if (arg
[0] != '-') {
1706 Tcl_AppendResult (interp
, argv
[-1], ": bad option \"", arg
,
1707 "\" -- each option must begin with a hyphen.",
1712 length
= strlen (++arg
);
1715 if (c
== 'c' && strncmp (arg
, "command", length
) == 0) {
1717 /* Command option -- Get the command name */
1721 Tcl_AppendResult (interp
, argv
[-1],
1722 ": \"-command\" must be followed by a string.",
1727 /* Free the old command name */
1729 if (server
-> freeCommand
!= NULL
) {
1730 (*(server
-> freeCommand
)) (server
-> command
);
1733 /* Put in the new command name */
1735 server
-> command
= (char *) malloc (strlen (argv
[a
]) + 1);
1736 strcpy (server
-> command
, argv
[a
]);
1737 server
-> freeCommand
= (Tcl_FreeProc
*) free
;
1739 } else if (c
== 'p' && strncmp (arg
, "port", length
) == 0) {
1741 /* Port option -- get the port number */
1745 struct sockaddr_in portaddr
;
1749 Tcl_AppendResult (interp
, argv
[-1],
1750 ": \"-port\" must be followed by a number.",
1755 status
= Tcl_GetInt (interp
, portstr
, &portno
);
1756 if (status
) return status
;
1758 /* Set the port number */
1760 memset ((void *) & portaddr
, 0, sizeof (struct sockaddr_in
));
1761 portaddr
.sin_port
= htons (portno
);
1762 unixStatus
= bind (server
-> socketfd
,
1763 (struct sockaddr
*) &portaddr
,
1764 sizeof (struct sockaddr_in
));
1765 if (unixStatus
< 0) {
1766 Tcl_AppendResult (interp
, argv
[-1],
1767 ": can't set port number: ",
1768 Tcl_UnixError (interp
), (char *) NULL
);
1772 } else if (c
== 'r' && strncmp (arg
, "raw", length
) == 0) {
1774 /* raw option -- set raw socket mode */
1780 /* Unknown option */
1782 Tcl_AppendResult (interp
, argv
[-1],
1783 ": unknown option \"", arg
- 1,
1784 "\" -- must be -command or -port", (char *) NULL
);
1790 Tcl_SetResult (interp
, server
-> name
, TCL_STATIC
);
1795 * tcpClientObjectCmd --
1797 * This procedure handles the object command for a Tcp client (on
1798 * the server side). It takes several forms:
1799 * $client command ?command?
1800 * With no arguments, returns the client's
1801 * current command. With arguments, replaces the
1802 * client's command with the arguments
1804 * Deletes the client. If a command is being
1805 * processed on the client's behalf, the client
1806 * will not be deleted until the command's result
1809 * Concatenate the client's command with ?args?,
1810 * and execute the result. Called in background
1811 * when a command arrives and on initial
1814 * Returns the name of the host where the client
1817 * Returns the name of the server to which the client
1822 tcpClientObjectCmd (clientData
, interp
, argc
, argv
)
1823 ClientData clientData
;
1824 Tcl_Interp
* interp
;
1832 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[0],
1833 " command ?args?\"", (char *) NULL
);
1838 length
= strlen (argv
[1]);
1840 if (c
== 'c' && length
>= 2 && strncmp (argv
[1], "close", length
) == 0) {
1841 return tcpClientObjectCloseCmd (clientData
, interp
, argc
-1, argv
+1);
1843 if (c
== 'c' && length
>= 2 && strncmp (argv
[1], "command", length
) == 0) {
1844 return tcpClientObjectCommandCmd (clientData
, interp
, argc
-1, argv
+1);
1846 if (c
== 'd' && strncmp (argv
[1], "do", length
) == 0) {
1847 return tcpClientObjectDoCmd (clientData
, interp
, argc
-1, argv
+1);
1849 if (c
== 'h' && strncmp (argv
[1], "hostname", length
) == 0) {
1850 return tcpClientObjectHostnameCmd (clientData
, interp
, argc
-1, argv
+1);
1852 if (c
== 's' && strncmp (argv
[1], "server", length
) == 0) {
1853 return tcpClientObjectServerCmd (clientData
, interp
, argc
-1, argv
+1);
1856 Tcl_AppendResult (interp
, "bad option \"", argv
[1],
1857 "\": should be close, command, do, hostname or server",
1863 * tcpClientObjectCloseCmd --
1865 * This procedure is called when the Tcl program wants to close a client.
1866 * If the client is active, it sets a flag to close the client when it
1867 * becomes quiescent. Otherwise, it closes the client immediately.
1871 tcpClientObjectCloseCmd (clientData
, interp
, argc
, argv
)
1872 ClientData clientData
;
1873 Tcl_Interp
* interp
;
1877 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
1880 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
1881 argv
[0], "\"", (char *) NULL
);
1885 if (client
-> activeFlag
)
1886 client
-> closeFlag
= 1;
1888 tcpCloseClient (client
);
1894 * tcpClientObjectCommandCmd --
1896 * Query/change the command associated with a client object
1899 * $client command ?newcommand?
1902 * A standard Tcl result containing the client's command.
1906 tcpClientObjectCommandCmd (clientData
, interp
, argc
, argv
)
1907 ClientData clientData
;
1908 Tcl_Interp
* interp
;
1912 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
1917 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
1918 argv
[0], " ?command?\"", (char *) NULL
);
1922 /* Set command if necessary */
1925 if (client
-> freeCommand
!= (Tcl_FreeProc
*) NULL
) {
1926 (*client
-> freeCommand
) (client
-> command
);
1928 client
-> command
= malloc (strlen (argv
[1]) + 1);
1929 strcpy (client
-> command
, argv
[1]);
1930 client
-> freeCommand
= (Tcl_FreeProc
*) free
;
1933 /* Return command in any case */
1935 Tcl_SetResult (interp
, client
-> command
, TCL_STATIC
);
1941 * tcpClientObjectDoCmd --
1943 * The following procedure handles the `do' command on a client
1944 * object. It is called
1945 * (a) as "$client do", at login.
1946 * (b) as "$client do <command>", when the client sends a
1948 * (c) as "$client do", with no further arguments, when
1949 * the connection is closed.
1950 * It concatenates the client's saved command string with the
1951 * client's name, and then with the passed command, resulting in
1953 * saved_command client passed_command
1954 * which is then passed to Tcl_Eval for processing.
1955 * During the processing of the command, the `active' flag is set for
1956 * the client, to avoid having the client closed prematurely.
1959 tcpClientObjectDoCmd (clientData
, interp
, argc
, argv
)
1960 ClientData clientData
;
1961 Tcl_Interp
* interp
;
1966 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
1974 /* Check command syntax */
1977 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
1978 " ", argv
[0], " ?command?\"", (char *) NULL
);
1982 prevClient
= tcpCurrentClient
;
1983 tcpCurrentClient
= client
-> name
;
1985 /* Evaluate the client's command, passing the client name and message */
1988 client
-> activeFlag
= 1;
1991 excmdl
= Tcl_ScanElement (argv
[1], &scanflags
) + 1;
1992 excmd
= (char *) ckalloc (excmdl
);
1993 excmdl
= Tcl_ConvertElement (argv
[1], excmd
, scanflags
);
1994 excmd
[excmdl
] = '\0';
1996 excmd
= (char *) NULL
;
1999 status
= Tcl_VarEval (interp
, client
-> command
, " ", client
-> name
, " ",
2000 excmd
, (char *) NULL
);
2005 if (status
!= TCL_OK
&& argc
< 2) {
2009 client
-> activeFlag
= 0;
2010 tcpCurrentClient
= prevClient
;
2012 /* If the client command throws an error on login or logout,
2013 * the client should be disconnected.
2014 * In any case, the result should be reported back to the client.
2017 if (! (client
-> server
-> raw
)) {
2018 tcpReturnResultToClient (client
, interp
, status
, closeflag
);
2020 tcpPrepareClientForInput (client
);
2023 /* The client may have been closed by the ReturnResult operation. DON'T
2024 * USE IT AFTER THIS POINT.
2031 * tcpClientObjectHostnameCmd --
2033 * This procedure is invoked in response to the `$client hostname'
2034 * Tcl command. It returns the name of the peer host on which the client
2039 tcpClientObjectHostnameCmd (clientData
, interp
, argc
, argv
)
2040 ClientData clientData
;
2041 Tcl_Interp
* interp
;
2045 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2047 struct hostent
* hostdesc
;
2050 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
2051 argv
[0], "\"", (char *) NULL
);
2055 hostdesc
= gethostbyaddr ((char *) &(client
-> peeraddr
.sin_addr
.s_addr
),
2056 sizeof (client
-> peeraddr
.sin_addr
.s_addr
),
2059 if (hostdesc
!= (struct hostent
*) NULL
) {
2060 Tcl_SetResult (interp
, hostdesc
-> h_name
, TCL_VOLATILE
);
2062 Tcl_SetResult (interp
, inet_ntoa (client
-> peeraddr
.sin_addr
),
2070 * tcpClientObjectServerCmd --
2072 * This procedure is invoked in response to the `$client server'
2073 * Tcl command. It returns the name of the server to which the client
2078 tcpClientObjectServerCmd (clientData
, interp
, argc
, argv
)
2079 ClientData clientData
;
2080 Tcl_Interp
* interp
;
2084 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2087 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
2088 argv
[0], "\"", (char *) NULL
);
2092 Tcl_SetResult (interp
, client
-> server
-> name
, TCL_STATIC
);
2098 * deleteTcpClientObjectCmd --
2100 * This procedure is invoked when a client object's command has
2101 * been deleted. WARNING -- deleting a client object command when the
2102 * client is active is a FATAL error that cannot be reported through the
2105 * This procedure does all the cleanup necessary to getting rid of the
2110 deleteTcpClientObjectCmd (clientData
)
2111 ClientData clientData
;
2113 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2114 register Tcp_ServerData
* server
= client
-> server
;
2116 /* Make sure the client is really dead. */
2118 if (client
-> activeFlag
) {
2119 fprintf (stderr
, "attempt to delete an active TCP client!\n\n");
2123 /* Remove any handler for data on the client's socket. */
2125 simpleDeleteFileHandler (client
-> socketfd
);
2127 /* Now it's safe to close the socket */
2129 (void) close (client
-> socketfd
);
2131 /* Get rid of the command */
2133 if (client
-> command
!= NULL
&& client
-> freeCommand
!= NULL
) {
2134 (*(client
-> freeCommand
)) (client
-> command
);
2137 /* Get rid of the input buffer */
2139 Tcl_DeleteCmdBuf (client
-> inputBuffer
);
2141 /* Get rid of any pending result */
2143 if (client
-> resultString
!= NULL
&& client
-> freeResultString
!= NULL
) {
2144 (*(client
-> freeResultString
)) (client
-> resultString
);
2147 /* Unlink the client from the list of active clients */
2149 if (client
-> prev
== NULL
)
2150 client
-> server
-> firstClient
= client
-> next
;
2152 client
-> prev
-> next
= client
-> next
;
2154 if (client
-> next
!= NULL
)
2155 client
-> next
-> prev
= client
-> prev
;
2157 /* Now it's ok to destroy the client's data structure */
2159 ckfree ((char *) client
);
2161 /* Handle a deferred close on the server if necessary */
2163 if (server
-> stopFlag
&& server
-> firstClient
== NULL
)
2164 tcpDeleteServer (server
);
2168 * tcpConnectionObjectCmd --
2170 * This procedure is invoked to process the object command for a client-
2171 * side connection object. It takes a couple of diferent forms:
2174 * Closes the connection.
2175 * $connection send arg ?arg....?
2176 * Catenates the arguments into a Tcl command, and sends them
2181 tcpConnectionObjectCmd (clientData
, interp
, argc
, argv
)
2182 ClientData clientData
;
2183 Tcl_Interp
* interp
;
2192 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[0], " ",
2193 "command ?args?\"", (char *) NULL
);
2199 length
= strlen (arg
);
2201 if (c
== 'c' && strncmp (arg
, "close", length
) == 0) {
2202 return tcpConnectionObjectCloseCmd (clientData
, interp
, argc
-1, argv
+1);
2204 if (c
== 's' && strncmp (arg
, "send", length
) == 0) {
2205 return tcpConnectionObjectSendCmd (clientData
, interp
, argc
-1, argv
+1);
2208 Tcl_AppendResult (interp
, "unknown command \"", arg
,
2209 "\": must be close or send", (char *) NULL
);
2214 * tcpConnectionObjectCloseCmd --
2216 * This procedure is invoked in response to a `close' command on a
2217 * client-side connection object. It closes the socket and deletes the
2223 tcpConnectionObjectCloseCmd (clientData
, interp
, argc
, argv
)
2224 ClientData clientData
;
2225 Tcl_Interp
* interp
;
2230 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
2231 argv
[0], "\"", (char *) NULL
);
2235 Tcl_DeleteCommand (interp
, argv
[-1]);
2240 * tcpConnectionObjectSendCmd --
2242 * This procedure is invoked in response to a `send' command on a client-
2243 * side connection object. It catenates the `send' arguments into a single
2244 * string, presents that string to the server as a command, and returns the
2249 tcpConnectionObjectSendCmd (clientData
, interp
, argc
, argv
)
2250 ClientData clientData
;
2251 Tcl_Interp
* interp
;
2256 int f
= (int) clientData
;
2260 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
2261 argv
[0], " command\"", (char *) NULL
);
2265 /* Paste together the message */
2267 message
= Tcl_Merge (argc
-1, argv
+1);
2269 /* Send the command to the server */
2271 status
= tcpSendCmdToServer (interp
, f
, message
);
2272 if (status
!= TCL_OK
)
2275 /* Get the server's reply */
2277 return tcpReceiveResultFromServer (interp
, f
);
2281 * deleteTcpConnectionObjectCmd --
2283 * This procedure is called when a connection object is to be
2284 * deleted. It just has to close the socket that the object uses.
2288 deleteTcpConnectionObjectCmd (clientData
)
2289 ClientData clientData
;
2291 int f
= (int) clientData
;
2298 * This procedure is called when the program is completely done with
2299 * a client object. If the `active' flag is set, there is still a reference
2300 * to the dead client, but we shouldn't have come here in that case.
2304 tcpCloseClient (Tcp_ClientData
*client
)
2306 if (client
-> activeFlag
)
2309 /* Deleting the client command is all we need to do -- the delete
2310 * procedure does everything else.
2313 Tcl_DeleteCommand (client
-> server
-> interp
, client
-> name
);
2317 * tcpServerAcceptConnection --
2319 * This procedure is invoked as a file handler whenever a server's
2320 * socket is ready for `reading' -- i.e., has a connection request
2323 * It calls the `accept' command on the server to create a client.
2324 * If the `accept' is successful, it then calls the `do'
2325 * command on the client. If either call fails, a background error
2331 tcpServerAcceptConnection (clientData
, mask
)
2332 ClientData clientData
;
2335 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
2341 /* Accept the connection with `$server accept' */
2343 status
= Tcl_VarEval (server
-> interp
, server
-> name
, " accept",
2346 /* On success, try to execute the client's command with `$client do' */
2348 if (status
== TCL_OK
) {
2349 clientName
= (char *) ckalloc (strlen (server
-> interp
-> result
) + 1);
2350 strcpy (clientName
, server
-> interp
-> result
);
2351 status
= Tcl_VarEval (server
-> interp
, clientName
, " do", (char *) NULL
);
2353 /* Client may have been closed at this point. Don't refer to it again. */
2356 if (status
!= TCL_OK
) {
2357 simpleReportBackgroundError (server
-> interp
);
2359 Tcl_ResetResult (server
-> interp
);
2365 * This procedure is invoked whenever the code must determine whether
2366 * a host is trusted. A host is considered trusted only if it is the local
2370 * Returns a Boolean value that is TRUE iff the host is trusted.
2373 /* The HOSTCMP macro is just strcmp, but puts its args on stderr if
2374 * the DEBUG_TRUSTED_HOST flag is #define'd. It's used because this
2375 * code can be a little flaky; if `hostname' returns a name that is
2376 * completely unknown in the database, this macro will trace what happened.
2379 #ifdef DEBUG_TRUSTED_HOST
2380 #define HOSTCMP( name1, name2 ) \
2381 (fprintf (stderr, "tcpTrustedHost: comparing %s with %s\n", \
2382 (name1), (name2)), \
2383 strcmp ((name1), (name2)))
2385 #define HOSTCMP( name1, name2 ) \
2386 strcmp ((name1), (name2))
2390 tcpTrustedHost (char *hostName
)
2392 char localName
[128];
2393 struct hostent
* hostEnt
;
2394 struct in_addr hostAddr
;
2398 /* This procedure really has to do things the hard way. The problem is
2399 * that the hostname() kernel call returns the host name set by the system
2400 * administrator, which may not be the host's primary name as known to
2401 * the domain name system. Furthermore, the host presented may be one
2402 * of the names for the loopback port, 127.0.0.1, and this must be checked,
2406 /* Start assembling a list of possibilities for the host name. First
2407 * possibility is the name that the kernel returns as hostname ().
2410 unixStatus
= gethostname (localName
, 127);
2411 if (unixStatus
>= 0) {
2413 if (!HOSTCMP( hostName
, localName
)) return 1;
2415 /* Next possibility is a.b.c.d notation for all of the local addresses,
2416 * plus all the nicknames for the host.
2419 hostEnt
= gethostbyname (localName
);
2420 if (hostEnt
!= (struct hostent
*) NULL
) {
2421 if (!HOSTCMP( hostName
, hostEnt
-> h_name
)) return 1;
2422 if (hostEnt
-> h_aliases
!= (char * *) NULL
) {
2423 for (i
= 0; hostEnt
-> h_aliases
[i
] != (char *) NULL
; ++i
) {
2424 if (!HOSTCMP( hostName
, hostEnt
-> h_aliases
[i
] )) return 1;
2427 if (hostEnt
-> h_addr_list
!= (char * *) NULL
) {
2428 for (i
= 0; hostEnt
-> h_addr_list
[i
] != (char *) NULL
; ++i
) {
2429 /* note that the address doesn't have to be word-aligned (!) */
2430 memcpy ((char *) &hostAddr
,
2431 hostEnt
-> h_addr_list
[i
],
2432 hostEnt
-> h_length
);
2433 if (!HOSTCMP( hostName
, inet_ntoa (hostAddr
) )) return 1;
2439 /* Finally, there's the possibility of the loopback address, and all of
2442 if (!HOSTCMP( hostName
, "0.0.0.0" )) return 1;
2443 if (!HOSTCMP( hostName
, "127.0.0.1" )) return 1;
2444 hostAddr
.s_addr
= htonl (INADDR_LOOPBACK
);
2445 hostEnt
= gethostbyaddr ((char *) &hostAddr
, sizeof hostAddr
, AF_INET
);
2446 if (hostEnt
!= (struct hostent
*) NULL
) {
2447 if (!HOSTCMP( hostName
, hostEnt
-> h_name
)) return 1;
2448 if (hostEnt
-> h_aliases
!= (char * *) NULL
) {
2449 for (i
= 0; hostEnt
-> h_aliases
[i
] != (char *) NULL
; ++i
) {
2450 if (!HOSTCMP( hostName
, hostEnt
-> h_aliases
[i
] )) return 1;
2453 if (hostEnt
-> h_addr_list
!= (char * *) NULL
) {
2454 for (i
= 0; hostEnt
-> h_addr_list
[i
] != (char *) NULL
; ++i
) {
2455 /* note that the address doesn't have to be word-aligned (!) */
2456 memcpy ((char *) &hostAddr
,
2457 hostEnt
-> h_addr_list
[i
],
2458 hostEnt
-> h_length
);
2459 if (!HOSTCMP( hostName
, inet_ntoa (hostAddr
) )) return 1;
2468 * tcpReturnResultToClient --
2470 * This procedure is invoked to return a result to a client. It
2471 * extracts the interpreter's result string, bundles it with the return
2472 * status, and stores it in the client's `resultString' area.
2474 * It then calls tcpWriteResultToClient to try to start sending the
2479 tcpReturnResultToClient (client
, interp
, status
, closeflag
)
2480 Tcp_ClientData
* client
;
2481 Tcl_Interp
* interp
;
2490 /* Put together a message comprising the return status and the interpreter
2493 sprintf (rint
, "%d", status
);
2495 argv
[1] = interp
-> result
;
2496 result
= Tcl_Merge (2, argv
);
2497 length
= strlen (result
);
2498 client
-> resultString
= (char *) malloc (length
+ 2);
2499 strcpy (client
-> resultString
, result
);
2500 strcpy (client
-> resultString
+ length
, "\n");
2502 client
-> resultPointer
= client
-> resultString
;
2503 client
-> freeResultString
= (Tcl_FreeProc
*) free
;
2505 Tcl_ResetResult (interp
);
2506 client
-> closeFlag
|= closeflag
;
2508 /* Now try to send the reply. */
2510 tcpWriteResultToClient ((ClientData
) client
, TK_WRITABLE
);
2512 /* tcpWriteResultToClient closes the client if it fails; don't depend on
2513 * having the client still be usable. */
2517 * tcpWriteResultToClient --
2519 * This procedure is invoked to issue a write on a client socket.
2520 * It can be called directly by tcpReturnResultToClient, to attempt the
2521 * initial write of results. It can also be called as a file handler,
2522 * to retry a write that was previously blocked.
2527 tcpWriteResultToClient (clientData
, mask
)
2528 ClientData clientData
;
2531 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2536 length
= strlen (client
-> resultPointer
);
2538 /* Issue the write */
2540 unixStatus
= write (client
-> socketfd
, client
-> resultPointer
,
2543 /* Test for a total failure */
2545 if (unixStatus
< 0) {
2546 if (errno
!= EWOULDBLOCK
) {
2547 tcpClientWriteError (client
);
2548 /* tcpClientWriteError closes the client as a side effect. Don't depend
2549 * on the client still being there.
2553 unixStatus
= 0; /* Pretend that EWOULDBLOCK succeeded at
2554 * writing zero characters. */
2558 /* Test for a partial success */
2560 if (unixStatus
< length
) {
2561 client
-> resultPointer
+= unixStatus
;
2562 simpleCreateFileHandler (client
-> socketfd
, TK_WRITABLE
,
2563 (Tk_FileProc
*) tcpWriteResultToClient
,
2567 /* Total success -- prepare the client for the next input */
2570 if (client
-> freeResultString
!= NULL
) {
2571 (*(client
-> freeResultString
)) (client
-> resultString
);
2573 client
-> resultString
= client
-> resultPointer
= (char *) NULL
;
2574 client
-> freeResultString
= (Tcl_FreeProc
*) NULL
;
2575 simpleDeleteFileHandler (client
-> socketfd
);
2576 if (client
-> closeFlag
) {
2577 tcpCloseClient (client
);
2579 /* After tcpCloseClient executes, the client goes away. Don't depend
2580 on it's still being there. */
2583 tcpPrepareClientForInput (client
);
2589 * tcpPrepareClientForInput --
2591 * This procedure is invoked to prepare a client to accept command
2592 * input. It establishes a handler, tcpReceiveClientInput, that does the
2593 * actual command buffering.
2597 tcpPrepareClientForInput (Tcp_ClientData
*client
)
2599 simpleCreateFileHandler (client
-> socketfd
, TK_READABLE
,
2600 (Tk_FileProc
*) tcpReceiveClientInput
,
2601 (ClientData
) client
);
2605 * tcpReceiveClientInput --
2607 * This procedure is called when a server is awaiting input from a client
2608 * and the client socket tests to be `ready to read'. It reads a bufferload
2609 * of data from the client, and places it in the client's command buffer. If
2610 * the command is complete, it then tries to invoke the command.
2615 tcpReceiveClientInput (clientData
, mask
)
2616 ClientData clientData
;
2619 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2620 register Tcp_ServerData
* server
= client
-> server
;
2621 register Tcl_Interp
* interp
= server
-> interp
;
2623 static char buffer
[BUFSIZ
+1];
2632 /* Try to read from the client */
2635 unixStatus
= read (client
-> socketfd
, buffer
, BUFSIZ
);
2636 if (unixStatus
<= 0 && errno
!= EWOULDBLOCK
)
2637 tcpClientReadError (client
);
2639 /* tcpClientReadError closes the client and reports the error.
2640 In any case, if the read failed, we want to return. */
2642 if (unixStatus
<= 0)
2645 if (server
-> raw
) {
2648 sprintf(buf
, "RawInput %s %d %d", client
-> name
, buffer
, unixStatus
);
2649 printf("TCP executing: %s\n", buf
);
2650 status
= Tcl_Eval (interp
, buf
, 0, (char * *) NULL
);
2652 tcpPrepareClientForInput (client
);
2656 /* Assemble the received data into the buffer */
2658 buffer
[unixStatus
] = '\0';
2659 command
= Tcl_AssembleCmd (client
-> inputBuffer
, buffer
);
2660 if (command
!= (char *) NULL
) {
2662 /* Process the received command. */
2664 simpleDeleteFileHandler (client
-> socketfd
);
2666 argv
[0] = client
-> name
;
2669 docmd
= Tcl_Merge (argc
, argv
);
2670 status
= Tcl_Eval (interp
, docmd
, 0, (char * *) NULL
);
2673 /* At this point, the client may have been closed. Don't try to
2676 if (status
!= TCL_OK
) {
2677 simpleReportBackgroundError (interp
);
2683 /* tcpClientReadError --
2685 * This procedure is called when an attempt to read the command from a
2686 * client fails. There are two possibilities:
2688 * The first is that there really was a read error, originating in the
2689 * socket system. In this case, the error should be reported at background
2690 * level, and the client should be closed.
2692 * The second is that the read reached the end-of-information marker in
2693 * the client's stream. In this case, the `do' command should be called on
2694 * the client one last time, and then the client should be closed.
2696 * If the application needs to clean the client up after a read error,
2697 * it must define the `tcperror' procedure and process the error.
2701 tcpClientReadError (Tcp_ClientData
*client
)
2703 Tcp_ServerData
* server
= client
-> server
;
2704 Tcl_Interp
* interp
= server
-> interp
;
2711 status
= Tcl_VarEval (interp
, "error {", client
-> name
, ": read error: ",
2712 Tcl_UnixError (interp
), "}", (char *) NULL
);
2713 simpleReportBackgroundError (interp
);
2719 status
= Tcl_VarEval (interp
, client
-> name
, " do", (char *) NULL
);
2720 if (status
!= TCL_OK
)
2721 simpleReportBackgroundError (interp
);
2724 tcpCloseClient (client
);
2727 /* tcpClientWriteError --
2729 * This procedure is invoked when an attempt to return results to a client
2730 * has failed. It reports the error at background level and closes the client.
2732 * If the application needs to clean up the client after a write error,
2733 * it must define the `tcperror' procedure to catch the error.
2737 tcpClientWriteError (Tcp_ClientData
*client
)
2739 Tcp_ServerData
* server
= client
-> server
;
2740 Tcl_Interp
* interp
= server
-> interp
;
2742 (void) Tcl_VarEval (interp
, "error {", client
-> name
, ": read error: ",
2743 Tcl_UnixError (interp
), "}", (char *) NULL
);
2744 simpleReportBackgroundError (interp
);
2745 tcpCloseClient (client
);
2748 /* tcpSendCmdToServer --
2750 * This procedure is invoked to send a command originated by a client
2751 * using the `$connection send' Tcl command.
2753 * The message is passed without a newline appended. The server requires
2754 * a newline, which is sent in a separate call.
2758 tcpSendCmdToServer (interp
, s
, message
)
2759 Tcl_Interp
* interp
;
2766 static char newline
= '\n';
2767 void (*oldPipeHandler
) ();
2769 /* Set the socket for blocking I/O */
2772 unixStatus
= ioctl (s
, FIONBIO
, (char *) &rubbish
);
2773 if (unixStatus
< 0) {
2774 Tcl_AppendResult (interp
, "can't set blocking I/O on socket: ",
2775 Tcl_UnixError (interp
), (char *) NULL
);
2779 /* Keep a possible broken pipe from killing us silently */
2781 oldPipeHandler
= signal (SIGPIPE
, SIG_IGN
);
2783 /* Write the message */
2785 length
= strlen (message
);
2786 unixStatus
= write (s
, message
, length
);
2787 if (unixStatus
< length
) {
2788 (void) signal (SIGPIPE
, oldPipeHandler
);
2789 Tcl_AppendResult (interp
, "can't send message to server: ",
2790 Tcl_UnixError (interp
), (char *) NULL
);
2794 /* Write the terminating newline */
2796 unixStatus
= write (s
, &newline
, 1);
2797 if (unixStatus
< 1) {
2798 (void) signal (SIGPIPE
, oldPipeHandler
);
2799 Tcl_AppendResult (interp
, "can't send newline to server: ",
2800 Tcl_UnixError (interp
), (char *) NULL
);
2804 (void) signal (SIGPIPE
, oldPipeHandler
);
2809 * tcpReceiveResultFromServer --
2811 * This procedure is invoked to get the result transmitted from
2812 * a remote server, either on establishing the connection or on processing
2813 * a command. It returns a standard Tcl result that is usually the result
2814 * returned by the server.
2818 tcpReceiveResultFromServer (interp
, s
)
2819 Tcl_Interp
* interp
;
2826 struct timeval tick
;
2827 struct timeval
* tickp
;
2829 char buf
[BUFSIZ
+1];
2835 /* Read the result using non-blocking I/O */
2838 unixStatus
= ioctl (s
, FIONBIO
, (char *) &junk
);
2839 if (unixStatus
< 0) {
2840 Tcl_AppendResult (interp
, "can't set nonblocking I/O on socket: ",
2841 Tcl_UnixError (interp
), (char *) NULL
);
2845 /* Make a buffer to receive the result */
2847 cmdbuf
= Tcl_CreateCmdBuf ();
2849 /* Wait for the result to appear */
2851 tickp
= (struct timeval
*) 0;
2852 FD_ZERO( &readfds
);
2853 FD_SET( s
, &readfds
);
2856 unixStatus
= select (s
+ 1, &readfds
, (fd_set
*) NULL
, (fd_set
*) NULL
,
2859 if (unixStatus
< 0) {
2861 Tcl_AppendResult (interp
, "error selecting socket for reply: ",
2862 Tcl_UnixError (interp
), (char *) NULL
);
2866 if (unixStatus
== 0) {
2868 Tcl_SetResult (interp
, "timed out waiting for server reply", TCL_STATIC
);
2872 /* Read the result */
2874 unixStatus
= read (s
, buf
, BUFSIZ
);
2876 if (unixStatus
< 0) {
2878 Tcl_AppendResult (interp
, "error reading server reply: ",
2879 Tcl_UnixError (interp
), (char *) NULL
);
2883 if (unixStatus
== 0) {
2885 Tcl_SetResult (interp
, "Connection closed.", TCL_STATIC
);
2889 /* Parse the (partial) command */
2891 buf
[unixStatus
] = '\0';
2892 reply
= Tcl_AssembleCmd (cmdbuf
, buf
);
2893 if (reply
!= NULL
) {
2898 /* Partial command not yet complete. Set timeout for reading the
2899 * rest of the result. */
2906 /* When we come here, either the status is TCL_ERROR and the error
2907 * message is already set, or else the status is TCL_OK and `reply'
2908 * contains the result that we have to return. The first element of
2909 * `reply' has the status, and the second has the result string. */
2911 /* Split the list elements */
2913 if (status
== TCL_OK
) {
2914 status
= Tcl_SplitList (interp
, reply
, &rargc
, &rargv
);
2915 if (status
!= TCL_OK
) {
2916 Tcl_SetResult (interp
, "server returned malformed list", TCL_STATIC
);
2921 /* Verify the element count */
2923 if (status
== TCL_OK
) {
2925 Tcl_SetResult (interp
, "server returned malformed list", TCL_STATIC
);
2927 free ((char *) rargv
);
2929 status
= Tcl_GetInt (interp
, rargv
[0], &rstatus
);
2930 if (status
!= TCL_OK
) {
2931 Tcl_SetResult (interp
, "server returned unrecognizable status",
2934 free ((char *) rargv
);
2939 /* Return the result reported by the server */
2941 if (status
== TCL_OK
) {
2942 Tcl_SetResult (interp
, rargv
[1], TCL_VOLATILE
);
2944 free ((char *) rargv
);
2947 Tcl_DeleteCmdBuf (cmdbuf
);