]>
cvs.zerfleddert.de Git - micropolis/blob - src/tk/tkrawtcp.c
4 * This file contains a simple Tcl "connect" command
5 * that returns an standard Tcl File descriptor (as would
6 * be returned by Tcl_OpenCmd).
7 * Extended to create servers, accept connections, shutdown parts of full
8 * duplex connections and handle UNIX domain sockets.
10 * Author: Pekka Nikander <pnr@innopoli.ajk.tele.fi>
11 * Modified: Tim MacKenzie <tym@dibbler.cs.monash.edu.au)
13 * Copyright 1992 Telecom Finland
15 * Permission to use, copy, modify, and distribute this
16 * software and its documentation for any purpose and without
17 * fee is hereby granted, provided that this copyright
18 * notice appears in all copies. Telecom Finland
19 * makes no representations about the suitability of this
20 * software for any purpose. It is provided "as is" without
21 * express or implied warranty.
23 * Created: Sun Mar 22 18:20:29 1992
24 * based on: Last modified: Sun Mar 22 21:34:31 1992 pnr
25 * Last modified: Mon Jun 29 15:25:14 EST 1992 tym
30 static char rcsid
[] = "...";
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
42 #include <arpa/inet.h>
48 static int inet_connect
_ANSI_ARGS_((char *host
, char *port
,int server
));
49 static int unix_connect
_ANSI_ARGS_((char *path
, int server
));
50 static void HandleSocket
_ANSI_ARGS_ ((ClientData clientData
, int mask
));
60 *------------------------------------------------------------------
64 * Set up on OpenFile structure in the interpreter for a newly
71 * Adds an OpenFile to the list.
72 *------------------------------------------------------------------
77 Tcp_MakeOpenFile(interp
,fd
,r
,w
)
81 {/* Create an OpenFile structure using f and install it in the interpreter with
82 * Readable and Writable set to r and w
84 Interp
*iPtr
= (Interp
*) interp
;
85 register OpenFile
*filePtr
;
87 filePtr
= (OpenFile
*) ckalloc(sizeof(OpenFile
));
92 /* Open the file with the correct type (doesn't handle !r && !w) */
94 filePtr
->f
= fdopen(fd
,(r
&&w
)?"rb+":(r
?"rb":"wb"));
96 filePtr
->f
= fdopen(fd
,(r
&&w
)?"r+":(r
?"r":"w"));
98 /* Don't do buffered communication if full-duplex... it breaks! */
99 if (r
&w
) setbuf(filePtr
->f
,0);
101 filePtr
->readable
= r
;
102 filePtr
->writable
= w
;
103 filePtr
->numPids
= 0;
104 filePtr
->pidPtr
= NULL
;
105 filePtr
->errorId
= -1;
108 * Enter this new OpenFile structure in the table for the
109 * interpreter. May have to expand the table to do this.
112 TclMakeFileTable(iPtr
, fd
);
113 if (iPtr
->filePtrArray
[fd
] != NULL
) {
114 panic("Tcl_OpenCmd found file already open");
116 iPtr
->filePtrArray
[fd
] = filePtr
;
120 *------------------------------------------------------------------
124 * Open a socket connection to a given host and service.
127 * A standard Tcl result.
130 * An open socket connection.
131 * Sets the global variable connect_info(file%d) to the obtained
132 * port when setting up server.
133 *------------------------------------------------------------------
138 Tcp_ConnectCmd(notUsed
, interp
, argc
, argv
)
144 Interp
*iPtr
= (Interp
*) interp
;
150 if (argc
!= 2 && argc
!= 3 &&
151 (argc
!= 4 || (argc
== 4 && strcmp(argv
[1],"-server")))) {
152 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
153 "[{-server}] address_spec\"", (char *) NULL
);
157 if (!strcmp(argv
[1],"-server"))
161 * Create the connection
163 if (argc
- server
== 2) {/* Unix domain socket */
165 fd
= unix_connect(argv
[1+server
],server
);
167 fd
= inet_connect(argv
[1+server
], argv
[2+server
],server
);
170 /* Tell them why it fell apart */
173 Tcl_AppendResult(interp
,
174 "Couldn't setup listening socket with path \"",
175 argv
[1+server
],"\" : ",Tcl_UnixError(interp
),
178 Tcl_AppendResult(interp
,
179 "Couldn't connect to \"",argv
[1],"\" : ",
180 Tcl_UnixError(interp
),(char *) NULL
);
183 Tcl_AppendResult(interp
,
184 "couldn't setup listening socket on port:",
185 atoi(argv
[3])==0?"any":argv
[3]," using address \"",
186 strlen(argv
[2])?argv
[2]:"anywhere.","\": ",
187 Tcl_UnixError(interp
), (char *)NULL
);
189 Tcl_AppendResult(interp
, "couldn't open connection to \"",
190 argv
[1], "\" port \"", argv
[2], "\": ",
191 Tcl_UnixError(interp
), (char *) NULL
);
195 sprintf(interp
->result
, "file%d", fd
);
196 if (server
&& !unicks
) {
197 /* Find out what port we got */
199 struct sockaddr_in sockaddr
;
200 int res
,len
=sizeof(sockaddr
);
201 res
=getsockname(fd
,(struct sockaddr
*) &sockaddr
, &len
);
203 sprintf(buf
,"%d",errno
);
205 sprintf(buf
,"%d",(int)ntohs(sockaddr
.sin_port
));
206 Tcl_SetVar2(interp
,"connect_info",interp
->result
,buf
,TCL_GLOBAL_ONLY
);
209 Tcp_MakeOpenFile(iPtr
,fd
,1,1-server
);
215 *------------------------------------------------------------------
219 * Shutdown a socket for reading writing or both using shutdown(2)
222 * standard tcl result.
225 * Modifies the OpenFile structure appropriately
226 *------------------------------------------------------------------
231 Tcp_ShutdownCmd(notUsed
, interp
, argc
, argv
)
237 Interp
*iPtr
= (Interp
*) interp
;
244 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
245 " fileid <option>\"", (char *) NULL
);
249 if (TclGetOpenFile(interp
, argv
[1], &filePtr
) != TCL_OK
) {
254 fd
= fileno(filePtr
->f
);
255 if (!strcmp(argv
[2],"0") || !strcmp(argv
[2],"receives") ||
256 !strcmp(argv
[2],"read")) {
257 if (!filePtr
->readable
) {
258 Tcl_AppendResult(interp
, "File is not readable",(char *) NULL
);
261 if (shutdown(fd
,0)) {
262 Tcl_AppendResult(interp
, "shutdown: ", Tcl_UnixError(interp
),
267 } else if (!strcmp(argv
[2],"1") || !strcmp(argv
[2],"sends") ||
268 !strcmp(argv
[2],"write")) {
269 if (!filePtr
->writable
) {
270 Tcl_AppendResult(interp
, "File is not writable",(char *) NULL
);
273 if (shutdown(fd
,1)) {
274 Tcl_AppendResult(interp
, "shutdown: ", Tcl_UnixError(interp
),
279 } else if (!strcmp(argv
[2],"2") || !strcmp(argv
[2],"all") ||
280 !strcmp(argv
[2],"both")) {
281 if (shutdown(fd
,2)) {
282 Tcl_AppendResult(interp
, "shutdown: ", Tcl_UnixError(interp
),
296 *------------------------------------------------------------------
300 * Accept a connection on a listening socket
303 * a standard tcl result
307 * Sets the global variable connect_info(file%d) to a list
308 * containing the remote address (host ip, port) of the
310 *------------------------------------------------------------------
315 Tcp_AcceptCmd(notUsed
, interp
, argc
, argv
)
321 Interp
*iPtr
= (Interp
*) interp
;
322 struct sockaddr_in sockaddr
;
323 int len
= sizeof sockaddr
;
329 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
330 " listening_socket\"", (char *) NULL
);
334 if (TclGetOpenFile(interp
, argv
[1], &filePtr
) != TCL_OK
) {
337 if (!filePtr
->readable
) {
338 Tcl_AppendResult(interp
, "\"", argv
[1],
339 "\" wasn't opened for reading", (char *) NULL
);
344 fd
= fileno(filePtr
->f
);
346 fd
= accept(fd
,(struct sockaddr
*)&sockaddr
,&len
);
348 Tcl_AppendResult(interp
, "system error in accept()", (char *)NULL
);
352 {/* Set the global connect_info */
355 if (sockaddr
.sin_family
== AF_INET
)
356 sprintf(buf
,"%s %d",inet_ntoa(sockaddr
.sin_addr
),
357 ntohs(sockaddr
.sin_port
));
359 buf
[0]=0; /* Empty string for UNIX domain sockets */
360 sprintf(nm
,"file%d",fd
);
361 Tcl_SetVar2(interp
,"connect_info",nm
,buf
,TCL_GLOBAL_ONLY
);
367 Tcp_MakeOpenFile(iPtr
,fd
,1,1);
369 sprintf(interp
->result
, "file%d", fd
);
374 *----------------------------------------------------------------
378 * Create a (unix_domain) fd connection using given rendeavous
385 *----------------------------------------------------------------
389 unix_connect(path
,server
)
390 char *path
; /* Path name to create or use */
391 int server
; /* 1->make server, 0->connect to server */
393 struct sockaddr_un sockaddr
;
397 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
402 sockaddr
.sun_family
= AF_UNIX
;
403 strncpy(sockaddr
.sun_path
,path
,sizeof(sockaddr
.sun_path
)-1);
404 sockaddr
.sun_path
[sizeof(sockaddr
.sun_path
)-1] = 0; /* Just in case */
407 status
= bind(sock
,(struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
409 status
= connect(sock
, (struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
425 *----------------------------------------------------------------
429 * Create a (inet domain) fd connection to given host and port.
436 *----------------------------------------------------------------
440 inet_connect(host
, service
,server
)
441 char *host
; /* Host to connect, name or IP address */
442 char *service
; /* Port to use, service name or port number */
445 struct hostent
*hostent
, _hostent
;
446 struct servent
*servent
, _servent
;
447 struct protoent
*protoent
;
448 struct sockaddr_in sockaddr
;
450 int hostaddr
, hostaddrPtr
[2];
454 hostent
= gethostbyname(host
);
455 if (hostent
== NULL
) {
456 hostaddr
= inet_addr(host
);
457 if (hostaddr
== -1) {
458 if (server
&& !strlen(host
))
459 hostaddr
= INADDR_ANY
;
465 _hostent
.h_addr_list
= (char **)hostaddrPtr
;
466 _hostent
.h_addr_list
[0] = (char *)&hostaddr
;
467 _hostent
.h_addr_list
[1] = NULL
;
468 _hostent
.h_length
= sizeof(hostaddr
);
469 _hostent
.h_addrtype
= AF_INET
;
472 servent
= getservbyname(service
, "tcp");
473 if (servent
== NULL
) {
474 servport
= htons(atoi(service
));
475 if (servport
== -1) {
479 _servent
.s_port
= servport
;
480 _servent
.s_proto
= "tcp";
483 protoent
= getprotobyname(servent
->s_proto
);
484 if (protoent
== NULL
) {
489 sock
= socket(PF_INET
, SOCK_STREAM
, protoent
->p_proto
);
494 sockaddr
.sin_family
= AF_INET
;
495 memcpy((char *)&(sockaddr
.sin_addr
.s_addr
),
496 (char *) hostent
->h_addr_list
[0],
497 (size_t) hostent
->h_length
);
498 sockaddr
.sin_port
= servent
->s_port
;
501 status
= bind(sock
,(struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
503 status
= connect(sock
, (struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
519 *----------------------------------------------------------------
521 * Tcp_FileHandlerCmd --
523 * Register a file handler with an open file. If there is
524 * already and existing handler, it will be no longer called.
525 * If no mask and command are given, any existing handler
529 * A standard Tcl result. (Always OK).
532 * A new file handler is associated with a give TCL open file.
533 * Whenever the file is readable, writeable and/or there is
534 * an expection condition on the file, a user supplied TCL
537 *----------------------------------------------------------------
542 Tcp_FileHandlerCmd(notUsed
, interp
, argc
, argv
)
552 if (argc
!= 2 && argc
!= 4) {
553 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
554 " fileId ?mode command?\"", (char *) NULL
);
558 if (TclGetOpenFile(interp
, argv
[1], &filePtr
) != TCL_OK
) {
564 * NOTE! Currently the cmdPtr structure will be left
565 * *unfreed* if the file handler is deleted
566 * via this code. Tough. Would need a hash table
569 Tk_DeleteFileHandler(fileno(filePtr
->f
));
574 * Find out on what situations the user is interested in.
575 * This is not the most elegant or efficient way to do this,
576 * but who cares? (I do, but not much enough :-)
579 if (strchr(argv
[2], 'r')) {
582 if (strchr(argv
[2], 'w')) {
585 if (strchr(argv
[2], 'e')) {
586 mask
|= TK_EXCEPTION
;
588 if (mask
== 0 || (strlen(argv
[2]) != strspn(argv
[2], "rwe"))) {
589 Tcl_AppendResult(interp
, "bad mask argument \"", argv
[2],
590 "\": should be any combination of \"r\", \"w\" and \"e\"",
596 cmdPtr
= (FileCmd
*)ckalloc(sizeof(FileCmd
));
597 cmdPtr
->interp
= interp
;
598 cmdPtr
->filePtr
= filePtr
;
599 cmdPtr
->tclCmd
= ckalloc(strlen(argv
[3]) + 1);
600 strcpy(cmdPtr
->tclCmd
, argv
[3]);
601 cmdPtr
->fileId
= ckalloc(strlen(argv
[1]) + 1);
602 strcpy(cmdPtr
->fileId
, argv
[1]);
605 * NOTE! There may be an earlier file handler. Should do something.
607 Tk_CreateFileHandler(fileno(filePtr
->f
), mask
, HandleSocket
,
608 (ClientData
) cmdPtr
);
613 *----------------------------------------------------------------
617 * This procedure is called from Tk_DoOneEvent whenever there is
618 * a desired condition on a given open socket. An Tcl command
619 * given by the user is executed to handle the connection. If
620 * and EOF or ERROR condition is noticed, all memory resources
621 * associated with the socket are released and the socket is closed.
627 * The user supplied command can do anything.
629 *----------------------------------------------------------------
633 HandleSocket(clientData
, mask
)
634 ClientData clientData
;
638 FileCmd
*cmdPtr
= (FileCmd
*) clientData
;
639 OpenFile
*filePtr
= cmdPtr
->filePtr
;
640 Tcl_Interp
*interp
= cmdPtr
->interp
;
643 int fd
= fileno(filePtr
->f
);
645 Tk_Preserve((ClientData
)cmdPtr
);
648 if (TclGetOpenFile(interp
, cmdPtr
->fileId
, &dummy
) != TCL_OK
) {
649 /* File is closed! */
650 Tcl_ResetResult(interp
);
653 assert(dummy
== cmdPtr
->filePtr
);
655 if (mask
& TK_READABLE
) {
656 result
= Tcl_VarEval(interp
, cmdPtr
->tclCmd
, " r ", cmdPtr
->fileId
,
658 if (result
!= TCL_OK
) {
662 if (mask
& TK_WRITABLE
) {
663 result
= Tcl_VarEval(interp
, cmdPtr
->tclCmd
, " w ", cmdPtr
->fileId
,
665 if (result
!= TCL_OK
) {
669 if (mask
& TK_EXCEPTION
) {
670 result
= Tcl_VarEval(interp
, cmdPtr
->tclCmd
, " e ", cmdPtr
->fileId
,
672 if (result
!= TCL_OK
) {
677 if (feof(filePtr
->f
) || ferror(filePtr
->f
)) {
678 result
= Tcl_VarEval(interp
, "close ", cmdPtr
->fileId
,
680 if (result
!= TCL_OK
) {
687 Tk_Release((ClientData
)cmdPtr
);
690 Tk_DeleteFileHandler(fd
);
691 Tk_EventuallyFree((ClientData
)cmdPtr
, (Tk_FreeProc
*)free
);