OSDN Git Service

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