summaryrefslogtreecommitdiffstats
path: root/contrib/tcl/unix/tclUnixChan.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tcl/unix/tclUnixChan.c')
-rw-r--r--contrib/tcl/unix/tclUnixChan.c212
1 files changed, 173 insertions, 39 deletions
diff --git a/contrib/tcl/unix/tclUnixChan.c b/contrib/tcl/unix/tclUnixChan.c
index aa16fe3..a48806f 100644
--- a/contrib/tcl/unix/tclUnixChan.c
+++ b/contrib/tcl/unix/tclUnixChan.c
@@ -9,7 +9,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * SCCS: @(#) tclUnixChan.c 1.161 96/04/18 08:28:54
+ * SCCS: @(#) tclUnixChan.c 1.172 96/06/11 10:14:51
*/
#include "tclInt.h" /* Internal definitions for Tcl. */
@@ -26,6 +26,9 @@ typedef struct PipeState {
int numPids; /* How many processes are attached to this pipe? */
int *pidPtr; /* The process IDs themselves. Allocated by
* the creator of the pipe. */
+ int isNonBlocking; /* Nonzero when the pipe is in nonblocking mode.
+ * Used to decide whether to wait for the children
+ * at close time. */
} PipeState;
/*
@@ -49,6 +52,15 @@ typedef struct TcpState {
#define TCP_ASYNC_CONNECT (1<<1) /* Async connect in progress. */
/*
+ * The following defines the maximum length of the listen queue. This is
+ * the number of outstanding yet-to-be-serviced requests for a connection
+ * on a server socket, more than this number of outstanding requests and
+ * the connection request will fail.
+ */
+
+#define TCL_LISTEN_LIMIT 100
+
+/*
* The following defines how much buffer space the kernel should maintain
* for a socket.
*/
@@ -59,15 +71,15 @@ typedef struct TcpState {
* Static routines for this file:
*/
-static int CommonBlockModeProc _ANSI_ARGS_((
- ClientData instanceData, Tcl_File inFile,
- Tcl_File outFile, int mode));
static TcpState * CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
int port, char *host, int server,
char *myaddr, int myport, int async));
static int CreateSocketAddress _ANSI_ARGS_(
(struct sockaddr_in *sockaddrPtr,
char *host, int port));
+static int FileBlockModeProc _ANSI_ARGS_((
+ ClientData instanceData, Tcl_File inFile,
+ Tcl_File outFile, int mode));
static int FileCloseProc _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp, Tcl_File inFile,
Tcl_File outFile));
@@ -80,6 +92,9 @@ static int FilePipeOutputProc _ANSI_ARGS_((
static int FileSeekProc _ANSI_ARGS_((ClientData instanceData,
Tcl_File inFile, Tcl_File outFile, long offset,
int mode, int *errorCode));
+static int PipeBlockModeProc _ANSI_ARGS_((
+ ClientData instanceData, Tcl_File inFile,
+ Tcl_File outFile, int mode));
static int PipeCloseProc _ANSI_ARGS_((ClientData instanceData,
Tcl_Interp *interp, Tcl_File inFile,
Tcl_File outFile));
@@ -106,7 +121,7 @@ static int WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
static Tcl_ChannelType fileChannelType = {
"file", /* Type name. */
- CommonBlockModeProc, /* Set blocking/nonblocking mode.*/
+ FileBlockModeProc, /* Set blocking/nonblocking mode.*/
FileCloseProc, /* Close proc. */
FilePipeInputProc, /* Input proc. */
FilePipeOutputProc, /* Output proc. */
@@ -122,7 +137,7 @@ static Tcl_ChannelType fileChannelType = {
static Tcl_ChannelType pipeChannelType = {
"pipe", /* Type name. */
- CommonBlockModeProc, /* Set blocking/nonblocking mode.*/
+ PipeBlockModeProc, /* Set blocking/nonblocking mode.*/
PipeCloseProc, /* Close proc. */
FilePipeInputProc, /* Input proc. */
FilePipeOutputProc, /* Output proc. */
@@ -150,7 +165,7 @@ static Tcl_ChannelType tcpChannelType = {
/*
*----------------------------------------------------------------------
*
- * CommonBlockModeProc --
+ * FileBlockModeProc --
*
* Helper procedure to set blocking and nonblocking modes on a
* channel. Invoked either by generic IO level code or by other
@@ -167,7 +182,7 @@ static Tcl_ChannelType tcpChannelType = {
/* ARGSUSED */
static int
-CommonBlockModeProc(instanceData, inFile, outFile, mode)
+FileBlockModeProc(instanceData, inFile, outFile, mode)
ClientData instanceData; /* Unused. */
Tcl_File inFile, outFile; /* Input, output files for channel. */
int mode; /* The mode to set. Can be one of
@@ -209,6 +224,69 @@ CommonBlockModeProc(instanceData, inFile, outFile, mode)
/*
*----------------------------------------------------------------------
*
+ * PipeBlockModeProc --
+ *
+ * Helper procedure to set blocking and nonblocking modes on a
+ * channel. Invoked either by generic IO level code or by other
+ * channel drivers after doing channel-type-specific inialization.
+ *
+ * Results:
+ * 0 if successful, errno when failed.
+ *
+ * Side effects:
+ * Sets the device into blocking or non-blocking mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+PipeBlockModeProc(instanceData, inFile, outFile, mode)
+ ClientData instanceData; /* The pipe state. */
+ Tcl_File inFile, outFile; /* Input, output files for channel. */
+ int mode; /* The mode to set. Can be one of
+ * TCL_MODE_BLOCKING or
+ * TCL_MODE_NONBLOCKING. */
+{
+ PipeState *pipePtr;
+ int curStatus;
+ int fd;
+
+ if (inFile != NULL) {
+ fd = (int) Tcl_GetFileInfo(inFile, NULL);
+ curStatus = fcntl(fd, F_GETFL);
+ if (mode == TCL_MODE_BLOCKING) {
+ curStatus &= (~(O_NONBLOCK));
+ } else {
+ curStatus |= O_NONBLOCK;
+ }
+ if (fcntl(fd, F_SETFL, curStatus) < 0) {
+ return errno;
+ }
+ curStatus = fcntl(fd, F_GETFL);
+ }
+ if (outFile != NULL) {
+ fd = (int) Tcl_GetFileInfo(outFile, NULL);
+ curStatus = fcntl(fd, F_GETFL);
+ if (mode == TCL_MODE_BLOCKING) {
+ curStatus &= (~(O_NONBLOCK));
+ } else {
+ curStatus |= O_NONBLOCK;
+ }
+ if (fcntl(fd, F_SETFL, curStatus) < 0) {
+ return errno;
+ }
+ }
+
+ pipePtr = (PipeState *) instanceData;
+ pipePtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING) ? 1 : 0;
+
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* FilePipeInputProc --
*
* This procedure is invoked from the generic IO level to read
@@ -336,17 +414,29 @@ FileCloseProc(instanceData, interp, inFile, outFile)
fd = (int) Tcl_GetFileInfo(inFile, NULL);
Tcl_FreeFile(inFile);
- if (close(fd) < 0) {
- errorCode = errno;
- }
+ if (tclInInterpreterDeletion) {
+ if ((fd != 0) && (fd != 1) && (fd != 2)) {
+ if (close(fd) < 0) {
+ errorCode = errno;
+ }
+ }
+ } else if (close(fd) < 0) {
+ errorCode = errno;
+ }
}
if (outFile != NULL) {
fd = (int) Tcl_GetFileInfo(outFile, NULL);
Tcl_FreeFile(outFile);
- if ((close(fd) < 0) && (errorCode == 0)) {
- errorCode = errno;
- }
+ if (tclInInterpreterDeletion) {
+ if ((fd != 0) && (fd != 1) && (fd != 2)) {
+ if ((close(fd) < 0) && (errorCode == 0)) {
+ errorCode = errno;
+ }
+ }
+ } else if ((close(fd) < 0) && (errorCode == 0)) {
+ errorCode = errno;
+ }
}
return errorCode;
}
@@ -484,6 +574,7 @@ PipeCloseProc(instanceData, interp, inFile, outFile)
int fd, errorCode, result;
errorCode = 0;
+ result = 0;
pipePtr = (PipeState *) instanceData;
if (pipePtr->readFile != NULL) {
fd = (int) Tcl_GetFileInfo(pipePtr->readFile, NULL);
@@ -499,20 +590,34 @@ PipeCloseProc(instanceData, interp, inFile, outFile)
errorCode = errno;
}
}
-
- /*
- * Wrap the error file into a channel and give it to the cleanup
- * routine.
- */
- if (pipePtr->errorFile != NULL) {
- errChan = Tcl_CreateChannel(&fileChannelType, "pipeError",
- pipePtr->errorFile, NULL, NULL);
+ if (pipePtr->isNonBlocking) {
+
+ /*
+ * If the channel is non-blocking, just detach the children PIDs
+ * and discard the errorFile.
+ */
+
+ Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
+ if (pipePtr->errorFile != NULL) {
+ Tcl_FreeFile(pipePtr->errorFile);
+ }
} else {
- errChan = NULL;
+
+ /*
+ * Wrap the error file into a channel and give it to the cleanup
+ * routine.
+ */
+
+ if (pipePtr->errorFile != NULL) {
+ errChan = Tcl_CreateChannel(&fileChannelType, "pipeError",
+ pipePtr->errorFile, NULL, NULL);
+ } else {
+ errChan = NULL;
+ }
+ result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
+ errChan);
}
- result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
- errChan);
if (pipePtr->numPids != 0) {
ckfree((char *) pipePtr->pidPtr);
}
@@ -664,6 +769,8 @@ Tcl_MakeFileChannel(inFd, outFd, mode)
* TCL_WRITABLE to indicate whether inFile
* and/or outFile are valid. */
{
+ Tcl_Channel chan;
+ int fileUsed;
Tcl_File inFile, outFile;
char channelName[20];
@@ -684,6 +791,25 @@ Tcl_MakeFileChannel(inFd, outFd, mode)
outFile = Tcl_GetFile(outFd, TCL_UNIX_FD);
}
+ /*
+ * Look to see if a channel with those two Tcl_Files already exists.
+ * If so, return it.
+ */
+
+ chan = TclFindFileChannel(inFile, outFile, &fileUsed);
+ if (chan != (Tcl_Channel) NULL) {
+ return chan;
+ }
+
+ /*
+ * If one of the Tcl_Files is used in another channel, do not
+ * create a new channel containing it; this avoids core dumps
+ * later, when the Tcl_File would be freed twice.
+ */
+
+ if (fileUsed) {
+ return (Tcl_Channel) NULL;
+ }
return Tcl_CreateChannel(&fileChannelType, channelName, inFile, outFile,
(ClientData) NULL);
}
@@ -728,6 +854,7 @@ TclCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
statePtr->errorFile = errorFile;
statePtr->numPids = numPids;
statePtr->pidPtr = pidPtr;
+ statePtr->isNonBlocking = 0;
/*
* Use one of the fds associated with the channel as the
@@ -857,7 +984,7 @@ TcpBlockModeProc(instanceData, inFile, outFile, mode)
} else {
statePtr->flags |= TCP_ASYNC_SOCKET;
}
- return CommonBlockModeProc(instanceData, inFile, outFile, mode);
+ return FileBlockModeProc(instanceData, inFile, outFile, mode);
}
/*
@@ -1271,7 +1398,7 @@ CreateSocket(interp, port, host, server, myaddr, myport, async)
status = bind(sock, (struct sockaddr *) &sockaddr,
sizeof(struct sockaddr));
if (status != -1) {
- status = listen(sock, 5);
+ status = listen(sock, TCL_LISTEN_LIMIT);
}
} else {
if (myaddr != NULL || myport != 0) {
@@ -1380,7 +1507,7 @@ CreateSocketAddress(sockaddrPtr, host, port)
addr.s_addr = INADDR_ANY;
} else {
addr.s_addr = inet_addr(host);
- if (addr.s_addr == (unsigned long) -1) {
+ if (addr.s_addr == -1) {
hostent = gethostbyname(host);
if (hostent != NULL) {
memcpy((VOID *) &addr,
@@ -1664,27 +1791,34 @@ TclGetDefaultStdChannel(type)
int mode = 0; /* compiler warning (used before set). */
char *bufMode = NULL;
- /*
- * If the channels were not created yet, create them now and
- * store them in the static variables.
- */
-
switch (type) {
- case TCL_STDIN:
+ case TCL_STDIN:
+ if ((lseek(0, (off_t) 0, SEEK_CUR) == -1) &&
+ (errno == EBADF)) {
+ return (Tcl_Channel) NULL;
+ }
fd = 0;
mode = TCL_READABLE;
bufMode = "line";
- break;
- case TCL_STDOUT:
+ break;
+ case TCL_STDOUT:
+ if ((lseek(1, (off_t) 0, SEEK_CUR) == -1) &&
+ (errno == EBADF)) {
+ return (Tcl_Channel) NULL;
+ }
fd = 1;
mode = TCL_WRITABLE;
bufMode = "line";
- break;
- case TCL_STDERR:
+ break;
+ case TCL_STDERR:
+ if ((lseek(2, (off_t) 0, SEEK_CUR) == -1) &&
+ (errno == EBADF)) {
+ return (Tcl_Channel) NULL;
+ }
fd = 2;
mode = TCL_WRITABLE;
bufMode = "none";
- break;
+ break;
default:
panic("TclGetDefaultStdChannel: Unexpected channel type");
break;
OpenPOWER on IntegriCloud