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