/* * tkRawTCP.c -- * * This file contains a simple Tcl "connect" command * that returns an standard Tcl File descriptor (as would * be returned by Tcl_OpenCmd). * Extended to create servers, accept connections, shutdown parts of full * duplex connections and handle UNIX domain sockets. * * Author: Pekka Nikander * Modified: Tim MacKenzie #include #include #include #include #include #include #include #include static int inet_connect _ANSI_ARGS_((char *host, char *port,int server)); static int unix_connect _ANSI_ARGS_((char *path, int server)); static void HandleSocket _ANSI_ARGS_ ((ClientData clientData, int mask)); typedef struct { Tcl_Interp *interp; OpenFile *filePtr; char *tclCmd; char *fileId; } FileCmd; /* *------------------------------------------------------------------ * * Tcp_MakeOpenFile -- * * Set up on OpenFile structure in the interpreter for a newly * opened file * * Results: * none * * Side effects: * Adds an OpenFile to the list. *------------------------------------------------------------------ */ /* ARGSUSED */ void Tcp_MakeOpenFile(interp,fd,r,w) Tcl_Interp *interp; int fd; int r,w; {/* Create an OpenFile structure using f and install it in the interpreter with * Readable and Writable set to r and w */ Interp *iPtr = (Interp *) interp; register OpenFile *filePtr; filePtr = (OpenFile *) ckalloc(sizeof(OpenFile)); filePtr->f = NULL; filePtr->f2 = NULL; /* Open the file with the correct type (doesn't handle !r && !w) */ #ifdef MSDOS filePtr->f = fdopen(fd,(r&&w)?"rb+":(r?"rb":"wb")); #else filePtr->f = fdopen(fd,(r&&w)?"r+":(r?"r":"w")); #endif /* Don't do buffered communication if full-duplex... it breaks! */ if (r&w) setbuf(filePtr->f,0); filePtr->readable = r; filePtr->writable = w; filePtr->numPids = 0; filePtr->pidPtr = NULL; filePtr->errorId = -1; /* * Enter this new OpenFile structure in the table for the * interpreter. May have to expand the table to do this. */ TclMakeFileTable(iPtr, fd); if (iPtr->filePtrArray[fd] != NULL) { panic("Tcl_OpenCmd found file already open"); } iPtr->filePtrArray[fd] = filePtr; } /* *------------------------------------------------------------------ * * Tcp_ConnectCmd -- * * Open a socket connection to a given host and service. * * Results: * A standard Tcl result. * * Side effects: * An open socket connection. * Sets the global variable connect_info(file%d) to the obtained * port when setting up server. *------------------------------------------------------------------ */ /* ARGSUSED */ int Tcp_ConnectCmd(notUsed, interp, argc, argv) ClientData notUsed; Tcl_Interp *interp; int argc; char **argv; { Interp *iPtr = (Interp *) interp; char *host,*port; int fd; int server=0; int unicks = 0; if (argc != 2 && argc != 3 && (argc != 4 || (argc == 4 && strcmp(argv[1],"-server")))) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], "[{-server}] address_spec\"", (char *) NULL); return TCL_ERROR; } if (!strcmp(argv[1],"-server")) server = 1; /* * Create the connection */ if (argc - server == 2) {/* Unix domain socket */ unicks = 1; fd = unix_connect(argv[1+server],server); } else fd = inet_connect(argv[1+server], argv[2+server],server); if (fd < 0) { /* Tell them why it fell apart */ if (unicks) if (server) Tcl_AppendResult(interp, "Couldn't setup listening socket with path \"", argv[1+server],"\" : ",Tcl_UnixError(interp), (char *) NULL); else Tcl_AppendResult(interp, "Couldn't connect to \"",argv[1],"\" : ", Tcl_UnixError(interp),(char *) NULL); else if (server) Tcl_AppendResult(interp, "couldn't setup listening socket on port:", atoi(argv[3])==0?"any":argv[3]," using address \"", strlen(argv[2])?argv[2]:"anywhere.","\": ", Tcl_UnixError(interp), (char *)NULL); else Tcl_AppendResult(interp, "couldn't open connection to \"", argv[1], "\" port \"", argv[2], "\": ", Tcl_UnixError(interp), (char *) NULL); return TCL_ERROR; } sprintf(interp->result, "file%d", fd); if (server && !unicks) { /* Find out what port we got */ char buf[50]; struct sockaddr_in sockaddr; int res,len=sizeof(sockaddr); res =getsockname(fd,(struct sockaddr *) &sockaddr, &len); if (res < 0) { sprintf(buf,"%d",errno); } else sprintf(buf,"%d",(int)ntohs(sockaddr.sin_port)); Tcl_SetVar2(interp,"connect_info",interp->result,buf,TCL_GLOBAL_ONLY); } Tcp_MakeOpenFile(iPtr,fd,1,1-server); return TCL_OK; } /* *------------------------------------------------------------------ * * Tcp_ShutdownCmd -- * * Shutdown a socket for reading writing or both using shutdown(2) * * Results: * standard tcl result. * * Side effects: * Modifies the OpenFile structure appropriately *------------------------------------------------------------------ */ /* ARGSUSED */ int Tcp_ShutdownCmd(notUsed, interp, argc, argv) ClientData notUsed; Tcl_Interp *interp; int argc; char **argv; { Interp *iPtr = (Interp *) interp; OpenFile *filePtr; register FILE *f; int fd; if (argc != 3) { wrong_args: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " fileid