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