Apple2-IO-RPi/RaspberryPi/apple2driver/handlers/shell.go
Oliver Schmidt 7b3c5a13c3 Fix shell shutdown (?)
At least on the alternative hardware, things happen this way:
1. The shell 'exit' command has ptyOut signal outputComplete and terminate. At this point, ptyIn is still blocked up to one second in ReadCharacter - and can therefore not react on outputComplete.
2. The outputComplete handler in ShellCommand sends a zero byte. This makes on the A2 DumpOutput return and the caller send a zero byte. ShellCommand must not return here as ptyIn is still blocked.
3. The zero byte from the A2 has ptyIn return from ReadCharacter, signal userCancelled and terminate.
4. ShellCommand can now return, but it must not send a zero byte, as the A2 is not expecting it - and will therefore later misinterpret it.

Maybe the original hardware needs another handling, then differentiation of behavior in shell.go becomes necessary.
2024-02-04 21:45:24 -05:00

109 lines
2.1 KiB
Go
Executable File

// Copyright Terence J. Boldt (c)2020-2022
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
// This file contains the handler for executing Linux shell
package handlers
import (
"fmt"
"io"
"os"
"os/exec"
"github.com/creack/pty"
)
// ShellCommand handles requests for the Apple II executing a Linux shell
func ShellCommand() {
cmd := exec.Command("bash", "-i")
cmd.Env = append(os.Environ(),
"TERM=vt100",
"LINES=24",
"COLUMNS=79",
)
var ws pty.Winsize
ws.Cols = 79
ws.Rows = 24
ws.X = 0
ws.Y = 0
ptmx, _ := pty.StartWithSize(cmd, &ws)
defer func() { _ = ptmx.Close() }()
outputComplete := make(chan bool)
inputComplete := make(chan bool)
userCancelled := make(chan bool)
go ptyIn(ptmx, outputComplete, inputComplete, userCancelled)
go ptyOut(ptmx, outputComplete, userCancelled)
for {
select {
case <-outputComplete:
ptmx.Close()
comm.WriteByte(0)
// return
break
case <-userCancelled:
fmt.Printf("User cancelled, killing process\n")
ptmx.Close()
cmd.Process.Kill()
// comm.WriteByte(0)
return
case <-inputComplete:
cmd.Wait()
comm.WriteByte(0)
return
}
}
}
func ptyOut(stdout io.ReadCloser, outputComplete chan bool, userCancelled chan bool) {
for {
select {
case <-userCancelled:
fmt.Printf("User Cancelled stdout\n")
stdout.Close()
return
default:
bb := make([]byte, 1)
n, err := stdout.Read(bb)
if err != nil {
stdout.Close()
outputComplete <- true
fmt.Printf("stdout closed\n")
return
} else if n > 0 {
b := bb[0]
comm.SendCharacter(b)
}
}
}
}
func ptyIn(stdin io.WriteCloser, done chan bool, inputComplete chan bool, userCancelled chan bool) {
for {
select {
case <-done:
stdin.Close()
inputComplete <- true
fmt.Printf("stdin closed\n")
return
default:
s, err := comm.ReadCharacter()
if err == nil {
if s == string(byte(0x00)) {
stdin.Close()
userCancelled <- true
fmt.Printf("\nUser cancelled stdin\n")
return
}
io.WriteString(stdin, string(s))
}
}
}
}