add: #executeCommand:inputFrom:outputTo:errorTo:auxFrom:inDirectory:lineWise:onError:
authorca
Tue, 04 Mar 2003 09:00:26 +0100
changeset 7110 808437ea66af
parent 7109 0c5c8be61083
child 7111 3cc2963786e2
add: #executeCommand:inputFrom:outputTo:errorTo:auxFrom:inDirectory:lineWise:onError:
AbstractOperatingSystem.st
--- a/AbstractOperatingSystem.st	Tue Mar 04 08:59:01 2003 +0100
+++ b/AbstractOperatingSystem.st	Tue Mar 04 09:00:26 2003 +0100
@@ -961,6 +961,199 @@
     "Modified: / 10.11.1998 / 20:54:37 / cg"
 !
 
+executeCommand:aCommandString inputFrom:anInStream outputTo:anOutStream errorTo:anErrStream auxFrom:anAuxStream inDirectory:dirOrNil lineWise:lineWise onError:aBlock 
+    "execute the unix command specified by the argument, aCommandString.
+     The commandString is passed to a shell for execution - see the description of
+     'sh -c' in your UNIX manual.
+     Return true if successful.
+     If not successfull, aBlock is called with an OsProcessStatus
+     (containing the exit status) as argument.
+     The given in, out and err streams may be arbitrary (Smalltalk-) streams;
+     if any is not an external stream (which is required by the command),
+     extra pipes and shuffler processes are created, which stuff the data into
+     those internal stream(s).
+     Nil stream args will execute the command connected to ST/X's standard input, output or
+     error resp. - i.e. usually, i/o will be from/to the terminal"
+
+    |pid exitStatus sema pIn pOut pErr pAux externalInStream externalOutStream externalErrStream externalAuxStream 
+     shuffledInStream shuffledOutStream shuffledErrStream shuffledAuxStream
+     inputShufflerProcess outputShufflerProcess errorShufflerProcess auxShufflerProcess stopShufflers
+     inStreamToClose outStreamToClose errStreamToClose auxStreamToClose terminateLock|
+
+    terminateLock := Semaphore forMutualExclusion.
+    ((externalInStream := anInStream) notNil 
+     and:[externalInStream isExternalStream not]) ifTrue:[
+        pIn := ExternalStream makePipe.
+        inStreamToClose := externalInStream := pIn at:1.
+        shuffledInStream := pIn at:2.
+        lineWise ifTrue:[shuffledInStream buffered:false].
+
+        "/ start a reader process, shuffling data from the given
+        "/ inStream to the pipe (which is connected to the commands input)
+        inputShufflerProcess := [
+                    [
+                        [anInStream atEnd] whileFalse:[
+                            self shuffleFrom:anInStream to:shuffledInStream lineWise:lineWise.
+                            shuffledInStream flush
+                        ]
+                    ] ensure:[
+                        shuffledInStream close
+                    ]
+                ] forkNamed:'cmd input shuffler'.
+    ].
+    ((externalOutStream := anOutStream) notNil 
+     and:[externalOutStream isExternalStream not]) ifTrue:[
+        pOut := ExternalStream makePipe.
+        shuffledOutStream := (pOut at:1).
+        outStreamToClose := externalOutStream := pOut at:2.
+        lineWise ifTrue:[shuffledOutStream buffered:false].
+        outputShufflerProcess := 
+                    [
+                        self shuffleAllFrom:shuffledOutStream to:anOutStream lineWise:lineWise lockWith:terminateLock.    
+                    ] forkNamed:'cmd output shuffler'.
+    ].
+    (externalErrStream := anErrStream) notNil ifTrue:[
+        anErrStream == anOutStream ifTrue:[
+            externalErrStream := externalOutStream
+        ] ifFalse:[
+            anErrStream isExternalStream ifFalse:[
+                pErr := ExternalStream makePipe.
+                shuffledErrStream := (pErr at:1).
+                errStreamToClose := externalErrStream := pErr at:2.
+
+                lineWise ifTrue:[shuffledErrStream buffered:false].
+                errorShufflerProcess := 
+                        [
+                            self shuffleAllFrom:shuffledErrStream to:anErrStream lineWise:lineWise lockWith:terminateLock.    
+                        ] forkNamed:'cmd err-output shuffler'.
+            ]
+        ]
+    ].
+    ((externalAuxStream := anAuxStream) notNil 
+     and:[externalAuxStream isExternalStream not]) ifTrue:[
+        pAux := ExternalStream makePipe.
+        auxStreamToClose := externalAuxStream := pAux at:1.
+        shuffledAuxStream := pAux at:2.
+
+        "/ start a reader process, shuffling data from the given
+        "/ auxStream to the pipe (which is connected to the commands aux)
+        auxShufflerProcess := [
+                    [
+                        [anAuxStream atEnd] whileFalse:[
+                            self shuffleFrom:anAuxStream to:shuffledAuxStream lineWise:false.
+                            shuffledAuxStream flush
+                        ]
+                    ] ensure:[
+                        shuffledAuxStream close
+                    ]
+                ] forkNamed:'cmd aux shuffler'.
+    ].
+
+    sema := Semaphore new name:'OS command wait'.
+    pid := Processor 
+                monitor:[
+                    self 
+                        startProcess:aCommandString
+                        inputFrom:externalInStream
+                        outputTo:externalOutStream
+                        errorTo:externalErrStream
+                        auxFrom:externalAuxStream
+                        inDirectory:dirOrNil
+                ]
+                action:[:status | 
+                    status stillAlive ifFalse:[
+                        exitStatus := status.
+                        sema signal.
+                        self closePid:pid
+                    ]
+                ].
+
+    inStreamToClose notNil ifTrue:[
+        inStreamToClose close
+    ].
+    errStreamToClose notNil ifTrue:[
+        errStreamToClose close
+    ].
+    outStreamToClose notNil ifTrue:[
+        outStreamToClose close
+    ].
+    auxStreamToClose notNil ifTrue:[
+        auxStreamToClose close
+    ].
+
+    stopShufflers := [
+            inputShufflerProcess notNil ifTrue:[
+                terminateLock critical:[inputShufflerProcess terminate].
+                inputShufflerProcess waitUntilTerminated
+            ].
+            auxShufflerProcess notNil ifTrue:[
+                terminateLock critical:[auxShufflerProcess terminate].
+                auxShufflerProcess waitUntilTerminated
+            ].
+            outputShufflerProcess notNil ifTrue:[
+                terminateLock critical:[outputShufflerProcess terminate].
+                outputShufflerProcess waitUntilTerminated.
+                self shuffleRestFrom:shuffledOutStream to:anOutStream lineWise:lineWise.
+                shuffledOutStream close.
+            ].
+            errorShufflerProcess notNil ifTrue:[
+                terminateLock critical:[errorShufflerProcess terminate].
+                errorShufflerProcess waitUntilTerminated.
+                self shuffleRestFrom:shuffledErrStream to:anErrStream lineWise:lineWise.
+                shuffledErrStream close.
+            ].
+        ].
+
+    pid notNil ifTrue:[
+        [
+            sema wait.
+        ] ifCurtailed:[
+            "/ terminate the os-command (and all of its forked commands)
+            self terminateProcessGroup:pid.
+            self terminateProcess:pid.
+            self closePid:pid.
+            stopShufflers value.    
+        ]
+    ] ifFalse:[
+        exitStatus := self osProcessStatusClass processCreationFailure
+    ].
+    stopShufflers value.
+    exitStatus success ifFalse:[
+        ^ aBlock value:exitStatus
+    ].
+    ^ true
+
+    "
+        |outStream errStream|
+
+        outStream := '' writeStream.
+
+        OperatingSystem executeCommand:'ls -l'
+                        inputFrom:'abc' readStream
+                        outputTo:outStream
+                        errorTo:nil
+                        inDirectory:nil
+                        lineWise:true
+                        onError:[:exitStatus | ^ false].
+        outStream contents
+    "
+
+    "
+        |outStream errStream|
+
+        outStream := '' writeStream.
+
+        OperatingSystem executeCommand:'gpg -s --batch --no-tty --passphrase-fd 0 /tmp/passwd'
+                        inputFrom:'bla' readStream
+                        outputTo:outStream
+                        errorTo:nil
+                        inDirectory:nil
+                        lineWise:true
+                        onError:[:exitStatus |  false].
+        outStream contents
+    "
+!
+
 executeCommand:aCommandString inputFrom:anInStream outputTo:anOutStream errorTo:anErrStream inDirectory:dirOrNil lineWise:lineWise onError:aBlock 
     "execute the unix command specified by the argument, aCommandString.
      The commandString is passed to a shell for execution - see the description of
@@ -4283,7 +4476,7 @@
 !AbstractOperatingSystem class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/AbstractOperatingSystem.st,v 1.86 2003-03-03 20:09:17 stefan Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/AbstractOperatingSystem.st,v 1.87 2003-03-04 08:00:26 ca Exp $'
 ! !
 
 AbstractOperatingSystem initialize!