Environment in #executeCommand:...
authorStefan Vogel <sv@exept.de>
Thu, 03 Apr 2003 14:48:50 +0200
changeset 7170 19558aa463e0
parent 7169 74576ccad3c0
child 7171 e8eac6f03c77
Environment in #executeCommand:...
AbstractOperatingSystem.st
UnixOperatingSystem.st
--- a/AbstractOperatingSystem.st	Thu Apr 03 10:45:30 2003 +0200
+++ b/AbstractOperatingSystem.st	Thu Apr 03 14:48:50 2003 +0200
@@ -731,151 +731,6 @@
     self subclassResponsibility
 !
 
-exec:aCommandPath withArguments:argArray
-    "execute the OS command specified by the argument, aCommandPath, with
-     arguments in argArray (no arguments, if nil).
-     If successful, this method does NOT return and smalltalk is gone.
-     If not successful, it does return. 
-     Can be used on UNIX with fork or on other systems to chain to another program."
-
-    ^ self 
-        exec:aCommandPath 
-        withArguments:argArray
-        environment:nil
-        fileDescriptors:#(0 1 2)
-        fork:false 
-        newPgrp:false
-        inDirectory:nil
-
-    "/ never reached ...
-
-    "Modified: / 12.11.1998 / 14:44:26 / cg"
-!
-
-exec:aCommandPath withArguments:argArray environment:env fileDescriptors:fds fork:doFork newPgrp:newGrp inDirectory:aDirectory
-    "execute an OS command"
-
-    ^ self subclassResponsibility
-
-    "Created: / 12.11.1998 / 14:46:15 / cg"
-!
-
-exec:aCommandPath withArguments:argArray fileDescriptors:fileDescriptors fork:doFork newPgrp:newPgrp inDirectory:aDirectory
-    ^ self 
-        exec:aCommandPath
-        withArguments:argArray
-        environment:nil
-        fileDescriptors:fileDescriptors
-        fork:doFork 
-        newPgrp:newPgrp
-        inDirectory:aDirectory
-!
-
-exec:aCommandPath withArguments:argArray fork:doFork
-    "execute an OS command without I/O redirection.
-     The command reads its input and writes its output
-     from/to whatever terminal device ST/X was started
-     (typically, the terminal window)"
-
-    ^ self 
-        exec:aCommandPath 
-        withArguments:argArray 
-        environment:nil
-        fileDescriptors:#(0 1 2)
-        fork:doFork 
-        newPgrp:false
-        inDirectory:nil
-
-    "
-     |id|
-
-     id := OperatingSystem fork.
-     id == 0 ifTrue:[
-        'I am the child'.
-        OperatingSystem 
-            exec:'/bin/ls' 
-            withArguments:#('ls' '/tmp')
-            fork:false.
-        'not reached'.
-     ]
-    "
-
-    "
-     |id|
-
-     id := OperatingSystem fork.
-     id == 0 ifTrue:[
-        'I am the child'.
-        OperatingSystem 
-            exec:'/bin/sh' 
-            withArguments:#('sh' '-c' 'sleep 2;echo 1;sleep 2;echo 2')
-            fork:false.
-        'not reached'.
-     ].
-     id printNL.
-     (Delay forSeconds:3.5) wait.
-     'killing ...' printNL.
-     OperatingSystem sendSignal:(OperatingSystem sigTERM) to:id.
-     OperatingSystem sendSignal:(OperatingSystem sigKILL) to:id
-    "
-
-    "Modified: / 15.7.1997 / 15:54:32 / stefan"
-    "Modified: / 12.11.1998 / 14:44:46 / cg"
-!
-
-exec:aCommandPath withArguments:argArray fork:doFork inDirectory:aDirectory
-    "execute an OS command without I/O redirection.
-     The command reads its input and writes its output
-     from/to whatever terminal device ST/X was started
-     (typically, the terminal window)"
-
-    ^ self 
-        exec:aCommandPath
-        withArguments:argArray
-        environment:nil
-        fileDescriptors:#(0 1 2)
-        fork:doFork 
-        newPgrp:false
-        inDirectory:aDirectory
-
-    "
-     |id|
-
-     id := OperatingSystem fork.
-     id == 0 ifTrue:[
-        'I am the child'.
-        OperatingSystem 
-            exec:'/bin/ls' 
-            withArguments:#('ls' '/tmp')
-            fork:false.
-        'not reached'.
-     ]
-    "
-
-    "
-     |id|
-
-     id := OperatingSystem fork.
-     id == 0 ifTrue:[
-        'I am the child'.
-        OperatingSystem 
-            exec:'/bin/sh' 
-            withArguments:#('sh' '-c' 'sleep 2;echo 1;sleep 2;echo 2')
-            fork:false.
-        'not reached'.
-     ].
-     id printNL.
-     (Delay forSeconds:3.5) wait.
-     'killing ...' printNL.
-     OperatingSystem sendSignal:(OperatingSystem sigTERM) to:id.
-     OperatingSystem sendSignal:(OperatingSystem sigKILL) to:id
-    "
-
-    "Created: / 28.1.1998 / 14:14:03 / md"
-    "Modified: / 28.1.1998 / 14:14:45 / md"
-    "Modified: / 12.11.1998 / 14:45:06 / cg"
-!
-
 executableFileExtensions
     "return a collection of extensions for executable program files.
      Only req'd for msdos & vms like systems ..."
@@ -991,6 +846,203 @@
     "Modified: / 10.11.1998 / 20:54:37 / cg"
 !
 
+executeCommand:aCommandString inputFrom:anInStream outputTo:anOutStream 
+    errorTo:anErrStream auxFrom:anAuxStream environment:environmentDictionary
+    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, or the value of aBlock if not.
+     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
+                        environment:environmentDictionary                        
+                        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 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
@@ -1461,24 +1513,6 @@
     "Created: / 10.11.1998 / 21:05:45 / cg"
 !
 
-fork
-    "fork a new (HEAVY-weight) unix process.
-     Not supported with MSDOS & VMS systems.
-     Dont confuse this with Block>>fork, which creates
-     lightweight smalltalk processes. This method will return
-     0 to the child process, and a non-zero number (which is the childs
-     unix-process-id) to the parent (original) process.
-
-     In normal situations, you dont need to use this low level entry; see
-     #startProcess: and #executCommand: for higher level interfaces."
-
-    "/
-    "/ not supported by OS
-    "/
-
-    ^ UnsupportedOperationSignal raise
-!
-
 getCommandOutputFrom:aCommand
     "execute a simple command (such as hostname) and
      return the commands first line of output as a string (forget stdErr).
@@ -1574,15 +1608,6 @@
 
 !
 
-getVMSSymbol:aSymbolString
-    "get a symbols value, or nil if there is none"
-
-    ^ nil
-
-    "Created: / 5.6.1998 / 19:02:50 / cg"
-    "Modified: / 5.6.1998 / 19:03:15 / cg"
-!
-
 nameOfSTXExecutable
     "return the name of the running ST/X executable program.
      Usually, 'stx' is returned - but may be different for
@@ -1616,7 +1641,174 @@
     "
      OperatingSystem pathOfSTXExecutable
     "
