holy shit, full duplex-ish net.conn to exec.cmd is working
This commit is contained in:
parent
2a295b43b9
commit
cf2bf6f872
6
go.mod
6
go.mod
|
@ -2,4 +2,8 @@ module versestudios.com/go-telnet-asyncio-test
|
|||
|
||||
go 1.16
|
||||
|
||||
require github.com/xtaci/gaio v1.2.9
|
||||
require (
|
||||
github.com/creack/pty v1.1.11 // indirect
|
||||
github.com/xtaci/gaio v1.2.9
|
||||
golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0 // indirect
|
||||
)
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1,2 +1,6 @@
|
|||
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/xtaci/gaio v1.2.9 h1:EuVc7Q2JDzIY2mk5mjtq4K5BgTuO+kj5LXzCwjOK+mo=
|
||||
github.com/xtaci/gaio v1.2.9/go.mod h1:rJMerwiLCLnKa14YTM/sRggTPrnBZrlCg9U3DnV5VBE=
|
||||
golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0 h1:g9s1Ppvvun/fI+BptTMj909BBIcGrzQ32k9FNlcevOE=
|
||||
golang.org/x/sys v0.0.0-20210414055047-fe65e336abe0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
166
main.go
166
main.go
|
@ -1,17 +1,19 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"flag"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/xtaci/gaio"
|
||||
"versestudios.com/go-telnet-asyncio-test/openconn"
|
||||
)
|
||||
|
@ -305,7 +307,9 @@ func exitHandler() []byte {
|
|||
func doorHandler(ctx context.Context, c net.Conn, w *gaio.Watcher, menuwg *sync.WaitGroup) error {
|
||||
defer menuwg.Done()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
const bannerText = "\r\nCOLOSSAL CAVE\r\n\r\n"
|
||||
|
||||
err := w.Write(ctx, c, []byte(bannerText))
|
||||
if err != nil {
|
||||
log.Printf("error writing to connection: %v\n", err)
|
||||
|
@ -320,52 +324,154 @@ func doorHandler(ctx context.Context, c net.Conn, w *gaio.Watcher, menuwg *sync.
|
|||
|
||||
cmd := exec.Command("/usr/games/adventure")
|
||||
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
stdin, _ := cmd.StdinPipe()
|
||||
ptmx, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Make sure to close the pty at the end.
|
||||
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||
|
||||
cmd.Start()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
/*
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
stdin, _ := cmd.StdinPipe()
|
||||
*/
|
||||
//CmdOutChan := make(chan []byte)
|
||||
Terminator := make(chan bool)
|
||||
go func(Terminator *chan bool, wg *sync.WaitGroup) {
|
||||
log.Println("starting stdin reader goroutine")
|
||||
|
||||
// cmd.Start()
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
Check:
|
||||
waitingForInput := false
|
||||
IOLoop:
|
||||
for {
|
||||
select {
|
||||
case <-*Terminator:
|
||||
break Check
|
||||
case result, ok := <-inChan:
|
||||
var l int
|
||||
log.Println("i")
|
||||
if ok {
|
||||
if result.Operation == gaio.OpRead {
|
||||
if result.Operation == gaio.OpRead && result.Size > 0 {
|
||||
// we asked for a read - send it to stdin
|
||||
stdin.Write(result.Buffer[:result.Size])
|
||||
l, err = ptmx.Write(result.Buffer[:result.Size])
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// terminal closed? wtfever
|
||||
log.Println("EOF on cmd.write!!!")
|
||||
break IOLoop
|
||||
}
|
||||
}
|
||||
if l > 0 {
|
||||
log.Printf("wrote %d bytes to cmd stdin: %v\n", l, string(result.Buffer[:result.Size]))
|
||||
waitingForInput = false
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
log.Println("yikes, problem getting a result from inChan in doorHandler!")
|
||||
}
|
||||
case <-Terminator:
|
||||
log.Println("Terminator'd!!!")
|
||||
break IOLoop
|
||||
default:
|
||||
if !waitingForInput {
|
||||
log.Println("o")
|
||||
var l int
|
||||
outBuf := make([]byte, 2048)
|
||||
ptmx.SetReadDeadline(time.Now().Add(time.Millisecond))
|
||||
if l, err = ptmx.Read(outBuf); err != nil {
|
||||
if err == io.EOF {
|
||||
log.Println("EOF on cmd stdout - outta here")
|
||||
break IOLoop
|
||||
} else if err == os.ErrDeadlineExceeded {
|
||||
// do nothing but be quiet about it ffs
|
||||
} else if strings.TrimSpace(err.Error()) == "input/output error" {
|
||||
// program exited methinks - get outta here
|
||||
break IOLoop
|
||||
} else {
|
||||
log.Printf("err reading from cmd term: %v\n", err)
|
||||
break IOLoop
|
||||
}
|
||||
}
|
||||
if l > 0 {
|
||||
w.Write(ctx, c, outBuf[:l])
|
||||
log.Printf("wrote %d bytes to watcher: %v\n", l, string(outBuf[:l]))
|
||||
if outBuf[l-1] == byte('>') {
|
||||
// looks like a prompt to me, I hope!
|
||||
// let's add a space just for fun
|
||||
outBuf[0] = byte(' ')
|
||||
w.Write(ctx, c, outBuf[0:1])
|
||||
// and now block until we get a read from the connection dammit
|
||||
waitingForInput = true
|
||||
}
|
||||
} else {
|
||||
// zero output? let's get some input?
|
||||
// log.Println("0-read from cmd output - queueing watcher read")
|
||||
w.Read(ctx, c, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
/*
|
||||
// goroutine to read cmd stdout in a loop and write it to CmdOutChan.
|
||||
// on io.EOF in the scanner, will send Terminator to kill the IOLoop
|
||||
// that follows the goroutine
|
||||
go func(Terminator chan bool, wg *sync.WaitGroup, stdout *io.ReadCloser) {
|
||||
defer wg.Done()
|
||||
log.Println("starting stdout reader goroutine")
|
||||
scanner := bufio.NewScanner(*stdout)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
for scanner.Scan() {
|
||||
m := scanner.Bytes()
|
||||
m = append(m, '\n')
|
||||
CmdOutChan <- m
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}
|
||||
log.Println("exiting stdin reader goroutine")
|
||||
}(&Terminator, &wg)
|
||||
log.Println("exiting stdout reader goroutine")
|
||||
Terminator <- true
|
||||
}(Terminator, &wg, &stdout)
|
||||
|
||||
log.Println("starting stdout reader goroutine")
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
// goroutine to handle actual IO calls
|
||||
wg.Add(1)
|
||||
go func(Terminator chan bool, wg *sync.WaitGroup, stdout *io.ReadCloser) {
|
||||
defer wg.Done()
|
||||
log.Println("starting IOLoop")
|
||||
IOLoop:
|
||||
for {
|
||||
select {
|
||||
// if there's data coming out from cmd, write it to the watcher
|
||||
case cmdOutput := <-CmdOutChan:
|
||||
log.Printf("o")
|
||||
err := w.Write(ctx, c, cmdOutput)
|
||||
if err != nil {
|
||||
log.Printf("err writing to watcher from cmd: %v\n", err)
|
||||
} else {
|
||||
log.Printf("send %d bytes from CmdOutChan from cmd stdout to watcher: %v\n", len(cmdOutput), string(cmdOutput))
|
||||
}
|
||||
|
||||
for scanner.Scan() {
|
||||
m := scanner.Bytes()
|
||||
m = append(m, '\n')
|
||||
w.Write(ctx, c, m)
|
||||
log.Printf("wrote %d bytes to watcher from door stdout: %v\n", len(m)+1, string(m))
|
||||
}
|
||||
log.Println("exiting stdout reader goroutine")
|
||||
Terminator <- true
|
||||
|
||||
log.Println("waiting for wg")
|
||||
// if there's data coming in from remote conn, send it to stdin for cmd
|
||||
case result, ok := <-inChan:
|
||||
log.Printf("i")
|
||||
if ok {
|
||||
if result.Operation == gaio.OpRead && result.Size > 0 {
|
||||
// we asked for a read - send it to stdin
|
||||
stdin.Write(result.Buffer[:result.Size])
|
||||
}
|
||||
} else {
|
||||
log.Println("yikes, problem getting a result from inChan in doorHandler!")
|
||||
}
|
||||
// if the Terminator shows up we're dead
|
||||
case <-Terminator:
|
||||
break IOLoop
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
log.Println("IOLoop exited")
|
||||
}(Terminator, &wg, &stdout)
|
||||
*/
|
||||
wg.Wait()
|
||||
log.Println("waiting for cmd")
|
||||
exitErr := cmd.Wait()
|
||||
|
|
Loading…
Reference in New Issue