tclUnixPipe.c

Go to the documentation of this file.
00001 /*
00002  * tclUnixPipe.c --
00003  *
00004  *      This file implements the UNIX-specific exec pipeline functions, the
00005  *      "pipe" channel driver, and the "pid" Tcl command.
00006  *
00007  * Copyright (c) 1991-1994 The Regents of the University of California.
00008  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
00009  *
00010  * See the file "license.terms" for information on usage and redistribution of
00011  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00012  *
00013  * RCS: @(#) $Id: tclUnixPipe.c,v 1.40 2007/12/13 15:28:42 dgp Exp $
00014  */
00015 
00016 #include "tclInt.h"
00017 
00018 #ifdef USE_VFORK
00019 #define fork vfork
00020 #endif
00021 
00022 /*
00023  * The following macros convert between TclFile's and fd's. The conversion
00024  * simple involves shifting fd's up by one to ensure that no valid fd is ever
00025  * the same as NULL.
00026  */
00027 
00028 #define MakeFile(fd)    ((TclFile)INT2PTR(((int)(fd))+1))
00029 #define GetFd(file)     (PTR2INT(file)-1)
00030 
00031 /*
00032  * This structure describes per-instance state of a pipe based channel.
00033  */
00034 
00035 typedef struct PipeState {
00036     Tcl_Channel channel;        /* Channel associated with this file. */
00037     TclFile inFile;             /* Output from pipe. */
00038     TclFile outFile;            /* Input to pipe. */
00039     TclFile errorFile;          /* Error output from pipe. */
00040     int numPids;                /* How many processes are attached to this
00041                                  * pipe? */
00042     Tcl_Pid *pidPtr;            /* The process IDs themselves. Allocated by
00043                                  * the creator of the pipe. */
00044     int isNonBlocking;          /* Nonzero when the pipe is in nonblocking
00045                                  * mode. Used to decide whether to wait for
00046                                  * the children at close time. */
00047 } PipeState;
00048 
00049 /*
00050  * Declarations for local functions defined in this file:
00051  */
00052 
00053 static int              PipeBlockModeProc(ClientData instanceData, int mode);
00054 static int              PipeCloseProc(ClientData instanceData,
00055                             Tcl_Interp *interp);
00056 static int              PipeGetHandleProc(ClientData instanceData,
00057                             int direction, ClientData *handlePtr);
00058 static int              PipeInputProc(ClientData instanceData, char *buf,
00059                             int toRead, int *errorCode);
00060 static int              PipeOutputProc(ClientData instanceData,
00061                             const char *buf, int toWrite, int *errorCode);
00062 static void             PipeWatchProc(ClientData instanceData, int mask);
00063 static void             RestoreSignals(void);
00064 static int              SetupStdFile(TclFile file, int type);
00065 
00066 /*
00067  * This structure describes the channel type structure for command pipe based
00068  * I/O:
00069  */
00070 
00071 static Tcl_ChannelType pipeChannelType = {
00072     "pipe",                     /* Type name. */
00073     TCL_CHANNEL_VERSION_5,      /* v5 channel */
00074     PipeCloseProc,              /* Close proc. */
00075     PipeInputProc,              /* Input proc. */
00076     PipeOutputProc,             /* Output proc. */
00077     NULL,                       /* Seek proc. */
00078     NULL,                       /* Set option proc. */
00079     NULL,                       /* Get option proc. */
00080     PipeWatchProc,              /* Initialize notifier. */
00081     PipeGetHandleProc,          /* Get OS handles out of channel. */
00082     NULL,                       /* close2proc. */
00083     PipeBlockModeProc,          /* Set blocking or non-blocking mode.*/
00084     NULL,                       /* flush proc. */
00085     NULL,                       /* handler proc. */
00086     NULL,                       /* wide seek proc */
00087     NULL,                       /* thread action proc */
00088     NULL,                       /* truncation */
00089 };
00090 
00091 /*
00092  *----------------------------------------------------------------------
00093  *
00094  * TclpMakeFile --
00095  *
00096  *      Make a TclFile from a channel.
00097  *
00098  * Results:
00099  *      Returns a new TclFile or NULL on failure.
00100  *
00101  * Side effects:
00102  *      None.
00103  *
00104  *----------------------------------------------------------------------
00105  */
00106 
00107 TclFile
00108 TclpMakeFile(
00109     Tcl_Channel channel,        /* Channel to get file from. */
00110     int direction)              /* Either TCL_READABLE or TCL_WRITABLE. */
00111 {
00112     ClientData data;
00113 
00114     if (Tcl_GetChannelHandle(channel, direction,
00115             (ClientData *) &data) == TCL_OK) {
00116         return MakeFile(PTR2INT(data));
00117     } else {
00118         return (TclFile) NULL;
00119     }
00120 }
00121 
00122 /*
00123  *----------------------------------------------------------------------
00124  *
00125  * TclpOpenFile --
00126  *
00127  *      Open a file for use in a pipeline.
00128  *
00129  * Results:
00130  *      Returns a new TclFile handle or NULL on failure.
00131  *
00132  * Side effects:
00133  *      May cause a file to be created on the file system.
00134  *
00135  *----------------------------------------------------------------------
00136  */
00137 
00138 TclFile
00139 TclpOpenFile(
00140     const char *fname,          /* The name of the file to open. */
00141     int mode)                   /* In what mode to open the file? */
00142 {
00143     int fd;
00144     const char *native;
00145     Tcl_DString ds;
00146 
00147     native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds);
00148     fd = TclOSopen(native, mode, 0666);                 /* INTL: Native. */
00149     Tcl_DStringFree(&ds);
00150     if (fd != -1) {
00151         fcntl(fd, F_SETFD, FD_CLOEXEC);
00152 
00153         /*
00154          * If the file is being opened for writing, seek to the end so we can
00155          * append to any data already in the file.
00156          */
00157 
00158         if ((mode & O_WRONLY) && !(mode & O_APPEND)) {
00159             TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_END);
00160         }
00161 
00162         /*
00163          * Increment the fd so it can't be 0, which would conflict with the
00164          * NULL return for errors.
00165          */
00166 
00167         return MakeFile(fd);
00168     }
00169     return NULL;
00170 }
00171 
00172 /*
00173  *----------------------------------------------------------------------
00174  *
00175  * TclpCreateTempFile --
00176  *
00177  *      This function creates a temporary file initialized with an optional
00178  *      string, and returns a file handle with the file pointer at the
00179  *      beginning of the file.
00180  *
00181  * Results:
00182  *      A handle to a file.
00183  *
00184  * Side effects:
00185  *      None.
00186  *
00187  *----------------------------------------------------------------------
00188  */
00189 
00190 TclFile
00191 TclpCreateTempFile(
00192     const char *contents)       /* String to write into temp file, or NULL. */
00193 {
00194     char fileName[L_tmpnam + 9];
00195     const char *native;
00196     Tcl_DString dstring;
00197     int fd;
00198 
00199     /*
00200      * We should also check against making more then TMP_MAX of these.
00201      */
00202 
00203     strcpy(fileName, P_tmpdir);                         /* INTL: Native. */
00204     if (fileName[strlen(fileName) - 1] != '/') {
00205         strcat(fileName, "/");                          /* INTL: Native. */
00206     }
00207     strcat(fileName, "tclXXXXXX");
00208     fd = mkstemp(fileName);                             /* INTL: Native. */
00209     if (fd == -1) {
00210         return NULL;
00211     }
00212     fcntl(fd, F_SETFD, FD_CLOEXEC);
00213     unlink(fileName);                                   /* INTL: Native. */
00214 
00215     if (contents != NULL) {
00216         native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
00217         if (write(fd, native, strlen(native)) == -1) {
00218             close(fd);
00219             Tcl_DStringFree(&dstring);
00220             return NULL;
00221         }
00222         Tcl_DStringFree(&dstring);
00223         TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_SET);
00224     }
00225     return MakeFile(fd);
00226 }
00227 
00228 /*
00229  *----------------------------------------------------------------------
00230  *
00231  * TclpTempFileName --
00232  *
00233  *      This function returns unique filename.
00234  *
00235  * Results:
00236  *      Returns a valid Tcl_Obj* with refCount 0, or NULL on failure.
00237  *
00238  * Side effects:
00239  *      None.
00240  *
00241  *----------------------------------------------------------------------
00242  */
00243 
00244 Tcl_Obj *
00245 TclpTempFileName(void)
00246 {
00247     char fileName[L_tmpnam + 9];
00248     Tcl_Obj *result = NULL;
00249     int fd;
00250 
00251     /*
00252      * We should also check against making more then TMP_MAX of these.
00253      */
00254 
00255     strcpy(fileName, P_tmpdir);         /* INTL: Native. */
00256     if (fileName[strlen(fileName) - 1] != '/') {
00257         strcat(fileName, "/");          /* INTL: Native. */
00258     }
00259     strcat(fileName, "tclXXXXXX");
00260     fd = mkstemp(fileName);             /* INTL: Native. */
00261     if (fd == -1) {
00262         return NULL;
00263     }
00264     fcntl(fd, F_SETFD, FD_CLOEXEC);
00265     unlink(fileName);                   /* INTL: Native. */
00266 
00267     result = TclpNativeToNormalized((ClientData) fileName);
00268     close(fd);
00269     return result;
00270 }
00271 
00272 /*
00273  *----------------------------------------------------------------------
00274  *
00275  * TclpCreatePipe --
00276  *
00277  *      Creates a pipe - simply calls the pipe() function.
00278  *
00279  * Results:
00280  *      Returns 1 on success, 0 on failure.
00281  *
00282  * Side effects:
00283  *      Creates a pipe.
00284  *
00285  *----------------------------------------------------------------------
00286  */
00287 
00288 int
00289 TclpCreatePipe(
00290     TclFile *readPipe,          /* Location to store file handle for read side
00291                                  * of pipe. */
00292     TclFile *writePipe)         /* Location to store file handle for write
00293                                  * side of pipe. */
00294 {
00295     int pipeIds[2];
00296 
00297     if (pipe(pipeIds) != 0) {
00298         return 0;
00299     }
00300 
00301     fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC);
00302     fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC);
00303 
00304     *readPipe = MakeFile(pipeIds[0]);
00305     *writePipe = MakeFile(pipeIds[1]);
00306     return 1;
00307 }
00308 
00309 /*
00310  *----------------------------------------------------------------------
00311  *
00312  * TclpCloseFile --
00313  *
00314  *      Implements a mechanism to close a UNIX file.
00315  *
00316  * Results:
00317  *      Returns 0 on success, or -1 on error, setting errno.
00318  *
00319  * Side effects:
00320  *      The file is closed.
00321  *
00322  *----------------------------------------------------------------------
00323  */
00324 
00325 int
00326 TclpCloseFile(
00327     TclFile file)       /* The file to close. */
00328 {
00329     int fd = GetFd(file);
00330 
00331     /*
00332      * Refuse to close the fds for stdin, stdout and stderr.
00333      */
00334 
00335     if ((fd == 0) || (fd == 1) || (fd == 2)) {
00336         return 0;
00337     }
00338 
00339     Tcl_DeleteFileHandler(fd);
00340     return close(fd);
00341 }
00342 
00343 /*
00344  *---------------------------------------------------------------------------
00345  *
00346  * TclpCreateProcess --
00347  *
00348  *      Create a child process that has the specified files as its standard
00349  *      input, output, and error. The child process runs asynchronously and
00350  *      runs with the same environment variables as the creating process.
00351  *
00352  *      The path is searched to find the specified executable.
00353  *
00354  * Results:
00355  *      The return value is TCL_ERROR and an error message is left in the
00356  *      interp's result if there was a problem creating the child process.
00357  *      Otherwise, the return value is TCL_OK and *pidPtr is filled with the
00358  *      process id of the child process.
00359  *
00360  * Side effects:
00361  *      A process is created.
00362  *
00363  *---------------------------------------------------------------------------
00364  */
00365 
00366     /* ARGSUSED */
00367 int
00368 TclpCreateProcess(
00369     Tcl_Interp *interp,         /* Interpreter in which to leave errors that
00370                                  * occurred when creating the child process.
00371                                  * Error messages from the child process
00372                                  * itself are sent to errorFile. */
00373     int argc,                   /* Number of arguments in following array. */
00374     const char **argv,          /* Array of argument strings in UTF-8.
00375                                  * argv[0] contains the name of the executable
00376                                  * translated using Tcl_TranslateFileName
00377                                  * call). Additional arguments have not been
00378                                  * converted. */
00379     TclFile inputFile,          /* If non-NULL, gives the file to use as input
00380                                  * for the child process. If inputFile file is
00381                                  * not readable or is NULL, the child will
00382                                  * receive no standard input. */
00383     TclFile outputFile,         /* If non-NULL, gives the file that receives
00384                                  * output from the child process. If
00385                                  * outputFile file is not writeable or is
00386                                  * NULL, output from the child will be
00387                                  * discarded. */
00388     TclFile errorFile,          /* If non-NULL, gives the file that receives
00389                                  * errors from the child process. If errorFile
00390                                  * file is not writeable or is NULL, errors
00391                                  * from the child will be discarded. errorFile
00392                                  * may be the same as outputFile. */
00393     Tcl_Pid *pidPtr)            /* If this function is successful, pidPtr is
00394                                  * filled with the process id of the child
00395                                  * process. */
00396 {
00397     TclFile errPipeIn, errPipeOut;
00398     int count, status, fd;
00399     char errSpace[200 + TCL_INTEGER_SPACE];
00400     Tcl_DString *dsArray;
00401     char **newArgv;
00402     int pid, i;
00403 
00404     errPipeIn = NULL;
00405     errPipeOut = NULL;
00406     pid = -1;
00407 
00408     /*
00409      * Create a pipe that the child can use to return error information if
00410      * anything goes wrong.
00411      */
00412 
00413     if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) {
00414         Tcl_AppendResult(interp, "couldn't create pipe: ",
00415                 Tcl_PosixError(interp), NULL);
00416         goto error;
00417     }
00418 
00419     /*
00420      * We need to allocate and convert this before the fork so it is properly
00421      * deallocated later
00422      */
00423 
00424     dsArray = (Tcl_DString *)
00425             TclStackAlloc(interp, argc * sizeof(Tcl_DString));
00426     newArgv = (char **) TclStackAlloc(interp, (argc+1) * sizeof(char *));
00427     newArgv[argc] = NULL;
00428     for (i = 0; i < argc; i++) {
00429         newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]);
00430     }
00431 
00432 #ifdef USE_VFORK
00433     /*
00434      * After vfork(), do not call code in the child that changes global state,
00435      * because it is using the parent's memory space at that point and writes
00436      * might corrupt the parent: so ensure standard channels are initialized in
00437      * the parent, otherwise SetupStdFile() might initialize them in the child.
00438      */
00439     if (!inputFile) {
00440         Tcl_GetStdChannel(TCL_STDIN);
00441     }
00442     if (!outputFile) {
00443         Tcl_GetStdChannel(TCL_STDOUT);
00444     }
00445     if (!errorFile) {
00446         Tcl_GetStdChannel(TCL_STDERR);
00447     }
00448 #endif
00449     pid = fork();
00450     if (pid == 0) {
00451         int joinThisError = errorFile && (errorFile == outputFile);
00452 
00453         fd = GetFd(errPipeOut);
00454 
00455         /*
00456          * Set up stdio file handles for the child process.
00457          */
00458 
00459         if (!SetupStdFile(inputFile, TCL_STDIN)
00460                 || !SetupStdFile(outputFile, TCL_STDOUT)
00461                 || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))
00462                 || (joinThisError &&
00463                         ((dup2(1,2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) {
00464             sprintf(errSpace,
00465                     "%dforked process couldn't set up input/output: ", errno);
00466             write(fd, errSpace, (size_t) strlen(errSpace));
00467             _exit(1);
00468         }
00469 
00470         /*
00471          * Close the input side of the error pipe.
00472          */
00473 
00474         RestoreSignals();
00475         execvp(newArgv[0], newArgv);                    /* INTL: Native. */
00476         sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]);
00477         write(fd, errSpace, (size_t) strlen(errSpace));
00478         _exit(1);
00479     }
00480 
00481     /*
00482      * Free the mem we used for the fork
00483      */
00484 
00485     for (i = 0; i < argc; i++) {
00486         Tcl_DStringFree(&dsArray[i]);
00487     }
00488     TclStackFree(interp, newArgv);
00489     TclStackFree(interp, dsArray);
00490 
00491     if (pid == -1) {
00492         Tcl_AppendResult(interp, "couldn't fork child process: ",
00493                 Tcl_PosixError(interp), NULL);
00494         goto error;
00495     }
00496 
00497     /*
00498      * Read back from the error pipe to see if the child started up OK. The
00499      * info in the pipe (if any) consists of a decimal errno value followed by
00500      * an error message.
00501      */
00502 
00503     TclpCloseFile(errPipeOut);
00504     errPipeOut = NULL;
00505 
00506     fd = GetFd(errPipeIn);
00507     count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
00508     if (count > 0) {
00509         char *end;
00510         errSpace[count] = 0;
00511         errno = strtol(errSpace, &end, 10);
00512         Tcl_AppendResult(interp, end, Tcl_PosixError(interp), NULL);
00513         goto error;
00514     }
00515 
00516     TclpCloseFile(errPipeIn);
00517     *pidPtr = (Tcl_Pid) INT2PTR(pid);
00518     return TCL_OK;
00519 
00520   error:
00521     if (pid != -1) {
00522         /*
00523          * Reap the child process now if an error occurred during its startup.
00524          * We don't call this with WNOHANG because that can lead to defunct
00525          * processes on an MP system. We shouldn't have to worry about hanging
00526          * here, since this is the error case. [Bug: 6148]
00527          */
00528 
00529         Tcl_WaitPid((Tcl_Pid) INT2PTR(pid), &status, 0);
00530     }
00531 
00532     if (errPipeIn) {
00533         TclpCloseFile(errPipeIn);
00534     }
00535     if (errPipeOut) {
00536         TclpCloseFile(errPipeOut);
00537     }
00538     return TCL_ERROR;
00539 }
00540 
00541 /*
00542  *----------------------------------------------------------------------
00543  *
00544  * RestoreSignals --
00545  *
00546  *      This function is invoked in a forked child process just before
00547  *      exec-ing a new program to restore all signals to their default
00548  *      settings.
00549  *
00550  * Results:
00551  *      None.
00552  *
00553  * Side effects:
00554  *      Signal settings get changed.
00555  *
00556  *----------------------------------------------------------------------
00557  */
00558 
00559 static void
00560 RestoreSignals(void)
00561 {
00562 #ifdef SIGABRT
00563     signal(SIGABRT, SIG_DFL);
00564 #endif
00565 #ifdef SIGALRM
00566     signal(SIGALRM, SIG_DFL);
00567 #endif
00568 #ifdef SIGFPE
00569     signal(SIGFPE, SIG_DFL);
00570 #endif
00571 #ifdef SIGHUP
00572     signal(SIGHUP, SIG_DFL);
00573 #endif
00574 #ifdef SIGILL
00575     signal(SIGILL, SIG_DFL);
00576 #endif
00577 #ifdef SIGINT
00578     signal(SIGINT, SIG_DFL);
00579 #endif
00580 #ifdef SIGPIPE
00581     signal(SIGPIPE, SIG_DFL);
00582 #endif
00583 #ifdef SIGQUIT
00584     signal(SIGQUIT, SIG_DFL);
00585 #endif
00586 #ifdef SIGSEGV
00587     signal(SIGSEGV, SIG_DFL);
00588 #endif
00589 #ifdef SIGTERM
00590     signal(SIGTERM, SIG_DFL);
00591 #endif
00592 #ifdef SIGUSR1
00593     signal(SIGUSR1, SIG_DFL);
00594 #endif
00595 #ifdef SIGUSR2
00596     signal(SIGUSR2, SIG_DFL);
00597 #endif
00598 #ifdef SIGCHLD
00599     signal(SIGCHLD, SIG_DFL);
00600 #endif
00601 #ifdef SIGCONT
00602     signal(SIGCONT, SIG_DFL);
00603 #endif
00604 #ifdef SIGTSTP
00605     signal(SIGTSTP, SIG_DFL);
00606 #endif
00607 #ifdef SIGTTIN
00608     signal(SIGTTIN, SIG_DFL);
00609 #endif
00610 #ifdef SIGTTOU
00611     signal(SIGTTOU, SIG_DFL);
00612 #endif
00613 }
00614 
00615 /*
00616  *----------------------------------------------------------------------
00617  *
00618  * SetupStdFile --
00619  *
00620  *      Set up stdio file handles for the child process, using the current
00621  *      standard channels if no other files are specified. If no standard
00622  *      channel is defined, or if no file is associated with the channel, then
00623  *      the corresponding standard fd is closed.
00624  *
00625  * Results:
00626  *      Returns 1 on success, or 0 on failure.
00627  *
00628  * Side effects:
00629  *      Replaces stdio fds.
00630  *
00631  *----------------------------------------------------------------------
00632  */
00633 
00634 static int
00635 SetupStdFile(
00636     TclFile file,               /* File to dup, or NULL. */
00637     int type)                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
00638 {
00639     Tcl_Channel channel;
00640     int fd;
00641     int targetFd = 0;           /* Initializations here needed only to */
00642     int direction = 0;          /* prevent warnings about using uninitialized
00643                                  * variables. */
00644 
00645     switch (type) {
00646     case TCL_STDIN:
00647         targetFd = 0;
00648         direction = TCL_READABLE;
00649         break;
00650     case TCL_STDOUT:
00651         targetFd = 1;
00652         direction = TCL_WRITABLE;
00653         break;
00654     case TCL_STDERR:
00655         targetFd = 2;
00656         direction = TCL_WRITABLE;
00657         break;
00658     }
00659 
00660     if (!file) {
00661         channel = Tcl_GetStdChannel(type);
00662         if (channel) {
00663             file = TclpMakeFile(channel, direction);
00664         }
00665     }
00666     if (file) {
00667         fd = GetFd(file);
00668         if (fd != targetFd) {
00669             if (dup2(fd, targetFd) == -1) {
00670                 return 0;
00671             }
00672 
00673             /*
00674              * Must clear the close-on-exec flag for the target FD, since some
00675              * systems (e.g. Ultrix) do not clear the CLOEXEC flag on the
00676              * target FD.
00677              */
00678 
00679             fcntl(targetFd, F_SETFD, 0);
00680         } else {
00681             /*
00682              * Since we aren't dup'ing the file, we need to explicitly clear
00683              * the close-on-exec flag.
00684              */
00685 
00686             fcntl(fd, F_SETFD, 0);
00687         }
00688     } else {
00689         close(targetFd);
00690     }
00691     return 1;
00692 }
00693 
00694 /*
00695  *----------------------------------------------------------------------
00696  *
00697  * TclpCreateCommandChannel --
00698  *
00699  *      This function is called by the generic IO level to perform the
00700  *      platform specific channel initialization for a command channel.
00701  *
00702  * Results:
00703  *      Returns a new channel or NULL on failure.
00704  *
00705  * Side effects:
00706  *      Allocates a new channel.
00707  *
00708  *----------------------------------------------------------------------
00709  */
00710 
00711 Tcl_Channel
00712 TclpCreateCommandChannel(
00713     TclFile readFile,           /* If non-null, gives the file for reading. */
00714     TclFile writeFile,          /* If non-null, gives the file for writing. */
00715     TclFile errorFile,          /* If non-null, gives the file where errors
00716                                  * can be read. */
00717     int numPids,                /* The number of pids in the pid array. */
00718     Tcl_Pid *pidPtr)            /* An array of process identifiers. Allocated
00719                                  * by the caller, freed when the channel is
00720                                  * closed or the processes are detached (in a
00721                                  * background exec). */
00722 {
00723     char channelName[16 + TCL_INTEGER_SPACE];
00724     int channelId;
00725     PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
00726     int mode;
00727 
00728     statePtr->inFile = readFile;
00729     statePtr->outFile = writeFile;
00730     statePtr->errorFile = errorFile;
00731     statePtr->numPids = numPids;
00732     statePtr->pidPtr = pidPtr;
00733     statePtr->isNonBlocking = 0;
00734 
00735     mode = 0;
00736     if (readFile) {
00737         mode |= TCL_READABLE;
00738     }
00739     if (writeFile) {
00740         mode |= TCL_WRITABLE;
00741     }
00742 
00743     /*
00744      * Use one of the fds associated with the channel as the channel id.
00745      */
00746 
00747     if (readFile) {
00748         channelId = GetFd(readFile);
00749     } else if (writeFile) {
00750         channelId = GetFd(writeFile);
00751     } else if (errorFile) {
00752         channelId = GetFd(errorFile);
00753     } else {
00754         channelId = 0;
00755     }
00756 
00757     /*
00758      * For backward compatibility with previous versions of Tcl, we use
00759      * "file%d" as the base name for pipes even though it would be more
00760      * natural to use "pipe%d".
00761      */
00762 
00763     sprintf(channelName, "file%d", channelId);
00764     statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
00765             (ClientData) statePtr, mode);
00766     return statePtr->channel;
00767 }
00768 
00769 /*
00770  *----------------------------------------------------------------------
00771  *
00772  * TclGetAndDetachPids --
00773  *
00774  *      This function is invoked in the generic implementation of a
00775  *      background "exec" (an exec when invoked with a terminating "&") to
00776  *      store a list of the PIDs for processes in a command pipeline in the
00777  *      interp's result and to detach the processes.
00778  *
00779  * Results:
00780  *      None.
00781  *
00782  * Side effects:
00783  *      Modifies the interp's result. Detaches processes.
00784  *
00785  *----------------------------------------------------------------------
00786  */
00787 
00788 void
00789 TclGetAndDetachPids(
00790     Tcl_Interp *interp,         /* Interpreter to append the PIDs to. */
00791     Tcl_Channel chan)           /* Handle for the pipeline. */
00792 {
00793     PipeState *pipePtr;
00794     const Tcl_ChannelType *chanTypePtr;
00795     int i;
00796     char buf[TCL_INTEGER_SPACE];
00797 
00798     /*
00799      * Punt if the channel is not a command channel.
00800      */
00801 
00802     chanTypePtr = Tcl_GetChannelType(chan);
00803     if (chanTypePtr != &pipeChannelType) {
00804         return;
00805     }
00806 
00807     pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
00808     for (i = 0; i < pipePtr->numPids; i++) {
00809         TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i]));
00810         Tcl_AppendElement(interp, buf);
00811         Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
00812     }
00813     if (pipePtr->numPids > 0) {
00814         ckfree((char *) pipePtr->pidPtr);
00815         pipePtr->numPids = 0;
00816     }
00817 }
00818 
00819 /*
00820  *----------------------------------------------------------------------
00821  *
00822  * PipeBlockModeProc --
00823  *
00824  *      Helper function to set blocking and nonblocking modes on a pipe based
00825  *      channel. Invoked by generic IO level code.
00826  *
00827  * Results:
00828  *      0 if successful, errno when failed.
00829  *
00830  * Side effects:
00831  *      Sets the device into blocking or non-blocking mode.
00832  *
00833  *----------------------------------------------------------------------
00834  */
00835 
00836         /* ARGSUSED */
00837 static int
00838 PipeBlockModeProc(
00839     ClientData instanceData,    /* Pipe state. */
00840     int mode)                   /* The mode to set. Can be one of
00841                                  * TCL_MODE_BLOCKING or
00842                                  * TCL_MODE_NONBLOCKING. */
00843 {
00844     PipeState *psPtr = (PipeState *) instanceData;
00845     int curStatus;
00846     int fd;
00847 
00848 #ifndef USE_FIONBIO
00849     if (psPtr->inFile) {
00850         fd = GetFd(psPtr->inFile);
00851         curStatus = fcntl(fd, F_GETFL);
00852         if (mode == TCL_MODE_BLOCKING) {
00853             curStatus &= (~(O_NONBLOCK));
00854         } else {
00855             curStatus |= O_NONBLOCK;
00856         }
00857         if (fcntl(fd, F_SETFL, curStatus) < 0) {
00858             return errno;
00859         }
00860     }
00861     if (psPtr->outFile) {
00862         fd = GetFd(psPtr->outFile);
00863         curStatus = fcntl(fd, F_GETFL);
00864         if (mode == TCL_MODE_BLOCKING) {
00865             curStatus &= (~(O_NONBLOCK));
00866         } else {
00867             curStatus |= O_NONBLOCK;
00868         }
00869         if (fcntl(fd, F_SETFL, curStatus) < 0) {
00870             return errno;
00871         }
00872     }
00873 #endif  /* !FIONBIO */
00874 
00875 #ifdef  USE_FIONBIO
00876     if (psPtr->inFile) {
00877         fd = GetFd(psPtr->inFile);
00878         if (mode == TCL_MODE_BLOCKING) {
00879             curStatus = 0;
00880         } else {
00881             curStatus = 1;
00882         }
00883         if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
00884             return errno;
00885         }
00886     }
00887     if (psPtr->outFile != NULL) {
00888         fd = GetFd(psPtr->outFile);
00889         if (mode == TCL_MODE_BLOCKING) {
00890             curStatus = 0;
00891         } else {
00892             curStatus = 1;
00893         }
00894         if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
00895             return errno;
00896         }
00897     }
00898 #endif  /* USE_FIONBIO */
00899 
00900     psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING);
00901 
00902     return 0;
00903 }
00904 
00905 /*
00906  *----------------------------------------------------------------------
00907  *
00908  * PipeCloseProc --
00909  *
00910  *      This function is invoked by the generic IO level to perform
00911  *      channel-type-specific cleanup when a command pipeline channel is
00912  *      closed.
00913  *
00914  * Results:
00915  *      0 on success, errno otherwise.
00916  *
00917  * Side effects:
00918  *      Closes the command pipeline channel.
00919  *
00920  *----------------------------------------------------------------------
00921  */
00922 
00923         /* ARGSUSED */
00924 static int
00925 PipeCloseProc(
00926     ClientData instanceData,    /* The pipe to close. */
00927     Tcl_Interp *interp)         /* For error reporting. */
00928 {
00929     PipeState *pipePtr;
00930     Tcl_Channel errChan;
00931     int errorCode, result;
00932 
00933     errorCode = 0;
00934     result = 0;
00935     pipePtr = (PipeState *) instanceData;
00936     if (pipePtr->inFile) {
00937         if (TclpCloseFile(pipePtr->inFile) < 0) {
00938             errorCode = errno;
00939         }
00940     }
00941     if (pipePtr->outFile) {
00942         if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) {
00943             errorCode = errno;
00944         }
00945     }
00946 
00947     if (pipePtr->isNonBlocking || TclInExit()) {
00948         /*
00949          * If the channel is non-blocking or Tcl is being cleaned up, just
00950          * detach the children PIDs, reap them (important if we are in a
00951          * dynamic load module), and discard the errorFile.
00952          */
00953 
00954         Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
00955         Tcl_ReapDetachedProcs();
00956 
00957         if (pipePtr->errorFile) {
00958             TclpCloseFile(pipePtr->errorFile);
00959         }
00960     } else {
00961         /*
00962          * Wrap the error file into a channel and give it to the cleanup
00963          * routine.
00964          */
00965 
00966         if (pipePtr->errorFile) {
00967             errChan = Tcl_MakeFileChannel(
00968                 (ClientData) INT2PTR(GetFd(pipePtr->errorFile)), TCL_READABLE);
00969         } else {
00970             errChan = NULL;
00971         }
00972         result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
00973                 errChan);
00974     }
00975 
00976     if (pipePtr->numPids != 0) {
00977         ckfree((char *) pipePtr->pidPtr);
00978     }
00979     ckfree((char *) pipePtr);
00980     if (errorCode == 0) {
00981         return result;
00982     }
00983     return errorCode;
00984 }
00985 
00986 /*
00987  *----------------------------------------------------------------------
00988  *
00989  * PipeInputProc --
00990  *
00991  *      This function is invoked from the generic IO level to read input from
00992  *      a command pipeline based channel.
00993  *
00994  * Results:
00995  *      The number of bytes read is returned or -1 on error. An output
00996  *      argument contains a POSIX error code if an error occurs, or zero.
00997  *
00998  * Side effects:
00999  *      Reads input from the input device of the channel.
01000  *
01001  *----------------------------------------------------------------------
01002  */
01003 
01004 static int
01005 PipeInputProc(
01006     ClientData instanceData,    /* Pipe state. */
01007     char *buf,                  /* Where to store data read. */
01008     int toRead,                 /* How much space is available in the
01009                                  * buffer? */
01010     int *errorCodePtr)          /* Where to store error code. */
01011 {
01012     PipeState *psPtr = (PipeState *) instanceData;
01013     int bytesRead;              /* How many bytes were actually read from the
01014                                  * input device? */
01015 
01016     *errorCodePtr = 0;
01017 
01018     /*
01019      * Assume there is always enough input available. This will block
01020      * appropriately, and read will unblock as soon as a short read is
01021      * possible, if the channel is in blocking mode. If the channel is
01022      * nonblocking, the read will never block. Some OSes can throw an
01023      * interrupt error, for which we should immediately retry. [Bug #415131]
01024      */
01025 
01026     do {
01027         bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead);
01028     } while ((bytesRead < 0) && (errno == EINTR));
01029 
01030     if (bytesRead < 0) {
01031         *errorCodePtr = errno;
01032         return -1;
01033     } else {
01034         return bytesRead;
01035     }
01036 }
01037 
01038 /*
01039  *----------------------------------------------------------------------
01040  *
01041  * PipeOutputProc--
01042  *
01043  *      This function is invoked from the generic IO level to write output to
01044  *      a command pipeline based channel.
01045  *
01046  * Results:
01047  *      The number of bytes written is returned or -1 on error. An output
01048  *      argument contains a POSIX error code if an error occurred, or zero.
01049  *
01050  * Side effects:
01051  *      Writes output on the output device of the channel.
01052  *
01053  *----------------------------------------------------------------------
01054  */
01055 
01056 static int
01057 PipeOutputProc(
01058     ClientData instanceData,    /* Pipe state. */
01059     const char *buf,            /* The data buffer. */
01060     int toWrite,                /* How many bytes to write? */
01061     int *errorCodePtr)          /* Where to store error code. */
01062 {
01063     PipeState *psPtr = (PipeState *) instanceData;
01064     int written;
01065 
01066     *errorCodePtr = 0;
01067 
01068     /*
01069      * Some OSes can throw an interrupt error, for which we should immediately
01070      * retry. [Bug #415131]
01071      */
01072 
01073     do {
01074         written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite);
01075     } while ((written < 0) && (errno == EINTR));
01076 
01077     if (written < 0) {
01078         *errorCodePtr = errno;
01079         return -1;
01080     } else {
01081         return written;
01082     }
01083 }
01084 
01085 /*
01086  *----------------------------------------------------------------------
01087  *
01088  * PipeWatchProc --
01089  *
01090  *      Initialize the notifier to watch the fds from this channel.
01091  *
01092  * Results:
01093  *      None.
01094  *
01095  * Side effects:
01096  *      Sets up the notifier so that a future event on the channel will be
01097  *      seen by Tcl.
01098  *
01099  *----------------------------------------------------------------------
01100  */
01101 
01102 static void
01103 PipeWatchProc(
01104     ClientData instanceData,    /* The pipe state. */
01105     int mask)                   /* Events of interest; an OR-ed combination of
01106                                  * TCL_READABLE, TCL_WRITABLE and
01107                                  * TCL_EXCEPTION. */
01108 {
01109     PipeState *psPtr = (PipeState *) instanceData;
01110     int newmask;
01111 
01112     if (psPtr->inFile) {
01113         newmask = mask & (TCL_READABLE | TCL_EXCEPTION);
01114         if (newmask) {
01115             Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask,
01116                     (Tcl_FileProc *) Tcl_NotifyChannel,
01117                     (ClientData) psPtr->channel);
01118         } else {
01119             Tcl_DeleteFileHandler(GetFd(psPtr->inFile));
01120         }
01121     }
01122     if (psPtr->outFile) {
01123         newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);
01124         if (newmask) {
01125             Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask,
01126                     (Tcl_FileProc *) Tcl_NotifyChannel,
01127                     (ClientData) psPtr->channel);
01128         } else {
01129             Tcl_DeleteFileHandler(GetFd(psPtr->outFile));
01130         }
01131     }
01132 }
01133 
01134 /*
01135  *----------------------------------------------------------------------
01136  *
01137  * PipeGetHandleProc --
01138  *
01139  *      Called from Tcl_GetChannelHandle to retrieve OS handles from inside a
01140  *      command pipeline based channel.
01141  *
01142  * Results:
01143  *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
01144  *      handle for the specified direction.
01145  *
01146  * Side effects:
01147  *      None.
01148  *
01149  *----------------------------------------------------------------------
01150  */
01151 
01152 static int
01153 PipeGetHandleProc(
01154     ClientData instanceData,    /* The pipe state. */
01155     int direction,              /* TCL_READABLE or TCL_WRITABLE */
01156     ClientData *handlePtr)      /* Where to store the handle. */
01157 {
01158     PipeState *psPtr = (PipeState *) instanceData;
01159 
01160     if (direction == TCL_READABLE && psPtr->inFile) {
01161         *handlePtr = (ClientData) INT2PTR(GetFd(psPtr->inFile));
01162         return TCL_OK;
01163     }
01164     if (direction == TCL_WRITABLE && psPtr->outFile) {
01165         *handlePtr = (ClientData) INT2PTR(GetFd(psPtr->outFile));
01166         return TCL_OK;
01167     }
01168     return TCL_ERROR;
01169 }
01170 
01171 /*
01172  *----------------------------------------------------------------------
01173  *
01174  * Tcl_WaitPid --
01175  *
01176  *      Implements the waitpid system call on Unix systems.
01177  *
01178  * Results:
01179  *      Result of calling waitpid.
01180  *
01181  * Side effects:
01182  *      Waits for a process to terminate.
01183  *
01184  *----------------------------------------------------------------------
01185  */
01186 
01187 Tcl_Pid
01188 Tcl_WaitPid(
01189     Tcl_Pid pid,
01190     int *statPtr,
01191     int options)
01192 {
01193     int result;
01194     pid_t real_pid;
01195 
01196     real_pid = (pid_t) PTR2INT(pid);
01197     while (1) {
01198         result = (int) waitpid(real_pid, statPtr, options);
01199         if ((result != -1) || (errno != EINTR)) {
01200             return (Tcl_Pid) INT2PTR(result);
01201         }
01202     }
01203 }
01204 
01205 /*
01206  *----------------------------------------------------------------------
01207  *
01208  * Tcl_PidObjCmd --
01209  *
01210  *      This function is invoked to process the "pid" Tcl command. See the
01211  *      user documentation for details on what it does.
01212  *
01213  * Results:
01214  *      A standard Tcl result.
01215  *
01216  * Side effects:
01217  *      See the user documentation.
01218  *
01219  *----------------------------------------------------------------------
01220  */
01221 
01222         /* ARGSUSED */
01223 int
01224 Tcl_PidObjCmd(
01225     ClientData dummy,           /* Not used. */
01226     Tcl_Interp *interp,         /* Current interpreter. */
01227     int objc,                   /* Number of arguments. */
01228     Tcl_Obj *const *objv)       /* Argument strings. */
01229 {
01230     if (objc > 2) {
01231         Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
01232         return TCL_ERROR;
01233     }
01234     if (objc == 1) {
01235         Tcl_SetObjResult(interp, Tcl_NewLongObj((long) getpid()));
01236     } else {
01237         Tcl_Channel chan;
01238         const Tcl_ChannelType *chanTypePtr;
01239         PipeState *pipePtr;
01240         int i;
01241         Tcl_Obj *resultPtr, *longObjPtr;
01242 
01243         chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
01244         if (chan == (Tcl_Channel) NULL) {
01245             return TCL_ERROR;
01246         }
01247         chanTypePtr = Tcl_GetChannelType(chan);
01248         if (chanTypePtr != &pipeChannelType) {
01249             return TCL_OK;
01250         }
01251         pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
01252         resultPtr = Tcl_NewObj();
01253         for (i = 0; i < pipePtr->numPids; i++) {
01254             longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i]));
01255             Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr);
01256         }
01257         Tcl_SetObjResult(interp, resultPtr);
01258     }
01259     return TCL_OK;
01260 }
01261 
01262 /*
01263  *----------------------------------------------------------------------
01264  *
01265  * TclpFinalizePipes --
01266  *
01267  *      Cleans up the pipe subsystem from Tcl_FinalizeThread
01268  *
01269  * Results:
01270  *      None.
01271  *
01272  * Notes:
01273  *      This function carries out no operation on Unix.
01274  *
01275  *----------------------------------------------------------------------
01276  */
01277 
01278 void
01279 TclpFinalizePipes(void)
01280 {
01281 }
01282 
01283 /*
01284  * Local Variables:
01285  * mode: c
01286  * c-basic-offset: 4
01287  * fill-column: 78
01288  * End:
01289  */



Generated on Wed Mar 12 12:18:26 2008 by  doxygen 1.5.1