#BUGFIX by cg
class: TerminalSession
added: #closeDownShell:
removed: #killShell
comment/format in:
#closeDownShell
#defineWindowSizeLines:columns:
#startCommand:in:environment:setupTerminalWith:terminatedAction:
changed:
#outputFromAction:prompt:timeout:to:
#readerProcessLoop
#startReaderProcess
#stopReaderProcess
class: TerminalSession class
added: #examples
comment/format in:
#documentation
#initialize
--- a/TerminalSession.st Thu Apr 26 10:17:02 2018 +0200
+++ b/TerminalSession.st Fri Apr 27 15:13:16 2018 +0200
@@ -1,3 +1,5 @@
+"{ Encoding: utf8 }"
+
"
COPYRIGHT (c) 2013 by eXept Software AG
All Rights Reserved
@@ -43,6 +45,8 @@
documentation
"
+ This is basically a TerminalView without a view.
+
This keeps the state and API to interact with another program
via a terminal session. Under Unix, a pseudo-tty connection
is used; other operating systems might use other mechanisms.
@@ -67,6 +71,37 @@
inStream - the controlled program's input (a pty-half)
errStream - the controlled program's output (a pty-half)
"
+!
+
+examples
+"
+ |session|
+
+ session := TerminalSession new.
+ session startReaderProcess.
+ session
+ pluggableProcessInputAction:[:buffer :n |
+ Transcript show:(buffer copyTo:n).
+ Transcript endEntry.
+ ].
+ session
+ startCommand:'ls -l' in:'~'
+ environment:nil
+ setupTerminalWith:[]
+ terminatedAction:[ Transcript showCR:'finished' ].
+
+ session
+ startCommand:'(cd ~/work/cg/schemeNew ; make)' in:'~'
+ environment:nil
+ setupTerminalWith:[]
+ terminatedAction:[ Transcript showCR:'finished' ].
+
+ session
+ startCommand:'(ls ~/work/cg/schemeNew)' in:'~'
+ environment:nil
+ setupTerminalWith:[]
+ terminatedAction:[ Transcript showCR:'finished' ].
+"
! !
!TerminalSession class methodsFor:'initialization'!
@@ -77,6 +112,7 @@
"
self initialize
+ Debug := true.
"
! !
@@ -144,19 +180,32 @@
closeDownShell
"shut down my shell process"
- |pid|
+ self closeDownShell:1
+!
+
+closeDownShell:waitTimeInSeconds
+ "shut down my shell process"
+
+ |pid waitTime|
(pid := shellPid) notNil ifTrue:[
+ Logger info:'killing shell pid=%1' with:shellPid.
Debug ifTrue:[
- Transcript show:'killing shell pid='; showCR:pid.
+ Logger info:'killing shell pid=%1' with:pid.
].
- OperatingSystem isMSWINDOWSlike ifFalse:[
- OperatingSystem terminateProcessGroup:pid.
- ].
+ OperatingSystem terminateProcessGroup:pid.
OperatingSystem terminateProcess:pid.
- Delay waitForSeconds:0.2.
+
+ waitTime := 0.
+ [shellPid notNil and:[waitTime < waitTimeInSeconds]] whileTrue:[
+ Delay waitForSeconds:0.1.
+ waitTime := waitTime + 0.1
+ ].
+ Logger info:'shell pid after SIGTERM=%1' with:shellPid.
+
+ "/ still not dead?
shellPid notNil ifTrue:[
- "/ Delay waitForSeconds:1.
+ Logger info:'stil not dead after %1' with:waitTimeInSeconds.
OperatingSystem isMSWINDOWSlike ifFalse:[
OperatingSystem killProcessGroup:pid.
].
@@ -256,29 +305,6 @@
].
!
-killShell
- "shut down my shell process and stop the background reader thread."
-
- |pid|
-
- (pid := shellPid) notNil ifTrue:[
- Debug ifTrue:[
- Transcript show:'killing shell pid='; showCR:pid.
- ].
- OperatingSystem terminateProcessGroup:pid.
- OperatingSystem terminateProcess:pid.
- Delay waitForSeconds:1.
- shellPid notNil ifTrue:[
- OperatingSystem isMSWINDOWSlike ifFalse:[
- OperatingSystem killProcessGroup:pid.
- ].
- OperatingSystem killProcess:pid.
- shellPid := nil.
- ].
- OperatingSystem closePid:pid.
- ].
-!
-
reinitialize
shellPid := nil.
inStream := outStream := errStream := nil.
@@ -328,8 +354,7 @@
shellPid := Processor
monitor:[
Debug ifTrue:[
- Transcript show:'exec:'; show:shell.
- Transcript show:' args:'; showCR:args.
+ Logger info:'exec: "%1" args: "%2"' with:shell with:args.
].
OperatingSystem
exec:shell
@@ -343,10 +368,10 @@
]
action:[:status |
Debug ifTrue:[
- Transcript show:'pid:'; showCR:status pid.
- Transcript show:'status:'; showCR:status status.
- Transcript show:'code:'; showCR:status code.
- Transcript show:'core:'; showCR:status core.
+ Logger info:'pid: %1' with:status pid.
+ Logger info:'status %1:' with:status status.
+ Logger info:'code: %1' with:status code.
+ Logger info:'core %1:' with:status core.
].
status stillAlive ifFalse:[
exitStatus := status.
@@ -356,6 +381,8 @@
].
].
+ Logger info:'started shell pid: %1' with:shellPid.
+
"close the slave side of the pty/pipes (only used by the child)"
pty notNil ifTrue:[
(pty at:2) close.
@@ -437,10 +464,7 @@
!
defineWindowSizeLines:numberOfLines columns:numberOfColumns
- | delta prevNumCols prevNumLines|
-
"/ any idea, how to do this under windows ?
-
OperatingSystem isUNIXlike ifTrue:[
"/
"/ tell the pty;
@@ -450,14 +474,14 @@
and:[inStream isExternalStream
and:[inStream isOpen]]) ifTrue:[
Debug ifTrue:[
- Transcript showCR:'TerminalSession [info]: changed len to ', numberOfLines printString.
+ Logger info:'TerminalSession [info]: changed len to %1' with:numberOfLines.
].
(OperatingSystem
setWindowSizeOnFileDescriptor:inStream fileDescriptor
width:numberOfColumns
height:numberOfLines) ifFalse:[
Debug ifTrue:[
- Transcript showCR:'TerminalSession [warning]: cannot change windowSize'.
+ Logger info:'TerminalSession [warning]: cannot change windowSize'.
].
].
@@ -534,9 +558,9 @@
(gotPrompt := (sema waitWithTimeout:seconds) notNil) ifFalse:[
newSize := collectedOutput size.
self debuggingCodeFor:#cg is:[
- Transcript show:'timeout - output size is: '; showCR:newSize.
+ Logger info:'timeout - output size is: %1' with:newSize.
(newSize between:1 and:1000) ifTrue:[
- Transcript show:'output is: '; showCR:collectedOutput contents.
+ Logger info:'output is: "%1"' with:collectedOutput contents.
].
].
@@ -759,30 +783,33 @@
|n sensor|
readerDelay notNil ifTrue:[ Delay waitForSeconds:readerDelay].
- outStream isNil ifTrue:[^ self].
- outStream readWait.
+ outStream isNil ifTrue:[
+ Delay waitForSeconds:0.1
+ ] ifFalse:[
+ outStream readWait.
- (pluggableCheckBeforeReadAction isNil
- or:[pluggableCheckBeforeReadAction value]) ifTrue:[
- n := self readAnyAvailableData.
- n == 0 ifTrue:[
- "/ Windows IPC has a bug - it always
- "/ returns 0 (when the command is idle)
- "/ and says it's at the end (sigh)
+ (pluggableCheckBeforeReadAction isNil
+ or:[pluggableCheckBeforeReadAction value]) ifTrue:[
+ n := self readAnyAvailableData.
+ n == 0 ifTrue:[
+ "/ Windows IPC has a bug - it always
+ "/ returns 0 (when the command is idle)
+ "/ and says it's at the end (sigh)
- OperatingSystem isMSWINDOWSlike ifTrue:[
- Delay waitForSeconds:0.1
- ] ifFalse:[
- outStream atEnd ifTrue:[
- outStream close. outStream := nil.
- inStream close. inStream := nil.
- Processor activeProcess terminate.
+ OperatingSystem isMSWINDOWSlike ifTrue:[
+ Delay waitForSeconds:0.1
] ifFalse:[
- "/ this should not happen.
+ outStream atEnd ifTrue:[
+ outStream close. outStream := nil.
+ inStream close. inStream := nil.
+ Processor activeProcess terminate.
+ ] ifFalse:[
+ "/ this should not happen.
- Delay waitForSeconds:0.1
- ]
- ].
+ Delay waitForSeconds:0.1
+ ]
+ ].
+ ]
]
]
]
@@ -794,6 +821,7 @@
"Start a reader process, which looks for the commands output,
and sends me #processInput:n: events whenever something arrives."
+ Logger info:'start reader'.
readerProcess isNil ifTrue:[
readerProcess := [
[
@@ -818,6 +846,7 @@
|p|
+ Logger info:'stop reader'.
(p := readerProcess) notNil ifTrue:[
readerProcess := nil.
p terminate.