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 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/ActiveState/termtest/conpty v0.5.0 | ||||
| 	github.com/creack/pty v1.1.21 | ||||
| 	github.com/gliderlabs/ssh v0.3.7 | ||||
| 	github.com/gorilla/websocket v1.5.3 | ||||
| @ -13,6 +14,7 @@ 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/kr/fs v0.1.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/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= | ||||
| 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-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-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-20210615035016-665e8c7367d1/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 | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/ActiveState/termtest/conpty" | ||||
| 	"github.com/gliderlabs/ssh" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"syscall" | ||||
| ) | ||||
| 
 | ||||
| var PtySpawner = Spawner(func(sshSession ssh.Session, env []string, name string, arg ...string) (Process, error) { | ||||
| 	return nil, nil | ||||
| var PtySpawner = Spawner(func(sshSession ssh.Session, env []string, name string, args ...string) (Process, error) { | ||||
| 	_, 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 { | ||||
| 	cmd *exec.Cmd | ||||
| 	f   *os.File | ||||
| 	cpty    *conpty.ConPty | ||||
| 	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 { | ||||
| 	return p.f | ||||
| 	return p | ||||
| } | ||||
| 
 | ||||
| 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