Apple2-IO-RPi/RaspberryPi/apple2driver/handlers/exec.go

309 lines
7.7 KiB
Go
Raw Normal View History

// Copyright Terence J. Boldt (c)2020-2023
// Use of this source code is governed by an MIT
// license that can be found in the LICENSE file.
2022-01-11 04:00:58 +00:00
// This file contains the handler for executing Linux and internal
// commands
2021-11-03 09:33:09 +00:00
2021-05-30 11:18:39 +00:00
package handlers
import (
2021-10-23 20:39:02 +00:00
"errors"
2021-05-30 11:18:39 +00:00
"fmt"
"os"
"os/exec"
2022-03-04 03:23:04 +00:00
"strconv"
2021-05-30 11:18:39 +00:00
"strings"
2022-03-04 03:23:04 +00:00
"time"
2022-02-08 03:01:06 +00:00
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/drive"
2022-02-08 03:01:06 +00:00
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/info"
"github.com/tjboldt/ProDOS-Utilities/prodos"
2021-05-30 11:18:39 +00:00
)
var forceLowercase = false
2022-03-04 03:23:04 +00:00
var execTimeoutSeconds = int(10)
2021-11-03 09:33:09 +00:00
// ExecCommand handles requests for the Apple II executing Linux commands
func ExecCommand(drive1 *prodos.ReaderWriterAt, drive2 *prodos.ReaderWriterAt) {
2021-05-30 11:18:39 +00:00
workingDirectory, err := os.Getwd()
if err != nil {
workingDirectory = "/home"
2021-10-30 11:03:18 +00:00
comm.WriteString("Failed to get current working directory, setting to /home\r")
2021-05-30 11:18:39 +00:00
}
fmt.Printf("Reading command to execute...\n")
2021-10-30 11:03:18 +00:00
linuxCommand, err := comm.ReadString()
if forceLowercase {
linuxCommand = strings.ToLower(linuxCommand)
}
2021-12-28 21:23:27 +00:00
linuxCommand = strings.Trim(linuxCommand, " ")
if linuxCommand == "" {
linuxCommand = "a2help"
}
2021-05-30 11:18:39 +00:00
fmt.Printf("Command to run: %s\n", linuxCommand)
if strings.HasPrefix(linuxCommand, "cd ") {
workingDirectory = strings.Replace(linuxCommand, "cd ", "", 1)
err = os.Chdir(workingDirectory)
if err != nil {
2021-10-30 11:03:18 +00:00
comm.WriteString("Failed to set working directory\r")
2021-05-30 11:18:39 +00:00
return
}
2021-10-30 11:03:18 +00:00
comm.WriteString("Working directory set\r")
2021-05-30 11:18:39 +00:00
return
}
2022-02-08 03:01:06 +00:00
if linuxCommand == "a2version" {
a2version()
return
}
2021-05-30 11:18:39 +00:00
if linuxCommand == "a2help" {
2021-10-23 20:39:02 +00:00
a2help()
2021-05-30 11:18:39 +00:00
return
}
if linuxCommand == "a2lower" {
a2lower(false)
return
}
if linuxCommand == "A2LOWER" {
a2lower(true)
2021-09-28 21:47:52 +00:00
return
}
2021-05-30 13:37:00 +00:00
if linuxCommand == "a2wifi" {
2021-10-23 20:39:02 +00:00
a2wifi()
2021-05-30 13:37:00 +00:00
return
}
2022-03-04 03:23:04 +00:00
if strings.HasPrefix(linuxCommand, "a2timeout") {
a2timeout(linuxCommand)
return
}
if strings.HasPrefix(linuxCommand, "a2drive") {
a2drive(linuxCommand, drive1, drive2)
return
}
2021-11-05 19:15:54 +00:00
if linuxCommand == "a2prompt" {
prompt := fmt.Sprintf("A2IO:%s ", workingDirectory)
comm.WriteString(prompt)
return
}
2021-05-30 13:37:00 +00:00
if linuxCommand == "a2wifi list" {
2021-10-23 20:39:02 +00:00
linuxCommand = a2wifiList()
2021-05-30 13:37:00 +00:00
}
if strings.HasPrefix(linuxCommand, "a2wifi select") {
2021-10-23 20:39:02 +00:00
linuxCommand, err = a2wifiSelect(linuxCommand)
}
if err == nil {
execCommand(linuxCommand, workingDirectory)
2021-05-30 13:37:00 +00:00
}
2021-10-23 20:39:02 +00:00
}
func execCommand(linuxCommand string, workingDirectory string) {
2021-11-05 19:15:54 +00:00
// force the command to combine stderr(2) into stdout(1)
linuxCommand += " 2>&1"
2021-05-30 11:18:39 +00:00
cmd := exec.Command("bash", "-c", linuxCommand)
cmd.Dir = workingDirectory
cmd.Env = append(os.Environ(),
"TERM=vt100",
"LINES=24",
"COLUMNS=80",
)
2021-10-12 01:19:40 +00:00
stdout, err := cmd.StdoutPipe()
2021-05-30 11:18:39 +00:00
if err != nil {
2021-10-12 01:19:40 +00:00
fmt.Printf("Failed to set stdout\n")
2021-10-30 11:03:18 +00:00
comm.WriteString("Failed to set stdout\r")
2021-05-30 11:18:39 +00:00
return
}
2021-11-05 19:15:54 +00:00
2021-10-12 01:19:40 +00:00
err = cmd.Start()
2021-05-30 11:18:39 +00:00
if err != nil {
2021-10-12 01:19:40 +00:00
fmt.Printf("Failed to start command\n")
2021-10-30 11:03:18 +00:00
comm.WriteString("Failed to start command\r")
2021-05-30 11:18:39 +00:00
return
}
2021-10-12 01:19:40 +00:00
2022-03-04 03:23:04 +00:00
timeout := time.After(time.Duration(execTimeoutSeconds) * time.Second)
2021-10-12 01:19:40 +00:00
2021-10-23 20:39:02 +00:00
for {
select {
2022-03-04 03:23:04 +00:00
case <-timeout:
comm.WriteString("\rCancelled by apple2driver\r")
2021-10-23 20:39:02 +00:00
cmd.Process.Kill()
return
default:
bb := make([]byte, 1)
2022-03-04 03:23:04 +00:00
n, stdOutErr := stdout.Read(bb)
if stdOutErr == nil {
if n > 0 {
b := bb[0]
comm.SendCharacter(b)
2021-10-23 20:39:02 +00:00
}
2022-03-04 03:23:04 +00:00
} else {
comm.WriteByte(0)
cmd.Wait()
return
2021-10-12 01:19:40 +00:00
}
}
}
2021-10-23 20:39:02 +00:00
}
2021-10-12 01:19:40 +00:00
2022-02-08 03:01:06 +00:00
func a2version() {
2022-03-04 23:24:59 +00:00
comm.WriteString("\rDriver version: " + info.Version + "\r")
2022-02-08 03:01:06 +00:00
}
2021-10-23 20:39:02 +00:00
func a2help() {
2022-03-04 23:24:59 +00:00
comm.WriteString("\rDriver version: " + info.Version + "\r" +
"\r" +
"Example from ] prompt:\r" +
"]RPI ls /home/pi\r" +
"\r" +
"Example from Applesoft BASIC:\r" +
"]10 PRINT CHR$(4)\"RPI ping apple.com\"\r" +
"]RUN\r" +
"\r" +
"Driver commands called with RPI:\r" +
2021-10-23 20:39:02 +00:00
"a2help - display this message\r" +
2022-03-04 23:24:59 +00:00
"a2version - display driver version\r" +
2021-10-23 20:39:02 +00:00
"a2wifi - set up wifi\r" +
2022-03-04 03:23:04 +00:00
"a2timeout - seconds to timeout commands\r" +
2021-10-23 20:39:02 +00:00
"A2LOWER - force lowercase for II+\r" +
2022-03-04 23:24:59 +00:00
"a2lower - disable force lowercase\r" +
"a2drive - change drive images\r" +
2021-10-23 20:39:02 +00:00
"\r")
}
2022-03-04 03:23:04 +00:00
func a2timeout(linuxCommand string) {
params := strings.Fields(linuxCommand)
switch len(params) {
case 1:
2022-03-04 23:24:59 +00:00
comm.WriteString("\rCommand timeout: " + strconv.FormatInt(int64(execTimeoutSeconds), 10) + "\r")
2022-03-04 03:23:04 +00:00
case 2:
timeoutSeconds, err := strconv.ParseInt(params[1], 10, 32)
if err != nil {
comm.WriteString("\rFailed to parse timeout\r")
} else {
execTimeoutSeconds = int(timeoutSeconds)
2022-03-04 23:24:59 +00:00
comm.WriteString("\rCommand timeout set to: " + strconv.FormatInt(int64(execTimeoutSeconds), 10) + "\r")
2022-03-04 03:23:04 +00:00
}
default:
comm.WriteString("\rToo many parameters\n")
}
}
func a2drive(linuxCommand string, drive1 *prodos.ReaderWriterAt, drive2 *prodos.ReaderWriterAt) {
params := strings.Fields(linuxCommand)
if len(params) < 3 {
showa2DriveUsage()
return
}
driveNumber, err := strconv.ParseInt(params[1], 10, 32)
if err != nil {
comm.WriteString("\rFailed to parse drive number\r")
showa2DriveUsage()
return
}
if params[2] == "regen" {
directory, err := drive.GetDriveImageDirectory()
if err != nil {
comm.WriteString("\rFailed to parse source directory\r")
return
}
if len(params) > 3 {
directory = params[3]
}
switch driveNumber {
case 1:
*drive1, err = drive.GenerateDriveFromDirectory("APPLE2.IO.RPI", directory)
if err != nil {
comm.WriteString("\rFailed to regenerate drive 1\r")
return
}
comm.WriteString("\rDrive 1 regenerated\r")
case 2:
*drive2, err = drive.GenerateDriveFromDirectory("APPLE2.IO.RPI2", directory)
if err != nil {
comm.WriteString("\rFailed to regenerate drive 2\r")
return
}
comm.WriteString("\rDrive 2 regenerated\r")
default:
comm.WriteString("\rOnly drives 1 or 2 are supported\r")
showa2DriveUsage()
return
}
}
2023-02-05 00:09:57 +00:00
if params[2] == "load" {
if len(params) != 4 {
comm.WriteString("\rMust specify a drive image to load\r")
showa2DriveUsage()
return
}
imageFileName := params[3]
switch driveNumber {
case 1:
*drive1, err = os.OpenFile(imageFileName, os.O_RDWR, 0755)
if err != nil {
comm.WriteString("\rFailed to load drive 1\r")
return
}
comm.WriteString("\rDrive 1 loaded\r")
case 2:
*drive2, err = os.OpenFile(imageFileName, os.O_RDWR, 0755)
if err != nil {
comm.WriteString("\rFailed to load drive 2\r")
return
}
comm.WriteString("\rDrive 2 loaded\r")
default:
comm.WriteString("\rOnly drives 1 or 2 are supported\r")
showa2DriveUsage()
return
}
}
}
func showa2DriveUsage() {
comm.WriteString("\rUsage: a2drive DRIVENUMBER [regen [PATH] | load FILENAME]\rExamples: a2drive 1 regen ~/Apple2-IO-RPi/RaspberryPi/driveimage\r a2drive 2 load /home/pi/Games.hdv\r")
}
func a2lower(enable bool) {
forceLowercase = enable
comm.WriteString(fmt.Sprintf("All commands will be converted to lowercase: %t\r", forceLowercase))
2021-10-23 20:39:02 +00:00
}
func a2wifi() {
2021-10-30 11:03:18 +00:00
comm.WriteString("\r" +
2021-10-23 20:39:02 +00:00
"Usage: a2wifi list\r" +
2021-12-31 13:43:48 +00:00
" a2wifi select SSID PASSWORD REGION\r" +
2021-10-23 20:39:02 +00:00
"\r")
}
func a2wifiList() string {
return "sudo iwlist wlan0 scanning | grep ESSID | sed s/.*ESSID://g | sed s/\\\"//g"
}
func a2wifiSelect(linuxCommand string) (string, error) {
params := strings.Fields(linuxCommand)
2021-12-31 13:43:48 +00:00
if len(params) != 5 {
comm.WriteString("\rIncorrect number of parameters. Usage: a2wifi select SSID PASSWORD REGION\r\r")
return "", errors.New("Incorrect number of parameters. Usage: a2wifi select SSID PASSWORD REGION")
2021-10-23 20:39:02 +00:00
}
ssid := params[2]
psk := params[3]
2021-12-31 13:43:48 +00:00
region := params[4]
linuxCommand = "printf \"country=%s\\nupdate_config=1\\nctrl_interface=/var/run/wpa_supplicant\\n\\nnetwork={\\n scan_ssid=1\\n ssid=\\\"%s\\\"\n psk=\\\"%s\\\"\\n}\\n\" " +
region + " " +
2022-01-11 04:00:58 +00:00
ssid + " " +
2021-10-23 20:39:02 +00:00
psk + " " +
" > /tmp/wpa_supplicant.conf; " +
"sudo mv /tmp/wpa_supplicant.conf /etc/wpa_supplicant/; " +
"sudo wpa_cli -i wlan0 reconfigure"
return linuxCommand, nil
2021-05-30 11:18:39 +00:00
}