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.2.1 2010/01/29 09:38:47 nijtmans 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
440    if (!inputFile) {
441	Tcl_GetStdChannel(TCL_STDIN);
442    }
443    if (!outputFile) {
444        Tcl_GetStdChannel(TCL_STDOUT);
445    }
446    if (!errorFile) {
447        Tcl_GetStdChannel(TCL_STDERR);
448    }
449#endif
450
451    pid = fork();
452    if (pid == 0) {
453	size_t len;
454	int joinThisError = errorFile && (errorFile == outputFile);
455
456	fd = GetFd(errPipeOut);
457
458	/*
459	 * Set up stdio file handles for the child process.
460	 */
461
462	if (!SetupStdFile(inputFile, TCL_STDIN)
463		|| !SetupStdFile(outputFile, TCL_STDOUT)
464		|| (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))
465		|| (joinThisError &&
466			((dup2(1,2) == -1) || (fcntl(2, F_SETFD, 0) != 0)))) {
467	    sprintf(errSpace,
468		    "%dforked process couldn't set up input/output: ", errno);
469	    len = strlen(errSpace);
470	    if (len != (size_t) write(fd, errSpace, len)) {
471		    Tcl_Panic("TclpCreateProcess: unable to write to errPipeOut");
472	    }
473	    _exit(1);
474	}
475
476	/*
477	 * Close the input side of the error pipe.
478	 */
479
480	RestoreSignals();
481	execvp(newArgv[0], newArgv);			/* INTL: Native. */
482	sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno, argv[0]);
483	len = strlen(errSpace);
484    if (len != (size_t) write(fd, errSpace, len)) {
485	    Tcl_Panic("TclpCreateProcess: unable to write to errPipeOut");
486    }
487	_exit(1);
488    }
489
490    /*
491     * Free the mem we used for the fork
492     */
493
494    for (i = 0; i < argc; i++) {
495	Tcl_DStringFree(&dsArray[i]);
496    }
497    TclStackFree(interp, newArgv);
498    TclStackFree(interp, dsArray);
499
500    if (pid == -1) {
501	Tcl_AppendResult(interp, "couldn't fork child process: ",
502		Tcl_PosixError(interp), NULL);
503	goto error;
504    }
505
506    /*
507     * Read back from the error pipe to see if the child started up OK. The
508     * info in the pipe (if any) consists of a decimal errno value followed by
509     * an error message.
510     */
511
512    TclpCloseFile(errPipeOut);
513    errPipeOut = NULL;
514
515    fd = GetFd(errPipeIn);
516    count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
517    if (count > 0) {
518	char *end;
519	errSpace[count] = 0;
520	errno = strtol(errSpace, &end, 10);
521	Tcl_AppendResult(interp, end, Tcl_PosixError(interp), NULL);
522	goto error;
523    }
524
525    TclpCloseFile(errPipeIn);
526    *pidPtr = (Tcl_Pid) INT2PTR(pid);
527    return TCL_OK;
528
529  error:
530    if (pid != -1) {
531	/*
532	 * Reap the child process now if an error occurred during its startup.
533	 * We don't call this with WNOHANG because that can lead to defunct
534	 * processes on an MP system. We shouldn't have to worry about hanging
535	 * here, since this is the error case. [Bug: 6148]
536	 */
537
538	Tcl_WaitPid((Tcl_Pid) INT2PTR(pid), &status, 0);
539    }
540
541    if (errPipeIn) {
542	TclpCloseFile(errPipeIn);
543    }
544    if (errPipeOut) {
545	TclpCloseFile(errPipeOut);
546    }
547    return TCL_ERROR;
548}
549
550/*
551 *----------------------------------------------------------------------
552 *
553 * RestoreSignals --
554 *
555 *	This function is invoked in a forked child process just before
556 *	exec-ing a new program to restore all signals to their default
557 *	settings.
558 *
559 * Results:
560 *	None.
561 *
562 * Side effects:
563 *	Signal settings get changed.
564 *
565 *----------------------------------------------------------------------
566 */
567
568static void
569RestoreSignals(void)
570{
571#ifdef SIGABRT
572    signal(SIGABRT, SIG_DFL);
573#endif
574#ifdef SIGALRM
575    signal(SIGALRM, SIG_DFL);
576#endif
577#ifdef SIGFPE
578    signal(SIGFPE, SIG_DFL);
579#endif
580#ifdef SIGHUP
581    signal(SIGHUP, SIG_DFL);
582#endif
583#ifdef SIGILL
584    signal(SIGILL, SIG_DFL);
585#endif
586#ifdef SIGINT
587    signal(SIGINT, SIG_DFL);
588#endif
589#ifdef SIGPIPE
590    signal(SIGPIPE, SIG_DFL);
591#endif
592#ifdef SIGQUIT
593    signal(SIGQUIT, SIG_DFL);
594#endif
595#ifdef SIGSEGV
596    signal(SIGSEGV, SIG_DFL);
597#endif
598#ifdef SIGTERM
599    signal(SIGTERM, SIG_DFL);
600#endif
601#ifdef SIGUSR1
602    signal(SIGUSR1, SIG_DFL);
603#endif
604#ifdef SIGUSR2
605    signal(SIGUSR2, SIG_DFL);
606#endif
607#ifdef SIGCHLD
608    signal(SIGCHLD, SIG_DFL);
609#endif
610#ifdef SIGCONT
611    signal(SIGCONT, SIG_DFL);
612#endif
613#ifdef SIGTSTP
614    signal(SIGTSTP, SIG_DFL);
615#endif
616#ifdef SIGTTIN
617    signal(SIGTTIN, SIG_DFL);
618#endif
619#ifdef SIGTTOU
620    signal(SIGTTOU, SIG_DFL);
621#endif
622}
623
624/*
625 *----------------------------------------------------------------------
626 *
627 * SetupStdFile --
628 *
629 *	Set up stdio file handles for the child process, using the current
630 *	standard channels if no other files are specified. If no standard
631 *	channel is defined, or if no file is associated with the channel, then
632 *	the corresponding standard fd is closed.
633 *
634 * Results:
635 *	Returns 1 on success, or 0 on failure.
636 *
637 * Side effects:
638 *	Replaces stdio fds.
639 *
640 *----------------------------------------------------------------------
641 */
642
643static int
644SetupStdFile(
645    TclFile file,		/* File to dup, or NULL. */
646    int type)			/* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
647{
648    Tcl_Channel channel;
649    int fd;
650    int targetFd = 0;		/* Initializations here needed only to */
651    int direction = 0;		/* prevent warnings about using uninitialized
652				 * variables. */
653
654    switch (type) {
655    case TCL_STDIN:
656	targetFd = 0;
657	direction = TCL_READABLE;
658	break;
659    case TCL_STDOUT:
660	targetFd = 1;
661	direction = TCL_WRITABLE;
662	break;
663    case TCL_STDERR:
664	targetFd = 2;
665	direction = TCL_WRITABLE;
666	break;
667    }
668
669    if (!file) {
670	channel = Tcl_GetStdChannel(type);
671	if (channel) {
672	    file = TclpMakeFile(channel, direction);
673	}
674    }
675    if (file) {
676	fd = GetFd(file);
677	if (fd != targetFd) {
678	    if (dup2(fd, targetFd) == -1) {
679		return 0;
680	    }
681
682	    /*
683	     * Must clear the close-on-exec flag for the target FD, since some
684	     * systems (e.g. Ultrix) do not clear the CLOEXEC flag on the
685	     * target FD.
686	     */
687
688	    fcntl(targetFd, F_SETFD, 0);
689	} else {
690	    /*
691	     * Since we aren't dup'ing the file, we need to explicitly clear
692	     * the close-on-exec flag.
693	     */
694
695	    fcntl(fd, F_SETFD, 0);
696	}
697    } else {
698	close(targetFd);
699    }
700    return 1;
701}
702
703/*
704 *----------------------------------------------------------------------
705 *
706 * TclpCreateCommandChannel --
707 *
708 *	This function is called by the generic IO level to perform the
709 *	platform specific channel initialization for a command channel.
710 *
711 * Results:
712 *	Returns a new channel or NULL on failure.
713 *
714 * Side effects:
715 *	Allocates a new channel.
716 *
717 *----------------------------------------------------------------------
718 */
719
720Tcl_Channel
721TclpCreateCommandChannel(
722    TclFile readFile,		/* If non-null, gives the file for reading. */
723    TclFile writeFile,		/* If non-null, gives the file for writing. */
724    TclFile errorFile,		/* If non-null, gives the file where errors
725				 * can be read. */
726    int numPids,		/* The number of pids in the pid array. */
727    Tcl_Pid *pidPtr)		/* An array of process identifiers. Allocated
728				 * by the caller, freed when the channel is
729				 * closed or the processes are detached (in a
730				 * background exec). */
731{
732    char channelName[16 + TCL_INTEGER_SPACE];
733    int channelId;
734    PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
735    int mode;
736
737    statePtr->inFile = readFile;
738    statePtr->outFile = writeFile;
739    statePtr->errorFile = errorFile;
740    statePtr->numPids = numPids;
741    statePtr->pidPtr = pidPtr;
742    statePtr->isNonBlocking = 0;
743
744    mode = 0;
745    if (readFile) {
746	mode |= TCL_READABLE;
747    }
748    if (writeFile) {
749	mode |= TCL_WRITABLE;
750    }
751
752    /*
753     * Use one of the fds associated with the channel as the channel id.
754     */
755
756    if (readFile) {
757	channelId = GetFd(readFile);
758    } else if (writeFile) {
759	channelId = GetFd(writeFile);
760    } else if (errorFile) {
761	channelId = GetFd(errorFile);
762    } else {
763	channelId = 0;
764    }
765
766    /*
767     * For backward compatibility with previous versions of Tcl, we use
768     * "file%d" as the base name for pipes even though it would be more
769     * natural to use "pipe%d".
770     */
771
772    sprintf(channelName, "file%d", channelId);
773    statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
774	    (ClientData) statePtr, mode);
775    return statePtr->channel;
776}
777
778/*
779 *----------------------------------------------------------------------
780 *
781 * TclGetAndDetachPids --
782 *
783 *	This function is invoked in the generic implementation of a
784 *	background "exec" (an exec when invoked with a terminating "&") to
785 *	store a list of the PIDs for processes in a command pipeline in the
786 *	interp's result and to detach the processes.
787 *
788 * Results:
789 *	None.
790 *
791 * Side effects:
792 *	Modifies the interp's result. Detaches processes.
793 *
794 *----------------------------------------------------------------------
795 */
796
797void
798TclGetAndDetachPids(
799    Tcl_Interp *interp,		/* Interpreter to append the PIDs to. */
800    Tcl_Channel chan)		/* Handle for the pipeline. */
801{
802    PipeState *pipePtr;
803    const Tcl_ChannelType *chanTypePtr;
804    int i;
805    char buf[TCL_INTEGER_SPACE];
806
807    /*
808     * Punt if the channel is not a command channel.
809     */
810
811    chanTypePtr = Tcl_GetChannelType(chan);
812    if (chanTypePtr != &pipeChannelType) {
813	return;
814    }
815
816    pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
817    for (i = 0; i < pipePtr->numPids; i++) {
818	TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i]));
819	Tcl_AppendElement(interp, buf);
820	Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
821    }
822    if (pipePtr->numPids > 0) {
823	ckfree((char *) pipePtr->pidPtr);
824	pipePtr->numPids = 0;
825    }
826}
827
828/*
829 *----------------------------------------------------------------------
830 *
831 * PipeBlockModeProc --
832 *
833 *	Helper function to set blocking and nonblocking modes on a pipe based
834 *	channel. Invoked by generic IO level code.
835 *
836 * Results:
837 *	0 if successful, errno when failed.
838 *
839 * Side effects:
840 *	Sets the device into blocking or non-blocking mode.
841 *
842 *----------------------------------------------------------------------
843 */
844
845	/* ARGSUSED */
846static int
847PipeBlockModeProc(
848    ClientData instanceData,	/* Pipe state. */
849    int mode)			/* The mode to set. Can be one of
850				 * TCL_MODE_BLOCKING or
851				 * TCL_MODE_NONBLOCKING. */
852{
853    PipeState *psPtr = instanceData;
854
855    if (psPtr->inFile) {
856	if (TclUnixSetBlockingMode(GetFd(psPtr->inFile), mode) < 0) {
857	    return errno;
858	}
859    }
860    if (psPtr->outFile) {
861	if (TclUnixSetBlockingMode(GetFd(psPtr->outFile), mode) < 0) {
862	    return errno;
863	}
864    }
865
866    psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING);
867
868    return 0;
869}
870
871/*
872 *----------------------------------------------------------------------
873 *
874 * PipeCloseProc --
875 *
876 *	This function is invoked by the generic IO level to perform
877 *	channel-type-specific cleanup when a command pipeline channel is
878 *	closed.
879 *
880 * Results:
881 *	0 on success, errno otherwise.
882 *
883 * Side effects:
884 *	Closes the command pipeline channel.
885 *
886 *----------------------------------------------------------------------
887 */
888
889	/* ARGSUSED */
890static int
891PipeCloseProc(
892    ClientData instanceData,	/* The pipe to close. */
893    Tcl_Interp *interp)		/* For error reporting. */
894{
895    PipeState *pipePtr;
896    Tcl_Channel errChan;
897    int errorCode, result;
898
899    errorCode = 0;
900    result = 0;
901    pipePtr = (PipeState *) instanceData;
902    if (pipePtr->inFile) {
903	if (TclpCloseFile(pipePtr->inFile) < 0) {
904	    errorCode = errno;
905	}
906    }
907    if (pipePtr->outFile) {
908	if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) {
909	    errorCode = errno;
910	}
911    }
912
913    if (pipePtr->isNonBlocking || TclInExit()) {
914	/*
915	 * If the channel is non-blocking or Tcl is being cleaned up, just
916	 * detach the children PIDs, reap them (important if we are in a
917	 * dynamic load module), and discard the errorFile.
918	 */
919
920	Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
921	Tcl_ReapDetachedProcs();
922
923	if (pipePtr->errorFile) {
924	    TclpCloseFile(pipePtr->errorFile);
925	}
926    } else {
927	/*
928	 * Wrap the error file into a channel and give it to the cleanup
929	 * routine.
930	 */
931
932	if (pipePtr->errorFile) {
933	    errChan = Tcl_MakeFileChannel(
934		(ClientData) INT2PTR(GetFd(pipePtr->errorFile)), TCL_READABLE);
935	} else {
936	    errChan = NULL;
937	}
938	result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
939		errChan);
940    }
941
942    if (pipePtr->numPids != 0) {
943	ckfree((char *) pipePtr->pidPtr);
944    }
945    ckfree((char *) pipePtr);
946    if (errorCode == 0) {
947	return result;
948    }
949    return errorCode;
950}
951
952/*
953 *----------------------------------------------------------------------
954 *
955 * PipeInputProc --
956 *
957 *	This function is invoked from the generic IO level to read input from
958 *	a command pipeline based channel.
959 *
960 * Results:
961 *	The number of bytes read is returned or -1 on error. An output
962 *	argument contains a POSIX error code if an error occurs, or zero.
963 *
964 * Side effects:
965 *	Reads input from the input device of the channel.
966 *
967 *----------------------------------------------------------------------
968 */
969
970static int
971PipeInputProc(
972    ClientData instanceData,	/* Pipe state. */
973    char *buf,			/* Where to store data read. */
974    int toRead,			/* How much space is available in the
975				 * buffer? */
976    int *errorCodePtr)		/* Where to store error code. */
977{
978    PipeState *psPtr = (PipeState *) instanceData;
979    int bytesRead;		/* How many bytes were actually read from the
980				 * input device? */
981
982    *errorCodePtr = 0;
983
984    /*
985     * Assume there is always enough input available. This will block
986     * appropriately, and read will unblock as soon as a short read is
987     * possible, if the channel is in blocking mode. If the channel is
988     * nonblocking, the read will never block. Some OSes can throw an
989     * interrupt error, for which we should immediately retry. [Bug #415131]
990     */
991
992    do {
993	bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead);
994    } while ((bytesRead < 0) && (errno == EINTR));
995
996    if (bytesRead < 0) {
997	*errorCodePtr = errno;
998	return -1;
999    } else {
1000	return bytesRead;
1001    }
1002}
1003
1004/*
1005 *----------------------------------------------------------------------
1006 *
1007 * PipeOutputProc--
1008 *
1009 *	This function is invoked from the generic IO level to write output to
1010 *	a command pipeline based channel.
1011 *
1012 * Results:
1013 *	The number of bytes written is returned or -1 on error. An output
1014 *	argument contains a POSIX error code if an error occurred, or zero.
1015 *
1016 * Side effects:
1017 *	Writes output on the output device of the channel.
1018 *
1019 *----------------------------------------------------------------------
1020 */
1021
1022static int
1023PipeOutputProc(
1024    ClientData instanceData,	/* Pipe state. */
1025    const char *buf,		/* The data buffer. */
1026    int toWrite,		/* How many bytes to write? */
1027    int *errorCodePtr)		/* Where to store error code. */
1028{
1029    PipeState *psPtr = (PipeState *) instanceData;
1030    int written;
1031
1032    *errorCodePtr = 0;
1033
1034    /*
1035     * Some OSes can throw an interrupt error, for which we should immediately
1036     * retry. [Bug #415131]
1037     */
1038
1039    do {
1040	written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite);
1041    } while ((written < 0) && (errno == EINTR));
1042
1043    if (written < 0) {
1044	*errorCodePtr = errno;
1045	return -1;
1046    } else {
1047	return written;
1048    }
1049}
1050
1051/*
1052 *----------------------------------------------------------------------
1053 *
1054 * PipeWatchProc --
1055 *
1056 *	Initialize the notifier to watch the fds from this channel.
1057 *
1058 * Results:
1059 *	None.
1060 *
1061 * Side effects:
1062 *	Sets up the notifier so that a future event on the channel will be
1063 *	seen by Tcl.
1064 *
1065 *----------------------------------------------------------------------
1066 */
1067
1068static void
1069PipeWatchProc(
1070    ClientData instanceData,	/* The pipe state. */
1071    int mask)			/* Events of interest; an OR-ed combination of
1072				 * TCL_READABLE, TCL_WRITABLE and
1073				 * TCL_EXCEPTION. */
1074{
1075    PipeState *psPtr = (PipeState *) instanceData;
1076    int newmask;
1077
1078    if (psPtr->inFile) {
1079	newmask = mask & (TCL_READABLE | TCL_EXCEPTION);
1080	if (newmask) {
1081	    Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask,
1082		    (Tcl_FileProc *) Tcl_NotifyChannel,
1083		    (ClientData) psPtr->channel);
1084	} else {
1085	    Tcl_DeleteFileHandler(GetFd(psPtr->inFile));
1086	}
1087    }
1088    if (psPtr->outFile) {
1089	newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);
1090	if (newmask) {
1091	    Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask,
1092		    (Tcl_FileProc *) Tcl_NotifyChannel,
1093		    (ClientData) psPtr->channel);
1094	} else {
1095	    Tcl_DeleteFileHandler(GetFd(psPtr->outFile));
1096	}
1097    }
1098}
1099
1100/*
1101 *----------------------------------------------------------------------
1102 *
1103 * PipeGetHandleProc --
1104 *
1105 *	Called from Tcl_GetChannelHandle to retrieve OS handles from inside a
1106 *	command pipeline based channel.
1107 *
1108 * Results:
1109 *	Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if there is no
1110 *	handle for the specified direction.
1111 *
1112 * Side effects:
1113 *	None.
1114 *
1115 *----------------------------------------------------------------------
1116 */
1117
1118static int
1119PipeGetHandleProc(
1120    ClientData instanceData,	/* The pipe state. */
1121    int direction,		/* TCL_READABLE or TCL_WRITABLE */
1122    ClientData *handlePtr)	/* Where to store the handle. */
1123{
1124    PipeState *psPtr = (PipeState *) instanceData;
1125
1126    if (direction == TCL_READABLE && psPtr->inFile) {
1127	*handlePtr = (ClientData) INT2PTR(GetFd(psPtr->inFile));
1128	return TCL_OK;
1129    }
1130    if (direction == TCL_WRITABLE && psPtr->outFile) {
1131	*handlePtr = (ClientData) INT2PTR(GetFd(psPtr->outFile));
1132	return TCL_OK;
1133    }
1134    return TCL_ERROR;
1135}
1136
1137/*
1138 *----------------------------------------------------------------------
1139 *
1140 * Tcl_WaitPid --
1141 *
1142 *	Implements the waitpid system call on Unix systems.
1143 *
1144 * Results:
1145 *	Result of calling waitpid.
1146 *
1147 * Side effects:
1148 *	Waits for a process to terminate.
1149 *
1150 *----------------------------------------------------------------------
1151 */
1152
1153Tcl_Pid
1154Tcl_WaitPid(
1155    Tcl_Pid pid,
1156    int *statPtr,
1157    int options)
1158{
1159    int result;
1160    pid_t real_pid = (pid_t) PTR2INT(pid);
1161
1162    while (1) {
1163	result = (int) waitpid(real_pid, statPtr, options);
1164	if ((result != -1) || (errno != EINTR)) {
1165	    return (Tcl_Pid) INT2PTR(result);
1166	}
1167    }
1168}
1169
1170/*
1171 *----------------------------------------------------------------------
1172 *
1173 * Tcl_PidObjCmd --
1174 *
1175 *	This function is invoked to process the "pid" Tcl command. See the
1176 *	user documentation for details on what it does.
1177 *
1178 * Results:
1179 *	A standard Tcl result.
1180 *
1181 * Side effects:
1182 *	See the user documentation.
1183 *
1184 *----------------------------------------------------------------------
1185 */
1186
1187	/* ARGSUSED */
1188int
1189Tcl_PidObjCmd(
1190    ClientData dummy,		/* Not used. */
1191    Tcl_Interp *interp,		/* Current interpreter. */
1192    int objc,			/* Number of arguments. */
1193    Tcl_Obj *const *objv)	/* Argument strings. */
1194{
1195    if (objc > 2) {
1196	Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
1197	return TCL_ERROR;
1198    }
1199
1200    if (objc == 1) {
1201	Tcl_SetObjResult(interp, Tcl_NewLongObj((long) getpid()));
1202    } else {
1203	/*
1204	 * Get the channel and make sure that it refers to a pipe.
1205	 */
1206	Tcl_Channel chan;
1207	const Tcl_ChannelType *chanTypePtr;
1208	PipeState *pipePtr;
1209	int i;
1210	Tcl_Obj *resultPtr, *longObjPtr;
1211
1212	chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
1213	if (chan == (Tcl_Channel) NULL) {
1214	    return TCL_ERROR;
1215	}
1216	chanTypePtr = Tcl_GetChannelType(chan);
1217	if (chanTypePtr != &pipeChannelType) {
1218	    return TCL_OK;
1219	}
1220
1221	/*
1222	 * Extract the process IDs from the pipe structure.
1223	 */
1224
1225	pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
1226	resultPtr = Tcl_NewObj();
1227	for (i = 0; i < pipePtr->numPids; i++) {
1228	    longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i]));
1229	    Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr);
1230	}
1231	Tcl_SetObjResult(interp, resultPtr);
1232    }
1233    return TCL_OK;
1234}
1235
1236/*
1237 *----------------------------------------------------------------------
1238 *
1239 * TclpFinalizePipes --
1240 *
1241 *	Cleans up the pipe subsystem from Tcl_FinalizeThread
1242 *
1243 * Results:
1244 *	None.
1245 *
1246 * Notes:
1247 *	This function carries out no operation on Unix.
1248 *
1249 *----------------------------------------------------------------------
1250 */
1251
1252void
1253TclpFinalizePipes(void)
1254{
1255}
1256
1257/*
1258 * Local Variables:
1259 * mode: c
1260 * c-basic-offset: 4
1261 * fill-column: 78
1262 * End:
1263 */
1264