-!
+! !
+
+!AbstractOperatingSystem class methodsFor:'executing OS commands - low level'!
+
+exec:aCommandPath withArguments:argArray
+    "execute the OS command specified by the argument, aCommandPath, with
+     arguments in argArray (no arguments, if nil).
+     If successful, this method does NOT return and smalltalk is gone.
+     If not successful, it does return. 
+     Can be used on UNIX with fork or on other systems to chain to another program."
+
+    ^ self 
+        exec:aCommandPath 
+        withArguments:argArray
+        environment:nil
+        fileDescriptors:#(0 1 2)
+        fork:false 
+        newPgrp:false
+        inDirectory:nil
+
+    "/ never reached ...
+
+    "Modified: / 12.11.1998 / 14:44:26 / cg"
+!
+
+exec:aCommandPath withArguments:argArray environment:env fileDescriptors:fds fork:doFork newPgrp:newGrp inDirectory:aDirectory
+    "execute an OS command"
+
+    ^ self subclassResponsibility
+
+    "Created: / 12.11.1998 / 14:46:15 / cg"
+!
+
+exec:aCommandPath withArguments:argArray fileDescriptors:fileDescriptors fork:doFork newPgrp:newPgrp inDirectory:aDirectory
+    ^ self 
+        exec:aCommandPath
+        withArguments:argArray
+        environment:nil
+        fileDescriptors:fileDescriptors
+        fork:doFork 
+        newPgrp:newPgrp
+        inDirectory:aDirectory
+!
+
+exec:aCommandPath withArguments:argArray fork:doFork
+    "execute an OS command without I/O redirection.
+     The command reads its input and writes its output
+     from/to whatever terminal device ST/X was started
+     (typically, the terminal window)"
+
+    ^ self 
+        exec:aCommandPath 
+        withArguments:argArray 
+        environment:nil
+        fileDescriptors:#(0 1 2)
+        fork:doFork 
+        newPgrp:false
+        inDirectory:nil
+
+    "
+     |id|
+
+     id := OperatingSystem fork.
+     id == 0 ifTrue:[
+        'I am the child'.
+        OperatingSystem 
+            exec:'/bin/ls' 
+            withArguments:#('ls' '/tmp')
+            fork:false.
+        'not reached'.
+     ]
+    "
+
+    "
+     |id|
+
+     id := OperatingSystem fork.
+     id == 0 ifTrue:[
+        'I am the child'.
+        OperatingSystem 
+            exec:'/bin/sh' 
+            withArguments:#('sh' '-c' 'sleep 2;echo 1;sleep 2;echo 2')
+            fork:false.
+        'not reached'.
+     ].
+     id printNL.
+     (Delay forSeconds:3.5) wait.
+     'killing ...' printNL.
+     OperatingSystem sendSignal:(OperatingSystem sigTERM) to:id.
+     OperatingSystem sendSignal:(OperatingSystem sigKILL) to:id
+    "
+
+    "Modified: / 15.7.1997 / 15:54:32 / stefan"
+    "Modified: / 12.11.1998 / 14:44:46 / cg"
+!
+
+exec:aCommandPath withArguments:argArray fork:doFork inDirectory:aDirectory
+    "execute an OS command without I/O redirection.
+     The command reads its input and writes its output
+     from/to whatever terminal device ST/X was started
+     (typically, the terminal window)"
+
+    ^ self 
+        exec:aCommandPath
+        withArguments:argArray
+        environment:nil
+        fileDescriptors:#(0 1 2)
+        fork:doFork 
+        newPgrp:false
+        inDirectory:aDirectory
+
+    "
+     |id|
+
+     id := OperatingSystem fork.
+     id == 0 ifTrue:[
+        'I am the child'.
+        OperatingSystem 
+            exec:'/bin/ls' 
+            withArguments:#('ls' '/tmp')
+            fork:false.
+        'not reached'.
+     ]
+    "
+
+    "
+     |id|
+
+     id := OperatingSystem fork.
+     id == 0 ifTrue:[
+        'I am the child'.
+        OperatingSystem 
+            exec:'/bin/sh' 
+            withArguments:#('sh' '-c' 'sleep 2;echo 1;sleep 2;echo 2')
+            fork:false.
+        'not reached'.
+     ].
+     id printNL.
+     (Delay forSeconds:3.5) wait.
+     'killing ...' printNL.
+     OperatingSystem sendSignal:(OperatingSystem sigTERM) to:id.
+     OperatingSystem sendSignal:(OperatingSystem sigKILL) to:id
+    "
+
+    "Created: / 28.1.1998 / 14:14:03 / md"
+    "Modified: / 28.1.1998 / 14:14:45 / md"
+    "Modified: / 12.11.1998 / 14:45:06 / cg"
+!
+
+fork
+    "fork a new (HEAVY-weight) unix process.
+     Not supported with MSDOS & VMS systems.
+     Dont confuse this with Block>>fork, which creates
+     lightweight smalltalk processes. This method will return
+     0 to the child process, and a non-zero number (which is the childs
+     unix-process-id) to the parent (original) process.
+
+     In normal situations, you dont need to use this low level entry; see
+     #startProcess: and #executCommand: for higher level interfaces."
+
+    "/
+    "/ not supported by OS
+    "/
+
+    ^ UnsupportedOperationSignal raise
+! !
+
+!AbstractOperatingSystem class methodsFor:'executing OS commands - private'!
 
 startProcess:aCommandString
     "start executing the OS command as specified by the argument, aCommandString
@@ -2882,6 +3074,15 @@
     "
      OperatingSystem exitWithCoreDump - dont evaluate this
     "
