📄 tclunixpipe.c
字号:
fcntl(targetFd, F_SETFD, 0); } else { int result; /* * Since we aren't dup'ing the file, we need to explicitly clear * the close-on-exec flag. */ result = fcntl(fd, F_SETFD, 0); } } else { close(targetFd); } return 1;}/* *---------------------------------------------------------------------- * * TclpCreateCommandChannel -- * * This function is called by the generic IO level to perform * the platform specific channel initialization for a command * channel. * * Results: * Returns a new channel or NULL on failure. * * Side effects: * Allocates a new channel. * *---------------------------------------------------------------------- */Tcl_ChannelTclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr) TclFile readFile; /* If non-null, gives the file for reading. */ TclFile writeFile; /* If non-null, gives the file for writing. */ TclFile errorFile; /* If non-null, gives the file where errors * can be read. */ int numPids; /* The number of pids in the pid array. */ Tcl_Pid *pidPtr; /* An array of process identifiers. * Allocated by the caller, freed when * the channel is closed or the processes * are detached (in a background exec). */{ char channelName[20]; int channelId; PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState)); int mode; statePtr->inFile = readFile; statePtr->outFile = writeFile; statePtr->errorFile = errorFile; statePtr->numPids = numPids; statePtr->pidPtr = pidPtr; statePtr->isNonBlocking = 0; mode = 0; if (readFile) { mode |= TCL_READABLE; } if (writeFile) { mode |= TCL_WRITABLE; } /* * Use one of the fds associated with the channel as the * channel id. */ if (readFile) { channelId = GetFd(readFile); } else if (writeFile) { channelId = GetFd(writeFile); } else if (errorFile) { channelId = GetFd(errorFile); } else { channelId = 0; } /* * For backward compatibility with previous versions of Tcl, we * use "file%d" as the base name for pipes even though it would * be more natural to use "pipe%d". */ sprintf(channelName, "file%d", channelId); statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, (ClientData) statePtr, mode); return statePtr->channel;}/* *---------------------------------------------------------------------- * * TclGetAndDetachPids -- * * This procedure is invoked in the generic implementation of a * background "exec" (An exec when invoked with a terminating "&") * to store a list of the PIDs for processes in a command pipeline * in interp->result and to detach the processes. * * Results: * None. * * Side effects: * Modifies interp->result. Detaches processes. * *---------------------------------------------------------------------- */voidTclGetAndDetachPids(interp, chan) Tcl_Interp *interp; Tcl_Channel chan;{ PipeState *pipePtr; Tcl_ChannelType *chanTypePtr; int i; char buf[20]; /* * Punt if the channel is not a command channel. */ chanTypePtr = Tcl_GetChannelType(chan); if (chanTypePtr != &pipeChannelType) { return; } pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); for (i = 0; i < pipePtr->numPids; i++) { sprintf(buf, "%ld", TclpGetPid(pipePtr->pidPtr[i])); Tcl_AppendElement(interp, buf); Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); } if (pipePtr->numPids > 0) { ckfree((char *) pipePtr->pidPtr); pipePtr->numPids = 0; }}/* *---------------------------------------------------------------------- * * PipeBlockModeProc -- * * Helper procedure to set blocking and nonblocking modes on a * pipe based channel. Invoked by generic IO level code. * * Results: * 0 if successful, errno when failed. * * Side effects: * Sets the device into blocking or non-blocking mode. * *---------------------------------------------------------------------- */ /* ARGSUSED */static intPipeBlockModeProc(instanceData, mode) ClientData instanceData; /* Pipe state. */ int mode; /* The mode to set. Can be one of * TCL_MODE_BLOCKING or * TCL_MODE_NONBLOCKING. */{ PipeState *psPtr = (PipeState *) instanceData; int curStatus; int fd;#ifndef USE_FIONBIO if (psPtr->inFile) { fd = GetFd(psPtr->inFile); 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 (psPtr->outFile) { fd = GetFd(psPtr->outFile); 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; } }#endif /* !FIONBIO */#ifdef USE_FIONBIO if (psPtr->inFile) { fd = GetFd(psPtr->inFile); if (mode == TCL_MODE_BLOCKING) { curStatus = 0; } else { curStatus = 1; } if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) { return errno; } } if (psPtr->outFile != NULL) { fd = GetFd(psPtr->outFile); if (mode == TCL_MODE_BLOCKING) { curStatus = 0; } else { curStatus = 1; } if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) { return errno; } }#endif /* USE_FIONBIO */ return 0;}/* *---------------------------------------------------------------------- * * PipeCloseProc -- * * This procedure is invoked by the generic IO level to perform * channel-type-specific cleanup when a command pipeline channel * is closed. * * Results: * 0 on success, errno otherwise. * * Side effects: * Closes the command pipeline channel. * *---------------------------------------------------------------------- */ /* ARGSUSED */static intPipeCloseProc(instanceData, interp) ClientData instanceData; /* The pipe to close. */ Tcl_Interp *interp; /* For error reporting. */{ PipeState *pipePtr; Tcl_Channel errChan; int errorCode, result; errorCode = 0; result = 0; pipePtr = (PipeState *) instanceData; if (pipePtr->inFile) { if (TclpCloseFile(pipePtr->inFile) < 0) { errorCode = errno; } } if (pipePtr->outFile) { if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) { errorCode = errno; } } if (pipePtr->isNonBlocking || TclInExit()) { /* * If the channel is non-blocking or Tcl is being cleaned up, just * detach the children PIDs, reap them (important if we are in a * dynamic load module), and discard the errorFile. */ Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr); Tcl_ReapDetachedProcs(); if (pipePtr->errorFile) { TclpCloseFile(pipePtr->errorFile); } } else { /* * Wrap the error file into a channel and give it to the cleanup * routine. */ if (pipePtr->errorFile) { errChan = Tcl_MakeFileChannel( (ClientData) GetFd(pipePtr->errorFile), TCL_READABLE); } else { errChan = NULL; } result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr, errChan); } if (pipePtr->numPids != 0) { ckfree((char *) pipePtr->pidPtr); } ckfree((char *) pipePtr); if (errorCode == 0) { return result; } return errorCode;}/* *---------------------------------------------------------------------- * * PipeInputProc -- * * This procedure is invoked from the generic IO level to read * input from a command pipeline based channel. * * Results: * The number of bytes read is returned or -1 on error. An output * argument contains a POSIX error code if an error occurs, or zero. * * Side effects: * Reads input from the input device of the channel. * *---------------------------------------------------------------------- */static intPipeInputProc(instanceData, buf, toRead, errorCodePtr) ClientData instanceData; /* Pipe state. */ char *buf; /* Where to store data read. */ int toRead; /* How much space is available * in the buffer? */ int *errorCodePtr; /* Where to store error code. */{ PipeState *psPtr = (PipeState *) instanceData; int bytesRead; /* How many bytes were actually * read from the input device? */ *errorCodePtr = 0; /* * Assume there is always enough input available. This will block * appropriately, and read will unblock as soon as a short read is * possible, if the channel is in blocking mode. If the channel is * nonblocking, the read will never block. */ bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead); if (bytesRead > -1) { return bytesRead; } *errorCodePtr = errno; return -1;}/* *---------------------------------------------------------------------- * * PipeOutputProc-- * * This procedure is invoked from the generic IO level to write * output to a command pipeline based channel. * * Results: * The number of bytes written is returned or -1 on error. An * output argument contains a POSIX error code if an error occurred, * or zero. * * Side effects: * Writes output on the output device of the channel. * *---------------------------------------------------------------------- */static intPipeOutputProc(instanceData, buf, toWrite, errorCodePtr) ClientData instanceData; /* Pipe state. */ char *buf; /* The data buffer. */ int toWrite; /* How many bytes to write? */ int *errorCodePtr; /* Where to store error code. */{ PipeState *psPtr = (PipeState *) instanceData; int written; *errorCodePtr = 0; written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite); if (written > -1) { return written; } *errorCodePtr = errno; return -1;}/* *---------------------------------------------------------------------- * * PipeWatchProc -- * * Initialize the notifier to watch the fds from this channel. * * Results: * None. * * Side effects: * Sets up the notifier so that a future event on the channel will * be seen by Tcl. * *---------------------------------------------------------------------- */static voidPipeWatchProc(instanceData, mask) ClientData instanceData; /* The pipe state. */ int mask; /* Events of interest; an OR-ed * combination of TCL_READABLE, * TCL_WRITABEL and TCL_EXCEPTION. */{ PipeState *psPtr = (PipeState *) instanceData; int newmask; if (psPtr->inFile) { newmask = mask & (TCL_READABLE | TCL_EXCEPTION); if (newmask) { Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask, (Tcl_FileProc *) Tcl_NotifyChannel, (ClientData) psPtr->channel); } else { Tcl_DeleteFileHandler(GetFd(psPtr->inFile)); } } if (psPtr->outFile) { newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION); if (newmask) { Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask, (Tcl_FileProc *) Tcl_NotifyChannel, (ClientData) psPtr->channel); } else { Tcl_DeleteFileHandler(GetFd(psPtr->outFile)); } }}/* *---------------------------------------------------------------------- * * PipeGetHandleProc -- * * Called from Tcl_GetChannelHandle to retrieve OS handles from * inside a command pipeline based channel. * * Results: * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if * there is no handle for the specified direction. * * Side effects: * None. * *---------------------------------------------------------------------- */static intPipeGetHandleProc(instanceData, direction, handlePtr) ClientData instanceData; /* The pipe state. */ int direction; /* TCL_READABLE or TCL_WRITABLE */ ClientData *handlePtr; /* Where to store the handle. */{ PipeState *psPtr = (PipeState *) instanceData; if (direction == TCL_READABLE && psPtr->inFile) { *handlePtr = (ClientData) GetFd(psPtr->inFile); return TCL_OK; } if (direction == TCL_WRITABLE && psPtr->outFile) { *handlePtr = (ClientData) GetFd(psPtr->outFile); return TCL_OK; } return TCL_ERROR;}/* *---------------------------------------------------------------------- * * Tcl_WaitPid -- * * Implements the waitpid system call on Unix systems. * * Results: * Result of calling waitpid. * * Side effects: * Waits for a process to terminate. * *---------------------------------------------------------------------- */Tcl_PidTcl_WaitPid(pid, statPtr, options) Tcl_Pid pid; int *statPtr; int options;{ int result; pid_t real_pid; real_pid = (pid_t) pid; while (1) { result = (int) waitpid(real_pid, statPtr, options); if ((result != -1) || (errno != EINTR)) { return (Tcl_Pid) result; } }}/* *---------------------------------------------------------------------- * * Tcl_PidObjCmd -- * * This procedure is invoked to process the "pid" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ /* ARGSUSED */intTcl_PidObjCmd(dummy, interp, objc, objv) ClientData dummy; /* Not used. */ Tcl_Interp *interp; /* Current interpreter. */ int objc; /* Number of arguments. */ Tcl_Obj *CONST *objv; /* Argument strings. */{ Tcl_Channel chan; Tcl_ChannelType *chanTypePtr; PipeState *pipePtr; int i; Tcl_Obj *resultPtr, *longObjPtr; if (objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); return TCL_ERROR; } if (objc == 1) { Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) getpid()); } else { chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL), NULL); if (chan == (Tcl_Channel) NULL) { return TCL_ERROR; } chanTypePtr = Tcl_GetChannelType(chan); if (chanTypePtr != &pipeChannelType) { return TCL_OK; } pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); resultPtr = Tcl_GetObjResult(interp); for (i = 0; i < pipePtr->numPids; i++) { longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i])); Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr); } } return TCL_OK;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -