go.crypto/ssh: add terminal modes to ssh.RequestPty()

R=dave, agl
CC=golang-dev
https://golang.org/cl/6655046
This commit is contained in:
Willem van der Schyff
2012-10-21 16:16:55 +11:00
committed by Dave Cheney
parent c9c0e06eed
commit 2fccde5d00
3 changed files with 180 additions and 5 deletions
+36
View File
@@ -149,3 +149,39 @@ func ExampleClientConn_Listen() {
fmt.Fprintf(resp, "Hello world!\n")
}))
}
func ExampleSession_RequestPty() {
// Create client config
config := &ClientConfig{
User: "username",
Auth: []ClientAuth{
ClientAuthPassword(password("password")),
},
}
// Connect to ssh server
conn, err := Dial("tcp", "localhost:22", config)
if err != nil {
log.Fatalf("unable to connect: %s", err)
}
defer conn.Close()
// Create a session
session, err := conn.NewSession()
if err != nil {
log.Fatalf("unable to create session: %s", err)
}
defer session.Close()
// Set up terminal modes
modes := TerminalModes{
ECHO: 0, // disable echoing
TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
// Request pseudo terminal
if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
log.Fatalf("request for pseudo terminal failed: %s", err)
}
// Start remote shell
if err := session.Shell(); err != nil {
log.Fatalf("failed to start shell: %s", err)
}
}
+70 -5
View File
@@ -48,6 +48,68 @@ var signals = map[Signal]int{
SIGTERM: 15,
}
type TerminalModes map[uint8]uint32
// POSIX terminal mode flags as listed in RFC 4254 Section 8.
const (
tty_OP_END = 0
VINTR = 1
VQUIT = 2
VERASE = 3
VKILL = 4
VEOF = 5
VEOL = 6
VEOL2 = 7
VSTART = 8
VSTOP = 9
VSUSP = 10
VDSUSP = 11
VREPRINT = 12
VWERASE = 13
VLNEXT = 14
VFLUSH = 15
VSWTCH = 16
VSTATUS = 17
VDISCARD = 18
IGNPAR = 30
PARMRK = 31
INPCK = 32
ISTRIP = 33
INLCR = 34
IGNCR = 35
ICRNL = 36
IUCLC = 37
IXON = 38
IXANY = 39
IXOFF = 40
IMAXBEL = 41
ISIG = 50
ICANON = 51
XCASE = 52
ECHO = 53
ECHOE = 54
ECHOK = 55
ECHONL = 56
NOFLSH = 57
TOSTOP = 58
IEXTEN = 59
ECHOCTL = 60
ECHOKE = 61
PENDIN = 62
OPOST = 70
OLCUC = 71
ONLCR = 72
OCRNL = 73
ONOCR = 74
ONLRET = 75
CS7 = 90
CS8 = 91
PARENB = 92
PARODD = 93
TTY_OP_ISPEED = 128
TTY_OP_OSPEED = 129
)
// A Session represents a connection to a remote command or shell.
type Session struct {
// Stdin specifies the remote process's standard input.
@@ -109,9 +171,6 @@ func (s *Session) Setenv(name, value string) error {
return s.waitForResponse()
}
// An empty mode list, see RFC 4254 Section 8.
var emptyModelist = "\x00"
// RFC 4254 Section 6.2.
type ptyRequestMsg struct {
PeersId uint32
@@ -126,7 +185,13 @@ type ptyRequestMsg struct {
}
// RequestPty requests the association of a pty with the session on the remote host.
func (s *Session) RequestPty(term string, h, w int) error {
func (s *Session) RequestPty(term string, h, w int, termmodes TerminalModes) error {
var tm []byte
for k, v := range termmodes {
tm = append(tm, k)
tm = appendU32(tm, v)
}
tm = append(tm, tty_OP_END)
req := ptyRequestMsg{
PeersId: s.remoteId,
Request: "pty-req",
@@ -136,7 +201,7 @@ func (s *Session) RequestPty(term string, h, w int) error {
Rows: uint32(h),
Width: uint32(w * 8),
Height: uint32(h * 8),
Modelist: emptyModelist,
Modelist: string(tm),
}
if err := s.writePacket(marshal(msgChannelRequest, req)); err != nil {
return err
+74
View File
@@ -10,8 +10,11 @@ package test
import (
"bytes"
"code.google.com/p/go.crypto/ssh"
"io"
"strings"
"testing"
"time"
)
func TestRunCommandSuccess(t *testing.T) {
@@ -99,3 +102,74 @@ func TestFuncLargeRead(t *testing.T) {
t.Fatalf("Expected %d bytes but read only %d from remote command", 2048, n)
}
}
func TestInvalidTerminalMode(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial()
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
t.Fatalf("session failed: %v", err)
}
defer session.Close()
if err = session.RequestPty("vt100", 80, 40, ssh.TerminalModes{255: 1984}); err == nil {
t.Fatalf("req-pty failed: successful request with invalid mode")
}
}
func TestValidTerminalMode(t *testing.T) {
server := newServer(t)
defer server.Shutdown()
conn := server.Dial()
defer conn.Close()
session, err := conn.NewSession()
if err != nil {
t.Fatalf("session failed: %v", err)
}
defer session.Close()
stdout, err := session.StdoutPipe()
if err != nil {
t.Fatalf("unable to acquire stdout pipe: %s", err)
}
stdin, err := session.StdinPipe()
if err != nil {
t.Fatalf("unable to acquire stdin pipe: %s", err)
}
tm := ssh.TerminalModes{ssh.ECHO: 0}
if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
t.Fatalf("req-pty failed: %s", err)
}
err = session.Shell()
if err != nil {
t.Fatalf("session failed: %s", err)
}
stdin.Write([]byte("stty -a && exit\n"))
rc := make(chan string)
go func() {
var buf bytes.Buffer
if _, err := io.Copy(&buf, stdout); err != nil {
t.Fatalf("reading failed: %s", err)
}
rc <- buf.String()
}()
result := ""
select {
case result = <-rc:
case <-time.After(1 * time.Second):
}
if !strings.Contains(result, "-echo") {
t.Fatalf("terminal mode failure: expected '%s'", "-echo")
}
}