📄 tclmacsock.c
字号:
/* * tclMacSock.c * * Channel drivers for Macintosh sockets. * * Copyright (c) 1996-1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tclMacSock.c 1.59 97/10/09 18:24:42 */#include "tclInt.h"#include "tclPort.h"#include "tclMacInt.h"#include <AddressXlation.h>#include <Aliases.h>#undef Status#include <Devices.h>#include <Errors.h>#include <Events.h>#include <Files.h>#include <Gestalt.h>#include <MacTCP.h>#include <Processes.h>#include <Strings.h>/* * The following variable is used to tell whether this module has been * initialized. */static int initialized = 0;/* * If debugging is on we may drop into the debugger to handle certain cases * that are not supposed to happen. Otherwise, we change ignore the error * and most code should handle such errors ok. */#ifndef TCL_DEBUG #define Debugger()#endif/* * The preferred buffer size for Macintosh channels. */#define CHANNEL_BUF_SIZE 8192/* * Port information structure. Used to match service names * to a Tcp/Ip port number. */typedef struct { char *name; /* Name of service. */ int port; /* Port number. */} PortInfo;/* * This structure describes per-instance state of a tcp based channel. */typedef struct TcpState { TCPiopb pb; /* Parameter block used by this stream. * This must be in the first position. */ ProcessSerialNumber psn; /* PSN used to wake up process. */ StreamPtr tcpStream; /* Macintosh tcp stream pointer. */ int port; /* The port we are connected to. */ int flags; /* Bit field comprised of the flags * described below. */ int checkMask; /* OR'ed combination of TCL_READABLE and * TCL_WRITABLE as set by an asynchronous * event handler. */ int watchMask; /* OR'ed combination of TCL_READABLE and * TCL_WRITABLE as set by Tcl_WatchFile. */ Tcl_TcpAcceptProc *acceptProc; /* Proc to call on accept. */ ClientData acceptProcData; /* The data for the accept proc. */ wdsEntry dataSegment[2]; /* List of buffers to be written async. */ rdsEntry rdsarray[5+1]; /* Array used when cleaning out recieve * buffers on a closing socket. */ Tcl_Channel channel; /* Channel associated with this socket. */ struct TcpState *nextPtr; /* The next socket on the global socket * list. */} TcpState;/* * This structure is used by domain name resolver callback. */typedef struct DNRState { struct hostInfo hostInfo; /* Data structure used by DNR functions. */ int done; /* Flag to determine when we are done. */ ProcessSerialNumber psn; /* Process to wake up when we are done. */} DNRState;/* * The following macros may be used to set the flags field of * a TcpState structure. */#define TCP_ASYNC_SOCKET (1<<0) /* The socket is in async mode. */#define TCP_ASYNC_CONNECT (1<<1) /* The socket is trying to connect. */#define TCP_CONNECTED (1<<2) /* The socket is connected. */#define TCP_PENDING (1<<3) /* A SocketEvent is on the queue. */#define TCP_LISTENING (1<<4) /* This socket is listening for * a connection. */#define TCP_LISTEN_CONNECT (1<<5) /* Someone has connect to the * listening port. */#define TCP_REMOTE_CLOSED (1<<6) /* The remote side has closed * the connection. */#define TCP_RELEASE (1<<7) /* The socket may now be released. */#define TCP_WRITING (1<<8) /* A background write is in progress. */#define TCP_SERVER_ZOMBIE (1<<9) /* The server can no longer accept connects. *//* * The following structure is what is added to the Tcl event queue when * a socket event occurs. */typedef struct SocketEvent { Tcl_Event header; /* Information that is standard for * all events. */ TcpState *statePtr; /* Socket descriptor that is ready. */ StreamPtr tcpStream; /* Low level Macintosh stream. */} SocketEvent;/* * Static routines for this file: */static pascal void CleanUpExitProc _ANSI_ARGS_((void));static void ClearZombieSockets _ANSI_ARGS_((void));static void CloseCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));static TcpState * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp, int port, char *host, char *myAddr, int myPort, int server, int async));static pascal void DNRCompletionRoutine _ANSI_ARGS_(( struct hostInfo *hostinfoPtr, DNRState *dnrStatePtr));static void FreeSocketInfo _ANSI_ARGS_((TcpState *statePtr));static long GetBufferSize _ANSI_ARGS_((void));static OSErr GetHostFromString _ANSI_ARGS_((char *name, ip_addr *address));static OSErr GetLocalAddress _ANSI_ARGS_((unsigned long *addr));static void IOCompletionRoutine _ANSI_ARGS_((TCPiopb *pb));static void InitMacTCPParamBlock _ANSI_ARGS_((TCPiopb *pBlock, int csCode));static void InitSockets _ANSI_ARGS_((void));static TcpState * NewSocketInfo _ANSI_ARGS_((StreamPtr stream));static OSErr ResolveAddress _ANSI_ARGS_((ip_addr tcpAddress, Tcl_DString *dsPtr));static void SocketCheckProc _ANSI_ARGS_((ClientData clientData, int flags));static int SocketEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int flags));static void SocketExitHandler _ANSI_ARGS_((ClientData clientData));static void SocketFreeProc _ANSI_ARGS_((ClientData clientData));static int SocketReady _ANSI_ARGS_((TcpState *statePtr));static void SocketSetupProc _ANSI_ARGS_((ClientData clientData, int flags));static void TcpAccept _ANSI_ARGS_((TcpState *statePtr));static int TcpBlockMode _ANSI_ARGS_((ClientData instanceData, int mode));static int TcpClose _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp));static int TcpGetHandle _ANSI_ARGS_((ClientData instanceData, int direction, ClientData *handlePtr));static int TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, char *optionName, Tcl_DString *dsPtr));static int TcpInput _ANSI_ARGS_((ClientData instanceData, char *buf, int toRead, int *errorCodePtr));static int TcpOutput _ANSI_ARGS_((ClientData instanceData, char *buf, int toWrite, int *errorCodePtr));static void TcpWatch _ANSI_ARGS_((ClientData instanceData, int mask));static int WaitForSocketEvent _ANSI_ARGS_((TcpState *infoPtr, int mask, int *errorCodePtr));/* * This structure describes the channel type structure for TCP socket * based IO: */static Tcl_ChannelType tcpChannelType = { "tcp", /* Type name. */ TcpBlockMode, /* Set blocking or * non-blocking mode.*/ TcpClose, /* Close proc. */ TcpInput, /* Input proc. */ TcpOutput, /* Output proc. */ NULL, /* Seek proc. */ NULL, /* Set option proc. */ TcpGetOptionProc, /* Get option proc. */ TcpWatch, /* Initialize notifier. */ TcpGetHandle /* Get handles out of channel. */};/* * Universal Procedure Pointers (UPP) for various callback * routines used by MacTcp code. */ResultUPP resultUPP = NULL;TCPIOCompletionUPP completeUPP = NULL;TCPIOCompletionUPP closeUPP = NULL;/* * Built-in commands, and the procedures associated with them: */static PortInfo portServices[] = { {"echo", 7}, {"discard", 9}, {"systat", 11}, {"daytime", 13}, {"netstat", 15}, {"chargen", 19}, {"ftp-data", 20}, {"ftp", 21}, {"telnet", 23}, {"telneto", 24}, {"smtp", 25}, {"time", 37}, {"whois", 43}, {"domain", 53}, {"gopher", 70}, {"finger", 79}, {"hostnames", 101}, {"sunrpc", 111}, {"nntp", 119}, {"exec", 512}, {"login", 513}, {"shell", 514}, {"printer", 515}, {"courier", 530}, {"uucp", 540}, {NULL, 0},};/* * Every open socket has an entry on the following list. */static TcpState *socketList = NULL;/* * Globals for holding information about OS support for sockets. */static int socketsTestInited = false;static int hasSockets = false;static short driverRefNum = 0;static int socketNumber = 0;static int socketBufferSize = CHANNEL_BUF_SIZE;static ProcessSerialNumber applicationPSN;/* *---------------------------------------------------------------------- * * InitSockets -- * * Load the MacTCP driver and open the name resolver. We also * create several UPP's used by our code. Lastly, we install * a patch to ExitToShell to clean up socket connections if * we are about to exit. * * Results: * 1 if successful, 0 on failure. * * Side effects: * Creates a new event source, loads the MacTCP driver, * registers an exit to shell callback. * *---------------------------------------------------------------------- */#define gestaltMacTCPVersion 'mtcp'static voidInitSockets(){ ParamBlockRec pb; OSErr err; long response; initialized = 1; Tcl_CreateExitHandler(SocketExitHandler, (ClientData) NULL); if (Gestalt(gestaltMacTCPVersion, &response) == noErr) { hasSockets = true; } else { hasSockets = false; } if (!hasSockets) { return; } /* * Load MacTcp driver and name server resolver. */ pb.ioParam.ioCompletion = 0L; pb.ioParam.ioNamePtr = "\p.IPP"; pb.ioParam.ioPermssn = fsCurPerm; err = PBOpenSync(&pb); if (err != noErr) { hasSockets = 0; return; } driverRefNum = pb.ioParam.ioRefNum; socketBufferSize = GetBufferSize(); err = OpenResolver(NULL); if (err != noErr) { hasSockets = 0; return; } GetCurrentProcess(&applicationPSN); /* * Create UPP's for various callback routines. */ resultUPP = NewResultProc(DNRCompletionRoutine); completeUPP = NewTCPIOCompletionProc(IOCompletionRoutine); closeUPP = NewTCPIOCompletionProc(CloseCompletionRoutine); /* * Install an ExitToShell patch. We use this patch instead * of the Tcl exit mechanism because we need to ensure that * these routines are cleaned up even if we crash or are forced * to quit. There are some circumstances when the Tcl exit * handlers may not fire. */ TclMacInstallExitToShellPatch(CleanUpExitProc); Tcl_CreateEventSource(SocketSetupProc, SocketCheckProc, NULL); initialized = 1;}/* *---------------------------------------------------------------------- * * SocketExitHandler -- * * Callback invoked during exit clean up to deinitialize the * socket module. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */static voidSocketExitHandler( ClientData clientData) /* Not used. */{ if (hasSockets) { Tcl_DeleteEventSource(SocketSetupProc, SocketCheckProc, NULL); /* CleanUpExitProc(); TclMacDeleteExitToShellPatch(CleanUpExitProc); */ } initialized = 0;}/* *---------------------------------------------------------------------- * * TclHasSockets -- * * This function determines whether sockets are available on the * current system and returns an error in interp if they are not. * Note that interp may be NULL. * * Results: * Returns TCL_OK if the system supports sockets, or TCL_ERROR with * an error in interp. * * Side effects: * None. * *---------------------------------------------------------------------- */intTclHasSockets( Tcl_Interp *interp) /* Interp for error messages. */{ if (!initialized) { InitSockets(); } if (hasSockets) { return TCL_OK; } if (interp != NULL) { Tcl_AppendResult(interp, "sockets are not available on this system", NULL); } return TCL_ERROR;}/* *---------------------------------------------------------------------- * * SocketSetupProc -- * * This procedure is invoked before Tcl_DoOneEvent blocks waiting * for an event. * * Results: * None. * * Side effects: * Adjusts the block time if needed. * *---------------------------------------------------------------------- */static voidSocketSetupProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ TcpState *statePtr; Tcl_Time blockTime = { 0, 0 }; if (!(flags & TCL_FILE_EVENTS)) { return; } /* * Check to see if there is a ready socket. If so, poll. */ for (statePtr = socketList; statePtr != NULL; statePtr = statePtr->nextPtr) { if (statePtr->flags & TCP_RELEASE) { continue; } if (SocketReady(statePtr)) { Tcl_SetMaxBlockTime(&blockTime); break; } }}/* *---------------------------------------------------------------------- * * SocketCheckProc -- * * This procedure is called by Tcl_DoOneEvent to check the socket * event source for events. * * Results: * None. * * Side effects: * May queue an event. * *---------------------------------------------------------------------- */static voidSocketCheckProc( ClientData data, /* Not used. */ int flags) /* Event flags as passed to Tcl_DoOneEvent. */{ TcpState *statePtr; SocketEvent *evPtr; TcpState dummyState; if (!(flags & TCL_FILE_EVENTS)) { return; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -