mirror of
https://gogs.blitter.com/RLabs/xs
synced 2024-08-14 10:26:42 +00:00
Added pty lib to give true terminal capability. raw mode/restore for client working
This commit is contained in:
parent
e8fe31f6d7
commit
49c589ee8d
3 changed files with 142 additions and 7 deletions
|
@ -10,6 +10,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
hkex "blitter.com/herradurakex"
|
hkex "blitter.com/herradurakex"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Demo of a simple client that dials up to a simple test server to
|
// Demo of a simple client that dials up to a simple test server to
|
||||||
|
@ -45,6 +46,13 @@ func main() {
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Set stdin in raw mode.
|
||||||
|
oldState, err := MakeRaw(int(os.Stdin.Fd()))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer func() { _ = Restore(int(os.Stdin.Fd()), oldState) }() // Best effort.
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
// This will guarantee the side that closes first
|
// This will guarantee the side that closes first
|
||||||
|
@ -64,7 +72,7 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("[Got Write EOF]")
|
log.Println("[Got Write EOF]")
|
||||||
wg.Done() // client hanging up, close server read goroutine
|
wg.Done() // client hanging up, close server read goroutine
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -81,10 +89,68 @@ func main() {
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Println("[Got Read EOF]")
|
log.Println("[Got Read EOF]")
|
||||||
wg.Done() // server hung up, close client write goroutine
|
wg.Done() // server hung up, close client write goroutine
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Wait until both stdin and stdout goroutines finish
|
// Wait until both stdin and stdout goroutines finish
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------- minimal terminal APIs brought in from ssh/terminal
|
||||||
|
* (they have no real business being there as they aren't specific to
|
||||||
|
* ssh.)
|
||||||
|
* -------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TCGETS
|
||||||
|
const ioctlWriteTermios = unix.TCSETS
|
||||||
|
|
||||||
|
// State contains the state of a terminal.
|
||||||
|
type State struct {
|
||||||
|
termios unix.Termios
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd int) (*State, error) {
|
||||||
|
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldState := State{termios: *termios}
|
||||||
|
|
||||||
|
// This attempts to replicate the behaviour documented for cfmakeraw in
|
||||||
|
// the termios(3) manpage.
|
||||||
|
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
||||||
|
termios.Oflag &^= unix.OPOST
|
||||||
|
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
||||||
|
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
||||||
|
termios.Cflag |= unix.CS8
|
||||||
|
termios.Cc[unix.VMIN] = 1
|
||||||
|
termios.Cc[unix.VTIME] = 0
|
||||||
|
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns the current state of a terminal which may be useful to
|
||||||
|
// restore the terminal after a signal.
|
||||||
|
func GetState(fd int) (*State, error) {
|
||||||
|
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &State{termios: *termios}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func Restore(fd int, state *State) error {
|
||||||
|
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
hkex "blitter.com/herradurakex"
|
hkex "blitter.com/herradurakex"
|
||||||
|
"github.com/kr/pty"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,6 +39,60 @@ type cmdRunner struct {
|
||||||
status int
|
status int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------- minimal terminal APIs brought in from ssh/terminal
|
||||||
|
* (they have no real business being there as they aren't specific to
|
||||||
|
* ssh.)
|
||||||
|
* -------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||||
|
// mode and returns the previous state of the terminal so that it can be
|
||||||
|
// restored.
|
||||||
|
func MakeRaw(fd int) (*State, error) {
|
||||||
|
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
oldState := State{termios: *termios}
|
||||||
|
|
||||||
|
// This attempts to replicate the behaviour documented for cfmakeraw in
|
||||||
|
// the termios(3) manpage.
|
||||||
|
termios.Iflag &^= unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON
|
||||||
|
termios.Oflag &^= unix.OPOST
|
||||||
|
termios.Lflag &^= unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN
|
||||||
|
termios.Cflag &^= unix.CSIZE | unix.PARENB
|
||||||
|
termios.Cflag |= unix.CS8
|
||||||
|
termios.Cc[unix.VMIN] = 1
|
||||||
|
termios.Cc[unix.VTIME] = 0
|
||||||
|
if err := unix.IoctlSetTermios(fd, ioctlWriteTermios, termios); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &oldState, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns the current state of a terminal which may be useful to
|
||||||
|
// restore the terminal after a signal.
|
||||||
|
func GetState(fd int) (*State, error) {
|
||||||
|
termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &State{termios: *termios}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore restores the terminal connected to the given file descriptor to a
|
||||||
|
// previous state.
|
||||||
|
func Restore(fd int, state *State) error {
|
||||||
|
return unix.IoctlSetTermios(fd, ioctlWriteTermios, &state.termios)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------- */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func cmd(r *cmdRunner) {
|
func cmd(r *cmdRunner) {
|
||||||
switch r.op {
|
switch r.op {
|
||||||
|
@ -72,7 +128,19 @@ func runCmdAs(who string, cmd string, conn hkex.Conn) (err error) {
|
||||||
c.Stdout = conn
|
c.Stdout = conn
|
||||||
c.Stderr = conn
|
c.Stderr = conn
|
||||||
|
|
||||||
err = c.Run()
|
// Start the command with a pty.
|
||||||
|
ptmx, err := pty.Start(c) // returns immediately with ptmx file
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Make sure to close the pty at the end.
|
||||||
|
defer func() { _ = ptmx.Close() }() // Best effort.
|
||||||
|
// Copy stdin to the pty and the pty to stdout.
|
||||||
|
go func() { _, _ = io.Copy(ptmx, conn) }()
|
||||||
|
_, _ = io.Copy(conn, ptmx)
|
||||||
|
|
||||||
|
//err = c.Run() // returns when c finishes.
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Command finished with error: %v", err)
|
log.Printf("Command finished with error: %v", err)
|
||||||
log.Printf("[%s]\n", cmd)
|
log.Printf("[%s]\n", cmd)
|
||||||
|
@ -175,6 +243,7 @@ func main() {
|
||||||
// Clear current op so user can enter next, or EOF
|
// Clear current op so user can enter next, or EOF
|
||||||
connOp = nil
|
connOp = nil
|
||||||
fmt.Println("[Exiting shell]")
|
fmt.Println("[Exiting shell]")
|
||||||
|
conn.Close()
|
||||||
}
|
}
|
||||||
if strings.Trim(string(data), "\r\n") == "exit" {
|
if strings.Trim(string(data), "\r\n") == "exit" {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
|
|
@ -175,7 +175,7 @@ func Dial(protocol string, ipport string, extensions ...string) (hc *Conn, err e
|
||||||
// Close a hkex.Conn
|
// Close a hkex.Conn
|
||||||
func (c Conn) Close() (err error) {
|
func (c Conn) Close() (err error) {
|
||||||
err = c.c.Close()
|
err = c.c.Close()
|
||||||
fmt.Println("[Conn Closing]")
|
log.Println("[Conn Closing]")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ func Listen(protocol string, ipport string) (hl HKExListener, e error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return HKExListener{nil}, err
|
return HKExListener{nil}, err
|
||||||
}
|
}
|
||||||
fmt.Println("[Listening]")
|
log.Println("[Listening]")
|
||||||
hl.l = l
|
hl.l = l
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -250,7 +250,7 @@ func Listen(protocol string, ipport string) (hl HKExListener, e error) {
|
||||||
//
|
//
|
||||||
// See go doc io.Close
|
// See go doc io.Close
|
||||||
func (hl HKExListener) Close() error {
|
func (hl HKExListener) Close() error {
|
||||||
fmt.Println("[Listener Closed]")
|
log.Println("[Listener Closed]")
|
||||||
return hl.l.Close()
|
return hl.l.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +263,7 @@ func (hl HKExListener) Accept() (hc Conn, err error) {
|
||||||
return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,
|
return Conn{c: nil, h: nil, cipheropts: 0, opts: 0,
|
||||||
r: nil, w: nil}, err
|
r: nil, w: nil}, err
|
||||||
}
|
}
|
||||||
fmt.Println("[Accepted]")
|
log.Println("[Accepted]")
|
||||||
|
|
||||||
hc = Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, op: 0, r: nil, w: nil}
|
hc = Conn{c: c, h: New(0, 0), cipheropts: 0, opts: 0, op: 0, r: nil, w: nil}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue