]>
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 (Tcl_Interp
*interp
, int fd
, int r
, int w
)
78 {/* Create an OpenFile structure using f and install it in the interpreter with
79 * Readable and Writable set to r and w
81 Interp
*iPtr
= (Interp
*) interp
;
82 register OpenFile
*filePtr
;
84 filePtr
= (OpenFile
*) ckalloc(sizeof(OpenFile
));
89 /* Open the file with the correct type (doesn't handle !r && !w) */
91 filePtr
->f
= fdopen(fd
,(r
&&w
)?"rb+":(r
?"rb":"wb"));
93 filePtr
->f
= fdopen(fd
,(r
&&w
)?"r+":(r
?"r":"w"));
95 /* Don't do buffered communication if full-duplex... it breaks! */
96 if (r
&w
) setbuf(filePtr
->f
,0);
98 filePtr
->readable
= r
;
99 filePtr
->writable
= w
;
100 filePtr
->numPids
= 0;
101 filePtr
->pidPtr
= NULL
;
102 filePtr
->errorId
= -1;
105 * Enter this new OpenFile structure in the table for the
106 * interpreter. May have to expand the table to do this.
109 TclMakeFileTable(iPtr
, fd
);
110 if (iPtr
->filePtrArray
[fd
] != NULL
) {
111 panic("Tcl_OpenCmd found file already open");
113 iPtr
->filePtrArray
[fd
] = filePtr
;
117 *------------------------------------------------------------------
121 * Open a socket connection to a given host and service.
124 * A standard Tcl result.
127 * An open socket connection.
128 * Sets the global variable connect_info(file%d) to the obtained
129 * port when setting up server.
130 *------------------------------------------------------------------
135 Tcp_ConnectCmd (ClientData notUsed
, Tcl_Interp
*interp
, int argc
, char **argv
)
142 if (argc
!= 2 && argc
!= 3 &&
143 (argc
!= 4 || (argc
== 4 && strcmp(argv
[1],"-server")))) {
144 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
145 "[{-server}] address_spec\"", (char *) NULL
);
149 if (!strcmp(argv
[1],"-server"))
153 * Create the connection
155 if (argc
- server
== 2) {/* Unix domain socket */
157 fd
= unix_connect(argv
[1+server
],server
);
159 fd
= inet_connect(argv
[1+server
], argv
[2+server
],server
);
162 /* Tell them why it fell apart */
165 Tcl_AppendResult(interp
,
166 "Couldn't setup listening socket with path \"",
167 argv
[1+server
],"\" : ",Tcl_UnixError(interp
),
170 Tcl_AppendResult(interp
,
171 "Couldn't connect to \"",argv
[1],"\" : ",
172 Tcl_UnixError(interp
),(char *) NULL
);
175 Tcl_AppendResult(interp
,
176 "couldn't setup listening socket on port:",
177 atoi(argv
[3])==0?"any":argv
[3]," using address \"",
178 strlen(argv
[2])?argv
[2]:"anywhere.","\": ",
179 Tcl_UnixError(interp
), (char *)NULL
);
181 Tcl_AppendResult(interp
, "couldn't open connection to \"",
182 argv
[1], "\" port \"", argv
[2], "\": ",
183 Tcl_UnixError(interp
), (char *) NULL
);
187 sprintf(interp
->result
, "file%d", fd
);
188 if (server
&& !unicks
) {
189 /* Find out what port we got */
191 struct sockaddr_in sockaddr
;
192 int res
,len
=sizeof(sockaddr
);
193 res
=getsockname(fd
,(struct sockaddr
*) &sockaddr
, &len
);
195 sprintf(buf
,"%d",errno
);
197 sprintf(buf
,"%d",(int)ntohs(sockaddr
.sin_port
));
198 Tcl_SetVar2(interp
,"connect_info",interp
->result
,buf
,TCL_GLOBAL_ONLY
);
201 Tcp_MakeOpenFile(interp
,fd
,1,1-server
);
207 *------------------------------------------------------------------
211 * Shutdown a socket for reading writing or both using shutdown(2)
214 * standard tcl result.
217 * Modifies the OpenFile structure appropriately
218 *------------------------------------------------------------------
223 Tcp_ShutdownCmd (ClientData notUsed
, Tcl_Interp
*interp
, int argc
, char **argv
)
225 Interp
*iPtr
= (Interp
*) interp
;
232 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
233 " fileid <option>\"", (char *) NULL
);
237 if (TclGetOpenFile(interp
, argv
[1], &filePtr
) != TCL_OK
) {
242 fd
= fileno(filePtr
->f
);
243 if (!strcmp(argv
[2],"0") || !strcmp(argv
[2],"receives") ||
244 !strcmp(argv
[2],"read")) {
245 if (!filePtr
->readable
) {
246 Tcl_AppendResult(interp
, "File is not readable",(char *) NULL
);
249 if (shutdown(fd
,0)) {
250 Tcl_AppendResult(interp
, "shutdown: ", Tcl_UnixError(interp
),
255 } else if (!strcmp(argv
[2],"1") || !strcmp(argv
[2],"sends") ||
256 !strcmp(argv
[2],"write")) {
257 if (!filePtr
->writable
) {
258 Tcl_AppendResult(interp
, "File is not writable",(char *) NULL
);
261 if (shutdown(fd
,1)) {
262 Tcl_AppendResult(interp
, "shutdown: ", Tcl_UnixError(interp
),
267 } else if (!strcmp(argv
[2],"2") || !strcmp(argv
[2],"all") ||
268 !strcmp(argv
[2],"both")) {
269 if (shutdown(fd
,2)) {
270 Tcl_AppendResult(interp
, "shutdown: ", Tcl_UnixError(interp
),
284 *------------------------------------------------------------------
288 * Accept a connection on a listening socket
291 * a standard tcl result
295 * Sets the global variable connect_info(file%d) to a list
296 * containing the remote address (host ip, port) of the
298 *------------------------------------------------------------------
303 Tcp_AcceptCmd (ClientData notUsed
, Tcl_Interp
*interp
, int argc
, char **argv
)
305 struct sockaddr_in sockaddr
;
306 int len
= sizeof sockaddr
;
312 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
313 " listening_socket\"", (char *) NULL
);
317 if (TclGetOpenFile(interp
, argv
[1], &filePtr
) != TCL_OK
) {
320 if (!filePtr
->readable
) {
321 Tcl_AppendResult(interp
, "\"", argv
[1],
322 "\" wasn't opened for reading", (char *) NULL
);
327 fd
= fileno(filePtr
->f
);
329 fd
= accept(fd
,(struct sockaddr
*)&sockaddr
,&len
);
331 Tcl_AppendResult(interp
, "system error in accept()", (char *)NULL
);
335 {/* Set the global connect_info */
338 if (sockaddr
.sin_family
== AF_INET
)
339 sprintf(buf
,"%s %d",inet_ntoa(sockaddr
.sin_addr
),
340 ntohs(sockaddr
.sin_port
));
342 buf
[0]=0; /* Empty string for UNIX domain sockets */
343 sprintf(nm
,"file%d",fd
);
344 Tcl_SetVar2(interp
,"connect_info",nm
,buf
,TCL_GLOBAL_ONLY
);
350 Tcp_MakeOpenFile(interp
,fd
,1,1);
352 sprintf(interp
->result
, "file%d", fd
);
357 *----------------------------------------------------------------
361 * Create a (unix_domain) fd connection using given rendeavous
368 *----------------------------------------------------------------
373 char *path
, /* Path name to create or use */
374 int server
/* 1->make server, 0->connect to server */
377 struct sockaddr_un sockaddr
;
381 sock
= socket(PF_UNIX
, SOCK_STREAM
, 0);
386 sockaddr
.sun_family
= AF_UNIX
;
387 strncpy(sockaddr
.sun_path
,path
,sizeof(sockaddr
.sun_path
)-1);
388 sockaddr
.sun_path
[sizeof(sockaddr
.sun_path
)-1] = 0; /* Just in case */
391 status
= bind(sock
,(struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
393 status
= connect(sock
, (struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
409 *----------------------------------------------------------------
413 * Create a (inet domain) fd connection to given host and port.
420 *----------------------------------------------------------------
425 char *host
, /* Host to connect, name or IP address */
426 char *service
, /* Port to use, service name or port number */
430 struct hostent
*hostent
, _hostent
;
431 struct servent
*servent
, _servent
;
432 struct protoent
*protoent
;
433 struct sockaddr_in sockaddr
;
435 int hostaddr
, hostaddrPtr
[2];
439 hostent
= gethostbyname(host
);
440 if (hostent
== NULL
) {
441 hostaddr
= inet_addr(host
);
442 if (hostaddr
== -1) {
443 if (server
&& !strlen(host
))
444 hostaddr
= INADDR_ANY
;
450 _hostent
.h_addr_list
= (char **)hostaddrPtr
;
451 _hostent
.h_addr_list
[0] = (char *)&hostaddr
;
452 _hostent
.h_addr_list
[1] = NULL
;
453 _hostent
.h_length
= sizeof(hostaddr
);
454 _hostent
.h_addrtype
= AF_INET
;
457 servent
= getservbyname(service
, "tcp");
458 if (servent
== NULL
) {
459 servport
= htons(atoi(service
));
460 if (servport
== -1) {
464 _servent
.s_port
= servport
;
465 _servent
.s_proto
= "tcp";
468 protoent
= getprotobyname(servent
->s_proto
);
469 if (protoent
== NULL
) {
474 sock
= socket(PF_INET
, SOCK_STREAM
, protoent
->p_proto
);
479 sockaddr
.sin_family
= AF_INET
;
480 memcpy((char *)&(sockaddr
.sin_addr
.s_addr
),
481 (char *) hostent
->h_addr_list
[0],
482 (size_t) hostent
->h_length
);
483 sockaddr
.sin_port
= servent
->s_port
;
486 status
= bind(sock
,(struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
488 status
= connect(sock
, (struct sockaddr
*) &sockaddr
, sizeof(sockaddr
));
504 *----------------------------------------------------------------
506 * Tcp_FileHandlerCmd --
508 * Register a file handler with an open file. If there is
509 * already and existing handler, it will be no longer called.
510 * If no mask and command are given, any existing handler
514 * A standard Tcl result. (Always OK).
517 * A new file handler is associated with a give TCL open file.
518 * Whenever the file is readable, writeable and/or there is
519 * an expection condition on the file, a user supplied TCL
522 *----------------------------------------------------------------
527 Tcp_FileHandlerCmd (ClientData notUsed
, Tcl_Interp
*interp
, int argc
, char **argv
)
533 if (argc
!= 2 && argc
!= 4) {
534 Tcl_AppendResult(interp
, "wrong # args: should be \"", argv
[0],
535 " fileId ?mode command?\"", (char *) NULL
);
539 if (TclGetOpenFile(interp
, argv
[1], &filePtr
) != TCL_OK
) {
545 * NOTE! Currently the cmdPtr structure will be left
546 * *unfreed* if the file handler is deleted
547 * via this code. Tough. Would need a hash table
550 Tk_DeleteFileHandler(fileno(filePtr
->f
));
555 * Find out on what situations the user is interested in.
556 * This is not the most elegant or efficient way to do this,
557 * but who cares? (I do, but not much enough :-)
560 if (strchr(argv
[2], 'r')) {
563 if (strchr(argv
[2], 'w')) {
566 if (strchr(argv
[2], 'e')) {
567 mask
|= TK_EXCEPTION
;
569 if (mask
== 0 || (strlen(argv
[2]) != strspn(argv
[2], "rwe"))) {
570 Tcl_AppendResult(interp
, "bad mask argument \"", argv
[2],
571 "\": should be any combination of \"r\", \"w\" and \"e\"",
577 cmdPtr
= (FileCmd
*)ckalloc(sizeof(FileCmd
));
578 cmdPtr
->interp
= interp
;
579 cmdPtr
->filePtr
= filePtr
;
580 cmdPtr
->tclCmd
= ckalloc(strlen(argv
[3]) + 1);
581 strcpy(cmdPtr
->tclCmd
, argv
[3]);
582 cmdPtr
->fileId
= ckalloc(strlen(argv
[1]) + 1);
583 strcpy(cmdPtr
->fileId
, argv
[1]);
586 * NOTE! There may be an earlier file handler. Should do something.
588 Tk_CreateFileHandler(fileno(filePtr
->f
), mask
, HandleSocket
,
589 (ClientData
) cmdPtr
);
594 *----------------------------------------------------------------
598 * This procedure is called from Tk_DoOneEvent whenever there is
599 * a desired condition on a given open socket. An Tcl command
600 * given by the user is executed to handle the connection. If
601 * and EOF or ERROR condition is noticed, all memory resources
602 * associated with the socket are released and the socket is closed.
608 * The user supplied command can do anything.
610 *----------------------------------------------------------------
614 HandleSocket (ClientData clientData
, int mask
)
617 FileCmd
*cmdPtr
= (FileCmd
*) clientData
;
618 OpenFile
*filePtr
= cmdPtr
->filePtr
;
619 Tcl_Interp
*interp
= cmdPtr
->interp
;
622 int fd
= fileno(filePtr
->f
);
624 Tk_Preserve((ClientData
)cmdPtr
);
627 if (TclGetOpenFile(interp
, cmdPtr
->fileId
, &dummy
) != TCL_OK
) {
628 /* File is closed! */
629 Tcl_ResetResult(interp
);
632 assert(dummy
== cmdPtr
->filePtr
);
634 if (mask
& TK_READABLE
) {
635 result
= Tcl_VarEval(interp
, cmdPtr
->tclCmd
, " r ", cmdPtr
->fileId
,
637 if (result
!= TCL_OK
) {
641 if (mask
& TK_WRITABLE
) {
642 result
= Tcl_VarEval(interp
, cmdPtr
->tclCmd
, " w ", cmdPtr
->fileId
,
644 if (result
!= TCL_OK
) {
648 if (mask
& TK_EXCEPTION
) {
649 result
= Tcl_VarEval(interp
, cmdPtr
->tclCmd
, " e ", cmdPtr
->fileId
,
651 if (result
!= TCL_OK
) {
656 if (feof(filePtr
->f
) || ferror(filePtr
->f
)) {
657 result
= Tcl_VarEval(interp
, "close ", cmdPtr
->fileId
,
659 if (result
!= TCL_OK
) {
666 Tk_Release((ClientData
)cmdPtr
);
669 Tk_DeleteFileHandler(fd
);
670 Tk_EventuallyFree((ClientData
)cmdPtr
, (Tk_FreeProc
*)free
);