diff options
Diffstat (limited to 'unix/tclUnixPipe.c')
-rw-r--r-- | unix/tclUnixPipe.c | 568 |
1 files changed, 310 insertions, 258 deletions
diff --git a/unix/tclUnixPipe.c b/unix/tclUnixPipe.c index 3dcf829..9c21b28 100644 --- a/unix/tclUnixPipe.c +++ b/unix/tclUnixPipe.c @@ -9,8 +9,6 @@ * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclUnixPipe.c,v 1.31 2005/07/28 10:55:37 dkf Exp $ */ #include "tclInt.h" @@ -25,8 +23,8 @@ * the same as NULL. */ -#define MakeFile(fd) ((TclFile)(((int)fd)+1)) -#define GetFd(file) (((int)file)-1) +#define MakeFile(fd) ((TclFile) INT2PTR(((int) (fd)) + 1)) +#define GetFd(file) (PTR2INT(file) - 1) /* * This structure describes per-instance state of a pipe based channel. @@ -47,33 +45,31 @@ typedef struct PipeState { } PipeState; /* - * Declarations for local procedures defined in this file: + * Declarations for local functions 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, CONST 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)); +static int PipeBlockModeProc(ClientData instanceData, int mode); +static int PipeClose2Proc(ClientData instanceData, + Tcl_Interp *interp, int flags); +static int PipeGetHandleProc(ClientData instanceData, + int direction, ClientData *handlePtr); +static int PipeInputProc(ClientData instanceData, char *buf, + int toRead, int *errorCode); +static int PipeOutputProc(ClientData instanceData, + const char *buf, int toWrite, int *errorCode); +static void PipeWatchProc(ClientData instanceData, int mask); +static void RestoreSignals(void); +static int SetupStdFile(TclFile file, int type); /* * This structure describes the channel type structure for command pipe based * I/O: */ -static Tcl_ChannelType pipeChannelType = { +static const Tcl_ChannelType pipeChannelType = { "pipe", /* Type name. */ - TCL_CHANNEL_VERSION_4, /* v4 channel */ - PipeCloseProc, /* Close proc. */ + TCL_CHANNEL_VERSION_5, /* v5 channel */ + TCL_CLOSE2PROC, /* Close proc. */ PipeInputProc, /* Input proc. */ PipeOutputProc, /* Output proc. */ NULL, /* Seek proc. */ @@ -81,12 +77,13 @@ static Tcl_ChannelType pipeChannelType = { NULL, /* Get option proc. */ PipeWatchProc, /* Initialize notifier. */ PipeGetHandleProc, /* Get OS handles out of channel. */ - NULL, /* close2proc. */ + PipeClose2Proc, /* close2proc. */ PipeBlockModeProc, /* Set blocking or non-blocking mode.*/ NULL, /* flush proc. */ NULL, /* handler proc. */ NULL, /* wide seek proc */ NULL, /* thread action proc */ + NULL /* truncation */ }; /* @@ -106,18 +103,17 @@ static Tcl_ChannelType pipeChannelType = { */ TclFile -TclpMakeFile(channel, direction) - Tcl_Channel channel; /* Channel to get file from. */ - int direction; /* Either TCL_READABLE or TCL_WRITABLE. */ +TclpMakeFile( + 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; + if (Tcl_GetChannelHandle(channel, direction, &data) != TCL_OK) { + return NULL; } + + return MakeFile(PTR2INT(data)); } /* @@ -137,12 +133,12 @@ TclpMakeFile(channel, direction) */ TclFile -TclpOpenFile(fname, mode) - CONST char *fname; /* The name of the file to open. */ - int mode; /* In what mode to open the file? */ +TclpOpenFile( + const char *fname, /* The name of the file to open. */ + int mode) /* In what mode to open the file? */ { int fd; - CONST char *native; + const char *native; Tcl_DString ds; native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds); @@ -189,33 +185,21 @@ TclpOpenFile(fname, mode) */ TclFile -TclpCreateTempFile(contents) - CONST char *contents; /* String to write into temp file, or NULL. */ +TclpCreateTempFile( + const char *contents) /* String to write into temp file, or NULL. */ { - char fileName[L_tmpnam + 9]; - CONST char *native; - Tcl_DString dstring; - int fd; - - /* - * We should also check against making more then TMP_MAX of these. - */ + int fd = TclUnixOpenTemporaryFile(NULL, NULL, NULL, NULL); - strcpy(fileName, P_tmpdir); /* INTL: Native. */ - if (fileName[strlen(fileName) - 1] != '/') { - strcat(fileName, "/"); /* INTL: Native. */ - } - strcat(fileName, "tclXXXXXX"); - fd = mkstemp(fileName); /* INTL: Native. */ if (fd == -1) { return NULL; } fcntl(fd, F_SETFD, FD_CLOEXEC); - unlink(fileName); /* INTL: Native. */ - if (contents != NULL) { + Tcl_DString dstring; + char *native; + native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring); - if (write(fd, native, strlen(native)) == -1) { + if (write(fd, native, Tcl_DStringLength(&dstring)) == -1) { close(fd); Tcl_DStringFree(&dstring); return NULL; @@ -243,31 +227,55 @@ TclpCreateTempFile(contents) */ Tcl_Obj * -TclpTempFileName() +TclpTempFileName(void) { - char fileName[L_tmpnam + 9]; - Tcl_Obj *result = NULL; + Tcl_Obj *nameObj = Tcl_NewObj(); int fd; - /* - * We should also check against making more then TMP_MAX of these. - */ - - strcpy(fileName, P_tmpdir); /* INTL: Native. */ - if (fileName[strlen(fileName) - 1] != '/') { - strcat(fileName, "/"); /* INTL: Native. */ - } - strcat(fileName, "tclXXXXXX"); - fd = mkstemp(fileName); /* INTL: Native. */ + Tcl_IncrRefCount(nameObj); + fd = TclUnixOpenTemporaryFile(NULL, NULL, NULL, nameObj); if (fd == -1) { + Tcl_DecrRefCount(nameObj); return NULL; } - fcntl(fd, F_SETFD, FD_CLOEXEC); - unlink(fileName); /* INTL: Native. */ - result = TclpNativeToNormalized((ClientData) fileName); + fcntl(fd, F_SETFD, FD_CLOEXEC); + TclpObjDeleteFile(nameObj); close(fd); - return result; + return nameObj; +} + +/* + *---------------------------------------------------------------------------- + * + * TclpTempFileNameForLibrary -- + * + * Constructs a file name in the native file system where a dynamically + * loaded library may be placed. + * + * Results: + * Returns the constructed file name. If an error occurs, returns NULL + * and leaves an error message in the interpreter result. + * + * On Unix, it works to load a shared object from a file of any name, so this + * function is merely a thin wrapper around TclpTempFileName(). + * + *---------------------------------------------------------------------------- + */ + +Tcl_Obj * +TclpTempFileNameForLibrary( + Tcl_Interp *interp, /* Tcl interpreter. */ + Tcl_Obj *path) /* Path name of the library in the VFS. */ +{ + Tcl_Obj *retval = TclpTempFileName(); + + if (retval == NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't create temporary file: %s", + Tcl_PosixError(interp))); + } + return retval; } /* @@ -287,10 +295,10 @@ TclpTempFileName() */ int -TclpCreatePipe(readPipe, writePipe) - TclFile *readPipe; /* Location to store file handle for read side +TclpCreatePipe( + TclFile *readPipe, /* Location to store file handle for read side * of pipe. */ - TclFile *writePipe; /* Location to store file handle for write + TclFile *writePipe) /* Location to store file handle for write * side of pipe. */ { int pipeIds[2]; @@ -324,8 +332,8 @@ TclpCreatePipe(readPipe, writePipe) */ int -TclpCloseFile(file) - TclFile file; /* The file to close. */ +TclpCloseFile( + TclFile file) /* The file to close. */ { int fd = GetFd(file); @@ -366,38 +374,37 @@ TclpCloseFile(file) /* ARGSUSED */ int -TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, - pidPtr) - Tcl_Interp *interp; /* Interpreter in which to leave errors that +TclpCreateProcess( + 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. */ - CONST char **argv; /* Array of argument strings in UTF-8. + int argc, /* Number of arguments in following array. */ + const char **argv, /* Array of argument strings in UTF-8. * argv[0] contains the name of the executable * translated using Tcl_TranslateFileName * call). Additional arguments have not been * converted. */ - TclFile inputFile; /* If non-NULL, gives the file to use as input + 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 + 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 + 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 + Tcl_Pid *pidPtr) /* If this function is successful, pidPtr is * filled with the process id of the child * process. */ { TclFile errPipeIn, errPipeOut; - int joinThisError, count, status, fd; + int count, status, fd; char errSpace[200 + TCL_INTEGER_SPACE]; Tcl_DString *dsArray; char **newArgv; @@ -413,8 +420,8 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, */ if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) { - Tcl_AppendResult(interp, "couldn't create pipe: ", - Tcl_PosixError(interp), (char *) NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't create pipe: %s", Tcl_PosixError(interp))); goto error; } @@ -423,16 +430,38 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, * deallocated later */ - dsArray = (Tcl_DString *) ckalloc(argc * sizeof(Tcl_DString)); - newArgv = (char **) ckalloc((argc+1) * sizeof(char *)); + dsArray = TclStackAlloc(interp, argc * sizeof(Tcl_DString)); + newArgv = TclStackAlloc(interp, (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); +#ifdef USE_VFORK + /* + * After vfork(), do not call code in the child that changes global state, + * because it is using the parent's memory space at that point and writes + * might corrupt the parent: so ensure standard channels are initialized + * in the parent, otherwise SetupStdFile() might initialize them in the + * child. + */ + + if (!inputFile) { + Tcl_GetStdChannel(TCL_STDIN); + } + if (!outputFile) { + Tcl_GetStdChannel(TCL_STDOUT); + } + if (!errorFile) { + Tcl_GetStdChannel(TCL_STDERR); + } +#endif + pid = fork(); if (pid == 0) { + size_t len; + int joinThisError = errorFile && (errorFile == outputFile); + fd = GetFd(errPipeOut); /* @@ -445,8 +474,11 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, || (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)); + "%dforked process couldn't set up input/output", errno); + len = strlen(errSpace); + if (len != (size_t) write(fd, errSpace, len)) { + Tcl_Panic("TclpCreateProcess: unable to write to errPipeOut"); + } _exit(1); } @@ -456,8 +488,11 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, RestoreSignals(); execvp(newArgv[0], newArgv); /* INTL: Native. */ - sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]); - write(fd, errSpace, (size_t) strlen(errSpace)); + sprintf(errSpace, "%dcouldn't execute \"%.150s\"", errno, argv[0]); + len = strlen(errSpace); + if (len != (size_t) write(fd, errSpace, len)) { + Tcl_Panic("TclpCreateProcess: unable to write to errPipeOut"); + } _exit(1); } @@ -468,12 +503,12 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, for (i = 0; i < argc; i++) { Tcl_DStringFree(&dsArray[i]); } - ckfree((char *) dsArray); - ckfree((char *) newArgv); + TclStackFree(interp, newArgv); + TclStackFree(interp, dsArray); if (pid == -1) { - Tcl_AppendResult(interp, "couldn't fork child process: ", - Tcl_PosixError(interp), (char *) NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "couldn't fork child process: %s", Tcl_PosixError(interp))); goto error; } @@ -490,15 +525,16 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, 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); + Tcl_SetObjResult(interp, Tcl_ObjPrintf("%s: %s", + end, Tcl_PosixError(interp))); goto error; } TclpCloseFile(errPipeIn); - *pidPtr = (Tcl_Pid) pid; + *pidPtr = (Tcl_Pid) INT2PTR(pid); return TCL_OK; error: @@ -510,7 +546,7 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, * here, since this is the error case. [Bug: 6148] */ - Tcl_WaitPid((Tcl_Pid) pid, &status, 0); + Tcl_WaitPid((Tcl_Pid) INT2PTR(pid), &status, 0); } if (errPipeIn) { @@ -527,7 +563,7 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, * * RestoreSignals -- * - * This procedure is invoked in a forked child process just before + * This function is invoked in a forked child process just before * exec-ing a new program to restore all signals to their default * settings. * @@ -541,7 +577,7 @@ TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile, */ static void -RestoreSignals() +RestoreSignals(void) { #ifdef SIGABRT signal(SIGABRT, SIG_DFL); @@ -616,9 +652,9 @@ RestoreSignals() */ static int -SetupStdFile(file, type) - TclFile file; /* File to dup, or NULL. */ - int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */ +SetupStdFile( + TclFile file, /* File to dup, or NULL. */ + int type) /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */ { Tcl_Channel channel; int fd; @@ -693,20 +729,20 @@ SetupStdFile(file, type) */ Tcl_Channel -TclpCreateCommandChannel(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 +TclpCreateCommandChannel( + 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 + 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)); + PipeState *statePtr = ckalloc(sizeof(PipeState)); int mode; statePtr->inFile = readFile; @@ -746,16 +782,59 @@ TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr) sprintf(channelName, "file%d", channelId); statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName, - (ClientData) statePtr, mode); + statePtr, mode); return statePtr->channel; } /* *---------------------------------------------------------------------- * + * Tcl_CreatePipe -- + * + * System dependent interface to create a pipe for the [chan pipe] + * command. Stolen from TclX. + * + * Results: + * TCL_OK or TCL_ERROR. + * + * Side effects: + * Registers two channels. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_CreatePipe( + Tcl_Interp *interp, /* Errors returned in result. */ + Tcl_Channel *rchan, /* Returned read side. */ + Tcl_Channel *wchan, /* Returned write side. */ + int flags) /* Reserved for future use. */ +{ + int fileNums[2]; + + if (pipe(fileNums) < 0) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf("pipe creation failed: %s", + Tcl_PosixError(interp))); + return TCL_ERROR; + } + + fcntl(fileNums[0], F_SETFD, FD_CLOEXEC); + fcntl(fileNums[1], F_SETFD, FD_CLOEXEC); + + *rchan = Tcl_MakeFileChannel(INT2PTR(fileNums[0]), TCL_READABLE); + Tcl_RegisterChannel(interp, *rchan); + *wchan = Tcl_MakeFileChannel(INT2PTR(fileNums[1]), TCL_WRITABLE); + Tcl_RegisterChannel(interp, *wchan); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * * TclGetAndDetachPids -- * - * This procedure is invoked in the generic implementation of a + * This function 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. @@ -770,14 +849,14 @@ TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr) */ void -TclGetAndDetachPids(interp, chan) - Tcl_Interp *interp; /* Interpreter to append the PIDs to. */ - Tcl_Channel chan; /* Handle for the pipeline. */ +TclGetAndDetachPids( + Tcl_Interp *interp, /* Interpreter to append the PIDs to. */ + Tcl_Channel chan) /* Handle for the pipeline. */ { PipeState *pipePtr; - Tcl_ChannelType *chanTypePtr; + const Tcl_ChannelType *chanTypePtr; + Tcl_Obj *pidsObj; int i; - char buf[TCL_INTEGER_SPACE]; /* * Punt if the channel is not a command channel. @@ -788,14 +867,16 @@ TclGetAndDetachPids(interp, chan) return; } - pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); + pipePtr = Tcl_GetChannelInstanceData(chan); + TclNewObj(pidsObj); for (i = 0; i < pipePtr->numPids; i++) { - TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i])); - Tcl_AppendElement(interp, buf); - Tcl_DetachPids(1, &(pipePtr->pidPtr[i])); + Tcl_ListObjAppendElement(NULL, pidsObj, Tcl_NewIntObj( + PTR2INT(pipePtr->pidPtr[i]))); + Tcl_DetachPids(1, &pipePtr->pidPtr[i]); } + Tcl_SetObjResult(interp, pidsObj); if (pipePtr->numPids > 0) { - ckfree((char *) pipePtr->pidPtr); + ckfree(pipePtr->pidPtr); pipePtr->numPids = 0; } } @@ -805,7 +886,7 @@ TclGetAndDetachPids(interp, chan) * * PipeBlockModeProc -- * - * Helper procedure to set blocking and nonblocking modes on a pipe based + * Helper function to set blocking and nonblocking modes on a pipe based * channel. Invoked by generic IO level code. * * Results: @@ -819,67 +900,22 @@ TclGetAndDetachPids(interp, chan) /* ARGSUSED */ static int -PipeBlockModeProc(instanceData, mode) - ClientData instanceData; /* Pipe state. */ - int mode; /* The mode to set. Can be one of +PipeBlockModeProc( + 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; + PipeState *psPtr = instanceData; -#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; - } + if (psPtr->inFile + && TclUnixSetBlockingMode(GetFd(psPtr->inFile), mode) < 0) { + return errno; } - 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; - } + if (psPtr->outFile + && TclUnixSetBlockingMode(GetFd(psPtr->outFile), mode) < 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 */ psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING); @@ -889,11 +925,10 @@ PipeBlockModeProc(instanceData, mode) /* *---------------------------------------------------------------------- * - * PipeCloseProc -- + * PipeClose2Proc * - * This procedure is invoked by the generic IO level to perform - * channel-type-specific cleanup when a command pipeline channel is - * closed. + * This function is invoked by the generic IO level to perform + * pipeline-type-specific half or full-close. * * Results: * 0 on success, errno otherwise. @@ -904,29 +939,42 @@ PipeBlockModeProc(instanceData, mode) *---------------------------------------------------------------------- */ - /* ARGSUSED */ static int -PipeCloseProc(instanceData, interp) - ClientData instanceData; /* The pipe to close. */ - Tcl_Interp *interp; /* For error reporting. */ +PipeClose2Proc( + ClientData instanceData, /* The pipe to close. */ + Tcl_Interp *interp, /* For error reporting. */ + int flags) /* Flags that indicate which side to close. */ { - PipeState *pipePtr; + PipeState *pipePtr = instanceData; Tcl_Channel errChan; int errorCode, result; errorCode = 0; result = 0; - pipePtr = (PipeState *) instanceData; - if (pipePtr->inFile) { + + if (((!flags) || (flags & TCL_CLOSE_READ)) && (pipePtr->inFile != NULL)) { if (TclpCloseFile(pipePtr->inFile) < 0) { errorCode = errno; + } else { + pipePtr->inFile = NULL; } } - if (pipePtr->outFile) { - if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) { + if (((!flags) || (flags & TCL_CLOSE_WRITE)) && (pipePtr->outFile != NULL) + && (errorCode == 0)) { + if (TclpCloseFile(pipePtr->outFile) < 0) { errorCode = errno; + } else { + pipePtr->outFile = NULL; } } + + /* + * If half-closing, stop here. + */ + + if (flags) { + return errorCode; + } if (pipePtr->isNonBlocking || TclInExit()) { /* @@ -949,7 +997,8 @@ PipeCloseProc(instanceData, interp) if (pipePtr->errorFile) { errChan = Tcl_MakeFileChannel( - (ClientData) GetFd(pipePtr->errorFile), TCL_READABLE); + INT2PTR(GetFd(pipePtr->errorFile)), + TCL_READABLE); } else { errChan = NULL; } @@ -958,9 +1007,9 @@ PipeCloseProc(instanceData, interp) } if (pipePtr->numPids != 0) { - ckfree((char *) pipePtr->pidPtr); + ckfree(pipePtr->pidPtr); } - ckfree((char *) pipePtr); + ckfree(pipePtr); if (errorCode == 0) { return result; } @@ -972,7 +1021,7 @@ PipeCloseProc(instanceData, interp) * * PipeInputProc -- * - * This procedure is invoked from the generic IO level to read input from + * This function is invoked from the generic IO level to read input from * a command pipeline based channel. * * Results: @@ -986,14 +1035,14 @@ PipeCloseProc(instanceData, interp) */ static int -PipeInputProc(instanceData, buf, toRead, errorCodePtr) - ClientData instanceData; /* Pipe state. */ - char *buf; /* Where to store data read. */ - int toRead; /* How much space is available in the +PipeInputProc( + 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. */ + int *errorCodePtr) /* Where to store error code. */ { - PipeState *psPtr = (PipeState *) instanceData; + PipeState *psPtr = instanceData; int bytesRead; /* How many bytes were actually read from the * input device? */ @@ -1014,9 +1063,8 @@ PipeInputProc(instanceData, buf, toRead, errorCodePtr) if (bytesRead < 0) { *errorCodePtr = errno; return -1; - } else { - return bytesRead; } + return bytesRead; } /* @@ -1024,7 +1072,7 @@ PipeInputProc(instanceData, buf, toRead, errorCodePtr) * * PipeOutputProc-- * - * This procedure is invoked from the generic IO level to write output to + * This function is invoked from the generic IO level to write output to * a command pipeline based channel. * * Results: @@ -1038,13 +1086,13 @@ PipeInputProc(instanceData, buf, toRead, errorCodePtr) */ static int -PipeOutputProc(instanceData, buf, toWrite, errorCodePtr) - ClientData instanceData; /* Pipe state. */ - CONST char *buf; /* The data buffer. */ - int toWrite; /* How many bytes to write? */ - int *errorCodePtr; /* Where to store error code. */ +PipeOutputProc( + ClientData instanceData, /* Pipe state. */ + const char *buf, /* The data buffer. */ + int toWrite, /* How many bytes to write? */ + int *errorCodePtr) /* Where to store error code. */ { - PipeState *psPtr = (PipeState *) instanceData; + PipeState *psPtr = instanceData; int written; *errorCodePtr = 0; @@ -1061,9 +1109,8 @@ PipeOutputProc(instanceData, buf, toWrite, errorCodePtr) if (written < 0) { *errorCodePtr = errno; return -1; - } else { - return written; } + return written; } /* @@ -1084,21 +1131,20 @@ PipeOutputProc(instanceData, buf, toWrite, errorCodePtr) */ static void -PipeWatchProc(instanceData, mask) - ClientData instanceData; /* The pipe state. */ - int mask; /* Events of interest; an OR-ed combination of +PipeWatchProc( + ClientData instanceData, /* The pipe state. */ + int mask) /* Events of interest; an OR-ed combination of * TCL_READABLE, TCL_WRITABLE and * TCL_EXCEPTION. */ { - PipeState *psPtr = (PipeState *) instanceData; + PipeState *psPtr = 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); + (Tcl_FileProc *) Tcl_NotifyChannel, psPtr->channel); } else { Tcl_DeleteFileHandler(GetFd(psPtr->inFile)); } @@ -1107,8 +1153,7 @@ PipeWatchProc(instanceData, mask) newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION); if (newmask) { Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask, - (Tcl_FileProc *) Tcl_NotifyChannel, - (ClientData) psPtr->channel); + (Tcl_FileProc *) Tcl_NotifyChannel, psPtr->channel); } else { Tcl_DeleteFileHandler(GetFd(psPtr->outFile)); } @@ -1134,19 +1179,19 @@ PipeWatchProc(instanceData, mask) */ static int -PipeGetHandleProc(instanceData, direction, handlePtr) - ClientData instanceData; /* The pipe state. */ - int direction; /* TCL_READABLE or TCL_WRITABLE */ - ClientData *handlePtr; /* Where to store the handle. */ +PipeGetHandleProc( + ClientData instanceData, /* The pipe state. */ + int direction, /* TCL_READABLE or TCL_WRITABLE */ + ClientData *handlePtr) /* Where to store the handle. */ { - PipeState *psPtr = (PipeState *) instanceData; + PipeState *psPtr = instanceData; if (direction == TCL_READABLE && psPtr->inFile) { - *handlePtr = (ClientData) GetFd(psPtr->inFile); + *handlePtr = INT2PTR(GetFd(psPtr->inFile)); return TCL_OK; } if (direction == TCL_WRITABLE && psPtr->outFile) { - *handlePtr = (ClientData) GetFd(psPtr->outFile); + *handlePtr = INT2PTR(GetFd(psPtr->outFile)); return TCL_OK; } return TCL_ERROR; @@ -1169,19 +1214,18 @@ PipeGetHandleProc(instanceData, direction, handlePtr) */ Tcl_Pid -Tcl_WaitPid(pid, statPtr, options) - Tcl_Pid pid; - int *statPtr; - int options; +Tcl_WaitPid( + Tcl_Pid pid, + int *statPtr, + int options) { int result; - pid_t real_pid; + pid_t real_pid = (pid_t) PTR2INT(pid); - real_pid = (pid_t) pid; while (1) { result = (int) waitpid(real_pid, statPtr, options); if ((result != -1) || (errno != EINTR)) { - return (Tcl_Pid) result; + return (Tcl_Pid) INT2PTR(result); } } } @@ -1191,7 +1235,7 @@ Tcl_WaitPid(pid, statPtr, options) * * Tcl_PidObjCmd -- * - * This procedure is invoked to process the "pid" Tcl command. See the + * This function is invoked to process the "pid" Tcl command. See the * user documentation for details on what it does. * * Results: @@ -1205,38 +1249,46 @@ Tcl_WaitPid(pid, statPtr, options) /* ARGSUSED */ int -Tcl_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_PidObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const *objv) /* Argument strings. */ { + Tcl_Channel chan; + PipeState *pipePtr; + int i; + Tcl_Obj *resultPtr; + if (objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, "?channelId?"); return TCL_ERROR; } + if (objc == 1) { Tcl_SetObjResult(interp, Tcl_NewLongObj((long) getpid())); } else { - Tcl_Channel chan; - Tcl_ChannelType *chanTypePtr; - PipeState *pipePtr; - int i; - Tcl_Obj *resultPtr, *longObjPtr; + /* + * Get the channel and make sure that it refers to a pipe. + */ chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL); - if (chan == (Tcl_Channel) NULL) { + if (chan == NULL) { return TCL_ERROR; } - chanTypePtr = Tcl_GetChannelType(chan); - if (chanTypePtr != &pipeChannelType) { + if (Tcl_GetChannelType(chan) != &pipeChannelType) { return TCL_OK; } - pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan); + + /* + * Extract the process IDs from the pipe structure. + */ + + pipePtr = Tcl_GetChannelInstanceData(chan); resultPtr = Tcl_NewObj(); for (i = 0; i < pipePtr->numPids; i++) { - longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i])); - Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr); + Tcl_ListObjAppendElement(NULL, resultPtr, + Tcl_NewIntObj(PTR2INT(TclpGetPid(pipePtr->pidPtr[i])))); } Tcl_SetObjResult(interp, resultPtr); } @@ -1254,13 +1306,13 @@ Tcl_PidObjCmd(dummy, interp, objc, objv) * None. * * Notes: - * This procedure carries out no operation on Unix. + * This function carries out no operation on Unix. * *---------------------------------------------------------------------- */ void -TclpFinalizePipes() +TclpFinalizePipes(void) { } |