sl@0: /* sl@0: * tclUnixPipe.c -- sl@0: * sl@0: * This file implements the UNIX-specific exec pipeline functions, sl@0: * the "pipe" channel driver, and the "pid" Tcl command. sl@0: * sl@0: * Copyright (c) 1991-1994 The Regents of the University of California. sl@0: * Copyright (c) 1994-1997 Sun Microsystems, Inc. sl@0: * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved. sl@0: * sl@0: * See the file "license.terms" for information on usage and redistribution sl@0: * of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: * sl@0: * RCS: @(#) $Id: tclUnixPipe.c,v 1.23.2.7 2006/08/02 20:04:40 das Exp $ sl@0: */ sl@0: sl@0: #include "tclInt.h" sl@0: #include "tclPort.h" sl@0: sl@0: #if defined(__SYMBIAN32__) sl@0: #include "tclSymbianGlobals.h" sl@0: #include sl@0: #define ADDPARAMTOCHILD 4 sl@0: #endif sl@0: sl@0: void TclPrint1(const char* aFmt, const char* aStr); sl@0: void TclPrint2(const char* aFmt, const char* aStr, int aNum); sl@0: void TclPrint3(const char* aFmt); sl@0: sl@0: sl@0: #ifdef USE_VFORK sl@0: #define fork vfork sl@0: #endif sl@0: sl@0: /* sl@0: * The following macros convert between TclFile's and fd's. The conversion sl@0: * simple involves shifting fd's up by one to ensure that no valid fd is ever sl@0: * the same as NULL. sl@0: */ sl@0: sl@0: #define MakeFile(fd) ((TclFile)(((int)fd)+1)) sl@0: #define GetFd(file) (((int)file)-1) sl@0: /* sl@0: * This structure describes per-instance state of a pipe based channel. sl@0: */ sl@0: sl@0: typedef struct PipeState { sl@0: Tcl_Channel channel;/* Channel associated with this file. */ sl@0: TclFile inFile; /* Output from pipe. */ sl@0: TclFile outFile; /* Input to pipe. */ sl@0: TclFile errorFile; /* Error output from pipe. */ sl@0: int numPids; /* How many processes are attached to this pipe? */ sl@0: Tcl_Pid *pidPtr; /* The process IDs themselves. Allocated by sl@0: * the creator of the pipe. */ sl@0: int isNonBlocking; /* Nonzero when the pipe is in nonblocking mode. sl@0: * Used to decide whether to wait for the children sl@0: * at close time. */ sl@0: } PipeState; sl@0: sl@0: /* sl@0: * Declarations for local procedures defined in this file: sl@0: */ sl@0: sl@0: static int PipeBlockModeProc _ANSI_ARGS_((ClientData instanceData, sl@0: int mode)); sl@0: static int PipeCloseProc _ANSI_ARGS_((ClientData instanceData, sl@0: Tcl_Interp *interp)); sl@0: static int PipeGetHandleProc _ANSI_ARGS_((ClientData instanceData, sl@0: int direction, ClientData *handlePtr)); sl@0: static int PipeInputProc _ANSI_ARGS_((ClientData instanceData, sl@0: char *buf, int toRead, int *errorCode)); sl@0: static int PipeOutputProc _ANSI_ARGS_(( sl@0: ClientData instanceData, CONST char *buf, int toWrite, sl@0: int *errorCode)); sl@0: static void PipeWatchProc _ANSI_ARGS_((ClientData instanceData, int mask)); sl@0: static void RestoreSignals _ANSI_ARGS_((void)); sl@0: #ifndef __SYMBIAN32__ sl@0: static int SetupStdFile _ANSI_ARGS_((TclFile file, int type)); sl@0: #endif sl@0: /* sl@0: * This structure describes the channel type structure for command pipe sl@0: * based IO: sl@0: */ sl@0: sl@0: static Tcl_ChannelType pipeChannelType = { sl@0: "pipe", /* Type name. */ sl@0: TCL_CHANNEL_VERSION_4, /* v4 channel */ sl@0: PipeCloseProc, /* Close proc. */ sl@0: PipeInputProc, /* Input proc. */ sl@0: PipeOutputProc, /* Output proc. */ sl@0: NULL, /* Seek proc. */ sl@0: NULL, /* Set option proc. */ sl@0: NULL, /* Get option proc. */ sl@0: PipeWatchProc, /* Initialize notifier. */ sl@0: PipeGetHandleProc, /* Get OS handles out of channel. */ sl@0: NULL, /* close2proc. */ sl@0: PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ sl@0: NULL, /* flush proc. */ sl@0: NULL, /* handler proc. */ sl@0: NULL, /* wide seek proc */ sl@0: NULL, /* thread action proc */ sl@0: }; sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpMakeFile -- sl@0: * sl@0: * Make a TclFile from a channel. sl@0: * sl@0: * Results: sl@0: * Returns a new TclFile or NULL on failure. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: TclFile sl@0: TclpMakeFile(channel, direction) sl@0: Tcl_Channel channel; /* Channel to get file from. */ sl@0: int direction; /* Either TCL_READABLE or TCL_WRITABLE. */ sl@0: { sl@0: ClientData data; sl@0: sl@0: if (Tcl_GetChannelHandle(channel, direction, (ClientData *) &data) sl@0: == TCL_OK) { sl@0: return MakeFile((int)data); sl@0: } else { sl@0: return (TclFile) NULL; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpOpenFile -- sl@0: * sl@0: * Open a file for use in a pipeline. sl@0: * sl@0: * Results: sl@0: * Returns a new TclFile handle or NULL on failure. sl@0: * sl@0: * Side effects: sl@0: * May cause a file to be created on the file system. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: TclFile sl@0: TclpOpenFile(fname, mode) sl@0: CONST char *fname; /* The name of the file to open. */ sl@0: int mode; /* In what mode to open the file? */ sl@0: { sl@0: int fd; sl@0: CONST char *native; sl@0: Tcl_DString ds; sl@0: sl@0: native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds); sl@0: fd = TclOSopen(native, mode, 0666); /* INTL: Native. */ sl@0: Tcl_DStringFree(&ds); sl@0: if (fd != -1) { sl@0: fcntl(fd, F_SETFD, FD_CLOEXEC); sl@0: sl@0: /* sl@0: * If the file is being opened for writing, seek to the end sl@0: * so we can append to any data already in the file. sl@0: */ sl@0: sl@0: if ((mode & O_WRONLY) && !(mode & O_APPEND)) { sl@0: TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_END); sl@0: } sl@0: sl@0: /* sl@0: * Increment the fd so it can't be 0, which would conflict with sl@0: * the NULL return for errors. sl@0: */ sl@0: sl@0: return MakeFile(fd); sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpCreateTempFile -- sl@0: * sl@0: * This function creates a temporary file initialized with an sl@0: * optional string, and returns a file handle with the file pointer sl@0: * at the beginning of the file. sl@0: * sl@0: * Results: sl@0: * A handle to a file. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: TclFile sl@0: TclpCreateTempFile(contents) sl@0: CONST char *contents; /* String to write into temp file, or NULL. */ sl@0: { sl@0: char fileName[L_tmpnam + 9]; sl@0: CONST char *native; sl@0: Tcl_DString dstring; sl@0: int fd; sl@0: sl@0: /* sl@0: * We should also check against making more then TMP_MAX of these. sl@0: */ sl@0: sl@0: // Symbian doesn't have a default temp directory, so we use cwd (root of private data cage) sl@0: #ifdef __SYMBIAN32__ sl@0: if (getcwd(fileName, L_tmpnam) == NULL) sl@0: { sl@0: return NULL; sl@0: } sl@0: /* TODO - the line bellow is a temporary patch. The defect number is: DEF116621. */ sl@0: fileName[0] = 'c'; sl@0: #else sl@0: strcpy(fileName, P_tmpdir); /* INTL: Native. */ sl@0: #endif sl@0: if (fileName[strlen(fileName) - 1] != '/') { sl@0: #ifdef __SYMBIAN32__ sl@0: strcat(fileName, "\\"); sl@0: #else sl@0: strcat(fileName, "/"); /* INTL: Native. */ sl@0: #endif sl@0: } sl@0: strcat(fileName, "tclXXXXXX"); sl@0: fd = mkstemp(fileName); /* INTL: Native. */ sl@0: if (fd == -1) { sl@0: return NULL; sl@0: } sl@0: fcntl(fd, F_SETFD, FD_CLOEXEC); sl@0: #ifdef __SYMBIAN32__ sl@0: strcpy(tmpFileName, fileName); sl@0: #endif sl@0: unlink(fileName); /* INTL: Native. */ sl@0: sl@0: if (contents != NULL) { sl@0: native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring); sl@0: if (write(fd, native, strlen(native)) == -1) { sl@0: close(fd); sl@0: Tcl_DStringFree(&dstring); sl@0: return NULL; sl@0: } sl@0: Tcl_DStringFree(&dstring); sl@0: TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_SET); sl@0: } sl@0: return MakeFile(fd); sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpTempFileName -- sl@0: * sl@0: * This function returns unique filename. sl@0: * sl@0: * Results: sl@0: * Returns a valid Tcl_Obj* with refCount 0, or NULL on failure. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Obj* sl@0: TclpTempFileName() sl@0: { sl@0: char fileName[L_tmpnam + 9]; sl@0: Tcl_Obj *result = NULL; sl@0: int fd; sl@0: sl@0: /* sl@0: * We should also check against making more then TMP_MAX of these. sl@0: */ sl@0: sl@0: // Symbian doesn't have a default temp directory, so we use cwd (root of private data cage) sl@0: #ifdef __SYMBIAN32__ sl@0: if (getcwd(fileName, L_tmpnam) == NULL) sl@0: { sl@0: return NULL; sl@0: } sl@0: /* TODO - the line bellow is a temporary patch. The defect number is: DEF116621. */ sl@0: fileName[0] = 'c'; sl@0: #else sl@0: strcpy(fileName, P_tmpdir); /* INTL: Native. */ sl@0: #endif sl@0: if (fileName[strlen(fileName) - 1] != '/') { sl@0: #ifdef __SYMBIAN32__ sl@0: strcat(fileName, "\\"); sl@0: #else sl@0: strcat(fileName, "/"); /* INTL: Native. */ sl@0: #endif sl@0: } sl@0: strcat(fileName, "tclXXXXXX"); sl@0: fd = mkstemp(fileName); /* INTL: Native. */ sl@0: if (fd == -1) { sl@0: return NULL; sl@0: } sl@0: fcntl(fd, F_SETFD, FD_CLOEXEC); sl@0: unlink(fileName); /* INTL: Native. */ sl@0: sl@0: result = TclpNativeToNormalized((ClientData) fileName); sl@0: close (fd); sl@0: return result; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpCreatePipe -- sl@0: * sl@0: * Creates a pipe - simply calls the pipe() function. sl@0: * sl@0: * Results: sl@0: * Returns 1 on success, 0 on failure. sl@0: * sl@0: * Side effects: sl@0: * Creates a pipe. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpCreatePipe(readPipe, writePipe) sl@0: TclFile *readPipe; /* Location to store file handle for sl@0: * read side of pipe. */ sl@0: TclFile *writePipe; /* Location to store file handle for sl@0: * write side of pipe. */ sl@0: { sl@0: int pipeIds[2]; sl@0: sl@0: if (pipe(pipeIds) != 0) { sl@0: return 0; sl@0: } sl@0: sl@0: fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC); sl@0: fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC); sl@0: sl@0: *readPipe = MakeFile(pipeIds[0]); sl@0: *writePipe = MakeFile(pipeIds[1]); sl@0: return 1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpCloseFile -- sl@0: * sl@0: * Implements a mechanism to close a UNIX file. sl@0: * sl@0: * Results: sl@0: * Returns 0 on success, or -1 on error, setting errno. sl@0: * sl@0: * Side effects: sl@0: * The file is closed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: int sl@0: TclpCloseFile(file) sl@0: TclFile file; /* The file to close. */ sl@0: { sl@0: int fd = GetFd(file); sl@0: sl@0: /* sl@0: * Refuse to close the fds for stdin, stdout and stderr. sl@0: */ sl@0: sl@0: if ((fd == 0) || (fd == 1) || (fd == 2)) { sl@0: return 0; sl@0: } sl@0: sl@0: Tcl_DeleteFileHandler(fd); sl@0: return close(fd); sl@0: } sl@0: sl@0: /* sl@0: *--------------------------------------------------------------------------- sl@0: * sl@0: * TclpCreateProcess -- sl@0: * sl@0: * Create a child process that has the specified files as its sl@0: * standard input, output, and error. The child process runs sl@0: * asynchronously and runs with the same environment variables sl@0: * as the creating process. sl@0: * sl@0: * The path is searched to find the specified executable. sl@0: * sl@0: * Results: sl@0: * The return value is TCL_ERROR and an error message is left in sl@0: * the interp's result if there was a problem creating the child sl@0: * process. Otherwise, the return value is TCL_OK and *pidPtr is sl@0: * filled with the process id of the child process. sl@0: * sl@0: * Side effects: sl@0: * A process is created. sl@0: * sl@0: *--------------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: int sl@0: TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, sl@0: pidPtr) sl@0: Tcl_Interp *interp; /* Interpreter in which to leave errors that sl@0: * occurred when creating the child process. sl@0: * Error messages from the child process sl@0: * itself are sent to errorFile. */ sl@0: int argc; /* Number of arguments in following array. */ sl@0: CONST char **argv; /* Array of argument strings in UTF-8. sl@0: * argv[0] contains the name of the executable sl@0: * translated using Tcl_TranslateFileName sl@0: * call). Additional arguments have not been sl@0: * converted. */ sl@0: TclFile inputFile; /* If non-NULL, gives the file to use as sl@0: * input for the child process. If inputFile sl@0: * file is not readable or is NULL, the child sl@0: * will receive no standard input. */ sl@0: TclFile outputFile; /* If non-NULL, gives the file that sl@0: * receives output from the child process. If sl@0: * outputFile file is not writeable or is sl@0: * NULL, output from the child will be sl@0: * discarded. */ sl@0: TclFile errorFile; /* If non-NULL, gives the file that sl@0: * receives errors from the child process. If sl@0: * errorFile file is not writeable or is NULL, sl@0: * errors from the child will be discarded. sl@0: * errorFile may be the same as outputFile. */ sl@0: Tcl_Pid *pidPtr; /* If this procedure is successful, pidPtr sl@0: * is filled with the process id of the child sl@0: * process. */ sl@0: { sl@0: TclFile errPipeIn, errPipeOut; sl@0: int count, status, fd; sl@0: char errSpace[200 + TCL_INTEGER_SPACE]; sl@0: Tcl_DString *dsArray; sl@0: char **newArgv = 0; sl@0: int pid, i; sl@0: #ifdef __SYMBIAN32__ sl@0: int fd1; sl@0: int RetVal; sl@0: char buf[256]; sl@0: int fifoResult; sl@0: struct timeval tv; sl@0: fd_set readfds; sl@0: #endif sl@0: sl@0: errPipeIn = NULL; sl@0: errPipeOut = NULL; sl@0: pid = -1; sl@0: sl@0: /* sl@0: * Create a pipe that the child can use to return error sl@0: * information if anything goes wrong. sl@0: */ sl@0: #ifdef __SYMBIAN32__ sl@0: // change the communication between parent and child process, it just report the failure of child process creation sl@0: sl@0: tmpnam(fifoFileName); sl@0: fifoResult = mkfifo(fifoFileName,S_IXGRP); sl@0: if(fifoResult == -1) sl@0: { sl@0: //fifo creation failure. sl@0: fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno); sl@0: goto error; sl@0: } sl@0: else sl@0: { sl@0: int ReadFifoFd = open(fifoFileName,O_RDONLY | O_NONBLOCK); sl@0: sl@0: if(ReadFifoFd == -1) sl@0: { sl@0: //Failed to open the Fifo sl@0: printf("\n*** failure Fifo Open ***\n"); sl@0: goto error; sl@0: } sl@0: else sl@0: { sl@0: errPipeIn = MakeFile(ReadFifoFd); sl@0: } sl@0: } sl@0: sl@0: //set the stdin,stdout,stderr and pipeid to the child process. the fds are passed to the posix_spawn() in argv sl@0: argc = argc + ADDPARAMTOCHILD; sl@0: sl@0: #else sl@0: if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) { sl@0: Tcl_AppendResult(interp, "couldn't create pipe: ", sl@0: Tcl_PosixError(interp), (char *) NULL); sl@0: goto error; sl@0: } sl@0: #endif sl@0: sl@0: /* sl@0: * We need to allocate and convert this before the fork sl@0: * so it is properly deallocated later sl@0: */ sl@0: dsArray = (Tcl_DString *) ckalloc(argc * sizeof(Tcl_DString)); sl@0: newArgv = (char **) ckalloc((argc+1) * sizeof(char *)); sl@0: newArgv[argc] = NULL; sl@0: #ifdef __SYMBIAN32__ sl@0: for (i = 0; i < (argc-ADDPARAMTOCHILD); i++) { sl@0: #else sl@0: for (i = 0; i < argc; i++) { sl@0: #endif sl@0: newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]); sl@0: } sl@0: sl@0: #ifdef __SYMBIAN32__ sl@0: fprintf(stderr,"\r\n"); sl@0: //set the stdin,stdout,stderr to the child process. the fds pass to the posix_spawn() in argv sl@0: for (i = argc-ADDPARAMTOCHILD; i < argc; i++) sl@0: { sl@0: if(i == (argc-ADDPARAMTOCHILD)) sl@0: { sl@0: strcpy(buf,fifoFileName); sl@0: fprintf(stderr,"fifoFileName is %s\r\n", fifoFileName); sl@0: } sl@0: else if(i == (argc-ADDPARAMTOCHILD+1)) sl@0: { sl@0: if (inputFile) sl@0: { sl@0: strcpy(buf,inFileName); sl@0: } sl@0: else sl@0: { sl@0: strcpy(buf,"STD"); sl@0: } sl@0: fprintf(stderr, "inFileName is %s\r\n", inFileName); sl@0: } sl@0: else if(i == (argc-ADDPARAMTOCHILD+2)) sl@0: { sl@0: if (outputFile) sl@0: { sl@0: strcpy(buf,outFileName); sl@0: } sl@0: else sl@0: { sl@0: strcpy(buf,"STD"); sl@0: } sl@0: fprintf(stderr, "outFileName is %s\r\n", outFileName); sl@0: } sl@0: else if(i == (argc-ADDPARAMTOCHILD+3)) sl@0: { sl@0: if (errorFile) sl@0: { sl@0: strcpy(buf,errFileName); sl@0: } sl@0: else sl@0: { sl@0: strcpy(buf,"STD"); sl@0: } sl@0: fprintf(stderr, "errFileName is %s\r\n", errFileName); sl@0: } sl@0: sl@0: newArgv[i] = Tcl_UtfToExternalDString(NULL, buf, -1, &dsArray[i]); sl@0: } sl@0: #endif sl@0: #ifdef USE_VFORK sl@0: /* sl@0: * After vfork(), do not call code in the child that changes global state, sl@0: * because it is using the parent's memory space at that point and writes sl@0: * might corrupt the parent: so ensure standard channels are initialized in sl@0: * the parent, otherwise SetupStdFile() might initialize them in the child. sl@0: */ sl@0: if (!inputFile) { sl@0: Tcl_GetStdChannel(TCL_STDIN); sl@0: } sl@0: if (!outputFile) { sl@0: Tcl_GetStdChannel(TCL_STDOUT); sl@0: } sl@0: if (!errorFile) { sl@0: Tcl_GetStdChannel(TCL_STDERR); sl@0: } sl@0: #endif sl@0: TclPrint1(" == TclpCreateProcess(), posix_spawn(), process %S\r\n", newArgv[0]); sl@0: #ifdef __SYMBIAN32__ sl@0: // change fork() using posix_spawn() sl@0: if (argc > 1) sl@0: { sl@0: RetVal= posix_spawn(&pid,newArgv[0],NULL,NULL,newArgv,NULL); sl@0: } sl@0: else sl@0: { sl@0: RetVal= posix_spawn(&pid,newArgv[0],NULL,NULL,NULL,NULL); sl@0: } sl@0: if (RetVal != 0) sl@0: { sl@0: pid = -1; //if error, the value of pid is unspecified. ensure we still report the error. sl@0: } sl@0: #else sl@0: pid = fork(); sl@0: if (pid == 0) { sl@0: int joinThisError = errorFile && (errorFile == outputFile); sl@0: sl@0: fd = GetFd(errPipeOut); sl@0: sl@0: /* sl@0: * Set up stdio file handles for the child process. sl@0: */ sl@0: sl@0: if (!SetupStdFile(inputFile, TCL_STDIN) sl@0: || !SetupStdFile(outputFile, TCL_STDOUT) sl@0: || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR)) sl@0: || (joinThisError && sl@0: ((dup2(1,2) == -1) || sl@0: (fcntl(2, F_SETFD, 0) != 0)))) { sl@0: sprintf(errSpace, "forked process couldn't set up input/output, error: %d\r\n", errno); sl@0: write(fd, errSpace, (size_t) strlen(errSpace)); sl@0: _exit(1); sl@0: } sl@0: sl@0: /* sl@0: * Close the input side of the error pipe. sl@0: */ sl@0: sl@0: RestoreSignals(); sl@0: execvp(newArgv[0], newArgv); /* INTL: Native. */ sl@0: sprintf(errSpace, "couldn't execute \"%.150s\", error: %d\r\n", argv[0], errno); sl@0: write(fd, errSpace, (size_t) strlen(errSpace)); sl@0: _exit(1); sl@0: } sl@0: #endif sl@0: TclPrint2(" == TclpCreateProcess(), posix_spawn(), process %S, pid %d\r\n", newArgv[0], (int)pid); sl@0: sl@0: // sl@0: // Free the mem we used for the fork sl@0: // sl@0: for (i = 0; i < argc; i++) { sl@0: Tcl_DStringFree(&dsArray[i]); sl@0: } sl@0: ckfree((char *) dsArray); sl@0: ckfree((char *) newArgv); sl@0: sl@0: if (pid == -1) { sl@0: #ifdef __SYMBIAN32__ sl@0: Tcl_AppendResult(interp, "couldn't posix_spawn child process: ", sl@0: #else sl@0: Tcl_AppendResult(interp, "couldn't fork child process: ", sl@0: #endif sl@0: Tcl_PosixError(interp), (char *) NULL); sl@0: goto error; sl@0: } sl@0: sl@0: /* sl@0: * Read back from the error pipe to see if the child started sl@0: * up OK. The info in the pipe (if any) consists of a decimal sl@0: * errno value followed by an error message. sl@0: */ sl@0: //* sl@0: #ifdef __SYMBIAN32__ sl@0: TclpCloseFile(errPipeOut); sl@0: errPipeOut = NULL; sl@0: #endif sl@0: fd = GetFd(errPipeIn); sl@0: #ifdef __SYMBIAN32__ sl@0: TclPrint3(" == TclpCreateProcess(), select()\r\n"); sl@0: tv.tv_sec = 3; sl@0: tv.tv_usec = 0; sl@0: sl@0: FD_ZERO(&readfds); sl@0: FD_SET(fd, &readfds); sl@0: // Use select to wait for child process sl@0: i = select(fd+1, &readfds, NULL, NULL,&tv); sl@0: if ( i != -1 && FD_ISSET(fd, &readfds)) sl@0: { sl@0: count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1)); sl@0: if (count > 0) { sl@0: char *end; sl@0: errSpace[count] = 0; sl@0: errno = strtol(errSpace, &end, 10); sl@0: if (errno != 0) sl@0: { sl@0: Tcl_AppendResult(interp, end, Tcl_PosixError(interp), sl@0: (char *) NULL); sl@0: goto error; sl@0: } sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // Process not started properly sl@0: Tcl_AppendResult(interp, "couldn't read error info from child process: ", sl@0: Tcl_PosixError(interp), (char *) NULL); sl@0: goto error; sl@0: } sl@0: #else sl@0: count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1)); sl@0: if (count > 0) { sl@0: char *end; sl@0: errSpace[count] = 0; sl@0: errno = strtol(errSpace, &end, 10); sl@0: Tcl_AppendResult(interp, end, Tcl_PosixError(interp), sl@0: (char *) NULL); sl@0: goto error; sl@0: } sl@0: #endif sl@0: TclpCloseFile(errPipeIn); sl@0: #ifdef __SYMBIAN32__ sl@0: unlink(fifoFileName); sl@0: #endif sl@0: *pidPtr = (Tcl_Pid) pid; sl@0: TclPrint3(" == TclpCreateProcess(), TCL_OK\r\n"); sl@0: return TCL_OK; sl@0: sl@0: error: sl@0: TclPrint2(" == TclpCreateProcess(), TCL_ERROR,%S errno %d\r\n", "", errno); sl@0: if (pid != -1) { sl@0: /* sl@0: * Reap the child process now if an error occurred during its sl@0: * startup. We don't call this with WNOHANG because that can lead to sl@0: * defunct processes on an MP system. We shouldn't have to worry sl@0: * about hanging here, since this is the error case. [Bug: 6148] sl@0: */ sl@0: sl@0: Tcl_WaitPid((Tcl_Pid) pid, &status, 0); sl@0: } sl@0: sl@0: if (errPipeIn) { sl@0: TclpCloseFile(errPipeIn); sl@0: } sl@0: if (errPipeOut) { sl@0: TclpCloseFile(errPipeOut); sl@0: } sl@0: #ifdef __SYMBIAN32__ sl@0: unlink(fifoFileName); sl@0: #endif sl@0: sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * RestoreSignals -- sl@0: * sl@0: * This procedure is invoked in a forked child process just before sl@0: * exec-ing a new program to restore all signals to their default sl@0: * settings. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Signal settings get changed. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: RestoreSignals() sl@0: { sl@0: // Symbian & PIPS don't support signals. sl@0: #ifndef __SYMBIAN32__ sl@0: #ifdef SIGABRT sl@0: signal(SIGABRT, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGALRM sl@0: signal(SIGALRM, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGFPE sl@0: signal(SIGFPE, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGHUP sl@0: signal(SIGHUP, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGILL sl@0: signal(SIGILL, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGINT sl@0: signal(SIGINT, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGPIPE sl@0: signal(SIGPIPE, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGQUIT sl@0: signal(SIGQUIT, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGSEGV sl@0: signal(SIGSEGV, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGTERM sl@0: signal(SIGTERM, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGUSR1 sl@0: signal(SIGUSR1, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGUSR2 sl@0: signal(SIGUSR2, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGCHLD sl@0: signal(SIGCHLD, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGCONT sl@0: signal(SIGCONT, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGTSTP sl@0: signal(SIGTSTP, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGTTIN sl@0: signal(SIGTTIN, SIG_DFL); sl@0: #endif sl@0: #ifdef SIGTTOU sl@0: signal(SIGTTOU, SIG_DFL); sl@0: #endif sl@0: #endif sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * SetupStdFile -- sl@0: * sl@0: * Set up stdio file handles for the child process, using the sl@0: * current standard channels if no other files are specified. sl@0: * If no standard channel is defined, or if no file is associated sl@0: * with the channel, then the corresponding standard fd is closed. sl@0: * sl@0: * Results: sl@0: * Returns 1 on success, or 0 on failure. sl@0: * sl@0: * Side effects: sl@0: * Replaces stdio fds. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: #ifdef __SYMBIAN32__ sl@0: int sl@0: #else sl@0: static int sl@0: #endif sl@0: SetupStdFile(file, type) sl@0: TclFile file; /* File to dup, or NULL. */ sl@0: int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */ sl@0: { sl@0: Tcl_Channel channel; sl@0: int fd; sl@0: int targetFd = 0; /* Initializations here needed only to */ sl@0: int direction = 0; /* prevent warnings about using uninitialized sl@0: * variables. */ sl@0: sl@0: switch (type) { sl@0: case TCL_STDIN: sl@0: targetFd = 0; sl@0: direction = TCL_READABLE; sl@0: break; sl@0: case TCL_STDOUT: sl@0: targetFd = 1; sl@0: direction = TCL_WRITABLE; sl@0: break; sl@0: case TCL_STDERR: sl@0: targetFd = 2; sl@0: direction = TCL_WRITABLE; sl@0: break; sl@0: } sl@0: sl@0: if (!file) { sl@0: channel = Tcl_GetStdChannel(type); sl@0: if (channel) { sl@0: file = TclpMakeFile(channel, direction); sl@0: } sl@0: } sl@0: if (file) { sl@0: fd = GetFd(file); sl@0: if (fd != targetFd) { sl@0: if (dup2(fd, targetFd) == -1) { sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: * Must clear the close-on-exec flag for the target FD, since sl@0: * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on sl@0: * the target FD. sl@0: */ sl@0: sl@0: fcntl(targetFd, F_SETFD, 0); sl@0: } else { sl@0: /* sl@0: * Since we aren't dup'ing the file, we need to explicitly clear sl@0: * the close-on-exec flag. sl@0: */ sl@0: sl@0: fcntl(fd, F_SETFD, 0); sl@0: } sl@0: } else { sl@0: close(targetFd); sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpCreateCommandChannel -- sl@0: * sl@0: * This function is called by the generic IO level to perform sl@0: * the platform specific channel initialization for a command sl@0: * channel. sl@0: * sl@0: * Results: sl@0: * Returns a new channel or NULL on failure. sl@0: * sl@0: * Side effects: sl@0: * Allocates a new channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: Tcl_Channel sl@0: TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr) sl@0: TclFile readFile; /* If non-null, gives the file for reading. */ sl@0: TclFile writeFile; /* If non-null, gives the file for writing. */ sl@0: TclFile errorFile; /* If non-null, gives the file where errors sl@0: * can be read. */ sl@0: int numPids; /* The number of pids in the pid array. */ sl@0: Tcl_Pid *pidPtr; /* An array of process identifiers. sl@0: * Allocated by the caller, freed when sl@0: * the channel is closed or the processes sl@0: * are detached (in a background exec). */ sl@0: { sl@0: char channelName[16 + TCL_INTEGER_SPACE]; sl@0: int channelId; sl@0: PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState)); sl@0: int mode; sl@0: sl@0: statePtr->inFile = readFile; sl@0: statePtr->outFile = writeFile; sl@0: statePtr->errorFile = errorFile; sl@0: statePtr->numPids = numPids; sl@0: statePtr->pidPtr = pidPtr; sl@0: statePtr->isNonBlocking = 0; sl@0: sl@0: mode = 0; sl@0: if (readFile) { sl@0: mode |= TCL_READABLE; sl@0: } sl@0: if (writeFile) { sl@0: mode |= TCL_WRITABLE; sl@0: } sl@0: sl@0: /* sl@0: * Use one of the fds associated with the channel as the sl@0: * channel id. sl@0: */ sl@0: sl@0: if (readFile) { sl@0: channelId = GetFd(readFile); sl@0: } else if (writeFile) { sl@0: channelId = GetFd(writeFile); sl@0: } else if (errorFile) { sl@0: channelId = GetFd(errorFile); sl@0: } else { sl@0: channelId = 0; sl@0: } sl@0: sl@0: /* sl@0: * For backward compatibility with previous versions of Tcl, we sl@0: * use "file%d" as the base name for pipes even though it would sl@0: * be more natural to use "pipe%d". sl@0: */ sl@0: sl@0: sprintf(channelName, "file%d", channelId); sl@0: statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, sl@0: (ClientData) statePtr, mode); sl@0: return statePtr->channel; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclGetAndDetachPids -- sl@0: * sl@0: * This procedure is invoked in the generic implementation of a sl@0: * background "exec" (An exec when invoked with a terminating "&") sl@0: * to store a list of the PIDs for processes in a command pipeline sl@0: * in the interp's result and to detach the processes. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Modifies the interp's result. Detaches processes. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclGetAndDetachPids(interp, chan) sl@0: Tcl_Interp *interp; sl@0: Tcl_Channel chan; sl@0: { sl@0: PipeState *pipePtr; sl@0: Tcl_ChannelType *chanTypePtr; sl@0: int i; sl@0: char buf[TCL_INTEGER_SPACE]; sl@0: sl@0: /* sl@0: * Punt if the channel is not a command channel. sl@0: */ sl@0: sl@0: chanTypePtr = Tcl_GetChannelType(chan); sl@0: if (chanTypePtr != &pipeChannelType) { sl@0: return; sl@0: } sl@0: sl@0: pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); sl@0: for (i = 0; i < pipePtr->numPids; i++) { sl@0: TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i])); sl@0: Tcl_AppendElement(interp, buf); sl@0: Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); sl@0: } sl@0: if (pipePtr->numPids > 0) { sl@0: ckfree((char *) pipePtr->pidPtr); sl@0: pipePtr->numPids = 0; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * PipeBlockModeProc -- sl@0: * sl@0: * Helper procedure to set blocking and nonblocking modes on a sl@0: * pipe based channel. Invoked by generic IO level code. sl@0: * sl@0: * Results: sl@0: * 0 if successful, errno when failed. sl@0: * sl@0: * Side effects: sl@0: * Sets the device into blocking or non-blocking mode. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: static int sl@0: PipeBlockModeProc(instanceData, mode) sl@0: ClientData instanceData; /* Pipe state. */ sl@0: int mode; /* The mode to set. Can be one of sl@0: * TCL_MODE_BLOCKING or sl@0: * TCL_MODE_NONBLOCKING. */ sl@0: { sl@0: PipeState *psPtr = (PipeState *) instanceData; sl@0: int curStatus; sl@0: int fd; sl@0: sl@0: #ifndef USE_FIONBIO sl@0: if (psPtr->inFile) { sl@0: fd = GetFd(psPtr->inFile); sl@0: curStatus = fcntl(fd, F_GETFL); sl@0: if (mode == TCL_MODE_BLOCKING) { sl@0: curStatus &= (~(O_NONBLOCK)); sl@0: } else { sl@0: curStatus |= O_NONBLOCK; sl@0: } sl@0: if (fcntl(fd, F_SETFL, curStatus) < 0) { sl@0: return errno; sl@0: } sl@0: } sl@0: if (psPtr->outFile) { sl@0: fd = GetFd(psPtr->outFile); sl@0: curStatus = fcntl(fd, F_GETFL); sl@0: if (mode == TCL_MODE_BLOCKING) { sl@0: curStatus &= (~(O_NONBLOCK)); sl@0: } else { sl@0: curStatus |= O_NONBLOCK; sl@0: } sl@0: if (fcntl(fd, F_SETFL, curStatus) < 0) { sl@0: return errno; sl@0: } sl@0: } sl@0: #endif /* !FIONBIO */ sl@0: sl@0: #ifdef USE_FIONBIO sl@0: if (psPtr->inFile) { sl@0: fd = GetFd(psPtr->inFile); sl@0: if (mode == TCL_MODE_BLOCKING) { sl@0: curStatus = 0; sl@0: } else { sl@0: curStatus = 1; sl@0: } sl@0: if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) { sl@0: return errno; sl@0: } sl@0: } sl@0: if (psPtr->outFile != NULL) { sl@0: fd = GetFd(psPtr->outFile); sl@0: if (mode == TCL_MODE_BLOCKING) { sl@0: curStatus = 0; sl@0: } else { sl@0: curStatus = 1; sl@0: } sl@0: if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) { sl@0: return errno; sl@0: } sl@0: } sl@0: #endif /* USE_FIONBIO */ sl@0: sl@0: psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING); sl@0: sl@0: return 0; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * PipeCloseProc -- sl@0: * sl@0: * This procedure is invoked by the generic IO level to perform sl@0: * channel-type-specific cleanup when a command pipeline channel sl@0: * is closed. sl@0: * sl@0: * Results: sl@0: * 0 on success, errno otherwise. sl@0: * sl@0: * Side effects: sl@0: * Closes the command pipeline channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: static int sl@0: PipeCloseProc(instanceData, interp) sl@0: ClientData instanceData; /* The pipe to close. */ sl@0: Tcl_Interp *interp; /* For error reporting. */ sl@0: { sl@0: PipeState *pipePtr; sl@0: Tcl_Channel errChan; sl@0: int errorCode, result; sl@0: sl@0: errorCode = 0; sl@0: result = 0; sl@0: pipePtr = (PipeState *) instanceData; sl@0: if (pipePtr->inFile) { sl@0: if (TclpCloseFile(pipePtr->inFile) < 0) { sl@0: errorCode = errno; sl@0: } sl@0: } sl@0: if (pipePtr->outFile) { sl@0: if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) { sl@0: errorCode = errno; sl@0: } sl@0: } sl@0: sl@0: if (pipePtr->isNonBlocking || TclInExit()) { sl@0: sl@0: /* sl@0: * If the channel is non-blocking or Tcl is being cleaned up, just sl@0: * detach the children PIDs, reap them (important if we are in a sl@0: * dynamic load module), and discard the errorFile. sl@0: */ sl@0: sl@0: Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr); sl@0: Tcl_ReapDetachedProcs(); sl@0: sl@0: if (pipePtr->errorFile) { sl@0: TclpCloseFile(pipePtr->errorFile); sl@0: } sl@0: } else { sl@0: sl@0: /* sl@0: * Wrap the error file into a channel and give it to the cleanup sl@0: * routine. sl@0: */ sl@0: sl@0: if (pipePtr->errorFile) { sl@0: errChan = Tcl_MakeFileChannel( sl@0: (ClientData) GetFd(pipePtr->errorFile), TCL_READABLE); sl@0: } else { sl@0: errChan = NULL; sl@0: } sl@0: result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr, sl@0: errChan); sl@0: } sl@0: sl@0: if (pipePtr->numPids != 0) { sl@0: ckfree((char *) pipePtr->pidPtr); sl@0: } sl@0: ckfree((char *) pipePtr); sl@0: if (errorCode == 0) { sl@0: return result; sl@0: } sl@0: return errorCode; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * PipeInputProc -- sl@0: * sl@0: * This procedure is invoked from the generic IO level to read sl@0: * input from a command pipeline based channel. sl@0: * sl@0: * Results: sl@0: * The number of bytes read is returned or -1 on error. An output sl@0: * argument contains a POSIX error code if an error occurs, or zero. sl@0: * sl@0: * Side effects: sl@0: * Reads input from the input device of the channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: PipeInputProc(instanceData, buf, toRead, errorCodePtr) sl@0: ClientData instanceData; /* Pipe state. */ sl@0: char *buf; /* Where to store data read. */ sl@0: int toRead; /* How much space is available sl@0: * in the buffer? */ sl@0: int *errorCodePtr; /* Where to store error code. */ sl@0: { sl@0: PipeState *psPtr = (PipeState *) instanceData; sl@0: int bytesRead; /* How many bytes were actually sl@0: * read from the input device? */ sl@0: sl@0: *errorCodePtr = 0; sl@0: sl@0: /* sl@0: * Assume there is always enough input available. This will block sl@0: * appropriately, and read will unblock as soon as a short read is sl@0: * possible, if the channel is in blocking mode. If the channel is sl@0: * nonblocking, the read will never block. sl@0: * Some OSes can throw an interrupt error, for which we should sl@0: * immediately retry. [Bug #415131] sl@0: */ sl@0: sl@0: do { sl@0: bytesRead = read (GetFd(psPtr->inFile), buf, (size_t) toRead); sl@0: } while ((bytesRead < 0) && (errno == EINTR)); sl@0: sl@0: if (bytesRead < 0) { sl@0: *errorCodePtr = errno; sl@0: return -1; sl@0: } else { sl@0: return bytesRead; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * PipeOutputProc-- sl@0: * sl@0: * This procedure is invoked from the generic IO level to write sl@0: * output to a command pipeline based channel. sl@0: * sl@0: * Results: sl@0: * The number of bytes written is returned or -1 on error. An sl@0: * output argument contains a POSIX error code if an error occurred, sl@0: * or zero. sl@0: * sl@0: * Side effects: sl@0: * Writes output on the output device of the channel. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: PipeOutputProc(instanceData, buf, toWrite, errorCodePtr) sl@0: ClientData instanceData; /* Pipe state. */ sl@0: CONST char *buf; /* The data buffer. */ sl@0: int toWrite; /* How many bytes to write? */ sl@0: int *errorCodePtr; /* Where to store error code. */ sl@0: { sl@0: PipeState *psPtr = (PipeState *) instanceData; sl@0: int written; sl@0: sl@0: *errorCodePtr = 0; sl@0: sl@0: /* sl@0: * Some OSes can throw an interrupt error, for which we should sl@0: * immediately retry. [Bug #415131] sl@0: */ sl@0: sl@0: do { sl@0: written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite); sl@0: } while ((written < 0) && (errno == EINTR)); sl@0: sl@0: if (written < 0) { sl@0: *errorCodePtr = errno; sl@0: return -1; sl@0: } else { sl@0: return written; sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * PipeWatchProc -- sl@0: * sl@0: * Initialize the notifier to watch the fds from this channel. sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * Side effects: sl@0: * Sets up the notifier so that a future event on the channel will sl@0: * be seen by Tcl. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static void sl@0: PipeWatchProc(instanceData, mask) sl@0: ClientData instanceData; /* The pipe state. */ sl@0: int mask; /* Events of interest; an OR-ed sl@0: * combination of TCL_READABLE, sl@0: * TCL_WRITABEL and TCL_EXCEPTION. */ sl@0: { sl@0: PipeState *psPtr = (PipeState *) instanceData; sl@0: int newmask; sl@0: sl@0: if (psPtr->inFile) { sl@0: newmask = mask & (TCL_READABLE | TCL_EXCEPTION); sl@0: if (newmask) { sl@0: Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask, sl@0: (Tcl_FileProc *) Tcl_NotifyChannel, sl@0: (ClientData) psPtr->channel); sl@0: } else { sl@0: Tcl_DeleteFileHandler(GetFd(psPtr->inFile)); sl@0: } sl@0: } sl@0: if (psPtr->outFile) { sl@0: newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION); sl@0: if (newmask) { sl@0: Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask, sl@0: (Tcl_FileProc *) Tcl_NotifyChannel, sl@0: (ClientData) psPtr->channel); sl@0: } else { sl@0: Tcl_DeleteFileHandler(GetFd(psPtr->outFile)); sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * PipeGetHandleProc -- sl@0: * sl@0: * Called from Tcl_GetChannelHandle to retrieve OS handles from sl@0: * inside a command pipeline based channel. sl@0: * sl@0: * Results: sl@0: * Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if sl@0: * there is no handle for the specified direction. sl@0: * sl@0: * Side effects: sl@0: * None. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: static int sl@0: PipeGetHandleProc(instanceData, direction, handlePtr) sl@0: ClientData instanceData; /* The pipe state. */ sl@0: int direction; /* TCL_READABLE or TCL_WRITABLE */ sl@0: ClientData *handlePtr; /* Where to store the handle. */ sl@0: { sl@0: PipeState *psPtr = (PipeState *) instanceData; sl@0: sl@0: if (direction == TCL_READABLE && psPtr->inFile) { sl@0: *handlePtr = (ClientData) GetFd(psPtr->inFile); sl@0: return TCL_OK; sl@0: } sl@0: if (direction == TCL_WRITABLE && psPtr->outFile) { sl@0: *handlePtr = (ClientData) GetFd(psPtr->outFile); sl@0: return TCL_OK; sl@0: } sl@0: return TCL_ERROR; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_WaitPid -- sl@0: * sl@0: * Implements the waitpid system call on Unix systems. sl@0: * sl@0: * Results: sl@0: * Result of calling waitpid. sl@0: * sl@0: * Side effects: sl@0: * Waits for a process to terminate. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: EXPORT_C Tcl_Pid sl@0: Tcl_WaitPid(pid, statPtr, options) sl@0: Tcl_Pid pid; sl@0: int *statPtr; sl@0: int options; sl@0: { sl@0: int result; sl@0: pid_t real_pid; sl@0: sl@0: real_pid = (pid_t) pid; sl@0: while (1) { sl@0: result = (int) waitpid(real_pid, statPtr, options); sl@0: if ((result != -1) || (errno != EINTR)) { sl@0: return (Tcl_Pid) result; sl@0: } sl@0: } sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * Tcl_PidObjCmd -- sl@0: * sl@0: * This procedure is invoked to process the "pid" Tcl command. sl@0: * See the user documentation for details on what it does. sl@0: * sl@0: * Results: sl@0: * A standard Tcl result. sl@0: * sl@0: * Side effects: sl@0: * See the user documentation. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: /* ARGSUSED */ sl@0: int sl@0: Tcl_PidObjCmd(dummy, interp, objc, objv) sl@0: ClientData dummy; /* Not used. */ sl@0: Tcl_Interp *interp; /* Current interpreter. */ sl@0: int objc; /* Number of arguments. */ sl@0: Tcl_Obj *CONST *objv; /* Argument strings. */ sl@0: { sl@0: Tcl_Channel chan; sl@0: Tcl_ChannelType *chanTypePtr; sl@0: PipeState *pipePtr; sl@0: int i; sl@0: Tcl_Obj *resultPtr, *longObjPtr; sl@0: sl@0: if (objc > 2) { sl@0: Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); sl@0: return TCL_ERROR; sl@0: } sl@0: if (objc == 1) { sl@0: Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) getpid()); sl@0: } else { sl@0: chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); sl@0: if (chan == (Tcl_Channel) NULL) { sl@0: return TCL_ERROR; sl@0: } sl@0: chanTypePtr = Tcl_GetChannelType(chan); sl@0: if (chanTypePtr != &pipeChannelType) { sl@0: return TCL_OK; sl@0: } sl@0: pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); sl@0: resultPtr = Tcl_GetObjResult(interp); sl@0: for (i = 0; i < pipePtr->numPids; i++) { sl@0: longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i])); sl@0: Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr); sl@0: } sl@0: } sl@0: return TCL_OK; sl@0: } sl@0: sl@0: /* sl@0: *---------------------------------------------------------------------- sl@0: * sl@0: * TclpFinalizePipes -- sl@0: * sl@0: * Cleans up the pipe subsystem from Tcl_FinalizeThread sl@0: * sl@0: * Results: sl@0: * None. sl@0: * sl@0: * This procedure carries out no operation on Unix. sl@0: * sl@0: *---------------------------------------------------------------------- sl@0: */ sl@0: sl@0: void sl@0: TclpFinalizePipes() sl@0: { sl@0: } sl@0: