|
|
/*++
Copyright (c) 1988-1999 Microsoft Corporation
Module Name:
cop.c
Abstract:
Conditional/sequential command execution
--*/
#include "cmd.h"
extern int LastRetCode;
extern int ExtCtrlc; /* @@1 */
/* M000 ends */
unsigned PipeCnt ; /* M007 - Active pipe count */ struct pipedata *PdHead = NULL; /* M007 - 1st element of pipedata list */ struct pipedata *PdTail = NULL; /* M007 - Last element of pipedata list */ HANDLE PipePid ; /* M007 - Communication with ECWork */
/*** eComSep - execute a statement containing a command separator
* * Purpose: * Execute the left and right hand sides of a command separator * operator. * * int eComSep(struct node *n) * * Args: * n - parse tree node containing the command separator node * * Returns: * Whatever the right hand side returns. * * Notes: * Revised to always supply both args to Dispatch(). */
int eComSep(n) struct node *n ; { Dispatch(RIO_OTHER,n->lhs) ; if (GotoFlag) { return SUCCESS; } else { return(Dispatch(RIO_OTHER,n->rhs)) ; } }
/*** eOr - execute an OR operation
* * Purpose: * Execute the left hand side of an OR operator (||). If it succeeds, * quit. Otherwise execute the right side of the operator. * * int eOr(struct node *n) * * Args: * n - parse tree node containing the OR operator node * * Returns: * If the left hand side succeeds, return SUCCESS. Otherwise, return * whatever the right side returns. * * Notes: * Revised to always supply both args to Dispatch(). */
int eOr(n) struct node *n ; { int i ; /* Retcode from L.H. side of OR */ if ((i = Dispatch(RIO_OTHER,n->lhs)) == SUCCESS) return(SUCCESS) ; else { LastRetCode = i; return(Dispatch(RIO_OTHER,n->rhs)) ; } }
/*** eAnd - execute an AND operation
* * Purpose: * Execute the left hand side of an AND operator (&&). If it fails, * quit. Otherwise execute the right side of the operator. * * int eAnd(struct node *n) * * Args: * n - parse tree node containing the AND operator node * * Returns: * If the left hand side fails, return its return code. Otherwise, return * whatever the right side returns. * * Notes: * Revised to always supply both args to Dispatch(). */
int eAnd(n) struct node *n ; { int i ; /* Retcode from L.H. side of AND */
if ((i = Dispatch(RIO_OTHER,n->lhs)) != SUCCESS) { return(i) ; } else if (GotoFlag) { return SUCCESS; } else { return(Dispatch(RIO_OTHER,n->rhs)) ; } }
/********************* START OF SPECIFICATION **************************/ /* */ /* SUBROUTINE NAME: ePipe */ /* */ /* DESCRIPTIVE NAME: Pipe Process */ /* */ /* FUNCTION: Execute the left side of the pipe and direct its output to*/ /* the right side of the pipe. */ /* */ /* NOTES: None */ /* */ /* */ /* ENTRY POINT: ePipe */ /* LINKAGE: NEAR */ /* */ /* INPUT: n - parse tree node containing the pipe operator */ /* */ /* OUTPUT: None */ /* */ /* EXIT-NORMAL: The return code of the right side process. */ /* */ /* EXIT-ERROR: Failure if no pipe redirection can take place. */ /* */ /* EFFECTS: */ /* */ /* struct pipedata { */ /* unsigned rh ; Pipe read handle */ /* unsigned wh ; Pipe write handle */ /* unsigned shr ; Handles where the normal... */ /* unsigned shw ; ...STDIN/OUT handles are saved */ /* unsigned lPID ; Pipe lh side PID */ /* unsigned rPID ; Pipe rh side PID */ /* unsigned lstart ; Start Information of lh side @@4*/ /* unsigned rstart ; Start Information of rh side @@4*/ /* struct pipedata *prvpds ; Ptr to previous pipedata struct */ /* struct pipedata *nxtpds ; Ptr to next pipedata struct */ /* } */ /* */ /* unsigned PipePID; Pipe Process ID */ /* */ /* unsigned start_type; Start Information */ /* */ /* */ /* INTERNAL REFERENCES: */ /* ROUTINES: */ /* PutStdErr - Print an error message */ /* Abort - Terminate program with abort */ /* SetList - Set Link List for pipedata structure */ /* Cdup - Duplicate supplied handle and save the new handle*/ /* Cdup2 - Duplicate supplied handle and save the new handle*/ /* Dispatch - Execute the program */ /* PipeErr - Handle pipe error */ /* Cclose - Close the specified handle */ /* PipeWait - Wait for the all pipe process completion */ /* */ /* EXTERNAL REFERENCES: */ /* ROUTINES: */ /* DOSMAKEPIPE - Make pipe */ /* */ /********************** END OF SPECIFICATION **************************/ /*** ePipe - Create a pipeline between two processes (M000)
* * Purpose: * Execute the left side of the pipe and direct its output to * the right side of the pipe. * * int ePipe(struct node *n) * * Args: * n - parse tree node containing the pipe operator * * Returns: * The return code of the right side process or failure if no * pipe redirection can take place. * * Notes: * M007 - This function has been completely rewritten for real pipes. * */
int ePipe(n) struct node *n ; { struct pipedata *Pd ; /* Pipe struct pointer */ int k = 0 ; /* RH side return code */ struct node *l ; /* Copy of left side arg */ struct node *r ; /* Copy of right side arg */ extern unsigned start_type ; /* API type used to start */ TCHAR cflags ; /* */
l = n->lhs ; /* Equate locals to... */ r = n->rhs ; /* ...L & R operations */
DEBUG((OPGRP,PILVL,"PIPES:LH = %d, RH = %d ",l->type,r->type)) ;
if (!(Pd = (struct pipedata *)mkstr(sizeof(struct pipedata)))) {
DEBUG((OPGRP,PILVL,"PIPES:Couldn't alloc structure!")) ;
return(FAILURE) ; };
//
// Create a pipe with a read handle and a write handle
//
if (_pipe((int *)Pd, 0, O_BINARY)) {
DEBUG((OPGRP,PILVL,"PIPES:pipe failed!")) ;
PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS); /* M013 */ return(FAILURE) ; Abort() ; };
SetList(Pd->rh) ; /* M009 */ SetList(Pd->wh) ; /* M009 */
DEBUG((OPGRP,PILVL,"PIPES:Pipe built. Handles: rd = %d wt = %d ",Pd->rh, Pd->wh)) ; DEBUG((OPGRP,PILVL,"PIPES:Pipe (pre-index) count = %d", PipeCnt)) ;
if (!PipeCnt++) { /* Already some pipes? */ PdHead = PdTail = Pd ; /* No, set head/tail ptrs */ Pd->prvpds = NULL ; /* No previous structure */
DEBUG((OPGRP,PILVL,"PIPES:This is first pipe.")) ;
} else {
DEBUG((OPGRP,PILVL,"PIPES:This is pipe %d.", PipeCnt)) ;
PdTail->nxtpds = Pd ; Pd->prvpds = PdTail ; Pd->nxtpds = NULL ; PdTail = Pd ; } ;
//
// Set up the redirection for the left-hand side; the writing side.
// We do this by saving the current stdout, duplicating the pipe-write
// handle onto stdout, and then invoking the left-hand side of the pipe.
// When we do this, the lefthand side will fill the pipe with data.
//
//
// Save stdout handle
//
if ((Pd->shw = Cdup(STDOUT)) == BADHANDLE) { /* Save STDOUT (M009) */ Pd->shw = BADHANDLE ; /* If err, go process it */ PipeErr() ; /* DOES NOT RETURN */ };
DEBUG((OPGRP,PILVL,"PIPES:STDOUT dup'd to %d.", Pd->shw)) ;
//
// Make stdout point to the write side of the pipe
//
if (Cdup2(Pd->wh, STDOUT) == BADHANDLE) /* Make wh STDOUT (M009) */ PipeErr() ; /* DOES NOT RETURN */
Cclose(Pd->wh) ; /* Close pipe hndl (M009) */ Pd->wh = 0 ; /* And zero the holder */
if (l->type <= CMDTYP) { /* @@5a */ /* @@5a */ FindAndFix( (struct cmdnode *) l, &cflags ) ; /* @@5a */ } /* @@5a */
DEBUG((OPGRP,PILVL,"PIPES:Write pipe now STDOUT")) ;
//
// Execute the left side of the pipe, fillng the pipe
//
k = Dispatch(RIO_PIPE,l) ;
//
// This closes the read handle in the left hand pipe. I don't know
// why we're doing this.
//
if (PipePid != NULL) { DuplicateHandle( PipePid, CRTTONT(Pd->rh), NULL, NULL, 0, FALSE, DUPLICATE_CLOSE_SOURCE); }
//
// Restore the saved stdout
//
if (Cdup2(Pd->shw, STDOUT) == BADHANDLE) PipeErr( );
//
// Closed the saved handle
//
Cclose(Pd->shw) ; /* M009 */ Pd->shw = 0 ;
DEBUG((OPGRP,PILVL,"PIPES:STDOUT now handle 1 again.")) ;
if (k) { ExtCtrlc = 2; /* @@1 */ Abort() ; }
Pd->lPID = PipePid ; Pd->lstart = start_type ; /* Save the start_type in pipedata struct */ PipePid = 0 ; start_type = NONEPGM ; /* Reset the start_type D64 */
DEBUG((OPGRP,PILVL,"PIPES:Dispatch LH side succeeded - LPID = %d.",Pd->lPID)) ;
//
// Start on the right hand side of the pipe. Save the current stdin,
// copy the pipe read handle to stdin and then execute the right hand side
// of the pipe
//
//
// Save stdin
//
if ((Pd->shr = Cdup(STDIN)) == BADHANDLE) { /* Save STDIN (M009) */ Pd->shr = BADHANDLE ; PipeErr() ; /* DOES NOT RETURN */ };
DEBUG((OPGRP,PILVL,"PIPES:STDIN dup'd to %d.", Pd->shr)) ;
//
// Point stdin at the pipe read handle
//
if (Cdup2(Pd->rh, STDIN) == BADHANDLE) /* Make rh STDIN (M009) */ PipeErr() ; /* DOES NOT RETURN */
Cclose(Pd->rh) ; /* Close pipe hndl (M009) */ Pd->rh = 0 ; /* And zero the holder */
if (r->type <= CMDTYP) { /* @@5a */ /* @@5a */ FindAndFix( (struct cmdnode *) r, &cflags) ; /* @@5a */ }; /* @@5a */
DEBUG((OPGRP,PILVL,"PIPES:Read pipe now STDIN")) ;
//
// Start off the right hand side of the pipe
//
k = Dispatch(RIO_PIPE,r) ;
//
// Restore the saved stdin
//
if (Cdup2(Pd->shr, STDIN) == BADHANDLE) /* M009 */ PipeErr() ; /* DOES NOT RETURN */
//
// Get rid of the saved stdin
//
Cclose(Pd->shr) ; /* M009 */ Pd->shr = 0 ;
DEBUG((OPGRP,PILVL,"PIPES:STDIN now handle 0 again.")) ;
if (k) { ExtCtrlc = 2; /* @@1 */ Abort() ; }
Pd->rPID = PipePid ; Pd->rstart = start_type ; /* Save the start_type in pipedata struct */ PipePid = 0 ; start_type = NONEPGM ; /* Reset the start_type D64 */
DEBUG((OPGRP,PILVL,"PIPES:Dispatch RH side succeeded - RPID = %d.",Pd->rPID)) ;
if (!(--PipeCnt)) { /* Additional pipes? */
DEBUG((OPGRP,PILVL,"PIPES:Returning from top level pipe. Cnt = %d", PipeCnt)) ;
return(PipeWait()) ; /* No, return CWAIT */ };
DEBUG((OPGRP,PILVL,"PIPES:Returning from pipe. Cnt = %d", PipeCnt)) ;
return(k) ; /* Else return exec ret */ }
/*** PipeErr - Fixup and error out following pipe error
* * Purpose: * To provide single error out point for multiple error conditions. * * int PipeErr() * * Args: * None. * * Returns: * DOES NOT RETURN TO CALLER. Instead it causes an internal Abort(). * */
void PipeErr() {
PutStdErr(MSG_PIPE_FAILURE, NOARGS) ; /* M013 */ Abort() ; }
/********************* START OF SPECIFICATION **************************/ /* */ /* SUBROUTINE NAME: PipeWait */ /* */ /* DESCRIPTIVE NAME: Wait and Collect Retcode for All Pipe Completion */ /* */ /* FUNCTION: This routine calls WaitProc or WaitTermQProc for all */ /* pipelined processes until entire pipeline is finished. */ /* The return code of right most element is returned. */ /* */ /* NOTES: If the pipelined process is started by DosExecPgm, */ /* WaitProc is called. If the pipelined process is started */ /* by DosStartSession, WaitTermQProc is called. */ /* */ /* */ /* ENTRY POINT: PipeWait */ /* LINKAGE: NEAR */ /* */ /* INPUT: None */ /* */ /* OUTPUT: None */ /* */ /* EXIT-NORMAL: No error return code */ /* */ /* EXIT-ERROR: Error return code from either WaitTermQProc or WaitProc*/ /* */ /* */ /* EFFECTS: None. */ /* */ /* INTERNAL REFERENCES: */ /* ROUTINES: */ /* WaitProc - wait for the termination of the specified process, */ /* its child process, and related pipelined */ /* processes. */ /* */ /* WaitTermQProc - wait for the termination of the specified */ /* session and related pipelined session. */ /* */ /* EXTERNAL REFERENCES: */ /* ROUTINES: */ /* WINCHANGESWITCHENTRY - Change switch list entry */ /* */ /********************** END OF SPECIFICATION **************************/ /*** PipeWait - wait and collect retcode for all pipe completion (M007)
* * Purpose: * To do cwaits on all pipelined processes until entire pipeline * is finished. The retcode of the rightmost element of the pipe * is returned. * * int PipeWait() * * Args: * None. * * Returns: * Retcode of rightmost pipe process. * */
PipeWait() { unsigned i = SUCCESS;
DEBUG((OPGRP,PILVL,"PIPEWAIT:Entered - PipeCnt = %d", PipeCnt)) ;
while (PdHead) { if (PdHead->lPID) { DEBUG((OPGRP, PILVL, "PIPEWAIT: lPID %d, lstart %d", PdHead->lPID, PdHead->lstart));
if ( PdHead->lstart == EXECPGM ) { i = WaitProc(PdHead->lPID) ; /* M012 - Wait LH */
DEBUG((OPGRP,PILVL,"PIPEWAIT:CWAIT on LH - Ret = %d, SPID = %d", i, PdHead->lPID)) ; } } if (PdHead->rPID) { DEBUG((OPGRP, PILVL, "PIPEWAIT: rPID %d, rstart %d", PdHead->rPID, PdHead->rstart)); if ( PdHead->rstart == EXECPGM ) { i = WaitProc(PdHead->rPID) ; /* M012 - Wait RH */
DEBUG((OPGRP,PILVL,"PIPEWAIT:CWAIT on RH - Ret = %d, PID = %d", i, PdHead->rPID)) ; }
}
PdHead = PdHead->nxtpds ; }
DEBUG((OPGRP,PILVL,"PIPEWAIT: complete, Retcode = %d", i)) ;
PdTail = NULL ; /* Cancel linked list... */ PipeCnt = 0 ; /* ...pipe count and pipe PID */ PipePid = 0 ; LastRetCode = i; return(i) ; }
/*** BreakPipes - disconnect all active pipes (M000)
* * Purpose: * To remove the temporary pipe files and invalidate the pipedata * structure when pipes are to be terminated, either through the * completion of the pipe operation or SigTerm. * * This routine is called directly by the signal handler and must * not generate any additional error conditions. * * void BreakPipes() * * Args: * None. * * Returns: * Nothing. * * Notes: * M007 - This function has been completely rewritten for real pipes. * * *** W A R N I N G ! *** * THIS ROUTINE IS CALLED AS A PART OF SIGNAL/ABORT RECOVERY AND * THEREFORE MUST NOT BE ABLE TO TRIGGER ANOTHER ABORT CONDITION. * */
void BreakPipes() { unsigned i ; struct pipedata *pnode;
DEBUG((OPGRP,PILVL,"BRKPIPES:Entered - PipeCnt = %d", PipeCnt)) ;
/* The following two lines have been commented out */ /* because the NULL test on PdTail should be enough, */ /* and more importantly, even if PipeCnt is 0, you */ /* may still have been servicing a pipe in Pipewait */
/* if (!PipeCnt) */ /* If no active pipes... */ /* return ; */ /* ...don't do anything */
pnode = PdTail;
/* First, kill all of the processes */ while (pnode) { if (pnode->lPID!=(HANDLE) NULL) { /* M012 */ i = KillProc(pnode->lPID, FALSE) ; /* Kill LH */
DEBUG((OPGRP,PILVL,"BRKPIPES:LH (Pid %d) killed - Retcode = %d", PdTail->lPID, i)) ; };
if (pnode->rPID!=(HANDLE) NULL) { /* M012 */ i = KillProc(pnode->rPID, FALSE) ; /* Kill RH */
DEBUG((OPGRP,PILVL,"BRKPIPES:RH (Pid %d) killed - Retcode = %d", PdTail->rPID, i)) ; }; pnode = pnode->prvpds ; }
/* Wait for the processes to die, and clean up file handles */ while (PdTail) { if (PdTail->lPID) { if (PdTail->lstart == EXECPGM) { i = WaitProc(PdTail->lPID); // } else {
// WaitTermQProc(PdTail->lPID, &i) ;
} };
if (PdTail->rPID) { if (PdTail->rstart == EXECPGM) { i = WaitProc(PdTail->rPID); // } else {
// WaitTermQProc(PdTail->rPID, &i) ;
} };
if (PdTail->rh) { Cclose(PdTail->rh) ; /* M009 */
DEBUG((OPGRP,PILVL,"BRKPIPES:Pipe read handle closed")) ; }; if (PdTail->wh) { Cclose(PdTail->wh) ; /* M009 */
DEBUG((OPGRP,PILVL,"BRKPIPES:Pipe write handle closed")) ; }; if (PdTail->shr) { FlushFileBuffers(CRTTONT(PdTail->shr)); Cdup2(PdTail->shr, STDIN) ; /* M009 */ Cclose(PdTail->shr) ; /* M009 */
DEBUG((OPGRP,PILVL,"BRKPIPES:STDIN restored.")) ;
}; if (PdTail->shw) { Cdup2(PdTail->shw, STDOUT) ; /* M009 */ Cclose(PdTail->shw) ; /* M009 */
DEBUG((OPGRP,PILVL,"BRKPIPES:STDOUT restored.")) ;
}; PdTail = PdTail->prvpds ; } ;
PdHead = NULL ; /* Cancel linked list... */ PipeCnt = 0 ; /* ...pipe count and pipe PID */ PipePid = 0;
DEBUG((OPGRP,PILVL,"BRKPIPES:Action complete, returning")) ; }
/*** eParen - execute a parenthesized statement group
* * Purpose: * Execute the group of statements enclosed by a statement grouping * operator; parenthesis(). * * int eParen(struct node *n) * * Args: * n - parse tree node containing the PAREN operator node * * Returns: * Whatever the statement group returns. * * Notes: * M000 - Altered to always supply both args to Dispatch(). * M004 - Debug statements were added for SILTYP operator. * ** WARNING ** * BOTH THE LEFT PAREN AND THE SILENT OPERATOR (@) USE eParen * WHEN DISPATCHED. CHANGING ONE WILL AFFECT THE OTHER !! */
int eParen(n) struct node *n ; { DEBUG((OPGRP,PNLVL,"ePAREN: Operator is %s", (n->type == PARTYP) ? "Paren" : "Silent")) ; return(Dispatch(RIO_OTHER,n->lhs)) ; }
|