📄 tclunixpipe.c
字号:
} /* * We need to allocate and convert this before the fork * so it is properly deallocated later */ dsArray = (Tcl_DString *) ckalloc(argc * sizeof(Tcl_DString)); newArgv = (char **) ckalloc((argc+1) * sizeof(char *)); newArgv[argc] = NULL; for (i = 0; i < argc; i++) { newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]); } joinThisError = errorFile && (errorFile == outputFile); pid = fork(); if (pid == 0) { fd = GetFd(errPipeOut); /* * Set up stdio file handles for the child process. */ if (!SetupStdFile(inputFile, TCL_STDIN) || !SetupStdFile(outputFile, TCL_STDOUT) || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR)) || (joinThisError && ((dup2(1,2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) { sprintf(errSpace, "%dforked process couldn't set up input/output: ", errno); write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } /* * Close the input side of the error pipe. */ RestoreSignals(); execvp(newArgv[0], newArgv); /* INTL: Native. */ sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]); write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } /* * Free the mem we used for the fork */ for (i = 0; i < argc; i++) { Tcl_DStringFree(&dsArray[i]); } ckfree((char *) dsArray); ckfree((char *) newArgv); if (pid == -1) { Tcl_AppendResult(interp, "couldn't fork child process: ", Tcl_PosixError(interp), (char *) NULL); goto error; } /* * Read back from the error pipe to see if the child started * up OK. The info in the pipe (if any) consists of a decimal * errno value followed by an error message. */ TclpCloseFile(errPipeOut); errPipeOut = NULL; fd = GetFd(errPipeIn); count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1)); if (count > 0) { char *end; errSpace[count] = 0; errno = strtol(errSpace, &end, 10); Tcl_AppendResult(interp, end, Tcl_PosixError(interp), (char *) NULL); goto error; } TclpCloseFile(errPipeIn); *pidPtr = (Tcl_Pid) pid; return TCL_OK; error: if (pid != -1) { /* * Reap the child process now if an error occurred during its * startup. We don't call this with WNOHANG because that can lead to * defunct processes on an MP system. We shouldn't have to worry * about hanging here, since this is the error case. [Bug: 6148] */ Tcl_WaitPid((Tcl_Pid) pid, &status, 0); } if (errPipeIn) { TclpCloseFile(errPipeIn); } if (errPipeOut) { TclpCloseFile(errPipeOut); } return TCL_ERROR;}/* *---------------------------------------------------------------------- * * RestoreSignals -- * * This procedure is invoked in a forked child process just before * exec-ing a new program to restore all signals to their default * settings. * * Results: * None. * * Side effects: * Signal settings get changed. * *---------------------------------------------------------------------- */ static voidRestoreSignals(){#ifdef SIGABRT signal(SIGABRT, SIG_DFL);#endif#ifdef SIGALRM signal(SIGALRM, SIG_DFL);#endif#ifdef SIGFPE signal(SIGFPE, SIG_DFL);#endif#ifdef SIGHUP signal(SIGHUP, SIG_DFL);#endif#ifdef SIGILL signal(SIGILL, SIG_DFL);#endif#ifdef SIGINT signal(SIGINT, SIG_DFL);#endif#ifdef SIGPIPE signal(SIGPIPE, SIG_DFL);#endif#ifdef SIGQUIT signal(SIGQUIT, SIG_DFL);#endif#ifdef SIGSEGV signal(SIGSEGV, SIG_DFL);#endif#ifdef SIGTERM signal(SIGTERM, SIG_DFL);#endif#ifdef SIGUSR1 signal(SIGUSR1, SIG_DFL);#endif#ifdef SIGUSR2 signal(SIGUSR2, SIG_DFL);#endif#ifdef SIGCHLD signal(SIGCHLD, SIG_DFL);#endif#ifdef SIGCONT signal(SIGCONT, SIG_DFL);#endif#ifdef SIGTSTP signal(SIGTSTP, SIG_DFL);#endif#ifdef SIGTTIN signal(SIGTTIN, SIG_DFL);#endif#ifdef SIGTTOU signal(SIGTTOU, SIG_DFL);#endif}/* *---------------------------------------------------------------------- * * SetupStdFile -- * * Set up stdio file handles for the child process, using the * current standard channels if no other files are specified. * If no standard channel is defined, or if no file is associated * with the channel, then the corresponding standard fd is closed. * * Results: * Returns 1 on success, or 0 on failure. * * Side effects: * Replaces stdio fds. * *---------------------------------------------------------------------- */static intSetupStdFile(file, type) TclFile file; /* File to dup, or NULL. */ int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */{ Tcl_Channel channel; int fd; int targetFd = 0; /* Initializations here needed only to */ int direction = 0; /* prevent warnings about using uninitialized * variables. */ switch (type) { case TCL_STDIN: targetFd = 0; direction = TCL_READABLE; break; case TCL_STDOUT: targetFd = 1; direction = TCL_WRITABLE; break; case TCL_STDERR: targetFd = 2; direction = TCL_WRITABLE; break; } if (!file) { channel = Tcl_GetStdChannel(type); if (channel) { file = TclpMakeFile(channel, direction); } } if (file) { fd = GetFd(file); if (fd != targetFd) { if (dup2(fd, targetFd) == -1) { return 0; } /* * Must clear the close-on-exec flag for the target FD, since * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on * the target FD. */ 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[16 + TCL_INTEGER_SPACE]; 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 the interp's result and to detach the processes. * * Results: * None. * * Side effects: * Modifies the interp's result. Detaches processes. * *---------------------------------------------------------------------- */voidTclGetAndDetachPids(interp, chan) Tcl_Interp *interp; Tcl_Channel chan;{ PipeState *pipePtr; Tcl_ChannelType *chanTypePtr; int i; char buf[TCL_INTEGER_SPACE]; /* * 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++) { TclFormatInt(buf, (long) 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -