Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/unix/tclUnixPipe.c @ 52

Last change on this file since 52 was 25, checked in by landauf, 17 years ago

added tcl to libs

File size: 30.7 KB
Line 
1/*
2 * tclUnixPipe.c --
3 *
4 *      This file implements the UNIX-specific exec pipeline functions, the
5 *      "pipe" channel driver, and the "pid" Tcl command.
6 *
7 * Copyright (c) 1991-1994 The Regents of the University of California.
8 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tclUnixPipe.c,v 1.42 2008/03/14 16:32:52 rmax Exp $
14 */
15
16#include "tclInt.h"
17
18#ifdef USE_VFORK
19#define fork vfork
20#endif
21
22/*
23 * The following macros convert between TclFile's and fd's. The conversion
24 * simple involves shifting fd's up by one to ensure that no valid fd is ever
25 * the same as NULL.
26 */
27
28#define MakeFile(fd)    ((TclFile)INT2PTR(((int)(fd))+1))
29#define GetFd(file)     (PTR2INT(file)-1)
30
31/*
32 * This structure describes per-instance state of a pipe based channel.
33 */
34
35typedef struct PipeState {
36    Tcl_Channel channel;        /* Channel associated with this file. */
37    TclFile inFile;             /* Output from pipe. */
38    TclFile outFile;            /* Input to pipe. */
39    TclFile errorFile;          /* Error output from pipe. */
40    int numPids;                /* How many processes are attached to this
41                                 * pipe? */
42    Tcl_Pid *pidPtr;            /* The process IDs themselves. Allocated by
43                                 * the creator of the pipe. */
44    int isNonBlocking;          /* Nonzero when the pipe is in nonblocking
45                                 * mode. Used to decide whether to wait for
46                                 * the children at close time. */
47} PipeState;
48
49/*
50 * Declarations for local functions defined in this file:
51 */
52
53static int              PipeBlockModeProc(ClientData instanceData, int mode);
54static int              PipeCloseProc(ClientData instanceData,
55                            Tcl_Interp *interp);
56static int              PipeGetHandleProc(ClientData instanceData,
57                            int direction, ClientData *handlePtr);
58static int              PipeInputProc(ClientData instanceData, char *buf,
59                            int toRead, int *errorCode);
60static int              PipeOutputProc(ClientData instanceData,
61                            const char *buf, int toWrite, int *errorCode);
62static void             PipeWatchProc(ClientData instanceData, int mask);
63static void             RestoreSignals(void);
64static int              SetupStdFile(TclFile file, int type);
65
66/*
67 * This structure describes the channel type structure for command pipe based
68 * I/O:
69 */
70
71static Tcl_ChannelType pipeChannelType = {
72    "pipe",                     /* Type name. */
73    TCL_CHANNEL_VERSION_5,      /* v5 channel */
74    PipeCloseProc,              /* Close proc. */
75    PipeInputProc,              /* Input proc. */
76    PipeOutputProc,             /* Output proc. */
77    NULL,                       /* Seek proc. */
78    NULL,                       /* Set option proc. */
79    NULL,                       /* Get option proc. */
80    PipeWatchProc,              /* Initialize notifier. */
81    PipeGetHandleProc,          /* Get OS handles out of channel. */
82    NULL,                       /* close2proc. */
83    PipeBlockModeProc,          /* Set blocking or non-blocking mode.*/
84    NULL,                       /* flush proc. */
85    NULL,                       /* handler proc. */
86    NULL,                       /* wide seek proc */
87    NULL,                       /* thread action proc */
88    NULL,                       /* truncation */
89};
90
91/*
92 *----------------------------------------------------------------------
93 *
94 * TclpMakeFile --
95 *
96 *      Make a TclFile from a channel.
97 *
98 * Results:
99 *      Returns a new TclFile or NULL on failure.
100 *
101 * Side effects:
102 *      None.
103 *
104 *----------------------------------------------------------------------
105 */
106
107TclFile
108TclpMakeFile(
109    Tcl_Channel channel,        /* Channel to get file from. */
110    int direction)              /* Either TCL_READABLE or TCL_WRITABLE. */
111{
112    ClientData data;
113
114    if (Tcl_GetChannelHandle(channel, direction,
115            (ClientData *) &data) == TCL_OK) {
116        return MakeFile(PTR2INT(data));
117    } else {
118        return (TclFile) NULL;
119    }
120}
121
122/*
123 *----------------------------------------------------------------------
124 *
125 * TclpOpenFile --
126 *
127 *      Open a file for use in a pipeline.
128 *
129 * Results:
130 *      Returns a new TclFile handle or NULL on failure.
131 *
132 * Side effects:
133 *      May cause a file to be created on the file system.
134 *
135 *----------------------------------------------------------------------
136 */
137
138TclFile
139TclpOpenFile(
140    const char *fname,          /* The name of the file to open. */
141    int mode)                   /* In what mode to open the file? */
142{
143    int fd;
144    const char *native;
145    Tcl_DString ds;
146
147    native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds);
148    fd = TclOSopen(native, mode, 0666);                 /* INTL: Native. */
149    Tcl_DStringFree(&ds);
150    if (fd != -1) {
151        fcntl(fd, F_SETFD, FD_CLOEXEC);
152
153        /*
154         * If the file is being opened for writing, seek to the end so we can
155         * append to any data already in the file.
156         */
157
158        if ((mode & O_WRONLY) && !(mode & O_APPEND)) {
159            TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_END);
160        }
161
162        /*
163         * Increment the fd so it can't be 0, which would conflict with the
164         * NULL return for errors.
165         */
166
167        return MakeFile(fd);
168    }
169    return NULL;
170}
171
172/*
173 *----------------------------------------------------------------------
174 *
175 * TclpCreateTempFile --
176 *
177 *      This function creates a temporary file initialized with an optional
178 *      string, and returns a file handle with the file pointer at the
179 *      beginning of the file.
180 *
181 * Results:
182 *      A handle to a file.
183 *
184 * Side effects:
185 *      None.
186 *
187 *----------------------------------------------------------------------
188 */
189
190TclFile
191TclpCreateTempFile(
192    const char *contents)       /* String to write into temp file, or NULL. */
193{
194    char fileName[L_tmpnam + 9];
195    const char *native;
196    Tcl_DString dstring;
197    int fd;
198
199    /*
200     * We should also check against making more then TMP_MAX of these.
201     */
202
203    strcpy(fileName, P_tmpdir);                         /* INTL: Native. */
204    if (fileName[strlen(fileName) - 1] != '/') {
205        strcat(fileName, "/");                          /* INTL: Native. */
206    }
207    strcat(fileName, "tclXXXXXX");
208    fd = mkstemp(fileName);                             /* INTL: Native. */
209    if (fd == -1) {
210        return NULL;
211    }
212    fcntl(fd, F_SETFD, FD_CLOEXEC);
213    unlink(fileName);                                   /* INTL: Native. */
214
215    if (contents != NULL) {
216        native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
217        if (write(fd, native, strlen(native)) == -1) {
218            close(fd);
219            Tcl_DStringFree(&dstring);
220            return NULL;
221        }
222        Tcl_DStringFree(&dstring);
223        TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_SET);
224    }
225    return MakeFile(fd);
226}
227
228/*
229 *----------------------------------------------------------------------
230 *
231 * TclpTempFileName --
232 *
233 *      This function returns unique filename.
234 *
235 * Results:
236 *      Returns a valid Tcl_Obj* with refCount 0, or NULL on failure.
237 *
238 * Side effects:
239 *      None.
240 *
241 *----------------------------------------------------------------------
242 */
243
244Tcl_Obj *
245TclpTempFileName(void)
246{
247    char fileName[L_tmpnam + 9];
248    Tcl_Obj *result = NULL;
249    int fd;
250
251    /*
252     * We should also check against making more then TMP_MAX of these.
253     */
254
255    strcpy(fileName, P_tmpdir);         /* INTL: Native. */
256    if (fileName[strlen(fileName) - 1] != '/') {
257        strcat(fileName, "/");          /* INTL: Native. */
258    }
259    strcat(fileName, "tclXXXXXX");
260    fd = mkstemp(fileName);             /* INTL: Native. */
261    if (fd == -1) {
262        return NULL;
263    }
264    fcntl(fd, F_SETFD, FD_CLOEXEC);
265    unlink(fileName);                   /* INTL: Native. */
266
267    result = TclpNativeToNormalized((ClientData) fileName);
268    close(fd);
269    return result;
270}
271
272/*
273 *----------------------------------------------------------------------
274 *
275 * TclpCreatePipe --
276 *
277 *      Creates a pipe - simply calls the pipe() function.
278 *
279 * Results:
280 *      Returns 1 on success, 0 on failure.
281 *
282 * Side effects:
283 *      Creates a pipe.
284 *
285 *----------------------------------------------------------------------
286 */
287
288int
289TclpCreatePipe(
290    TclFile *readPipe,          /* Location to store file handle for read side
291                                 * of pipe. */
292    TclFile *writePipe)         /* Location to store file handle for write
293                                 * side of pipe. */
294{
295    int pipeIds[2];
296
297    if (pipe(pipeIds) != 0) {
298        return 0;
299    }
300
301    fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC);
302    fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC);
303
304    *readPipe = MakeFile(pipeIds[0]);
305    *writePipe = MakeFile(pipeIds[1]);
306    return 1;
307}
308
309/*
310 *----------------------------------------------------------------------
311 *
312 * TclpCloseFile --
313 *
314 *      Implements a mechanism to close a UNIX file.
315 *
316 * Results:
317 *      Returns 0 on success, or -1 on error, setting errno.
318 *
319 * Side effects:
320 *      The file is closed.
321 *
322 *----------------------------------------------------------------------
323 */
324
325int
326TclpCloseFile(
327    TclFile file)       /* The file to close. */
328{
329    int fd = GetFd(file);
330
331    /*
332     * Refuse to close the fds for stdin, stdout and stderr.
333     */
334
335    if ((fd == 0) || (fd == 1) || (fd == 2)) {
336        return 0;
337    }
338
339    Tcl_DeleteFileHandler(fd);
340    return close(fd);
341}
342
343/*
344 *---------------------------------------------------------------------------
345 *
346 * TclpCreateProcess --
347 *
348 *      Create a child process that has the specified files as its standard
349 *      input, output, and error. The child process runs asynchronously and
350 *      runs with the same environment variables as the creating process.
351 *
352 *      The path is searched to find the specified executable.
353 *
354 * Results:
355 *      The return value is TCL_ERROR and an error message is left in the
356 *      interp's result if there was a problem creating the child process.
357 *      Otherwise, the return value is TCL_OK and *pidPtr is filled with the
358 *      process id of the child process.
359 *
360 * Side effects:
361 *      A process is created.
362 *
363 *---------------------------------------------------------------------------
364 */
365
366    /* ARGSUSED */
367int
368TclpCreateProcess(
369    Tcl_Interp *interp,         /* Interpreter in which to leave errors that
370                                 * occurred when creating the child process.
371                                 * Error messages from the child process
372                                 * itself are sent to errorFile. */
373    int argc,                   /* Number of arguments in following array. */
374    const char **argv,          /* Array of argument strings in UTF-8.
375                                 * argv[0] contains the name of the executable
376                                 * translated using Tcl_TranslateFileName
377                                 * call). Additional arguments have not been
378                                 * converted. */
379    TclFile inputFile,          /* If non-NULL, gives the file to use as input
380                                 * for the child process. If inputFile file is
381                                 * not readable or is NULL, the child will
382                                 * receive no standard input. */
383    TclFile outputFile,         /* If non-NULL, gives the file that receives
384                                 * output from the child process. If
385                                 * outputFile file is not writeable or is
386                                 * NULL, output from the child will be
387                                 * discarded. */
388    TclFile errorFile,          /* If non-NULL, gives the file that receives
389                                 * errors from the child process. If errorFile
390                                 * file is not writeable or is NULL, errors
391                                 * from the child will be discarded. errorFile
392                                 * may be the same as outputFile. */
393    Tcl_Pid *pidPtr)            /* If this function is successful, pidPtr is
394                                 * filled with the process id of the child
395                                 * process. */
396{
397    TclFile errPipeIn, errPipeOut;
398    int count, status, fd;
399    char errSpace[200 + TCL_INTEGER_SPACE];
400    Tcl_DString *dsArray;
401    char **newArgv;
402    int pid, i;
403
404    errPipeIn = NULL;
405    errPipeOut = NULL;
406    pid = -1;
407
408    /*
409     * Create a pipe that the child can use to return error information if
410     * anything goes wrong.
411     */
412
413    if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) {
414        Tcl_AppendResult(interp, "couldn't create pipe: ",
415                Tcl_PosixError(interp), NULL);
416        goto error;
417    }
418
419    /*
420     * We need to allocate and convert this before the fork so it is properly
421     * deallocated later
422     */
423
424    dsArray = (Tcl_DString *)
425            TclStackAlloc(interp, argc * sizeof(Tcl_DString));
426    newArgv = (char **) TclStackAlloc(interp, (argc+1) * sizeof(char *));
427    newArgv[argc] = NULL;
428    for (i = 0; i < argc; i++) {
429        newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]);
430    }
431
432#ifdef USE_VFORK
433    /*
434     * After vfork(), do not call code in the child that changes global state,
435     * because it is using the parent's memory space at that point and writes
436     * might corrupt the parent: so ensure standard channels are initialized in
437     * the parent, otherwise SetupStdFile() might initialize them in the child.
438     */
439    if (!inputFile) {
440        Tcl_GetStdChannel(TCL_STDIN);
441    }
442    if (!outputFile) {
443        Tcl_GetStdChannel(TCL_STDOUT);
444    }
445    if (!errorFile) {
446        Tcl_GetStdChannel(TCL_STDERR);
447    }
448#endif
449    pid = fork();
450    if (pid == 0) {
451        int joinThisError = errorFile && (errorFile == outputFile);
452
453        fd = GetFd(errPipeOut);
454
455        /*
456         * Set up stdio file handles for the child process.
457         */
458
459        if (!SetupStdFile(inputFile, TCL_STDIN)
460                || !SetupStdFile(outputFile, TCL_STDOUT)
461                || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))
462                || (joinThisError &&
463                        ((dup2(1,2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) {
464            sprintf(errSpace,
465                    "%dforked process couldn't set up input/output: ", errno);
466            (void)write(fd, errSpace, (size_t) strlen(errSpace));
467            _exit(1);
468        }
469
470        /*
471         * Close the input side of the error pipe.
472         */
473
474        RestoreSignals();
475        execvp(newArgv[0], newArgv);                    /* INTL: Native. */
476        sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]);
477        (void)write(fd, errSpace, (size_t) strlen(errSpace));
478        _exit(1);
479    }
480
481    /*
482     * Free the mem we used for the fork
483     */
484
485    for (i = 0; i < argc; i++) {
486        Tcl_DStringFree(&dsArray[i]);
487    }
488    TclStackFree(interp, newArgv);
489    TclStackFree(interp, dsArray);
490
491    if (pid == -1) {
492        Tcl_AppendResult(interp, "couldn't fork child process: ",
493                Tcl_PosixError(interp), NULL);
494        goto error;
495    }
496
497    /*
498     * Read back from the error pipe to see if the child started up OK. The
499     * info in the pipe (if any) consists of a decimal errno value followed by
500     * an error message.
501     */
502
503    TclpCloseFile(errPipeOut);
504    errPipeOut = NULL;
505
506    fd = GetFd(errPipeIn);
507    count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
508    if (count > 0) {
509        char *end;
510        errSpace[count] = 0;
511        errno = strtol(errSpace, &end, 10);
512        Tcl_AppendResult(interp, end, Tcl_PosixError(interp), NULL);
513        goto error;
514    }
515
516    TclpCloseFile(errPipeIn);
517    *pidPtr = (Tcl_Pid) INT2PTR(pid);
518    return TCL_OK;
519
520  error:
521    if (pid != -1) {
522        /*
523         * Reap the child process now if an error occurred during its startup.
524         * We don't call this with WNOHANG because that can lead to defunct
525         * processes on an MP system. We shouldn't have to worry about hanging
526         * here, since this is the error case. [Bug: 6148]
527         */
528
529        Tcl_WaitPid((Tcl_Pid) INT2PTR(pid), &status, 0);
530    }
531
532    if (errPipeIn) {
533        TclpCloseFile(errPipeIn);
534    }
535    if (errPipeOut) {
536        TclpCloseFile(errPipeOut);
537    }
538    return TCL_ERROR;
539}
540
541/*
542 *----------------------------------------------------------------------
543 *
544 * RestoreSignals --
545 *
546 *      This function is invoked in a forked child process just before
547 *      exec-ing a new program to restore all signals to their default
548 *      settings.
549 *
550 * Results:
551 *      None.
552 *
553 * Side effects:
554 *      Signal settings get changed.
555 *
556 *----------------------------------------------------------------------
557 */
558
559static void
560RestoreSignals(void)
561{
562#ifdef SIGABRT
563    signal(SIGABRT, SIG_DFL);
564#endif
565#ifdef SIGALRM
566    signal(SIGALRM, SIG_DFL);
567#endif
568#ifdef SIGFPE
569    signal(SIGFPE, SIG_DFL);
570#endif
571#ifdef SIGHUP
572    signal(SIGHUP, SIG_DFL);
573#endif
574#ifdef SIGILL
575    signal(SIGILL, SIG_DFL);
576#endif
577#ifdef SIGINT
578    signal(SIGINT, SIG_DFL);
579#endif
580#ifdef SIGPIPE
581    signal(SIGPIPE, SIG_DFL);
582#endif
583#ifdef SIGQUIT
584    signal(SIGQUIT, SIG_DFL);
585#endif
586#ifdef SIGSEGV
587    signal(SIGSEGV, SIG_DFL);
588#endif
589#ifdef SIGTERM
590    signal(SIGTERM, SIG_DFL);
591#endif
592#ifdef SIGUSR1
593    signal(SIGUSR1, SIG_DFL);
594#endif
595#ifdef SIGUSR2
596    signal(SIGUSR2, SIG_DFL);
597#endif
598#ifdef SIGCHLD
599    signal(SIGCHLD, SIG_DFL);
600#endif
601#ifdef SIGCONT
602    signal(SIGCONT, SIG_DFL);
603#endif
604#ifdef SIGTSTP
605    signal(SIGTSTP, SIG_DFL);
606#endif
607#ifdef SIGTTIN
608    signal(SIGTTIN, SIG_DFL);
609#endif
610#ifdef SIGTTOU
611    signal(SIGTTOU, SIG_DFL);
612#endif
613}
614
615/*
616 *----------------------------------------------------------------------
617 *
618 * SetupStdFile --
619 *
620 *      Set up stdio file handles for the child process, using the current
621 *      standard channels if no other files are specified. If no standard
622 *      channel is defined, or if no file is associated with the channel, then
623 *      the corresponding standard fd is closed.
624 *
625 * Results:
626 *      Returns 1 on success, or 0 on failure.
627 *
628 * Side effects:
629 *      Replaces stdio fds.
630 *
631 *----------------------------------------------------------------------
632 */
633
634static int
635SetupStdFile(
636    TclFile file,               /* File to dup, or NULL. */
637    int type)                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
638{
639    Tcl_Channel channel;
640    int fd;
641    int targetFd = 0;           /* Initializations here needed only to */
642    int direction = 0;          /* prevent warnings about using uninitialized
643                                 * variables. */
644
645    switch (type) {
646    case TCL_STDIN:
647        targetFd = 0;
648        direction = TCL_READABLE;
649        break;
650    case TCL_STDOUT:
651        targetFd = 1;
652        direction = TCL_WRITABLE;
653        break;
654    case TCL_STDERR:
655        targetFd = 2;
656        direction = TCL_WRITABLE;
657        break;
658    }
659
660    if (!file) {
661        channel = Tcl_GetStdChannel(type);
662        if (channel) {
663            file = TclpMakeFile(channel, direction);
664        }
665    }
666    if (file) {
667        fd = GetFd(file);
668        if (fd != targetFd) {
669            if (dup2(fd, targetFd) == -1) {
670                return 0;
671            }
672
673            /*
674             * Must clear the close-on-exec flag for the target FD, since some
675             * systems (e.g. Ultrix) do not clear the CLOEXEC flag on the
676             * target FD.
677             */
678
679            fcntl(targetFd, F_SETFD, 0);
680        } else {
681            /*
682             * Since we aren't dup'ing the file, we need to explicitly clear
683             * the close-on-exec flag.
684             */
685
686            fcntl(fd, F_SETFD, 0);
687        }
688    } else {
689        close(targetFd);
690    }
691    return 1;
692}
693
694/*
695 *----------------------------------------------------------------------
696 *
697 * TclpCreateCommandChannel --
698 *
699 *      This function is called by the generic IO level to perform the
700 *      platform specific channel initialization for a command channel.
701 *
702 * Results:
703 *      Returns a new channel or NULL on failure.
704 *
705 * Side effects:
706 *      Allocates a new channel.
707 *
708 *----------------------------------------------------------------------
709 */
710
711Tcl_Channel
712TclpCreateCommandChannel(
713    TclFile readFile,           /* If non-null, gives the file for reading. */
714    TclFile writeFile,          /* If non-null, gives the file for writing. */
715    TclFile errorFile,          /* If non-null, gives the file where errors
716                                 * can be read. */
717    int numPids,                /* The number of pids in the pid array. */
718    Tcl_Pid *pidPtr)            /* An array of process identifiers. Allocated
719                                 * by the caller, freed when the channel is
720                                 * closed or the processes are detached (in a
721                                 * background exec). */
722{
723    char channelName[16 + TCL_INTEGER_SPACE];
724    int channelId;
725    PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
726    int mode;
727
728    statePtr->inFile = readFile;
729    statePtr->outFile = writeFile;
730    statePtr->errorFile = errorFile;
731    statePtr->numPids = numPids;
732    statePtr->pidPtr = pidPtr;
733    statePtr->isNonBlocking = 0;
734
735    mode = 0;
736    if (readFile) {
737        mode |= TCL_READABLE;
738    }
739    if (writeFile) {
740        mode |= TCL_WRITABLE;
741    }
742
743    /*
744     * Use one of the fds associated with the channel as the channel id.
745     */
746
747    if (readFile) {
748        channelId = GetFd(readFile);
749    } else if (writeFile) {
750        channelId = GetFd(writeFile);
751    } else if (errorFile) {
752        channelId = GetFd(errorFile);
753    } else {
754        channelId = 0;
755    }
756
757    /*
758     * For backward compatibility with previous versions of Tcl, we use
759     * "file%d" as the base name for pipes even though it would be more
760     * natural to use "pipe%d".
761     */
762
763    sprintf(channelName, "file%d", channelId);
764    statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
765            (ClientData) statePtr, mode);
766    return statePtr->channel;
767}
768
769/*
770 *----------------------------------------------------------------------
771 *
772 * TclGetAndDetachPids --
773 *
774 *      This function is invoked in the generic implementation of a
775 *      background "exec" (an exec when invoked with a terminating "&") to
776 *      store a list of the PIDs for processes in a command pipeline in the
777 *      interp's result and to detach the processes.
778 *
779 * Results:
780 *      None.
781 *
782 * Side effects:
783 *      Modifies the interp's result. Detaches processes.
784 *
785 *----------------------------------------------------------------------
786 */
787
788void
789TclGetAndDetachPids(
790    Tcl_Interp *interp,         /* Interpreter to append the PIDs to. */
791    Tcl_Channel chan)           /* Handle for the pipeline. */
792{
793    PipeState *pipePtr;
794    const Tcl_ChannelType *chanTypePtr;
795    int i;
796    char buf[TCL_INTEGER_SPACE];
797
798    /*
799     * Punt if the channel is not a command channel.
800     */
801
802    chanTypePtr = Tcl_GetChannelType(chan);
803    if (chanTypePtr != &pipeChannelType) {
804        return;
805    }
806
807    pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
808    for (i = 0; i < pipePtr->numPids; i++) {
809        TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i]));
810        Tcl_AppendElement(interp, buf);
811        Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
812    }
813    if (pipePtr->numPids > 0) {
814        ckfree((char *) pipePtr->pidPtr);
815        pipePtr->numPids = 0;
816    }
817}
818
819/*
820 *----------------------------------------------------------------------
821 *
822 * PipeBlockModeProc --
823 *
824 *      Helper function to set blocking and nonblocking modes on a pipe based
825 *      channel. Invoked by generic IO level code.
826 *
827 * Results:
828 *      0 if successful, errno when failed.
829 *
830 * Side effects:
831 *      Sets the device into blocking or non-blocking mode.
832 *
833 *----------------------------------------------------------------------
834 */
835
836        /* ARGSUSED */
837static int
838PipeBlockModeProc(
839    ClientData instanceData,    /* Pipe state. */
840    int mode)                   /* The mode to set. Can be one of
841                                 * TCL_MODE_BLOCKING or
842                                 * TCL_MODE_NONBLOCKING. */
843{
844    PipeState *psPtr = instanceData;
845
846    if (psPtr->inFile) {
847        if (TclUnixSetBlockingMode(GetFd(psPtr->inFile), mode) < 0) {
848            return errno;
849        }
850    }
851    if (psPtr->outFile) {
852        if (TclUnixSetBlockingMode(GetFd(psPtr->outFile), mode) < 0) {
853            return errno;
854        }
855    }
856
857    psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING);
858
859    return 0;
860}
861
862/*
863 *----------------------------------------------------------------------
864 *
865 * PipeCloseProc --
866 *
867 *      This function is invoked by the generic IO level to perform
868 *      channel-type-specific cleanup when a command pipeline channel is
869 *      closed.
870 *
871 * Results:
872 *      0 on success, errno otherwise.
873 *
874 * Side effects:
875 *      Closes the command pipeline channel.
876 *
877 *----------------------------------------------------------------------
878 */
879
880        /* ARGSUSED */
881static int
882PipeCloseProc(
883    ClientData instanceData,    /* The pipe to close. */
884    Tcl_Interp *interp)         /* For error reporting. */
885{
886    PipeState *pipePtr;
887    Tcl_Channel errChan;
888    int errorCode, result;
889
890    errorCode = 0;
891    result = 0;
892    pipePtr = (PipeState *) instanceData;
893    if (pipePtr->inFile) {
894        if (TclpCloseFile(pipePtr->inFile) < 0) {
895            errorCode = errno;
896        }
897    }
898    if (pipePtr->outFile) {
899        if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) {
900            errorCode = errno;
901        }
902    }
903
904    if (pipePtr->isNonBlocking || TclInExit()) {
905        /*
906         * If the channel is non-blocking or Tcl is being cleaned up, just
907         * detach the children PIDs, reap them (important if we are in a
908         * dynamic load module), and discard the errorFile.
909         */
910
911        Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
912        Tcl_ReapDetachedProcs();
913
914        if (pipePtr->errorFile) {
915            TclpCloseFile(pipePtr->errorFile);
916        }
917    } else {
918        /*
919         * Wrap the error file into a channel and give it to the cleanup
920         * routine.
921         */
922
923        if (pipePtr->errorFile) {
924            errChan = Tcl_MakeFileChannel(
925                (ClientData) INT2PTR(GetFd(pipePtr->errorFile)), TCL_READABLE);
926        } else {
927            errChan = NULL;
928        }
929        result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
930                errChan);
931    }
932
933    if (pipePtr->numPids != 0) {
934        ckfree((char *) pipePtr->pidPtr);
935    }
936    ckfree((char *) pipePtr);
937    if (errorCode == 0) {
938        return result;
939    }
940    return errorCode;
941}
942
943/*
944 *----------------------------------------------------------------------
945 *
946 * PipeInputProc --
947 *
948 *      This function is invoked from the generic IO level to read input from
949 *      a command pipeline based channel.
950 *
951 * Results:
952 *      The number of bytes read is returned or -1 on error. An output
953 *      argument contains a POSIX error code if an error occurs, or zero.
954 *
955 * Side effects:
956 *      Reads input from the input device of the channel.
957 *
958 *----------------------------------------------------------------------
959 */
960
961static int
962PipeInputProc(
963    ClientData instanceData,    /* Pipe state. */
964    char *buf,                  /* Where to store data read. */
965    int toRead,                 /* How much space is available in the
966                                 * buffer? */
967    int *errorCodePtr)          /* Where to store error code. */
968{
969    PipeState *psPtr = (PipeState *) instanceData;
970    int bytesRead;              /* How many bytes were actually read from the
971                                 * input device? */
972
973    *errorCodePtr = 0;
974
975    /*
976     * Assume there is always enough input available. This will block
977     * appropriately, and read will unblock as soon as a short read is
978     * possible, if the channel is in blocking mode. If the channel is
979     * nonblocking, the read will never block. Some OSes can throw an
980     * interrupt error, for which we should immediately retry. [Bug #415131]
981     */
982
983    do {
984        bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead);
985    } while ((bytesRead < 0) && (errno == EINTR));
986
987    if (bytesRead < 0) {
988        *errorCodePtr = errno;
989        return -1;
990    } else {
991        return bytesRead;
992    }
993}
994
995/*
996 *----------------------------------------------------------------------
997 *
998 * PipeOutputProc--
999 *
1000 *      This function is invoked from the generic IO level to write output to
1001 *      a command pipeline based channel.
1002 *
1003 * Results:
1004 *      The number of bytes written is returned or -1 on error. An output
1005 *      argument contains a POSIX error code if an error occurred, or zero.
1006 *
1007 * Side effects:
1008 *      Writes output on the output device of the channel.
1009 *
1010 *----------------------------------------------------------------------
1011 */
1012
1013static int
1014PipeOutputProc(
1015    ClientData instanceData,    /* Pipe state. */
1016    const char *buf,            /* The data buffer. */
1017    int toWrite,                /* How many bytes to write? */
1018    int *errorCodePtr)          /* Where to store error code. */
1019{
1020    PipeState *psPtr = (PipeState *) instanceData;
1021    int written;
1022
1023    *errorCodePtr = 0;
1024
1025    /*
1026     * Some OSes can throw an interrupt error, for which we should immediately
1027     * retry. [Bug #415131]
1028     */
1029
1030    do {
1031        written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite);
1032    } while ((written < 0) && (errno == EINTR));
1033
1034    if (written < 0) {
1035        *errorCodePtr = errno;
1036        return -1;
1037    } else {
1038        return written;
1039    }
1040}
1041
1042/*
1043 *----------------------------------------------------------------------
1044 *
1045 * PipeWatchProc --
1046 *
1047 *      Initialize the notifier to watch the fds from this channel.
1048 *
1049 * Results:
1050 *      None.
1051 *
1052 * Side effects:
1053 *      Sets up the notifier so that a future event on the channel will be
1054 *      seen by Tcl.
1055 *
1056 *----------------------------------------------------------------------
1057 */
1058
1059static void
1060PipeWatchProc(
1061    ClientData instanceData,    /* The pipe state. */
1062    int mask)                   /* Events of interest; an OR-ed combination of
1063                                 * TCL_READABLE, TCL_WRITABLE and
1064                                 * TCL_EXCEPTION. */
1065{
1066    PipeState *psPtr = (PipeState *) instanceData;
1067    int newmask;
1068
1069    if (psPtr->inFile) {
1070        newmask = mask & (TCL_READABLE | TCL_EXCEPTION);
1071        if (newmask) {
1072            Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask,
1073                    (Tcl_FileProc *) Tcl_NotifyChannel,
1074                    (ClientData) psPtr->channel);
1075        } else {
1076            Tcl_DeleteFileHandler(GetFd(psPtr->inFile));
1077        }
1078    }
1079    if (psPtr->outFile) {
1080        newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);
1081        if (newmask) {
1082            Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask,
1083                    (Tcl_FileProc *) Tcl_NotifyChannel,
1084                    (ClientData) psPtr->channel);
1085        } else {
1086            Tcl_DeleteFileHandler(GetFd(psPtr->outFile));
1087        }
1088    }
1089}
1090
1091/*
1092 *----------------------------------------------------------------------
1093 *
1094 * PipeGetHandleProc --
1095 *
1096 *      Called from Tcl_GetChannelHandle to retrieve OS handles from inside a
1097 *      command pipeline based channel.
1098 *
1099 * Results:
1100 *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
1101 *      handle for the specified direction.
1102 *
1103 * Side effects:
1104 *      None.
1105 *
1106 *----------------------------------------------------------------------
1107 */
1108
1109static int
1110PipeGetHandleProc(
1111    ClientData instanceData,    /* The pipe state. */
1112    int direction,              /* TCL_READABLE or TCL_WRITABLE */
1113    ClientData *handlePtr)      /* Where to store the handle. */
1114{
1115    PipeState *psPtr = (PipeState *) instanceData;
1116
1117    if (direction == TCL_READABLE && psPtr->inFile) {
1118        *handlePtr = (ClientData) INT2PTR(GetFd(psPtr->inFile));
1119        return TCL_OK;
1120    }
1121    if (direction == TCL_WRITABLE && psPtr->outFile) {
1122        *handlePtr = (ClientData) INT2PTR(GetFd(psPtr->outFile));
1123        return TCL_OK;
1124    }
1125    return TCL_ERROR;
1126}
1127
1128/*
1129 *----------------------------------------------------------------------
1130 *
1131 * Tcl_WaitPid --
1132 *
1133 *      Implements the waitpid system call on Unix systems.
1134 *
1135 * Results:
1136 *      Result of calling waitpid.
1137 *
1138 * Side effects:
1139 *      Waits for a process to terminate.
1140 *
1141 *----------------------------------------------------------------------
1142 */
1143
1144Tcl_Pid
1145Tcl_WaitPid(
1146    Tcl_Pid pid,
1147    int *statPtr,
1148    int options)
1149{
1150    int result;
1151    pid_t real_pid;
1152
1153    real_pid = (pid_t) PTR2INT(pid);
1154    while (1) {
1155        result = (int) waitpid(real_pid, statPtr, options);
1156        if ((result != -1) || (errno != EINTR)) {
1157            return (Tcl_Pid) INT2PTR(result);
1158        }
1159    }
1160}
1161
1162/*
1163 *----------------------------------------------------------------------
1164 *
1165 * Tcl_PidObjCmd --
1166 *
1167 *      This function is invoked to process the "pid" Tcl command. See the
1168 *      user documentation for details on what it does.
1169 *
1170 * Results:
1171 *      A standard Tcl result.
1172 *
1173 * Side effects:
1174 *      See the user documentation.
1175 *
1176 *----------------------------------------------------------------------
1177 */
1178
1179        /* ARGSUSED */
1180int
1181Tcl_PidObjCmd(
1182    ClientData dummy,           /* Not used. */
1183    Tcl_Interp *interp,         /* Current interpreter. */
1184    int objc,                   /* Number of arguments. */
1185    Tcl_Obj *const *objv)       /* Argument strings. */
1186{
1187    if (objc > 2) {
1188        Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
1189        return TCL_ERROR;
1190    }
1191    if (objc == 1) {
1192        Tcl_SetObjResult(interp, Tcl_NewLongObj((long) getpid()));
1193    } else {
1194        Tcl_Channel chan;
1195        const Tcl_ChannelType *chanTypePtr;
1196        PipeState *pipePtr;
1197        int i;
1198        Tcl_Obj *resultPtr, *longObjPtr;
1199
1200        chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
1201        if (chan == (Tcl_Channel) NULL) {
1202            return TCL_ERROR;
1203        }
1204        chanTypePtr = Tcl_GetChannelType(chan);
1205        if (chanTypePtr != &pipeChannelType) {
1206            return TCL_OK;
1207        }
1208        pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
1209        resultPtr = Tcl_NewObj();
1210        for (i = 0; i < pipePtr->numPids; i++) {
1211            longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i]));
1212            Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr);
1213        }
1214        Tcl_SetObjResult(interp, resultPtr);
1215    }
1216    return TCL_OK;
1217}
1218
1219/*
1220 *----------------------------------------------------------------------
1221 *
1222 * TclpFinalizePipes --
1223 *
1224 *      Cleans up the pipe subsystem from Tcl_FinalizeThread
1225 *
1226 * Results:
1227 *      None.
1228 *
1229 * Notes:
1230 *      This function carries out no operation on Unix.
1231 *
1232 *----------------------------------------------------------------------
1233 */
1234
1235void
1236TclpFinalizePipes(void)
1237{
1238}
1239
1240/*
1241 * Local Variables:
1242 * mode: c
1243 * c-basic-offset: 4
1244 * fill-column: 78
1245 * End:
1246 */
Note: See TracBrowser for help on using the repository browser.