+!
+
+getVMSSymbol:aSymbolString
+    "get a symbols value, or nil if there is none"
+
+    ^ nil
+
+    "Created: / 5.6.1998 / 19:02:50 / cg"
+    "Modified: / 5.6.1998 / 19:03:15 / cg"
 ! !
 
 !AbstractOperatingSystem class methodsFor:'os queries'!
@@ -4430,7 +4631,7 @@
 !AbstractOperatingSystem class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/AbstractOperatingSystem.st,v 1.89 2003-03-26 13:14:53 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/AbstractOperatingSystem.st,v 1.90 2003-04-03 12:47:52 stefan Exp $'
 ! !
 
 AbstractOperatingSystem initialize!
--- a/UnixOperatingSystem.st	Thu Apr 03 10:45:30 2003 +0200
+++ b/UnixOperatingSystem.st	Thu Apr 03 14:48:50 2003 +0200
@@ -3296,7 +3296,9 @@
     "Modified: / 5.6.1998 / 17:40:48 / cg"
 !
 
-exec:aCommandPath withArguments:argColl environment:env fileDescriptors:fdColl fork:doFork newPgrp:newPgrp inDirectory:aDirectory
+exec:aCommandPath withArguments:argColl environment:environmenDictionary
+    fileDescriptors:fdColl fork:doFork newPgrp:newPgrp inDirectory:aDirectory
+
     "Internal lowLevel entry for combined fork & exec;
 
      If fork is false (chain a command):
@@ -3324,7 +3326,7 @@
         The processgroup will be equal to id.
         newPgrp is not used on WIN32 and VMS systems.
 
-     env specifies environment variables which are passed differently from
+     environmenDictionary specifies environment variables which are passed differently from
      the current environment. If non-nil, it must be a dictionary providing
      key-value pairs for changed/added environment variables.
      To pass a variable as empty (i.e. unset), pass a nil value.
@@ -3336,9 +3338,9 @@
 
     |envPairs argArray fdArray dirName|
 
-    env notNil ifTrue:[
+    environmenDictionary notNil ifTrue:[
         envPairs := OrderedCollection new.
-        env keysAndValuesDo:[:key :val |
+        environmenDictionary keysAndValuesDo:[:key :val |
             envPairs add:key; add:val
         ].
         envPairs := envPairs asArray.
@@ -3771,7 +3773,10 @@
     "Modified: / 5.6.1998 / 19:03:32 / cg"
 !
 
-startProcess:aCommandString inputFrom:anExternalInStream outputTo:anExternalOutStream errorTo:anExternalErrStream auxFrom:anAuxiliaryStream inDirectory:dir
+startProcess:aCommandString inputFrom:anExternalInStream outputTo:anExternalOutStream 
+    errorTo:anExternalErrStream auxFrom:anAuxiliaryStream 
+    environment:anEvironmentDictionary inDirectory:dir
+
     "start executing the OS command as specified by the argument, aCommandString
      as a separate process; do not wait for the command to finish.
      The commandString is passed to a shell for execution - see the description of
@@ -3806,7 +3811,7 @@
     rslt := self
         exec:(shellAndArgs at:1)
         withArguments:(shellAndArgs at:2)
-        environment:nil
+        environment:anEvironmentDictionary
         fileDescriptors:(Array with:in fileDescriptor
                                with:out fileDescriptor
                                with:err fileDescriptor
@@ -3853,6 +3858,14 @@
     "Modified: / 15.7.1997 / 16:03:51 / stefan"
     "Modified: / 5.6.1998 / 19:03:51 / cg"
     "Created: / 12.11.1998 / 14:39:20 / cg"
+!
+
+startProcess:aCommandString inputFrom:anExternalInStream outputTo:anExternalOutStream
+    errorTo:anExternalErrStream auxFrom:anAuxiliaryStream inDirectory:dir
+
+    ^ self startProcess:aCommandString inputFrom:anExternalInStream 
+            outputTo:anExternalOutStream errorTo:anExternalErrStream auxFrom:anAuxiliaryStream 
+            environment:nil inDirectory:dir
 ! !
 
 !UnixOperatingSystem class methodsFor:'file access'!
@@ -12731,7 +12744,7 @@
 !UnixOperatingSystem class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.152 2003-04-01 10:36:44 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/UnixOperatingSystem.st,v 1.153 2003-04-03 12:48:50 stefan Exp $'
 ! !
 
 UnixOperatingSystem initialize!