mirror of
https://github.com/tjboldt/Apple2-IO-RPi.git
synced 2024-05-29 00:41:45 +00:00
7b3c5a13c3
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.
109 lines
2.1 KiB
Go
Executable File
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))
|
|
}
|
|
}
|
|
}
|
|
}
|