summaryrefslogtreecommitdiffstats
path: root/contrib/tcl/unix/tclUnixPipe.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tcl/unix/tclUnixPipe.c')
-rw-r--r--contrib/tcl/unix/tclUnixPipe.c496
1 files changed, 496 insertions, 0 deletions
diff --git a/contrib/tcl/unix/tclUnixPipe.c b/contrib/tcl/unix/tclUnixPipe.c
new file mode 100644
index 0000000..a7ff1b3
--- /dev/null
+++ b/contrib/tcl/unix/tclUnixPipe.c
@@ -0,0 +1,496 @@
+/*
+ * tclUnixPipe.c -- This file implements the UNIX-specific exec pipeline
+ * functions.
+ *
+ * Copyright (c) 1991-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1996 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.29 96/04/18 15:56:26
+ */
+
+#include "tclInt.h"
+#include "tclPort.h"
+
+/*
+ * Declarations for local procedures defined in this file:
+ */
+
+static void RestoreSignals _ANSI_ARGS_((void));
+static int SetupStdFile _ANSI_ARGS_((Tcl_File file, int type));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 void
+RestoreSignals()
+{
+#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 int
+SetupStdFile(file, type)
+ Tcl_File 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 = Tcl_GetChannelFile(channel, direction);
+ }
+ }
+ if (file) {
+ fd = (int)Tcl_GetFileInfo(file, NULL);
+ 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclSpawnPipeline --
+ *
+ * Given an argc/argv array, instantiate a pipeline of processes
+ * as described by the argv.
+ *
+ * Results:
+ * The return value is 1 on success, 0 on error
+ *
+ * Side effects:
+ * Processes and pipes are created.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclSpawnPipeline(interp, pidPtr, numPids, argc, argv, inputFile,
+ outputFile, errorFile, intIn, finalOut)
+ Tcl_Interp *interp; /* Interpreter in which to process pipeline. */
+ int *pidPtr; /* Array of pids which are created. */
+ int *numPids; /* Number of pids created. */
+ int argc; /* Number of entries in argv. */
+ char **argv; /* Array of strings describing commands in
+ * pipeline plus I/O redirection with <,
+ * <<, >, etc. argv[argc] must be NULL. */
+ Tcl_File inputFile; /* If >=0, gives file id to use as input for
+ * first process in pipeline (specified via <
+ * or <@). */
+ Tcl_File outputFile; /* Writable file id for output from last
+ * command in pipeline (could be file or
+ * pipe). NULL means use stdout. */
+ Tcl_File errorFile; /* Writable file id for error output from all
+ * commands in the pipeline. NULL means use
+ * stderr */
+ char *intIn; /* File name for initial input (for Win32s). */
+ char *finalOut; /* File name for final output (for Win32s). */
+{
+ int firstArg, lastArg;
+ int pid, count;
+ Tcl_DString buffer;
+ char *execName;
+ char errSpace[200];
+ Tcl_File pipeIn, errPipeIn, errPipeOut;
+ int joinThisError;
+ Tcl_File curOutFile = NULL, curInFile;
+
+ Tcl_DStringInit(&buffer);
+ pipeIn = errPipeIn = errPipeOut = NULL;
+
+ curInFile = inputFile;
+
+ for (firstArg = 0; firstArg < argc; firstArg = lastArg+1) {
+
+ /*
+ * Convert the program name into native form.
+ */
+
+ Tcl_DStringFree(&buffer);
+ execName = Tcl_TranslateFileName(interp, argv[firstArg], &buffer);
+ if (execName == NULL) {
+ goto error;
+ }
+
+ /*
+ * Find the end of the current segment of the pipeline.
+ */
+
+ joinThisError = 0;
+ for (lastArg = firstArg; lastArg < argc; lastArg++) {
+ if (argv[lastArg][0] == '|') {
+ if (argv[lastArg][1] == 0) {
+ break;
+ }
+ if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == 0)) {
+ joinThisError = 1;
+ break;
+ }
+ }
+ }
+ argv[lastArg] = NULL;
+
+ /*
+ * If this is the last segment, use the specified outputFile.
+ * Otherwise create an intermediate pipe.
+ */
+
+ if (lastArg == argc) {
+ curOutFile = outputFile;
+ } else {
+ if (TclCreatePipe(&pipeIn, &curOutFile) == 0) {
+ Tcl_AppendResult(interp, "couldn't create pipe: ",
+ Tcl_PosixError(interp), (char *) NULL);
+ goto error;
+ }
+ }
+
+ /*
+ * Create a pipe that the child can use to return error
+ * information if anything goes wrong.
+ */
+
+ if (TclCreatePipe(&errPipeIn, &errPipeOut) == 0) {
+ Tcl_AppendResult(interp, "couldn't create pipe: ",
+ Tcl_PosixError(interp), (char *) NULL);
+ goto error;
+ }
+
+ pid = vfork();
+ if (pid == 0) {
+
+ /*
+ * Set up stdio file handles for the child process.
+ */
+
+ if (!SetupStdFile(curInFile, TCL_STDIN)
+ || !SetupStdFile(curOutFile, 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);
+ TclWriteFile(errPipeOut, 1, errSpace, (int) strlen(errSpace));
+ _exit(1);
+ }
+
+ /*
+ * Close the input side of the error pipe.
+ */
+
+ RestoreSignals();
+ execvp(execName, &argv[firstArg]);
+ sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno,
+ argv[firstArg]);
+ TclWriteFile(errPipeOut, 1, errSpace, (int) strlen(errSpace));
+ _exit(1);
+ }
+ Tcl_DStringFree(&buffer);
+ if (pid == -1) {
+ Tcl_AppendResult(interp, "couldn't fork child process: ",
+ Tcl_PosixError(interp), (char *) NULL);
+ goto error;
+ }
+
+ /*
+ * Add the child process to the list of those to be reaped.
+ * Note: must do it now, so that the process will be reaped even if
+ * an error occurs during its startup.
+ */
+
+ pidPtr[*numPids] = pid;
+ (*numPids)++;
+
+ /*
+ * Read back from the error pipe to see if the child startup
+ * up OK. The info in the pipe (if any) consists of a decimal
+ * errno value followed by an error message.
+ */
+
+ TclCloseFile(errPipeOut);
+ errPipeOut = NULL;
+
+ count = TclReadFile(errPipeIn, 1, 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;
+ }
+ TclCloseFile(errPipeIn);
+ errPipeIn = NULL;
+
+ /*
+ * Close off our copies of file descriptors that were set up for
+ * this child, then set up the input for the next child.
+ */
+
+ if (curInFile && (curInFile != inputFile)) {
+ TclCloseFile(curInFile);
+ }
+ curInFile = pipeIn;
+ pipeIn = NULL;
+
+ if (curOutFile && (curOutFile != outputFile)) {
+ TclCloseFile(curOutFile);
+ }
+ curOutFile = NULL;
+ }
+ return 1;
+
+ /*
+ * An error occured, so we need to clean up any open pipes.
+ */
+
+error:
+ Tcl_DStringFree(&buffer);
+ if (errPipeIn) {
+ TclCloseFile(errPipeIn);
+ }
+ if (errPipeOut) {
+ TclCloseFile(errPipeOut);
+ }
+ if (pipeIn) {
+ TclCloseFile(pipeIn);
+ }
+ if (curOutFile && (curOutFile != outputFile)) {
+ TclCloseFile(curOutFile);
+ }
+ if (curInFile && (curInFile != inputFile)) {
+ TclCloseFile(curInFile);
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TclCreatePipe --
+ *
+ * Creates a pipe - simply calls the pipe() function.
+ *
+ * Results:
+ * Returns 1 on success, 0 on failure.
+ *
+ * Side effects:
+ * Creates a pipe.
+ *
+ *----------------------------------------------------------------------
+ */
+int
+TclCreatePipe(readPipe, writePipe)
+ Tcl_File *readPipe; /* Location to store file handle for
+ * read side of pipe. */
+ Tcl_File *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 = Tcl_GetFile((ClientData)pipeIds[0], TCL_UNIX_FD);
+ *writePipe = Tcl_GetFile((ClientData)pipeIds[1], TCL_UNIX_FD);
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tcl_CreatePipeline --
+ *
+ * This function is a compatibility wrapper for TclCreatePipeline.
+ * It is only available under Unix, and may be removed from later
+ * versions.
+ *
+ * Results:
+ * Same as TclCreatePipeline.
+ *
+ * Side effects:
+ * Same as TclCreatePipeline.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_CreatePipeline(interp, argc, argv, pidArrayPtr, inPipePtr,
+ outPipePtr, errFilePtr)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+ int **pidArrayPtr;
+ int *inPipePtr;
+ int *outPipePtr;
+ int *errFilePtr;
+{
+ Tcl_File inFile, outFile, errFile;
+ int result;
+
+ result = TclCreatePipeline(interp, argc, argv, pidArrayPtr,
+ (inPipePtr ? &inFile : NULL),
+ (outPipePtr ? &outFile : NULL),
+ (errFilePtr ? &errFile : NULL));
+
+ if (inPipePtr) {
+ if (inFile) {
+ *inPipePtr = (int) Tcl_GetFileInfo(inFile, NULL);
+ Tcl_FreeFile(inFile);
+ } else {
+ *inPipePtr = -1;
+ }
+ }
+ if (outPipePtr) {
+ if (outFile) {
+ *outPipePtr = (int) Tcl_GetFileInfo(outFile, NULL);
+ Tcl_FreeFile(outFile);
+ } else {
+ *outPipePtr = -1;
+ }
+ }
+ if (errFilePtr) {
+ if (errFile) {
+ *errFilePtr = (int) Tcl_GetFileInfo(errFile, NULL);
+ Tcl_FreeFile(errFile);
+ } else {
+ *errFilePtr = -1;
+ }
+ }
+ return result;
+}
OpenPOWER on IntegriCloud