1/*
2 * tclPipe.c --
3 *
4 *	This file contains the generic portion of the command channel driver
5 *	as well as various utility routines used in managing subprocesses.
6 *
7 * Copyright (c) 1997 by Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution of
10 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tclPipe.c,v 1.19.4.1 2009/07/24 16:51:28 andreas_kupries Exp $
13 */
14
15#include "tclInt.h"
16
17/*
18 * A linked list of the following structures is used to keep track of child
19 * processes that have been detached but haven't exited yet, so we can make
20 * sure that they're properly "reaped" (officially waited for) and don't lie
21 * around as zombies cluttering the system.
22 */
23
24typedef struct Detached {
25    Tcl_Pid pid;		/* Id of process that's been detached but
26				 * isn't known to have exited. */
27    struct Detached *nextPtr;	/* Next in list of all detached processes. */
28} Detached;
29
30static Detached *detList = NULL;/* List of all detached proceses. */
31TCL_DECLARE_MUTEX(pipeMutex)	/* Guard access to detList. */
32
33/*
34 * Declarations for local functions defined in this file:
35 */
36
37static TclFile		FileForRedirect(Tcl_Interp *interp, CONST char *spec,
38			    int atOk, CONST char *arg, CONST char *nextArg,
39			    int flags, int *skipPtr, int *closePtr,
40			    int *releasePtr);
41
42/*
43 *----------------------------------------------------------------------
44 *
45 * FileForRedirect --
46 *
47 *	This function does much of the work of parsing redirection operators.
48 *	It handles "@" if specified and allowed, and a file name, and opens
49 *	the file if necessary.
50 *
51 * Results:
52 *	The return value is the descriptor number for the file. If an error
53 *	occurs then NULL is returned and an error message is left in the
54 *	interp's result. Several arguments are side-effected; see the argument
55 *	list below for details.
56 *
57 * Side effects:
58 *	None.
59 *
60 *----------------------------------------------------------------------
61 */
62
63static TclFile
64FileForRedirect(
65    Tcl_Interp *interp,		/* Intepreter to use for error reporting. */
66    CONST char *spec,		/* Points to character just after redirection
67				 * character. */
68    int atOK,			/* Non-zero means that '@' notation can be
69				 * used to specify a channel, zero means that
70				 * it isn't. */
71    CONST char *arg,		/* Pointer to entire argument containing spec:
72				 * used for error reporting. */
73    CONST char *nextArg,	/* Next argument in argc/argv array, if needed
74				 * for file name or channel name. May be
75				 * NULL. */
76    int flags,			/* Flags to use for opening file or to specify
77				 * mode for channel. */
78    int *skipPtr,		/* Filled with 1 if redirection target was in
79				 * spec, 2 if it was in nextArg. */
80    int *closePtr,		/* Filled with one if the caller should close
81				 * the file when done with it, zero
82				 * otherwise. */
83    int *releasePtr)
84{
85    int writing = (flags & O_WRONLY);
86    Tcl_Channel chan;
87    TclFile file;
88
89    *skipPtr = 1;
90    if ((atOK != 0) && (*spec == '@')) {
91	spec++;
92	if (*spec == '\0') {
93	    spec = nextArg;
94	    if (spec == NULL) {
95		goto badLastArg;
96	    }
97	    *skipPtr = 2;
98	}
99        chan = Tcl_GetChannel(interp, spec, NULL);
100        if (chan == (Tcl_Channel) NULL) {
101            return NULL;
102        }
103	file = TclpMakeFile(chan, writing ? TCL_WRITABLE : TCL_READABLE);
104        if (file == NULL) {
105	    Tcl_Obj* msg;
106	    Tcl_GetChannelError(chan, &msg);
107	    if (msg) {
108		Tcl_SetObjResult (interp, msg);
109	    } else {
110		Tcl_AppendResult(interp, "channel \"", Tcl_GetChannelName(chan),
111				 "\" wasn't opened for ",
112				 ((writing) ? "writing" : "reading"), NULL);
113	    }
114            return NULL;
115        }
116	*releasePtr = 1;
117	if (writing) {
118	    /*
119	     * Be sure to flush output to the file, so that anything written
120	     * by the child appears after stuff we've already written.
121	     */
122
123            Tcl_Flush(chan);
124	}
125    } else {
126	CONST char *name;
127	Tcl_DString nameString;
128
129	if (*spec == '\0') {
130	    spec = nextArg;
131	    if (spec == NULL) {
132		goto badLastArg;
133	    }
134	    *skipPtr = 2;
135	}
136	name = Tcl_TranslateFileName(interp, spec, &nameString);
137	if (name == NULL) {
138	    return NULL;
139	}
140	file = TclpOpenFile(name, flags);
141	Tcl_DStringFree(&nameString);
142	if (file == NULL) {
143	    Tcl_AppendResult(interp, "couldn't ",
144		    ((writing) ? "write" : "read"), " file \"", spec, "\": ",
145		    Tcl_PosixError(interp), NULL);
146	    return NULL;
147	}
148        *closePtr = 1;
149    }
150    return file;
151
152  badLastArg:
153    Tcl_AppendResult(interp, "can't specify \"", arg,
154	    "\" as last word in command", NULL);
155    return NULL;
156}
157
158/*
159 *----------------------------------------------------------------------
160 *
161 * Tcl_DetachPids --
162 *
163 *	This function is called to indicate that one or more child processes
164 *	have been placed in background and will never be waited for; they
165 *	should eventually be reaped by Tcl_ReapDetachedProcs.
166 *
167 * Results:
168 *	None.
169 *
170 * Side effects:
171 *	None.
172 *
173 *----------------------------------------------------------------------
174 */
175
176void
177Tcl_DetachPids(
178    int numPids,		/* Number of pids to detach: gives size of
179				 * array pointed to by pidPtr. */
180    Tcl_Pid *pidPtr)		/* Array of pids to detach. */
181{
182    register Detached *detPtr;
183    int i;
184
185    Tcl_MutexLock(&pipeMutex);
186    for (i = 0; i < numPids; i++) {
187	detPtr = (Detached *) ckalloc(sizeof(Detached));
188	detPtr->pid = pidPtr[i];
189	detPtr->nextPtr = detList;
190	detList = detPtr;
191    }
192    Tcl_MutexUnlock(&pipeMutex);
193
194}
195
196/*
197 *----------------------------------------------------------------------
198 *
199 * Tcl_ReapDetachedProcs --
200 *
201 *	This function checks to see if any detached processes have exited and,
202 *	if so, it "reaps" them by officially waiting on them. It should be
203 *	called "occasionally" to make sure that all detached processes are
204 *	eventually reaped.
205 *
206 * Results:
207 *	None.
208 *
209 * Side effects:
210 *	Processes are waited on, so that they can be reaped by the system.
211 *
212 *----------------------------------------------------------------------
213 */
214
215void
216Tcl_ReapDetachedProcs(void)
217{
218    register Detached *detPtr;
219    Detached *nextPtr, *prevPtr;
220    int status;
221    Tcl_Pid pid;
222
223    Tcl_MutexLock(&pipeMutex);
224    for (detPtr = detList, prevPtr = NULL; detPtr != NULL; ) {
225	pid = Tcl_WaitPid(detPtr->pid, &status, WNOHANG);
226	if ((pid == 0) || ((pid == (Tcl_Pid) -1) && (errno != ECHILD))) {
227	    prevPtr = detPtr;
228	    detPtr = detPtr->nextPtr;
229	    continue;
230	}
231	nextPtr = detPtr->nextPtr;
232	if (prevPtr == NULL) {
233	    detList = detPtr->nextPtr;
234	} else {
235	    prevPtr->nextPtr = detPtr->nextPtr;
236	}
237	ckfree((char *) detPtr);
238	detPtr = nextPtr;
239    }
240    Tcl_MutexUnlock(&pipeMutex);
241}
242
243/*
244 *----------------------------------------------------------------------
245 *
246 * TclCleanupChildren --
247 *
248 *	This is a utility function used to wait for child processes to exit,
249 *	record information about abnormal exits, and then collect any stderr
250 *	output generated by them.
251 *
252 * Results:
253 *	The return value is a standard Tcl result. If anything at weird
254 *	happened with the child processes, TCL_ERROR is returned and a message
255 *	is left in the interp's result.
256 *
257 * Side effects:
258 *	If the last character of the interp's result is a newline, then it is
259 *	removed unless keepNewline is non-zero. File errorId gets closed, and
260 *	pidPtr is freed back to the storage allocator.
261 *
262 *----------------------------------------------------------------------
263 */
264
265int
266TclCleanupChildren(
267    Tcl_Interp *interp,		/* Used for error messages. */
268    int numPids,		/* Number of entries in pidPtr array. */
269    Tcl_Pid *pidPtr,		/* Array of process ids of children. */
270    Tcl_Channel errorChan)	/* Channel for file containing stderr output
271				 * from pipeline. NULL means there isn't any
272				 * stderr output. */
273{
274    int result = TCL_OK;
275    int i, abnormalExit, anyErrorInfo;
276    Tcl_Pid pid;
277    WAIT_STATUS_TYPE waitStatus;
278    CONST char *msg;
279    unsigned long resolvedPid;
280
281    abnormalExit = 0;
282    for (i = 0; i < numPids; i++) {
283	/*
284	 * We need to get the resolved pid before we wait on it as the windows
285	 * implimentation of Tcl_WaitPid deletes the information such that any
286	 * following calls to TclpGetPid fail.
287	 */
288
289	resolvedPid = TclpGetPid(pidPtr[i]);
290        pid = Tcl_WaitPid(pidPtr[i], (int *) &waitStatus, 0);
291	if (pid == (Tcl_Pid) -1) {
292	    result = TCL_ERROR;
293            if (interp != NULL) {
294                msg = Tcl_PosixError(interp);
295                if (errno == ECHILD) {
296		    /*
297                     * This changeup in message suggested by Mark Diekhans to
298                     * remind people that ECHILD errors can occur on some
299                     * systems if SIGCHLD isn't in its default state.
300                     */
301
302                    msg =
303                        "child process lost (is SIGCHLD ignored or trapped?)";
304                }
305                Tcl_AppendResult(interp, "error waiting for process to exit: ",
306                        msg, NULL);
307            }
308	    continue;
309	}
310
311	/*
312	 * Create error messages for unusual process exits. An extra newline
313	 * gets appended to each error message, but it gets removed below (in
314	 * the same fashion that an extra newline in the command's output is
315	 * removed).
316	 */
317
318	if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
319	    char msg1[TCL_INTEGER_SPACE], msg2[TCL_INTEGER_SPACE];
320
321	    result = TCL_ERROR;
322	    sprintf(msg1, "%lu", resolvedPid);
323	    if (WIFEXITED(waitStatus)) {
324                if (interp != (Tcl_Interp *) NULL) {
325		    sprintf(msg2, "%lu",
326			    (unsigned long) WEXITSTATUS(waitStatus));
327                    Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2, NULL);
328                }
329		abnormalExit = 1;
330	    } else if (interp != NULL) {
331		CONST char *p;
332
333		if (WIFSIGNALED(waitStatus)) {
334                    p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
335                    Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
336                            Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
337                            NULL);
338                    Tcl_AppendResult(interp, "child killed: ", p, "\n", NULL);
339		} else if (WIFSTOPPED(waitStatus)) {
340                    p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
341                    Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
342                            Tcl_SignalId((int) (WSTOPSIG(waitStatus))), p,
343			    NULL);
344                    Tcl_AppendResult(interp, "child suspended: ", p, "\n",
345                            NULL);
346		} else {
347                    Tcl_AppendResult(interp,
348                            "child wait status didn't make sense\n", NULL);
349                }
350	    }
351	}
352    }
353
354    /*
355     * Read the standard error file. If there's anything there, then return an
356     * error and add the file's contents to the result string.
357     */
358
359    anyErrorInfo = 0;
360    if (errorChan != NULL) {
361	/*
362	 * Make sure we start at the beginning of the file.
363	 */
364
365        if (interp != NULL) {
366	    int count;
367	    Tcl_Obj *objPtr;
368
369	    Tcl_Seek(errorChan, (Tcl_WideInt)0, SEEK_SET);
370	    objPtr = Tcl_NewObj();
371	    count = Tcl_ReadChars(errorChan, objPtr, -1, 0);
372	    if (count < 0) {
373		result = TCL_ERROR;
374		Tcl_DecrRefCount(objPtr);
375		Tcl_ResetResult(interp);
376		Tcl_AppendResult(interp, "error reading stderr output file: ",
377			Tcl_PosixError(interp), NULL);
378	    } else if (count > 0) {
379		anyErrorInfo = 1;
380		Tcl_SetObjResult(interp, objPtr);
381		result = TCL_ERROR;
382	    } else {
383		Tcl_DecrRefCount(objPtr);
384	    }
385	}
386	Tcl_Close(NULL, errorChan);
387    }
388
389    /*
390     * If a child exited abnormally but didn't output any error information at
391     * all, generate an error message here.
392     */
393
394    if ((abnormalExit != 0) && (anyErrorInfo == 0) && (interp != NULL)) {
395	Tcl_AppendResult(interp, "child process exited abnormally", NULL);
396    }
397    return result;
398}
399
400/*
401 *----------------------------------------------------------------------
402 *
403 * TclCreatePipeline --
404 *
405 *	Given an argc/argv array, instantiate a pipeline of processes as
406 *	described by the argv.
407 *
408 *	This function is unofficially exported for use by BLT.
409 *
410 * Results:
411 *	The return value is a count of the number of new processes created, or
412 *	-1 if an error occurred while creating the pipeline. *pidArrayPtr is
413 *	filled in with the address of a dynamically allocated array giving the
414 *	ids of all of the processes. It is up to the caller to free this array
415 *	when it isn't needed anymore. If inPipePtr is non-NULL, *inPipePtr is
416 *	filled in with the file id for the input pipe for the pipeline (if
417 *	any): the caller must eventually close this file. If outPipePtr isn't
418 *	NULL, then *outPipePtr is filled in with the file id for the output
419 *	pipe from the pipeline: the caller must close this file. If errFilePtr
420 *	isn't NULL, then *errFilePtr is filled with a file id that may be used
421 *	to read error output after the pipeline completes.
422 *
423 * Side effects:
424 *	Processes and pipes are created.
425 *
426 *----------------------------------------------------------------------
427 */
428
429int
430TclCreatePipeline(
431    Tcl_Interp *interp,		/* Interpreter to use for error reporting. */
432    int argc,			/* Number of entries in argv. */
433    CONST char **argv,		/* Array of strings describing commands in
434				 * pipeline plus I/O redirection with <, <<,
435				 * >, etc. Argv[argc] must be NULL. */
436    Tcl_Pid **pidArrayPtr,	/* Word at *pidArrayPtr gets filled in with
437				 * address of array of pids for processes in
438				 * pipeline (first pid is first process in
439				 * pipeline). */
440    TclFile *inPipePtr,		/* If non-NULL, input to the pipeline comes
441				 * from a pipe (unless overridden by
442				 * redirection in the command). The file id
443				 * with which to write to this pipe is stored
444				 * at *inPipePtr. NULL means command specified
445				 * its own input source. */
446    TclFile *outPipePtr,	/* If non-NULL, output to the pipeline goes to
447				 * a pipe, unless overriden by redirection in
448				 * the command. The file id with which to read
449				 * frome this pipe is stored at *outPipePtr.
450				 * NULL means command specified its own output
451				 * sink. */
452    TclFile *errFilePtr)	/* If non-NULL, all stderr output from the
453				 * pipeline will go to a temporary file
454				 * created here, and a descriptor to read the
455				 * file will be left at *errFilePtr. The file
456				 * will be removed already, so closing this
457				 * descriptor will be the end of the file. If
458				 * this is NULL, then all stderr output goes
459				 * to our stderr. If the pipeline specifies
460				 * redirection then the file will still be
461				 * created but it will never get any data. */
462{
463    Tcl_Pid *pidPtr = NULL;	/* Points to malloc-ed array holding all the
464				 * pids of child processes. */
465    int numPids;		/* Actual number of processes that exist at
466				 * *pidPtr right now. */
467    int cmdCount;		/* Count of number of distinct commands found
468				 * in argc/argv. */
469    CONST char *inputLiteral = NULL;
470				/* If non-null, then this points to a string
471				 * containing input data (specified via <<) to
472				 * be piped to the first process in the
473				 * pipeline. */
474    TclFile inputFile = NULL;	/* If != NULL, gives file to use as input for
475				 * first process in pipeline (specified via <
476				 * or <@). */
477    int inputClose = 0;		/* If non-zero, then inputFile should be
478    				 * closed when cleaning up. */
479    int inputRelease = 0;
480    TclFile outputFile = NULL;	/* Writable file for output from last command
481				 * in pipeline (could be file or pipe). NULL
482				 * means use stdout. */
483    int outputClose = 0;	/* If non-zero, then outputFile should be
484    				 * closed when cleaning up. */
485    int outputRelease = 0;
486    TclFile errorFile = NULL;	/* Writable file for error output from all
487				 * commands in pipeline. NULL means use
488				 * stderr. */
489    int errorClose = 0;		/* If non-zero, then errorFile should be
490    				 * closed when cleaning up. */
491    int errorRelease = 0;
492    CONST char *p;
493    CONST char *nextArg;
494    int skip, lastBar, lastArg, i, j, atOK, flags, needCmd, errorToOutput = 0;
495    Tcl_DString execBuffer;
496    TclFile pipeIn;
497    TclFile curInFile, curOutFile, curErrFile;
498    Tcl_Channel channel;
499
500    if (inPipePtr != NULL) {
501	*inPipePtr = NULL;
502    }
503    if (outPipePtr != NULL) {
504	*outPipePtr = NULL;
505    }
506    if (errFilePtr != NULL) {
507	*errFilePtr = NULL;
508    }
509
510    Tcl_DStringInit(&execBuffer);
511
512    pipeIn = NULL;
513    curInFile = NULL;
514    curOutFile = NULL;
515    numPids = 0;
516
517    /*
518     * First, scan through all the arguments to figure out the structure of
519     * the pipeline. Process all of the input and output redirection arguments
520     * and remove them from the argument list in the pipeline. Count the
521     * number of distinct processes (it's the number of "|" arguments plus
522     * one) but don't remove the "|" arguments because they'll be used in the
523     * second pass to seperate the individual child processes. Cannot start
524     * the child processes in this pass because the redirection symbols may
525     * appear anywhere in the command line - e.g., the '<' that specifies the
526     * input to the entire pipe may appear at the very end of the argument
527     * list.
528     */
529
530    lastBar = -1;
531    cmdCount = 1;
532    needCmd = 1;
533    for (i = 0; i < argc; i++) {
534	errorToOutput = 0;
535	skip = 0;
536	p = argv[i];
537	switch (*p++) {
538	case '|':
539	    if (*p == '&') {
540		p++;
541	    }
542	    if (*p == '\0') {
543		if ((i == (lastBar + 1)) || (i == (argc - 1))) {
544		    Tcl_SetResult(interp, "illegal use of | or |& in command",
545			    TCL_STATIC);
546		    goto error;
547		}
548	    }
549	    lastBar = i;
550	    cmdCount++;
551	    needCmd = 1;
552	    break;
553
554	case '<':
555	    if (inputClose != 0) {
556		inputClose = 0;
557		TclpCloseFile(inputFile);
558	    }
559	    if (inputRelease != 0) {
560		inputRelease = 0;
561		TclpReleaseFile(inputFile);
562	    }
563	    if (*p == '<') {
564		inputFile = NULL;
565		inputLiteral = p + 1;
566		skip = 1;
567		if (*inputLiteral == '\0') {
568		    inputLiteral = ((i + 1) == argc) ? NULL : argv[i + 1];
569		    if (inputLiteral == NULL) {
570			Tcl_AppendResult(interp, "can't specify \"", argv[i],
571				"\" as last word in command", NULL);
572			goto error;
573		    }
574		    skip = 2;
575		}
576	    } else {
577		nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
578		inputLiteral = NULL;
579		inputFile = FileForRedirect(interp, p, 1, argv[i], nextArg,
580			O_RDONLY, &skip, &inputClose, &inputRelease);
581		if (inputFile == NULL) {
582		    goto error;
583		}
584	    }
585	    break;
586
587	case '>':
588	    atOK = 1;
589	    flags = O_WRONLY | O_CREAT | O_TRUNC;
590	    if (*p == '>') {
591		p++;
592		atOK = 0;
593
594		/*
595		 * Note that the O_APPEND flag only has an effect on POSIX
596		 * platforms. On Windows, we just have to carry on regardless.
597		 */
598
599		flags = O_WRONLY | O_CREAT | O_APPEND;
600	    }
601	    if (*p == '&') {
602		if (errorClose != 0) {
603		    errorClose = 0;
604		    TclpCloseFile(errorFile);
605		}
606		errorToOutput = 1;
607		p++;
608	    }
609
610	    /*
611	     * Close the old output file, but only if the error file is not
612	     * also using it.
613	     */
614
615	    if (outputClose != 0) {
616		outputClose = 0;
617		if (errorFile == outputFile) {
618		    errorClose = 1;
619		} else {
620		    TclpCloseFile(outputFile);
621		}
622	    }
623	    if (outputRelease != 0) {
624		outputRelease = 0;
625		if (errorFile == outputFile) {
626		    errorRelease = 1;
627		} else {
628		    TclpReleaseFile(outputFile);
629		}
630	    }
631	    nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
632	    outputFile = FileForRedirect(interp, p, atOK, argv[i], nextArg,
633		    flags, &skip, &outputClose, &outputRelease);
634	    if (outputFile == NULL) {
635		goto error;
636	    }
637	    if (errorToOutput) {
638		if (errorClose != 0) {
639		    errorClose = 0;
640		    TclpCloseFile(errorFile);
641		}
642		if (errorRelease != 0) {
643		    errorRelease = 0;
644		    TclpReleaseFile(errorFile);
645		}
646		errorFile = outputFile;
647	    }
648	    break;
649
650	case '2':
651	    if (*p != '>') {
652		break;
653	    }
654	    p++;
655	    atOK = 1;
656	    flags = O_WRONLY | O_CREAT | O_TRUNC;
657	    if (*p == '>') {
658		p++;
659		atOK = 0;
660		flags = O_WRONLY | O_CREAT;
661	    }
662	    if (errorClose != 0) {
663		errorClose = 0;
664		TclpCloseFile(errorFile);
665	    }
666	    if (errorRelease != 0) {
667		errorRelease = 0;
668		TclpReleaseFile(errorFile);
669	    }
670	    if (atOK && p[0] == '@' && p[1] == '1' && p[2] == '\0') {
671		/*
672		 * Special case handling of 2>@1 to redirect stderr to the
673		 * exec/open output pipe as well. This is meant for the end of
674		 * the command string, otherwise use |& between commands.
675		 */
676
677		if (i != argc-1) {
678		    Tcl_AppendResult(interp, "must specify \"", argv[i],
679			    "\" as last word in command", NULL);
680		    goto error;
681		}
682		errorFile = outputFile;
683		errorToOutput = 2;
684		skip = 1;
685	    } else {
686		nextArg = ((i + 1) == argc) ? NULL : argv[i + 1];
687		errorFile = FileForRedirect(interp, p, atOK, argv[i],
688			nextArg, flags, &skip, &errorClose, &errorRelease);
689		if (errorFile == NULL) {
690		    goto error;
691		}
692	    }
693	    break;
694
695	default:
696	  /* Got a command word, not a redirection */
697	  needCmd = 0;
698	  break;
699	}
700
701	if (skip != 0) {
702	    for (j = i + skip; j < argc; j++) {
703		argv[j - skip] = argv[j];
704	    }
705	    argc -= skip;
706	    i -= 1;
707	}
708    }
709
710    if (needCmd) {
711	/* We had a bar followed only by redirections. */
712
713        Tcl_SetResult(interp,
714		      "illegal use of | or |& in command",
715		      TCL_STATIC);
716	goto error;
717    }
718
719    if (inputFile == NULL) {
720	if (inputLiteral != NULL) {
721	    /*
722	     * The input for the first process is immediate data coming from
723	     * Tcl. Create a temporary file for it and put the data into the
724	     * file.
725	     */
726
727	    inputFile = TclpCreateTempFile(inputLiteral);
728	    if (inputFile == NULL) {
729		Tcl_AppendResult(interp,
730			"couldn't create input file for command: ",
731			Tcl_PosixError(interp), NULL);
732		goto error;
733	    }
734	    inputClose = 1;
735	} else if (inPipePtr != NULL) {
736	    /*
737	     * The input for the first process in the pipeline is to come from
738	     * a pipe that can be written from by the caller.
739	     */
740
741	    if (TclpCreatePipe(&inputFile, inPipePtr) == 0) {
742		Tcl_AppendResult(interp,
743			"couldn't create input pipe for command: ",
744			Tcl_PosixError(interp), NULL);
745		goto error;
746	    }
747	    inputClose = 1;
748	} else {
749	    /*
750	     * The input for the first process comes from stdin.
751	     */
752
753	    channel = Tcl_GetStdChannel(TCL_STDIN);
754	    if (channel != NULL) {
755		inputFile = TclpMakeFile(channel, TCL_READABLE);
756		if (inputFile != NULL) {
757		    inputRelease = 1;
758		}
759	    }
760	}
761    }
762
763    if (outputFile == NULL) {
764	if (outPipePtr != NULL) {
765	    /*
766	     * Output from the last process in the pipeline is to go to a pipe
767	     * that can be read by the caller.
768	     */
769
770	    if (TclpCreatePipe(outPipePtr, &outputFile) == 0) {
771		Tcl_AppendResult(interp,
772			"couldn't create output pipe for command: ",
773			Tcl_PosixError(interp), NULL);
774		goto error;
775	    }
776	    outputClose = 1;
777	} else {
778	    /*
779	     * The output for the last process goes to stdout.
780	     */
781
782	    channel = Tcl_GetStdChannel(TCL_STDOUT);
783	    if (channel) {
784		outputFile = TclpMakeFile(channel, TCL_WRITABLE);
785		if (outputFile != NULL) {
786		    outputRelease = 1;
787		}
788	    }
789	}
790    }
791
792    if (errorFile == NULL) {
793	if (errorToOutput == 2) {
794	    /*
795	     * Handle 2>@1 special case at end of cmd line.
796	     */
797
798	    errorFile = outputFile;
799	} else if (errFilePtr != NULL) {
800	    /*
801	     * Set up the standard error output sink for the pipeline, if
802	     * requested. Use a temporary file which is opened, then deleted.
803	     * Could potentially just use pipe, but if it filled up it could
804	     * cause the pipeline to deadlock: we'd be waiting for processes
805	     * to complete before reading stderr, and processes couldn't
806	     * complete because stderr was backed up.
807	     */
808
809	    errorFile = TclpCreateTempFile(NULL);
810	    if (errorFile == NULL) {
811		Tcl_AppendResult(interp,
812			"couldn't create error file for command: ",
813			Tcl_PosixError(interp), NULL);
814		goto error;
815	    }
816	    *errFilePtr = errorFile;
817	} else {
818	    /*
819	     * Errors from the pipeline go to stderr.
820	     */
821
822	    channel = Tcl_GetStdChannel(TCL_STDERR);
823	    if (channel) {
824		errorFile = TclpMakeFile(channel, TCL_WRITABLE);
825		if (errorFile != NULL) {
826		    errorRelease = 1;
827		}
828	    }
829	}
830    }
831
832    /*
833     * Scan through the argc array, creating a process for each group of
834     * arguments between the "|" characters.
835     */
836
837    Tcl_ReapDetachedProcs();
838    pidPtr = (Tcl_Pid *) ckalloc((unsigned) (cmdCount * sizeof(Tcl_Pid)));
839
840    curInFile = inputFile;
841
842    for (i = 0; i < argc; i = lastArg + 1) {
843	int result, joinThisError;
844	Tcl_Pid pid;
845	CONST char *oldName;
846
847	/*
848	 * Convert the program name into native form.
849	 */
850
851	if (Tcl_TranslateFileName(interp, argv[i], &execBuffer) == NULL) {
852	    goto error;
853	}
854
855	/*
856	 * Find the end of the current segment of the pipeline.
857	 */
858
859	joinThisError = 0;
860	for (lastArg = i; lastArg < argc; lastArg++) {
861	    if (argv[lastArg][0] != '|') {
862		continue;
863	    }
864	    if (argv[lastArg][1] == '\0') {
865		break;
866	    }
867	    if ((argv[lastArg][1] == '&') && (argv[lastArg][2] == '\0')) {
868		joinThisError = 1;
869		break;
870	    }
871	}
872
873	/*
874	 * If this is the last segment, use the specified outputFile.
875	 * Otherwise create an intermediate pipe. pipeIn will become the
876	 * curInFile for the next segment of the pipe.
877	 */
878
879	if (lastArg == argc) {
880	    curOutFile = outputFile;
881	} else {
882	    argv[lastArg] = NULL;
883	    if (TclpCreatePipe(&pipeIn, &curOutFile) == 0) {
884		Tcl_AppendResult(interp, "couldn't create pipe: ",
885			Tcl_PosixError(interp), NULL);
886		goto error;
887	    }
888	}
889
890	if (joinThisError != 0) {
891	    curErrFile = curOutFile;
892	} else {
893	    curErrFile = errorFile;
894	}
895
896	/*
897	 * Restore argv[i], since a caller wouldn't expect the contents of
898	 * argv to be modified.
899	 */
900
901	oldName = argv[i];
902	argv[i] = Tcl_DStringValue(&execBuffer);
903	result = TclpCreateProcess(interp, lastArg - i, argv + i,
904		curInFile, curOutFile, curErrFile, &pid);
905	argv[i] = oldName;
906	if (result != TCL_OK) {
907	    goto error;
908	}
909	Tcl_DStringFree(&execBuffer);
910
911	pidPtr[numPids] = pid;
912	numPids++;
913
914	/*
915	 * Close off our copies of file descriptors that were set up for this
916	 * child, then set up the input for the next child.
917	 */
918
919	if ((curInFile != NULL) && (curInFile != inputFile)) {
920	    TclpCloseFile(curInFile);
921	}
922	curInFile = pipeIn;
923	pipeIn = NULL;
924
925	if ((curOutFile != NULL) && (curOutFile != outputFile)) {
926	    TclpCloseFile(curOutFile);
927	}
928	curOutFile = NULL;
929    }
930
931    *pidArrayPtr = pidPtr;
932
933    /*
934     * All done. Cleanup open files lying around and then return.
935     */
936
937  cleanup:
938    Tcl_DStringFree(&execBuffer);
939
940    if (inputClose) {
941	TclpCloseFile(inputFile);
942    } else if (inputRelease) {
943	TclpReleaseFile(inputFile);
944    }
945    if (outputClose) {
946	TclpCloseFile(outputFile);
947    } else if (outputRelease) {
948	TclpReleaseFile(outputFile);
949    }
950    if (errorClose) {
951	TclpCloseFile(errorFile);
952    } else if (errorRelease) {
953	TclpReleaseFile(errorFile);
954    }
955    return numPids;
956
957    /*
958     * An error occurred. There could have been extra files open, such as
959     * pipes between children. Clean them all up. Detach any child processes
960     * that have been created.
961     */
962
963  error:
964    if (pipeIn != NULL) {
965	TclpCloseFile(pipeIn);
966    }
967    if ((curOutFile != NULL) && (curOutFile != outputFile)) {
968	TclpCloseFile(curOutFile);
969    }
970    if ((curInFile != NULL) && (curInFile != inputFile)) {
971	TclpCloseFile(curInFile);
972    }
973    if ((inPipePtr != NULL) && (*inPipePtr != NULL)) {
974	TclpCloseFile(*inPipePtr);
975	*inPipePtr = NULL;
976    }
977    if ((outPipePtr != NULL) && (*outPipePtr != NULL)) {
978	TclpCloseFile(*outPipePtr);
979	*outPipePtr = NULL;
980    }
981    if ((errFilePtr != NULL) && (*errFilePtr != NULL)) {
982	TclpCloseFile(*errFilePtr);
983	*errFilePtr = NULL;
984    }
985    if (pidPtr != NULL) {
986	for (i = 0; i < numPids; i++) {
987	    if (pidPtr[i] != (Tcl_Pid) -1) {
988		Tcl_DetachPids(1, &pidPtr[i]);
989	    }
990	}
991	ckfree((char *) pidPtr);
992    }
993    numPids = -1;
994    goto cleanup;
995}
996
997/*
998 *----------------------------------------------------------------------
999 *
1000 * Tcl_OpenCommandChannel --
1001 *
1002 *	Opens an I/O channel to one or more subprocesses specified by argc and
1003 *	argv. The flags argument determines the disposition of the stdio
1004 *	handles. If the TCL_STDIN flag is set then the standard input for the
1005 *	first subprocess will be tied to the channel: writing to the channel
1006 *	will provide input to the subprocess. If TCL_STDIN is not set, then
1007 *	standard input for the first subprocess will be the same as this
1008 *	application's standard input. If TCL_STDOUT is set then standard
1009 *	output from the last subprocess can be read from the channel;
1010 *	otherwise it goes to this application's standard output. If TCL_STDERR
1011 *	is set, standard error output for all subprocesses is returned to the
1012 *	channel and results in an error when the channel is closed; otherwise
1013 *	it goes to this application's standard error. If TCL_ENFORCE_MODE is
1014 *	not set, then argc and argv can redirect the stdio handles to override
1015 *	TCL_STDIN, TCL_STDOUT, and TCL_STDERR; if it is set, then it is an
1016 *	error for argc and argv to override stdio channels for which
1017 *	TCL_STDIN, TCL_STDOUT, and TCL_STDERR have been set.
1018 *
1019 * Results:
1020 *	A new command channel, or NULL on failure with an error message left
1021 *	in interp.
1022 *
1023 * Side effects:
1024 *	Creates processes, opens pipes.
1025 *
1026 *----------------------------------------------------------------------
1027 */
1028
1029Tcl_Channel
1030Tcl_OpenCommandChannel(
1031    Tcl_Interp *interp,		/* Interpreter for error reporting. Can NOT be
1032                                 * NULL. */
1033    int argc,			/* How many arguments. */
1034    CONST char **argv,		/* Array of arguments for command pipe. */
1035    int flags)			/* Or'ed combination of TCL_STDIN, TCL_STDOUT,
1036				 * TCL_STDERR, and TCL_ENFORCE_MODE. */
1037{
1038    TclFile *inPipePtr, *outPipePtr, *errFilePtr;
1039    TclFile inPipe, outPipe, errFile;
1040    int numPids;
1041    Tcl_Pid *pidPtr;
1042    Tcl_Channel channel;
1043
1044    inPipe = outPipe = errFile = NULL;
1045
1046    inPipePtr = (flags & TCL_STDIN) ? &inPipe : NULL;
1047    outPipePtr = (flags & TCL_STDOUT) ? &outPipe : NULL;
1048    errFilePtr = (flags & TCL_STDERR) ? &errFile : NULL;
1049
1050    numPids = TclCreatePipeline(interp, argc, argv, &pidPtr, inPipePtr,
1051            outPipePtr, errFilePtr);
1052
1053    if (numPids < 0) {
1054	goto error;
1055    }
1056
1057    /*
1058     * Verify that the pipes that were created satisfy the readable/writable
1059     * constraints.
1060     */
1061
1062    if (flags & TCL_ENFORCE_MODE) {
1063	if ((flags & TCL_STDOUT) && (outPipe == NULL)) {
1064	    Tcl_AppendResult(interp, "can't read output from command:"
1065		    " standard output was redirected", NULL);
1066	    goto error;
1067	}
1068	if ((flags & TCL_STDIN) && (inPipe == NULL)) {
1069	    Tcl_AppendResult(interp, "can't write input to command:"
1070		    " standard input was redirected", NULL);
1071	    goto error;
1072	}
1073    }
1074
1075    channel = TclpCreateCommandChannel(outPipe, inPipe, errFile,
1076	    numPids, pidPtr);
1077
1078    if (channel == (Tcl_Channel) NULL) {
1079        Tcl_AppendResult(interp, "pipe for command could not be created",
1080                NULL);
1081	goto error;
1082    }
1083    return channel;
1084
1085  error:
1086    if (numPids > 0) {
1087	Tcl_DetachPids(numPids, pidPtr);
1088	ckfree((char *) pidPtr);
1089    }
1090    if (inPipe != NULL) {
1091	TclpCloseFile(inPipe);
1092    }
1093    if (outPipe != NULL) {
1094	TclpCloseFile(outPipe);
1095    }
1096    if (errFile != NULL) {
1097	TclpCloseFile(errFile);
1098    }
1099    return NULL;
1100}
1101
1102/*
1103 * Local Variables:
1104 * mode: c
1105 * c-basic-offset: 4
1106 * fill-column: 78
1107 * End:
1108 */
1109