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
|
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 h1:EuVc7Q2JDzIY2mk5mjtq4K5BgTuO+kj5LXzCwjOK+mo=
|
||||||
github.com/xtaci/gaio v1.2.9/go.mod h1:rJMerwiLCLnKa14YTM/sRggTPrnBZrlCg9U3DnV5VBE=
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/creack/pty"
|
||||||
"github.com/xtaci/gaio"
|
"github.com/xtaci/gaio"
|
||||||
"versestudios.com/go-telnet-asyncio-test/openconn"
|
"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 {
|
func doorHandler(ctx context.Context, c net.Conn, w *gaio.Watcher, menuwg *sync.WaitGroup) error {
|
||||||
defer menuwg.Done()
|
defer menuwg.Done()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
const bannerText = "\r\nCOLOSSAL CAVE\r\n\r\n"
|
const bannerText = "\r\nCOLOSSAL CAVE\r\n\r\n"
|
||||||
|
|
||||||
err := w.Write(ctx, c, []byte(bannerText))
|
err := w.Write(ctx, c, []byte(bannerText))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error writing to connection: %v\n", err)
|
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")
|
cmd := exec.Command("/usr/games/adventure")
|
||||||
|
|
||||||
stdout, _ := cmd.StdoutPipe()
|
ptmx, err := pty.Start(cmd)
|
||||||
stdin, _ := cmd.StdinPipe()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Make sure to close the pty at the end.
|
||||||
|
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||||
|
|
||||||
cmd.Start()
|
/*
|
||||||
|
stdout, _ := cmd.StdoutPipe()
|
||||||
var wg sync.WaitGroup
|
stdin, _ := cmd.StdinPipe()
|
||||||
wg.Add(1)
|
*/
|
||||||
|
//CmdOutChan := make(chan []byte)
|
||||||
Terminator := make(chan bool)
|
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()
|
defer wg.Done()
|
||||||
Check:
|
waitingForInput := false
|
||||||
|
IOLoop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-*Terminator:
|
|
||||||
break Check
|
|
||||||
case result, ok := <-inChan:
|
case result, ok := <-inChan:
|
||||||
|
var l int
|
||||||
|
log.Println("i")
|
||||||
if ok {
|
if ok {
|
||||||
if result.Operation == gaio.OpRead {
|
if result.Operation == gaio.OpRead && result.Size > 0 {
|
||||||
// we asked for a read - send it to stdin
|
// 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 {
|
} else {
|
||||||
log.Println("yikes, problem getting a result from inChan in doorHandler!")
|
log.Println("yikes, problem getting a result from inChan in doorHandler!")
|
||||||
}
|
}
|
||||||
|
case <-Terminator:
|
||||||
|
log.Println("Terminator'd!!!")
|
||||||
|
break IOLoop
|
||||||
default:
|
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)
|
time.Sleep(time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
log.Println("exiting stdout reader goroutine")
|
||||||
log.Println("exiting stdin reader goroutine")
|
Terminator <- true
|
||||||
}(&Terminator, &wg)
|
}(Terminator, &wg, &stdout)
|
||||||
|
|
||||||
log.Println("starting stdout reader goroutine")
|
// goroutine to handle actual IO calls
|
||||||
scanner := bufio.NewScanner(stdout)
|
wg.Add(1)
|
||||||
scanner.Split(bufio.ScanLines)
|
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() {
|
// if there's data coming in from remote conn, send it to stdin for cmd
|
||||||
m := scanner.Bytes()
|
case result, ok := <-inChan:
|
||||||
m = append(m, '\n')
|
log.Printf("i")
|
||||||
w.Write(ctx, c, m)
|
if ok {
|
||||||
log.Printf("wrote %d bytes to watcher from door stdout: %v\n", len(m)+1, string(m))
|
if result.Operation == gaio.OpRead && result.Size > 0 {
|
||||||
}
|
// we asked for a read - send it to stdin
|
||||||
log.Println("exiting stdout reader goroutine")
|
stdin.Write(result.Buffer[:result.Size])
|
||||||
Terminator <- true
|
}
|
||||||
|
} else {
|
||||||
log.Println("waiting for wg")
|
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()
|
wg.Wait()
|
||||||
log.Println("waiting for cmd")
|
log.Println("waiting for cmd")
|
||||||
exitErr := cmd.Wait()
|
exitErr := cmd.Wait()
|
||||||
|
Loading…
Reference in New Issue
Block a user