📄 tclunixpipe.c
字号:
/* * tclUnixPipe.c -- * * This file implements the UNIX-specific exec pipeline functions, * the "pipe" channel driver, and the "pid" Tcl command. * * Copyright (c) 1991-1994 The Regents of the University of California. * Copyright (c) 1994-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: @(#) tclUnixPipe.c 1.37 97/10/31 17:23:37 */#include "tclInt.h"#include "tclPort.h"/* * The following macros convert between TclFile's and fd's. The conversion * simple involves shifting fd's up by one to ensure that no valid fd is ever * the same as NULL. */#define MakeFile(fd) ((TclFile)(((int)fd)+1))#define GetFd(file) (((int)file)-1)/* * This structure describes per-instance state of a pipe based channel. */typedef struct PipeState { Tcl_Channel channel;/* Channel associated with this file. */ TclFile inFile; /* Output from pipe. */ TclFile outFile; /* Input to pipe. */ TclFile errorFile; /* Error output from pipe. */ int numPids; /* How many processes are attached to this pipe? */ Tcl_Pid *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;/* * Declarations for local procedures defined in this file: */static int PipeBlockModeProc _ANSI_ARGS_((ClientData instanceData, int mode));static int PipeCloseProc _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp));static int PipeGetHandleProc _ANSI_ARGS_((ClientData instanceData, int direction, ClientData *handlePtr));static int PipeInputProc _ANSI_ARGS_((ClientData instanceData, char *buf, int toRead, int *errorCode));static int PipeOutputProc _ANSI_ARGS_(( ClientData instanceData, char *buf, int toWrite, int *errorCode));static void PipeWatchProc _ANSI_ARGS_((ClientData instanceData, int mask));static void RestoreSignals _ANSI_ARGS_((void));static int SetupStdFile _ANSI_ARGS_((TclFile file, int type));/* * This structure describes the channel type structure for command pipe * based IO: */static Tcl_ChannelType pipeChannelType = { "pipe", /* Type name. */ PipeBlockModeProc, /* Set blocking/nonblocking mode.*/ PipeCloseProc, /* Close proc. */ PipeInputProc, /* Input proc. */ PipeOutputProc, /* Output proc. */ NULL, /* Seek proc. */ NULL, /* Set option proc. */ NULL, /* Get option proc. */ PipeWatchProc, /* Initialize notifier. */ PipeGetHandleProc, /* Get OS handles out of channel. */};/* *---------------------------------------------------------------------- * * TclpMakeFile -- * * Make a TclFile from a channel. * * Results: * Returns a new TclFile or NULL on failure. * * Side effects: * None. * *---------------------------------------------------------------------- */TclFileTclpMakeFile(channel, direction) Tcl_Channel channel; /* Channel to get file from. */ int direction; /* Either TCL_READABLE or TCL_WRITABLE. */{ ClientData data; if (Tcl_GetChannelHandle(channel, direction, (ClientData *) &data) == TCL_OK) { return MakeFile((int)data); } else { return (TclFile) NULL; }}/* *---------------------------------------------------------------------- * * TclpOpenFile -- * * Open a file for use in a pipeline. * * Results: * Returns a new TclFile handle or NULL on failure. * * Side effects: * May cause a file to be created on the file system. * *---------------------------------------------------------------------- */TclFileTclpOpenFile(fname, mode) char *fname; /* The name of the file to open. */ int mode; /* In what mode to open the file? */{ int fd; fd = open(fname, mode, 0666); if (fd != -1) { fcntl(fd, F_SETFD, FD_CLOEXEC); /* * If the file is being opened for writing, seek to the end * so we can append to any data already in the file. */ if (mode & O_WRONLY) { lseek(fd, 0, SEEK_END); } /* * Increment the fd so it can't be 0, which would conflict with * the NULL return for errors. */ return MakeFile(fd); } return NULL;}/* *---------------------------------------------------------------------- * * TclpCreateTempFile -- * * This function creates a temporary file initialized with an * optional string, and returns a file handle with the file pointer * at the beginning of the file. * * Results: * A handle to a file. * * Side effects: * None. * *---------------------------------------------------------------------- */TclFileTclpCreateTempFile(contents, namePtr) char *contents; /* String to write into temp file, or NULL. */ Tcl_DString *namePtr; /* If non-NULL, pointer to initialized * DString that is filled with the name of * the temp file that was created. */{ char fileName[L_tmpnam]; TclFile file; size_t length = (contents == NULL) ? 0 : strlen(contents); tmpnam(fileName); file = TclpOpenFile(fileName, O_RDWR|O_CREAT|O_TRUNC); unlink(fileName); if ((file != NULL) && (length > 0)) { int fd = GetFd(file); while (1) { if (write(fd, contents, length) != -1) { break; } else if (errno != EINTR) { close(fd); return NULL; } } lseek(fd, 0, SEEK_SET); } if (namePtr != NULL) { Tcl_DStringAppend(namePtr, fileName, -1); } return file;}/* *---------------------------------------------------------------------- * * TclpCreatePipe -- * * Creates a pipe - simply calls the pipe() function. * * Results: * Returns 1 on success, 0 on failure. * * Side effects: * Creates a pipe. * *---------------------------------------------------------------------- */intTclpCreatePipe(readPipe, writePipe) TclFile *readPipe; /* Location to store file handle for * read side of pipe. */ TclFile *writePipe; /* Location to store file handle for * write side of pipe. */{ int pipeIds[2]; if (pipe(pipeIds) != 0) { return 0; } fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC); fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC); *readPipe = MakeFile(pipeIds[0]); *writePipe = MakeFile(pipeIds[1]); return 1;}/* *---------------------------------------------------------------------- * * TclpCloseFile -- * * Implements a mechanism to close a UNIX file. * * Results: * Returns 0 on success, or -1 on error, setting errno. * * Side effects: * The file is closed. * *---------------------------------------------------------------------- */intTclpCloseFile(file) TclFile file; /* The file to close. */{ int fd = GetFd(file); /* * Refuse to close the fds for stdin, stdout and stderr. */ if ((fd == 0) || (fd == 1) || (fd == 2)) { return 0; } Tcl_DeleteFileHandler(fd); return close(fd);}/* *---------------------------------------------------------------------- * * TclpCreateProcess -- * * Create a child process that has the specified files as its * standard input, output, and error. The child process runs * asynchronously and runs with the same environment variables * as the creating process. * * The path is searched to find the specified executable. * * Results: * The return value is TCL_ERROR and an error message is left in * interp->result if there was a problem creating the child * process. Otherwise, the return value is TCL_OK and *pidPtr is * filled with the process id of the child process. * * Side effects: * A process is created. * *---------------------------------------------------------------------- */ /* ARGSUSED */intTclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, pidPtr) Tcl_Interp *interp; /* Interpreter in which to leave errors that * occurred when creating the child process. * Error messages from the child process * itself are sent to errorFile. */ int argc; /* Number of arguments in following array. */ char **argv; /* Array of argument strings. argv[0] * contains the name of the executable * converted to native format (using the * Tcl_TranslateFileName call). Additional * arguments have not been converted. */ TclFile inputFile; /* If non-NULL, gives the file to use as * input for the child process. If inputFile * file is not readable or is NULL, the child * will receive no standard input. */ TclFile outputFile; /* If non-NULL, gives the file that * receives output from the child process. If * outputFile file is not writeable or is * NULL, output from the child will be * discarded. */ TclFile errorFile; /* If non-NULL, gives the file that * receives errors from the child process. If * errorFile file is not writeable or is NULL, * errors from the child will be discarded. * errorFile may be the same as outputFile. */ Tcl_Pid *pidPtr; /* If this procedure is successful, pidPtr * is filled with the process id of the child * process. */{ TclFile errPipeIn, errPipeOut; int joinThisError, count, status, fd; char errSpace[200]; int pid; errPipeIn = NULL; errPipeOut = NULL; pid = -1; /* * Create a pipe that the child can use to return error * information if anything goes wrong. */ if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) { Tcl_AppendResult(interp, "couldn't create pipe: ", Tcl_PosixError(interp), (char *) NULL); goto error; } joinThisError = (errorFile == outputFile); pid = vfork(); 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(argv[0], &argv[0]); sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]); write(fd, errSpace, (size_t) strlen(errSpace)); _exit(1); } 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. */ Tcl_WaitPid((Tcl_Pid) pid, &status, WNOHANG); } 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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -