Windows terminal support using the termtest library.
Should even support resizing. Fully untested.
This commit is contained in:
parent
5280b8e838
commit
50d410f090
2
go.mod
2
go.mod
@ -3,6 +3,7 @@ module converge
|
|||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/ActiveState/termtest/conpty v0.5.0
|
||||||
github.com/creack/pty v1.1.21
|
github.com/creack/pty v1.1.21
|
||||||
github.com/gliderlabs/ssh v0.3.7
|
github.com/gliderlabs/ssh v0.3.7
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
@ -13,6 +14,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // indirect
|
||||||
golang.org/x/sys v0.22.0 // indirect
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
|
5
go.sum
5
go.sum
@ -1,3 +1,7 @@
|
|||||||
|
github.com/ActiveState/termtest/conpty v0.5.0 h1:JLUe6YDs4Jw4xNPCU+8VwTpniYOGeKzQg4SM2YHQNA8=
|
||||||
|
github.com/ActiveState/termtest/conpty v0.5.0/go.mod h1:LO4208FLsxw6DcNZ1UtuGUMW+ga9PFtX4ntv8Ymg9og=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||||
|
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||||
@ -36,6 +40,7 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
|||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
@ -10,3 +10,15 @@ type ReadWriteAddrCloser interface {
|
|||||||
|
|
||||||
RemoteAddr() net.Addr
|
RemoteAddr() net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReadWriterCombiner struct {
|
||||||
|
io.Reader
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *ReadWriterCombiner) Read(p []byte) (n int, err error) {
|
||||||
|
return rw.Reader.Read(p)
|
||||||
|
}
|
||||||
|
func (rw *ReadWriterCombiner) Write(p []byte) (n int, err error) {
|
||||||
|
return rw.Writer.Write(p)
|
||||||
|
}
|
||||||
|
@ -1,24 +1,81 @@
|
|||||||
package terminal
|
package terminal
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ActiveState/termtest/conpty"
|
||||||
"github.com/gliderlabs/ssh"
|
"github.com/gliderlabs/ssh"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
var PtySpawner = Spawner(func(sshSession ssh.Session, env []string, name string, arg ...string) (Process, error) {
|
var PtySpawner = Spawner(func(sshSession ssh.Session, env []string, name string, args ...string) (Process, error) {
|
||||||
return nil, nil
|
_, winCh, isPty := sshSession.Pty()
|
||||||
|
if !isPty {
|
||||||
|
return nil, fmt.Errorf("ssh session is not a pty")
|
||||||
|
}
|
||||||
|
|
||||||
|
cpty, err := conpty.New(80, 25)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pid, _, err := cpty.Spawn(
|
||||||
|
"cmd.exe",
|
||||||
|
args,
|
||||||
|
&syscall.ProcAttr{
|
||||||
|
Env: env,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
cpty.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Printf("New process with pid %d spawned\n", pid)
|
||||||
|
process, err := os.FindProcess(pid)
|
||||||
|
if err != nil {
|
||||||
|
cpty.Close()
|
||||||
|
return nil, fmt.Errorf("Failed to find process: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for win := range winCh {
|
||||||
|
err = cpty.Resize(uint16(win.Width), uint16(win.Height))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Feiled to resize terminal to %d x %d", win.Width, win.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ptyProcess{
|
||||||
|
cpty: cpty,
|
||||||
|
process: process,
|
||||||
|
}, nil
|
||||||
})
|
})
|
||||||
|
|
||||||
type ptyProcess struct {
|
type ptyProcess struct {
|
||||||
cmd *exec.Cmd
|
cpty *conpty.ConPty
|
||||||
f *os.File
|
process *os.Process
|
||||||
|
}
|
||||||
|
|
||||||
|
func (proc ptyProcess) Read(p []byte) (n int, err error) {
|
||||||
|
return proc.cpty.OutPipe().Read(p)
|
||||||
|
}
|
||||||
|
func (proc ptyProcess) Write(p []byte) (n int, err error) {
|
||||||
|
return proc.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ptyProcess) Pipe() io.ReadWriter {
|
func (p ptyProcess) Pipe() io.ReadWriter {
|
||||||
return p.f
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p ptyProcess) Wait() error {
|
func (p ptyProcess) Wait() error {
|
||||||
return p.cmd.Wait()
|
defer p.cpty.Close()
|
||||||
|
ps, err := p.process.Wait()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for process: %v", err)
|
||||||
|
}
|
||||||
|
if ps.ExitCode() != 0 {
|
||||||
|
return fmt.Errorf("exit code was: %d\n", ps.ExitCode())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user