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 (fd
)
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
,
514 Tk_DoWhenIdle ((Tk_IdleProc
*) simpleDeleteFileHandler2
,
521 simpleDeleteFileHandler1 (clientData
, mask
)
522 ClientData clientData
;
525 (void) fprintf (stderr
, "in simpleDeleteFileHandler1: bug in tkEvent.c");
530 simpleDeleteFileHandler2 (clientData
)
531 ClientData clientData
;
533 int fd
= (int) clientData
;
535 Tk_DeleteFileHandler (fd
);
539 *----------------------------------------------------------------------
542 * This procedure implements a `tcp' command for Tcl. It provides the
543 * top-level actions for TCP/IP connections.
545 * This command is divided into variants, each with its own procedure:
548 * Returns the current active client, or an error if there is
550 * tcp connect host port
551 * Establish a connection to a server running at `port' on
553 * tcp eval client command
554 * Do default command processing for command "$command",
555 * originating at client "$client".
557 * Do default login processing for $client.
559 * Start the main loop for a server or group of servers.
561 * Poll for whether servers have work to do.
563 * Returns a list of the currently active servers.
565 * Set up a server to run in the current interpreter.
567 * Wait for a server to have work to do.
568 *----------------------------------------------------------------------
572 Tk_TcpCmd (clientData
, interp
, argc
, argv
)
573 ClientData clientData
;
582 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[0],
583 " command ?args?\"", (char *) NULL
);
588 length
= strlen (argv
[1]);
590 if ((c
== 'c') && (length
>= 2) &&
591 (strncmp (argv
[1], "client", length
) == 0)) {
592 return tcpClientCommand (clientData
, interp
, argc
-1, argv
+1);
594 if ((c
== 'c') && (length
>= 2) &&
595 (strncmp (argv
[1], "connect", length
) == 0)) {
596 return tcpConnectCommand (clientData
, interp
, argc
-1, argv
+1);
598 if ((c
== 'e') && (strncmp (argv
[1], "eval", length
) == 0)) {
599 return tcpEvalCommand (clientData
, interp
, argc
-1, argv
+1);
601 if ((c
== 'l') && (strncmp (argv
[1], "login", length
) == 0)) {
602 return tcpLoginCommand (clientData
, interp
, argc
-1, argv
+1);
604 if ((c
== 'm') && (strncmp (argv
[1], "mainloop", length
) == 0)) {
605 return tcpMainLoopCommand (clientData
, interp
, argc
-1, argv
+1);
607 if ((c
== 'p') && (strncmp (argv
[1], "poll", length
) == 0)) {
608 return tcpPollCommand (clientData
, interp
, argc
-1, argv
+1);
610 if ((c
== 's') && (length
>= 7)
611 && (strncmp (argv
[1], "servers", length
) == 0)) {
612 return tcpServersCommand (clientData
, interp
, argc
-1, argv
+1);
614 if ((c
== 's') && (strncmp (argv
[1], "server", length
) == 0)) {
615 return tcpServerCommand (clientData
, interp
, argc
-1, argv
+1);
617 if ((c
== 'w') && (strncmp (argv
[1], "wait", length
) == 0)) {
618 return tcpWaitCommand (clientData
, interp
, argc
-1, argv
+1);
620 Tcl_AppendResult (interp
, "bad option \"", argv
[1],
621 "\": should be client, eval, login,",
622 " mainloop, poll, servers, server or wait",
629 * tcpClientCommand --
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
638 tcpClientCommand (clientData
, interp
, argc
, argv
)
639 ClientData clientData
;
647 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
648 argv
[0], "\"", (char *) NULL
);
652 /* Make sure there is a current client */
654 if (tcpCurrentClient
== NULL
) {
655 Tcl_SetResult (interp
, "no current client", TCL_STATIC
);
659 Tcl_SetResult (interp
, tcpCurrentClient
, TCL_VOLATILE
);
663 /* tcpConnectCommand --
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.
672 tcpConnectCommand (clientData
, interp
, argc
, argv
)
673 ClientData clientData
;
678 struct hostent
* host
;
679 struct sockaddr_in sockaddr
;
689 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
690 argv
[0], " hostname port#\"", (char *) NULL
);
694 /* Decode the host name */
696 sockaddr
.sin_family
= AF_INET
;
697 host
= gethostbyname (argv
[1]);
699 memcpy ((char *) &(sockaddr
.sin_addr
.s_addr
),
700 (char *) (host
-> h_addr_list
[0]),
701 (size_t) (host
-> h_length
));
703 haddr
= inet_addr (argv
[1]);
705 Tcl_AppendResult (interp
, argv
[1], ": host unknown", (char *) NULL
);
708 sockaddr
.sin_addr
.s_addr
= haddr
;
711 /* Decode the port number */
713 status
= Tcl_GetInt (interp
, argv
[2], &port
);
714 if (status
) return status
;
715 sockaddr
.sin_port
= htons (port
);
717 /* Make a socket to talk to the server */
719 f
= socket (AF_INET
, SOCK_STREAM
, 0);
721 Tcl_AppendResult (interp
, "can't create socket: ",
722 Tcl_UnixError (interp
), (char *) NULL
);
726 /* Connect to the server */
728 status
= connect (f
, (struct sockaddr
*) &sockaddr
, sizeof sockaddr
);
730 Tcl_AppendResult (interp
, "can't connect to server: ",
731 Tcl_UnixError (interp
), (char *) NULL
);
736 /* Get the server's greeting message */
738 status
= tcpReceiveResultFromServer (interp
, f
);
740 if (status
== TCL_OK
) {
742 /* Stash the greeting, make the connection object and return it. */
744 sprintf (name
, "tcp_connection_%d", f
);
745 (void) Tcl_SetVar2 (interp
, "tcp_greeting", name
, interp
-> result
,
747 Tcl_CreateCommand (interp
, name
, (Tcl_CmdProc
*) tcpConnectionObjectCmd
,
749 (Tcl_CmdDeleteProc
*) deleteTcpConnectionObjectCmd
);
750 Tcl_SetResult (interp
, name
, TCL_VOLATILE
);
754 /* Error reading greeting, quit */
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".
771 tcpEvalCommand (clientData
, interp
, argc
, argv
)
772 ClientData clientData
;
779 /* Argc == 2 means that we're logging out a client. Default is to ignore
787 /* Three-argument form is a command from a client. Default is to eval
791 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
792 " ", argv
[0], " client command\"", (char *) NULL
);
796 status
= Tcl_Eval (interp
, argv
[2], 0, (char * *) NULL
);
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.
809 * The authentication procedure is as follows:
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.
816 * Obviously, there are other authentication techniques. The use can
817 * replace this command with an arbitrary Tcl script.
822 tcpLoginCommand (clientData
, interp
, argc
, argv
)
823 ClientData clientData
;
828 char * hostName
; /* Name of the client's host */
831 /* Check command syntax */
834 Tcl_AppendResult (interp
, "wrong # args; should be \"", argv
[-1], " ",
835 argv
[0], " clientName\"", (char *) NULL
);
839 /* Get the hostname by doing $client hostname */
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
);
846 /* Check that the host is trusted */
848 if (tcpTrustedHost (hostName
)) {
850 /* Change the command to `tcp eval' for next time */
852 status
= Tcl_VarEval (interp
, argv
[1], " command {tcp eval}",
856 if (status
== TCL_OK
) {
858 /* Return a greeting message */
860 Tcl_ResetResult (interp
);
861 Tcl_AppendResult (interp
, "GE DICE TCP-based Tcl server\n", RCSid
,
862 "\n", copyright
, (char *) NULL
);
870 ckfree ((char *) hostName
);
873 /* Host isn't trusted or one of the commands failed. */
875 Tcl_SetResult (interp
, "Permission denied", TCL_STATIC
);
880 * tcpMainLoopCommand:
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
887 * In a Tk environment, the procedure returns immediately.
892 tcpMainLoopCommand (clientData
, interp
, argc
, argv
)
893 ClientData clientData
;
902 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
903 " ", argv
[0], "\"", (char *) NULL
);
907 errno
= 0; status
= -1;
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.
914 Tcl_AppendResult (interp
, "select: ", Tcl_UnixError (interp
),
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.
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.
937 tcpPollCommand (clientData
, interp
, argc
, argv
)
938 ClientData clientData
;
946 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
947 " ", argv
[0], "\"", (char *) NULL
);
953 errno
= 0; status
= -1;
955 /* Check for trouble */
959 Tcl_SetResult (interp
, "no servers known to event handler", TCL_STATIC
);
961 Tcl_AppendResult (interp
, "select: ", Tcl_UnixError (interp
),
967 /* Return the number of events processed. */
969 sprintf (interp
-> result
, "%d", status
);
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.
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.
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.
990 * tcp server ?-port #? ?-command string?
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).
1000 tcpServerCommand (clientData
, interp
, argc
, argv
)
1001 ClientData clientData
;
1002 Tcl_Interp
* interp
;
1012 /* Create a structure to hold the tcp server's description. */
1014 Tcp_ServerData
* server
=
1015 (Tcp_ServerData
*) ckalloc (sizeof (Tcp_ServerData
));
1017 /* Set up the interpreter and the default command. Clear the list of
1020 server
-> interp
= interp
;
1021 server
-> command
= "tcp login";
1022 server
-> freeCommand
= TCL_STATIC
;
1023 server
-> stopFlag
= 0;
1025 server
-> firstClient
= (Tcp_ClientData
*) NULL
;
1027 /* Create the socket at which the server will listen. */
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
);
1035 /* Set up the socket for non-blocking I/O. */
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
);
1044 /* Server structure has been created and socket has been opened.
1045 * Now configure the server.
1048 if (tcpServerObjectConfig ((ClientData
) server
, interp
, argc
, argv
)
1052 /* Link the server on the list of active servers */
1055 tcpFirstServer
-> prev
= server
;
1056 server
-> next
= tcpFirstServer
;
1057 tcpFirstServer
= server
;
1058 server
-> prev
= NULL
;
1060 /* Add the server object command */
1062 sprintf (server
-> name
, "tcp_server_%d", server
-> socketfd
);
1064 Tcl_CreateCommand (interp
, server
-> name
,
1065 (Tcl_CmdProc
*) tcpServerObjectCmd
,
1066 (ClientData
) server
,
1067 (Tcl_CmdDeleteProc
*) deleteTcpServerObjectCmd
);
1069 Tcl_SetResult (interp
, server
-> name
, TCL_STATIC
);
1076 /* Error in configuring the server. Trash the socket. */
1078 unixStatus
= close (server
-> socketfd
);
1079 if (unixStatus
< 0) {
1081 nargv
[0] = "(also failed to close socket: ";
1082 nargv
[1] = Tcl_UnixError (interp
);
1084 message
= Tcl_Concat (nargc
, nargv
);
1085 Tcl_AddErrorInfo (interp
, message
);
1090 /* Error in creating the server -- get rid of the data structure */
1092 if (server
-> freeCommand
!= NULL
) {
1093 (*(server
-> freeCommand
)) (server
-> command
);
1095 ckfree ((char *) server
);
1100 * tcpServersCommand:
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.
1108 tcpServersCommand (clientData
, interp
, argc
, argv
)
1109 ClientData clientData
;
1110 Tcl_Interp
* interp
;
1114 Tcp_ServerData
* server
;
1119 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
1120 argv
[0], "\"", (char *) NULL
);
1124 for (server
= tcpFirstServer
; server
!= NULL
; server
= server
-> next
) {
1125 Tcl_AppendElement (interp
, server
-> name
, 0);
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.
1138 * It returns a count of pending events.
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
1148 tcpWaitCommand (clientData
, interp
, argc
, argv
)
1149 ClientData clientData
;
1150 Tcl_Interp
* interp
;
1157 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
1158 " ", argv
[0], "\"", (char *) NULL
);
1164 errno
= 0; status
= -1;
1166 /* Check for trouble */
1170 Tcl_SetResult (interp
, "no servers known to event handler", TCL_STATIC
);
1172 Tcl_AppendResult (interp
, "select: ", Tcl_UnixError (interp
),
1178 /* Return the number of events pending. */
1180 sprintf (interp
-> result
, "%d", status
);
1185 * tcpServerObjectCmd --
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.
1192 * [Internal call] - Accept a connection.
1194 * Return a list of all clients connected to a server.
1195 * $server configure ?args?
1196 * Revise or query a server's configuration.
1198 * Start a server running.
1200 * Terminate a server.
1204 tcpServerObjectCmd (clientData
, interp
, argc
, argv
)
1205 ClientData clientData
;
1206 Tcl_Interp
* interp
;
1214 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[0],
1215 " command ?args?\"", (char *) NULL
);
1220 length
= strlen (argv
[1]);
1222 if (c
== 'a' && strncmp (argv
[1], "accept", length
) == 0) {
1223 return tcpServerObjectAcceptCmd (clientData
, interp
, argc
-1, argv
+1);
1225 if (c
== 'c' && length
>= 2 && strncmp (argv
[1], "clients", length
) == 0) {
1226 return tcpServerObjectClientsCmd (clientData
, interp
, argc
-1, argv
+1);
1228 if (c
== 'c' && length
>= 2
1229 && strncmp (argv
[1], "configure", length
) == 0) {
1230 return tcpServerObjectConfigCmd (clientData
, interp
, argc
-1, argv
+1);
1232 if (c
== 's' && length
>= 3 && strncmp (argv
[1], "start", length
) == 0) {
1233 return tcpServerObjectStartCmd (clientData
, interp
, argc
-1, argv
+1);
1235 if (c
== 's' && length
>= 3 && strncmp (argv
[1], "stop", length
) == 0) {
1236 return tcpServerObjectStopCmd (clientData
, interp
, argc
-1, argv
+1);
1238 Tcl_AppendResult (interp
, argv
[0], ": ", "bad option \"", argv
[1],
1239 "\": should be clients, configure, start, or stop",
1245 * tcpServerObjectAcceptCmd --
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.
1254 * Returns a standard TCL result. The return value is the name
1255 * of the client if the call is successful.
1258 * A Tcl command named after the client object is created.
1262 tcpServerObjectAcceptCmd (clientData
, interp
, argc
, argv
)
1263 ClientData clientData
;
1264 Tcl_Interp
* interp
;
1268 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1269 register Tcp_ClientData
* client
;
1276 /* Check command syntax */
1279 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
1280 argv
[0], "\"", (char *) NULL
);
1284 /* Create the client data structure */
1286 client
= (Tcp_ClientData
*) ckalloc (sizeof (Tcp_ClientData
));
1288 /* Accept the client's connection request */
1290 rubbish
= sizeof (struct sockaddr_in
);
1291 client
-> socketfd
= accept (server
-> socketfd
,
1292 (struct sockaddr
*) &(client
-> peeraddr
),
1294 if (client
-> socketfd
< 0) {
1295 Tcl_AppendResult (interp
, "can't accept connection: ",
1296 Tcl_UnixError (interp
), (char *) NULL
);
1297 ckfree ((char *) client
);
1301 /* Set up the socket for non-blocking I/O */
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) {
1312 nargv
[0] = "(also failed to close socket: ";
1313 nargv
[1] = Tcl_UnixError (interp
);
1315 message
= Tcl_Concat (nargc
, nargv
);
1316 Tcl_AddErrorInfo (interp
, message
);
1319 ckfree ((char *) client
);
1323 /* Set up the client's description */
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
;
1339 client
-> prev
= NULL
;
1340 server
-> firstClient
= client
;
1342 /* Create the Tcl command for the client */
1344 Tcl_CreateCommand (interp
, client
-> name
,
1345 (Tcl_CmdProc
*) tcpClientObjectCmd
,
1346 (ClientData
) client
,
1347 (Tcl_CmdDeleteProc
*) deleteTcpClientObjectCmd
);
1349 /* Return the client's name */
1351 Tcl_SetResult (interp
, client
-> name
, TCL_STATIC
);
1356 * tcpServerObjectClientsCmd --
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.
1363 tcpServerObjectClientsCmd (clientData
, interp
, argc
, argv
)
1364 ClientData clientData
;
1365 Tcl_Interp
* interp
;
1369 Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1370 Tcp_ClientData
* client
;
1375 Tcl_AppendResult (interp
, "wrong # args, should be\"", argv
[-1], " ",
1376 argv
[0], "\"", (char *) NULL
);
1380 for (client
= server
-> firstClient
; client
!= NULL
;
1381 client
= client
-> next
) {
1382 Tcl_AppendElement (interp
, client
-> name
, 0);
1389 * tcpServerObjectConfigCmd --
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.
1397 * Returns a standard Tcl result.
1401 tcpServerObjectConfigCmd (clientData
, interp
, argc
, argv
)
1402 ClientData clientData
;
1403 Tcl_Interp
* interp
;
1411 /* No arguments -- return a list of valid options. */
1414 Tcl_SetResult (interp
, "-command -port", TCL_STATIC
);
1418 /* One argument -- query a particular option */
1421 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1422 char * arg
= argv
[1];
1424 if (arg
[0] != '-') {
1425 Tcl_AppendResult (interp
, argv
[-1], " ", argv
[0],
1426 ": bad option \"", arg
,
1427 "\" -- each option must begin with a hyphen.",
1433 length
= strlen (++arg
);
1436 if (c
== 'c' && strncmp (arg
, "command", length
) == 0) {
1438 /* Command option -- Get the command name */
1440 Tcl_SetResult (interp
, server
->name
, TCL_STATIC
);
1445 if (c
== 'p' && strncmp (arg
, "port", length
) == 0) {
1447 /* Port option -- Get the port number */
1449 struct sockaddr_in portaddr
;
1450 int rubbish
= sizeof (struct sockaddr_in
);
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
);
1459 Tcl_ResetResult (interp
);
1460 sprintf (interp
-> result
, "%d", (int) ntohs (portaddr
.sin_port
));
1464 /* Unknown option */
1466 Tcl_AppendResult (interp
, argv
[-1], ": unknown option \"", arg
,
1467 "\" -- must be -command or -port", (char *) NULL
);
1471 return tcpServerObjectConfig (clientData
, interp
, argc
, argv
);
1475 * tcpServerObjectStartCmd --
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.
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
1493 tcpServerObjectStartCmd (clientData
, interp
, argc
, argv
)
1494 ClientData clientData
;
1495 Tcl_Interp
* interp
;
1499 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1502 /* Check command syntax */
1505 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
1506 argv
[0], "\"", (char *) NULL
);
1510 /* Listen at the server's socket */
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
);
1519 /* Add a file handler to gain control at tcpServerAcceptConnection
1520 * whenever a client attempts to connect.
1523 simpleCreateFileHandler (server
-> socketfd
, TK_READABLE
,
1524 (Tk_FileProc
*) tcpServerAcceptConnection
,
1530 * tcpServerObjectStopCmd
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
1539 * If one or more clients are active, the server does not shut down
1540 * until they can be closed properly.
1544 tcpServerObjectStopCmd (clientData
, interp
, argc
, argv
)
1545 ClientData clientData
;
1546 Tcl_Interp
* interp
;
1550 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1553 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
1554 " ", argv
[0], "\"", (char *) NULL
);
1558 /* Delete the server command */
1560 Tcl_DeleteCommand (interp
, server
-> name
);
1566 * deleteTcpServerObjectCmd --
1568 * This procedure is called when a server's object command is deleted.
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.
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.
1578 * Once all the clients are gone, tcpDeleteServer removes the server's
1579 * client data structure.
1583 deleteTcpServerObjectCmd (clientData
)
1584 ClientData clientData
;
1586 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
1587 register Tcp_ClientData
* client
, * nextClient
;
1589 /* Close the listen socket and delete its handler */
1591 simpleDeleteFileHandler (server
-> socketfd
);
1592 (void) close (server
-> socketfd
);
1593 server
-> socketfd
= -1;
1595 /* Close all clients */
1597 for (client
= server
-> firstClient
; client
!= NULL
; client
= nextClient
) {
1598 nextClient
= client
-> next
;
1599 if (client
-> activeFlag
)
1600 client
-> closeFlag
= 1;
1602 tcpCloseClient (client
);
1605 /* Remove the server from the list of servers. */
1607 if (server
-> next
!= NULL
)
1608 server
-> next
-> prev
= server
-> prev
;
1609 if (server
-> prev
!= NULL
)
1610 server
-> prev
-> next
= server
-> next
;
1612 tcpFirstServer
= server
-> next
;
1614 /* If all clients are closed, get to tcpDeleteServer now. Otherwise, set
1615 * the server's stop flag and return.
1618 if (server
-> firstClient
== NULL
) {
1619 tcpDeleteServer (server
);
1621 server
-> stopFlag
= 1;
1626 * tcpDeleteServer --
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.
1637 tcpDeleteServer (server
)
1638 Tcp_ServerData
* server
;
1640 /* Get rid of the server's initial command */
1642 if (server
-> command
!= NULL
&& server
-> freeCommand
!= NULL
) {
1643 (*(server
-> freeCommand
)) (server
-> command
);
1646 /* Get rid of the server's own data structure */
1648 (void) ckfree ((char *) server
);
1652 * tcpServerObjectConfig --
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.
1659 * In any case, the arguments are expected to contain zero or
1660 * more of the following:
1663 * Requests that the server listen at a specific port.
1664 * Default is whatever the system assigns.
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:
1672 * Default is "tcp login"
1675 * Puts the server in raw socket mode.
1678 * A standard TCL result.
1682 tcpServerObjectConfig (clientData
, interp
, argc
, argv
)
1683 ClientData clientData
;
1684 Tcl_Interp
* interp
;
1689 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
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" */
1702 /* Step through the parameters */
1704 for (a
= 1; a
< argc
; ++a
) {
1705 char * arg
= argv
[a
];
1707 if (arg
[0] != '-') {
1708 Tcl_AppendResult (interp
, argv
[-1], ": bad option \"", arg
,
1709 "\" -- each option must begin with a hyphen.",
1714 length
= strlen (++arg
);
1717 if (c
== 'c' && strncmp (arg
, "command", length
) == 0) {
1719 /* Command option -- Get the command name */
1723 Tcl_AppendResult (interp
, argv
[-1],
1724 ": \"-command\" must be followed by a string.",
1729 /* Free the old command name */
1731 if (server
-> freeCommand
!= NULL
) {
1732 (*(server
-> freeCommand
)) (server
-> command
);
1735 /* Put in the new command name */
1737 server
-> command
= (char *) malloc (strlen (argv
[a
]) + 1);
1738 strcpy (server
-> command
, argv
[a
]);
1739 server
-> freeCommand
= (Tcl_FreeProc
*) free
;
1741 } else if (c
== 'p' && strncmp (arg
, "port", length
) == 0) {
1743 /* Port option -- get the port number */
1747 struct sockaddr_in portaddr
;
1751 Tcl_AppendResult (interp
, argv
[-1],
1752 ": \"-port\" must be followed by a number.",
1757 status
= Tcl_GetInt (interp
, portstr
, &portno
);
1758 if (status
) return status
;
1760 /* Set the port number */
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
);
1774 } else if (c
== 'r' && strncmp (arg
, "raw", length
) == 0) {
1776 /* raw option -- set raw socket mode */
1782 /* Unknown option */
1784 Tcl_AppendResult (interp
, argv
[-1],
1785 ": unknown option \"", arg
- 1,
1786 "\" -- must be -command or -port", (char *) NULL
);
1792 Tcl_SetResult (interp
, server
-> name
, TCL_STATIC
);
1797 * tcpClientObjectCmd --
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
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
1811 * Concatenate the client's command with ?args?,
1812 * and execute the result. Called in background
1813 * when a command arrives and on initial
1816 * Returns the name of the host where the client
1819 * Returns the name of the server to which the client
1824 tcpClientObjectCmd (clientData
, interp
, argc
, argv
)
1825 ClientData clientData
;
1826 Tcl_Interp
* interp
;
1834 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[0],
1835 " command ?args?\"", (char *) NULL
);
1840 length
= strlen (argv
[1]);
1842 if (c
== 'c' && length
>= 2 && strncmp (argv
[1], "close", length
) == 0) {
1843 return tcpClientObjectCloseCmd (clientData
, interp
, argc
-1, argv
+1);
1845 if (c
== 'c' && length
>= 2 && strncmp (argv
[1], "command", length
) == 0) {
1846 return tcpClientObjectCommandCmd (clientData
, interp
, argc
-1, argv
+1);
1848 if (c
== 'd' && strncmp (argv
[1], "do", length
) == 0) {
1849 return tcpClientObjectDoCmd (clientData
, interp
, argc
-1, argv
+1);
1851 if (c
== 'h' && strncmp (argv
[1], "hostname", length
) == 0) {
1852 return tcpClientObjectHostnameCmd (clientData
, interp
, argc
-1, argv
+1);
1854 if (c
== 's' && strncmp (argv
[1], "server", length
) == 0) {
1855 return tcpClientObjectServerCmd (clientData
, interp
, argc
-1, argv
+1);
1858 Tcl_AppendResult (interp
, "bad option \"", argv
[1],
1859 "\": should be close, command, do, hostname or server",
1865 * tcpClientObjectCloseCmd --
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.
1873 tcpClientObjectCloseCmd (clientData
, interp
, argc
, argv
)
1874 ClientData clientData
;
1875 Tcl_Interp
* interp
;
1879 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
1882 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
1883 argv
[0], "\"", (char *) NULL
);
1887 if (client
-> activeFlag
)
1888 client
-> closeFlag
= 1;
1890 tcpCloseClient (client
);
1896 * tcpClientObjectCommandCmd --
1898 * Query/change the command associated with a client object
1901 * $client command ?newcommand?
1904 * A standard Tcl result containing the client's command.
1908 tcpClientObjectCommandCmd (clientData
, interp
, argc
, argv
)
1909 ClientData clientData
;
1910 Tcl_Interp
* interp
;
1914 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
1919 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
1920 argv
[0], " ?command?\"", (char *) NULL
);
1924 /* Set command if necessary */
1927 if (client
-> freeCommand
!= (Tcl_FreeProc
*) NULL
) {
1928 (*client
-> freeCommand
) (client
-> command
);
1930 client
-> command
= malloc (strlen (argv
[1]) + 1);
1931 strcpy (client
-> command
, argv
[1]);
1932 client
-> freeCommand
= (Tcl_FreeProc
*) free
;
1935 /* Return command in any case */
1937 Tcl_SetResult (interp
, client
-> command
, TCL_STATIC
);
1943 * tcpClientObjectDoCmd --
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
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
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.
1961 tcpClientObjectDoCmd (clientData
, interp
, argc
, argv
)
1962 ClientData clientData
;
1963 Tcl_Interp
* interp
;
1968 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
1976 /* Check command syntax */
1979 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1],
1980 " ", argv
[0], " ?command?\"", (char *) NULL
);
1984 prevClient
= tcpCurrentClient
;
1985 tcpCurrentClient
= client
-> name
;
1987 /* Evaluate the client's command, passing the client name and message */
1990 client
-> activeFlag
= 1;
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';
1998 excmd
= (char *) NULL
;
2001 status
= Tcl_VarEval (interp
, client
-> command
, " ", client
-> name
, " ",
2002 excmd
, (char *) NULL
);
2007 if (status
!= TCL_OK
&& argc
< 2) {
2011 client
-> activeFlag
= 0;
2012 tcpCurrentClient
= prevClient
;
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.
2019 if (! (client
-> server
-> raw
)) {
2020 tcpReturnResultToClient (client
, interp
, status
, closeflag
);
2022 tcpPrepareClientForInput (client
);
2025 /* The client may have been closed by the ReturnResult operation. DON'T
2026 * USE IT AFTER THIS POINT.
2033 * tcpClientObjectHostnameCmd --
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
2041 tcpClientObjectHostnameCmd (clientData
, interp
, argc
, argv
)
2042 ClientData clientData
;
2043 Tcl_Interp
* interp
;
2047 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2049 struct hostent
* hostdesc
;
2052 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
2053 argv
[0], "\"", (char *) NULL
);
2057 hostdesc
= gethostbyaddr ((char *) &(client
-> peeraddr
.sin_addr
.s_addr
),
2058 sizeof (client
-> peeraddr
.sin_addr
.s_addr
),
2061 if (hostdesc
!= (struct hostent
*) NULL
) {
2062 Tcl_SetResult (interp
, hostdesc
-> h_name
, TCL_VOLATILE
);
2064 Tcl_SetResult (interp
, inet_ntoa (client
-> peeraddr
.sin_addr
),
2072 * tcpClientObjectServerCmd --
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
2080 tcpClientObjectServerCmd (clientData
, interp
, argc
, argv
)
2081 ClientData clientData
;
2082 Tcl_Interp
* interp
;
2086 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2089 Tcl_AppendResult (interp
, "wrong # args: should be \"", argv
[-1], " ",
2090 argv
[0], "\"", (char *) NULL
);
2094 Tcl_SetResult (interp
, client
-> server
-> name
, TCL_STATIC
);
2100 * deleteTcpClientObjectCmd --
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
2107 * This procedure does all the cleanup necessary to getting rid of the
2112 deleteTcpClientObjectCmd (clientData
)
2113 ClientData clientData
;
2115 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2116 register Tcp_ServerData
* server
= client
-> server
;
2118 /* Make sure the client is really dead. */
2120 if (client
-> activeFlag
) {
2121 fprintf (stderr
, "attempt to delete an active TCP client!\n\n");
2125 /* Remove any handler for data on the client's socket. */
2127 simpleDeleteFileHandler (client
-> socketfd
);
2129 /* Now it's safe to close the socket */
2131 (void) close (client
-> socketfd
);
2133 /* Get rid of the command */
2135 if (client
-> command
!= NULL
&& client
-> freeCommand
!= NULL
) {
2136 (*(client
-> freeCommand
)) (client
-> command
);
2139 /* Get rid of the input buffer */
2141 Tcl_DeleteCmdBuf (client
-> inputBuffer
);
2143 /* Get rid of any pending result */
2145 if (client
-> resultString
!= NULL
&& client
-> freeResultString
!= NULL
) {
2146 (*(client
-> freeResultString
)) (client
-> resultString
);
2149 /* Unlink the client from the list of active clients */
2151 if (client
-> prev
== NULL
)
2152 client
-> server
-> firstClient
= client
-> next
;
2154 client
-> prev
-> next
= client
-> next
;
2156 if (client
-> next
!= NULL
)
2157 client
-> next
-> prev
= client
-> prev
;
2159 /* Now it's ok to destroy the client's data structure */
2161 ckfree ((char *) client
);
2163 /* Handle a deferred close on the server if necessary */
2165 if (server
-> stopFlag
&& server
-> firstClient
== NULL
)
2166 tcpDeleteServer (server
);
2170 * tcpConnectionObjectCmd --
2172 * This procedure is invoked to process the object command for a client-
2173 * side connection object. It takes a couple of diferent forms:
2176 * Closes the connection.
2177 * $connection send arg ?arg....?
2178 * Catenates the arguments into a Tcl command, and sends them
2183 tcpConnectionObjectCmd (clientData
, interp
, argc
, argv
)
2184 ClientData clientData
;
2185 Tcl_Interp
* interp
;
2194 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[0], " ",
2195 "command ?args?\"", (char *) NULL
);
2201 length
= strlen (arg
);
2203 if (c
== 'c' && strncmp (arg
, "close", length
) == 0) {
2204 return tcpConnectionObjectCloseCmd (clientData
, interp
, argc
-1, argv
+1);
2206 if (c
== 's' && strncmp (arg
, "send", length
) == 0) {
2207 return tcpConnectionObjectSendCmd (clientData
, interp
, argc
-1, argv
+1);
2210 Tcl_AppendResult (interp
, "unknown command \"", arg
,
2211 "\": must be close or send", (char *) NULL
);
2216 * tcpConnectionObjectCloseCmd --
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
2225 tcpConnectionObjectCloseCmd (clientData
, interp
, argc
, argv
)
2226 ClientData clientData
;
2227 Tcl_Interp
* interp
;
2232 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
2233 argv
[0], "\"", (char *) NULL
);
2237 Tcl_DeleteCommand (interp
, argv
[-1]);
2242 * tcpConnectionObjectSendCmd --
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
2251 tcpConnectionObjectSendCmd (clientData
, interp
, argc
, argv
)
2252 ClientData clientData
;
2253 Tcl_Interp
* interp
;
2258 int f
= (int) clientData
;
2262 Tcl_AppendResult (interp
, "wrong # args, should be \"", argv
[-1], " ",
2263 argv
[0], " command\"", (char *) NULL
);
2267 /* Paste together the message */
2269 message
= Tcl_Merge (argc
-1, argv
+1);
2271 /* Send the command to the server */
2273 status
= tcpSendCmdToServer (interp
, f
, message
);
2274 if (status
!= TCL_OK
)
2277 /* Get the server's reply */
2279 return tcpReceiveResultFromServer (interp
, f
);
2283 * deleteTcpConnectionObjectCmd --
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.
2290 deleteTcpConnectionObjectCmd (clientData
)
2291 ClientData clientData
;
2293 int f
= (int) clientData
;
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.
2306 tcpCloseClient (client
)
2307 Tcp_ClientData
* client
;
2309 if (client
-> activeFlag
)
2312 /* Deleting the client command is all we need to do -- the delete
2313 * procedure does everything else.
2316 Tcl_DeleteCommand (client
-> server
-> interp
, client
-> name
);
2320 * tcpServerAcceptConnection --
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
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
2334 tcpServerAcceptConnection (clientData
, mask
)
2335 ClientData clientData
;
2338 register Tcp_ServerData
* server
= (Tcp_ServerData
*) clientData
;
2344 /* Accept the connection with `$server accept' */
2346 status
= Tcl_VarEval (server
-> interp
, server
-> name
, " accept",
2349 /* On success, try to execute the client's command with `$client do' */
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
);
2356 /* Client may have been closed at this point. Don't refer to it again. */
2359 if (status
!= TCL_OK
) {
2360 simpleReportBackgroundError (server
-> interp
);
2362 Tcl_ResetResult (server
-> interp
);
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
2373 * Returns a Boolean value that is TRUE iff the host is trusted.
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.
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)))
2388 #define HOSTCMP( name1, name2 ) \
2389 strcmp ((name1), (name2))
2393 tcpTrustedHost (hostName
)
2396 char localName
[128];
2397 struct hostent
* hostEnt
;
2398 struct in_addr hostAddr
;
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,
2410 /* Start assembling a list of possibilities for the host name. First
2411 * possibility is the name that the kernel returns as hostname ().
2414 unixStatus
= gethostname (localName
, 127);
2415 if (unixStatus
>= 0) {
2417 if (!HOSTCMP( hostName
, localName
)) return 1;
2419 /* Next possibility is a.b.c.d notation for all of the local addresses,
2420 * plus all the nicknames for the host.
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;
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;
2443 /* Finally, there's the possibility of the loopback address, and all of
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;
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;
2472 * tcpReturnResultToClient --
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.
2478 * It then calls tcpWriteResultToClient to try to start sending the
2483 tcpReturnResultToClient (client
, interp
, status
, closeflag
)
2484 Tcp_ClientData
* client
;
2485 Tcl_Interp
* interp
;
2494 /* Put together a message comprising the return status and the interpreter
2497 sprintf (rint
, "%d", status
);
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");
2506 client
-> resultPointer
= client
-> resultString
;
2507 client
-> freeResultString
= (Tcl_FreeProc
*) free
;
2509 Tcl_ResetResult (interp
);
2510 client
-> closeFlag
|= closeflag
;
2512 /* Now try to send the reply. */
2514 tcpWriteResultToClient ((ClientData
) client
, TK_WRITABLE
);
2516 /* tcpWriteResultToClient closes the client if it fails; don't depend on
2517 * having the client still be usable. */
2521 * tcpWriteResultToClient --
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.
2531 tcpWriteResultToClient (clientData
, mask
)
2532 ClientData clientData
;
2535 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2540 length
= strlen (client
-> resultPointer
);
2542 /* Issue the write */
2544 unixStatus
= write (client
-> socketfd
, client
-> resultPointer
,
2547 /* Test for a total failure */
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.
2557 unixStatus
= 0; /* Pretend that EWOULDBLOCK succeeded at
2558 * writing zero characters. */
2562 /* Test for a partial success */
2564 if (unixStatus
< length
) {
2565 client
-> resultPointer
+= unixStatus
;
2566 simpleCreateFileHandler (client
-> socketfd
, TK_WRITABLE
,
2567 (Tk_FileProc
*) tcpWriteResultToClient
,
2571 /* Total success -- prepare the client for the next input */
2574 if (client
-> freeResultString
!= NULL
) {
2575 (*(client
-> freeResultString
)) (client
-> resultString
);
2577 client
-> resultString
= client
-> resultPointer
= (char *) NULL
;
2578 client
-> freeResultString
= (Tcl_FreeProc
*) NULL
;
2579 simpleDeleteFileHandler (client
-> socketfd
);
2580 if (client
-> closeFlag
) {
2581 tcpCloseClient (client
);
2583 /* After tcpCloseClient executes, the client goes away. Don't depend
2584 on it's still being there. */
2587 tcpPrepareClientForInput (client
);
2593 * tcpPrepareClientForInput --
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.
2601 tcpPrepareClientForInput (client
)
2602 Tcp_ClientData
* client
;
2604 simpleCreateFileHandler (client
-> socketfd
, TK_READABLE
,
2605 (Tk_FileProc
*) tcpReceiveClientInput
,
2606 (ClientData
) client
);
2610 * tcpReceiveClientInput --
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.
2620 tcpReceiveClientInput (clientData
, mask
)
2621 ClientData clientData
;
2624 register Tcp_ClientData
* client
= (Tcp_ClientData
*) clientData
;
2625 register Tcp_ServerData
* server
= client
-> server
;
2626 register Tcl_Interp
* interp
= server
-> interp
;
2628 static char buffer
[BUFSIZ
+1];
2637 /* Try to read from the client */
2640 unixStatus
= read (client
-> socketfd
, buffer
, BUFSIZ
);
2641 if (unixStatus
<= 0 && errno
!= EWOULDBLOCK
)
2642 tcpClientReadError (client
);
2644 /* tcpClientReadError closes the client and reports the error.
2645 In any case, if the read failed, we want to return. */
2647 if (unixStatus
<= 0)
2650 if (server
-> raw
) {
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
);
2657 tcpPrepareClientForInput (client
);
2661 /* Assemble the received data into the buffer */
2663 buffer
[unixStatus
] = '\0';
2664 command
= Tcl_AssembleCmd (client
-> inputBuffer
, buffer
);
2665 if (command
!= (char *) NULL
) {
2667 /* Process the received command. */
2669 simpleDeleteFileHandler (client
-> socketfd
);
2671 argv
[0] = client
-> name
;
2674 docmd
= Tcl_Merge (argc
, argv
);
2675 status
= Tcl_Eval (interp
, docmd
, 0, (char * *) NULL
);
2678 /* At this point, the client may have been closed. Don't try to
2681 if (status
!= TCL_OK
) {
2682 simpleReportBackgroundError (interp
);
2688 /* tcpClientReadError --
2690 * This procedure is called when an attempt to read the command from a
2691 * client fails. There are two possibilities:
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.
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.
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.
2706 tcpClientReadError (client
)
2707 Tcp_ClientData
* client
;
2709 Tcp_ServerData
* server
= client
-> server
;
2710 Tcl_Interp
* interp
= server
-> interp
;
2717 status
= Tcl_VarEval (interp
, "error {", client
-> name
, ": read error: ",
2718 Tcl_UnixError (interp
), "}", (char *) NULL
);
2719 simpleReportBackgroundError (interp
);
2725 status
= Tcl_VarEval (interp
, client
-> name
, " do", (char *) NULL
);
2726 if (status
!= TCL_OK
)
2727 simpleReportBackgroundError (interp
);
2730 tcpCloseClient (client
);
2733 /* tcpClientWriteError --
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.
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.
2743 tcpClientWriteError (client
)
2744 Tcp_ClientData
* client
;
2746 Tcp_ServerData
* server
= client
-> server
;
2747 Tcl_Interp
* interp
= server
-> interp
;
2749 (void) Tcl_VarEval (interp
, "error {", client
-> name
, ": read error: ",
2750 Tcl_UnixError (interp
), "}", (char *) NULL
);
2751 simpleReportBackgroundError (interp
);
2752 tcpCloseClient (client
);
2755 /* tcpSendCmdToServer --
2757 * This procedure is invoked to send a command originated by a client
2758 * using the `$connection send' Tcl command.
2760 * The message is passed without a newline appended. The server requires
2761 * a newline, which is sent in a separate call.
2765 tcpSendCmdToServer (interp
, s
, message
)
2766 Tcl_Interp
* interp
;
2773 static char newline
= '\n';
2774 void (*oldPipeHandler
) ();
2776 /* Set the socket for blocking I/O */
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
);
2786 /* Keep a possible broken pipe from killing us silently */
2788 oldPipeHandler
= signal (SIGPIPE
, SIG_IGN
);
2790 /* Write the message */
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
);
2801 /* Write the terminating newline */
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
);
2811 (void) signal (SIGPIPE
, oldPipeHandler
);
2816 * tcpReceiveResultFromServer --
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.
2825 tcpReceiveResultFromServer (interp
, s
)
2826 Tcl_Interp
* interp
;
2833 struct timeval tick
;
2834 struct timeval
* tickp
;
2836 char buf
[BUFSIZ
+1];
2842 /* Read the result using non-blocking I/O */
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
);
2852 /* Make a buffer to receive the result */
2854 cmdbuf
= Tcl_CreateCmdBuf ();
2856 /* Wait for the result to appear */
2858 tickp
= (struct timeval
*) 0;
2859 FD_ZERO( &readfds
);
2860 FD_SET( s
, &readfds
);
2863 unixStatus
= select (s
+ 1, &readfds
, (fd_set
*) NULL
, (fd_set
*) NULL
,
2866 if (unixStatus
< 0) {
2868 Tcl_AppendResult (interp
, "error selecting socket for reply: ",
2869 Tcl_UnixError (interp
), (char *) NULL
);
2873 if (unixStatus
== 0) {
2875 Tcl_SetResult (interp
, "timed out waiting for server reply", TCL_STATIC
);
2879 /* Read the result */
2881 unixStatus
= read (s
, buf
, BUFSIZ
);
2883 if (unixStatus
< 0) {
2885 Tcl_AppendResult (interp
, "error reading server reply: ",
2886 Tcl_UnixError (interp
), (char *) NULL
);
2890 if (unixStatus
== 0) {
2892 Tcl_SetResult (interp
, "Connection closed.", TCL_STATIC
);
2896 /* Parse the (partial) command */
2898 buf
[unixStatus
] = '\0';
2899 reply
= Tcl_AssembleCmd (cmdbuf
, buf
);
2900 if (reply
!= NULL
) {
2905 /* Partial command not yet complete. Set timeout for reading the
2906 * rest of the result. */
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. */
2918 /* Split the list elements */
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
);
2928 /* Verify the element count */
2930 if (status
== TCL_OK
) {
2932 Tcl_SetResult (interp
, "server returned malformed list", TCL_STATIC
);
2934 free ((char *) rargv
);
2936 status
= Tcl_GetInt (interp
, rargv
[0], &rstatus
);
2937 if (status
!= TCL_OK
) {
2938 Tcl_SetResult (interp
, "server returned unrecognizable status",
2941 free ((char *) rargv
);
2946 /* Return the result reported by the server */
2948 if (status
== TCL_OK
) {
2949 Tcl_SetResult (interp
, rargv
[1], TCL_VOLATILE
);
2951 free ((char *) rargv
);
2954 Tcl_DeleteCmdBuf (cmdbuf
);