Commit c6691d1f authored by Gustav Paul's avatar Gustav Paul Committed by Adam Langley

exp/ssh: Add Start(cmd string) and Signal(sig string) to Session. Rename Exec to Run.

Exec() has been renamed to Run() in keeping with the os/exec API.

Added func (*Session) Start(cmd string) which starts a remote process but unlike Run() doesn't wait for it to finish before returning.

Run() has been refactored to use Start internally. Its really just a refactoring, no new code but some extra functionality was won.

Also added func (*Session) Signal(sig signal) which sends a UNIX signal to a remote process. This is espcially useful in conjunction with Start() as the two allow you to start a remote process, monitor its stdout/stderr, and send it a TERM/HUP/etc signal when you want it to close.

R=dave, rsc, agl, bradfitz, n13m3y3r, gustavo
CC=golang-dev
https://golang.org/cl/5437058
parent 175e60a2
...@@ -92,9 +92,9 @@ Each ClientConn can support multiple interactive sessions, represented by a Sess ...@@ -92,9 +92,9 @@ Each ClientConn can support multiple interactive sessions, represented by a Sess
session, err := client.NewSession() session, err := client.NewSession()
Once a Session is created, you can execute a single command on the remote side Once a Session is created, you can execute a single command on the remote side
using the Exec method. using the Run method.
if err := session.Exec("/usr/bin/whoami"); err != nil { if err := session.Run("/usr/bin/whoami"); err != nil {
panic("Failed to exec: " + err.String()) panic("Failed to exec: " + err.String())
} }
reader := bufio.NewReader(session.Stdin) reader := bufio.NewReader(session.Stdin)
......
...@@ -15,6 +15,25 @@ import ( ...@@ -15,6 +15,25 @@ import (
"io/ioutil" "io/ioutil"
) )
type signal string
// POSIX signals as listed in RFC 4254 Section 6.10.
const (
SIGABRT signal = "ABRT"
SIGALRM signal = "ALRM"
SIGFPE signal = "FPE"
SIGHUP signal = "HUP"
SIGILL signal = "ILL"
SIGINT signal = "INT"
SIGKILL signal = "KILL"
SIGPIPE signal = "PIPE"
SIGQUIT signal = "QUIT"
SIGSEGV signal = "SEGV"
SIGTERM signal = "TERM"
SIGUSR1 signal = "USR1"
SIGUSR2 signal = "USR2"
)
// A Session represents a connection to a remote command or shell. // A Session represents a connection to a remote command or shell.
type Session struct { type Session struct {
// Stdin specifies the remote process's standard input. // Stdin specifies the remote process's standard input.
...@@ -35,7 +54,7 @@ type Session struct { ...@@ -35,7 +54,7 @@ type Session struct {
*clientChan // the channel backing this session *clientChan // the channel backing this session
started bool // true once a Shell or Exec is invoked. started bool // true once a Shell or Run is invoked.
copyFuncs []func() error copyFuncs []func() error
errch chan error // one send per copyFunc errch chan error // one send per copyFunc
} }
...@@ -50,7 +69,7 @@ type setenvRequest struct { ...@@ -50,7 +69,7 @@ type setenvRequest struct {
} }
// Setenv sets an environment variable that will be applied to any // Setenv sets an environment variable that will be applied to any
// command executed by Shell or Exec. // command executed by Shell or Run.
func (s *Session) Setenv(name, value string) error { func (s *Session) Setenv(name, value string) error {
req := setenvRequest{ req := setenvRequest{
PeersId: s.peersId, PeersId: s.peersId,
...@@ -100,6 +119,26 @@ func (s *Session) RequestPty(term string, h, w int) error { ...@@ -100,6 +119,26 @@ func (s *Session) RequestPty(term string, h, w int) error {
return s.waitForResponse() return s.waitForResponse()
} }
// RFC 4254 Section 6.9.
type signalMsg struct {
PeersId uint32
Request string
WantReply bool
Signal string
}
// Signal sends the given signal to the remote process.
// sig is one of the SIG* constants.
func (s *Session) Signal(sig signal) error {
req := signalMsg{
PeersId: s.peersId,
Request: "signal",
WantReply: false,
Signal: string(sig),
}
return s.writePacket(marshal(msgChannelRequest, req))
}
// RFC 4254 Section 6.5. // RFC 4254 Section 6.5.
type execMsg struct { type execMsg struct {
PeersId uint32 PeersId uint32
...@@ -108,10 +147,10 @@ type execMsg struct { ...@@ -108,10 +147,10 @@ type execMsg struct {
Command string Command string
} }
// Exec runs cmd on the remote host. Typically, the remote // Start runs cmd on the remote host. Typically, the remote
// server passes cmd to the shell for interpretation. // server passes cmd to the shell for interpretation.
// A Session only accepts one call to Exec or Shell. // A Session only accepts one call to Run, Start or Shell.
func (s *Session) Exec(cmd string) error { func (s *Session) Start(cmd string) error {
if s.started { if s.started {
return errors.New("ssh: session already started") return errors.New("ssh: session already started")
} }
...@@ -127,14 +166,23 @@ func (s *Session) Exec(cmd string) error { ...@@ -127,14 +166,23 @@ func (s *Session) Exec(cmd string) error {
if err := s.waitForResponse(); err != nil { if err := s.waitForResponse(); err != nil {
return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err) return fmt.Errorf("ssh: could not execute command %s: %v", cmd, err)
} }
if err := s.start(); err != nil { return s.start()
}
// Run runs cmd on the remote host and waits for it to terminate.
// Typically, the remote server passes cmd to the shell for
// interpretation. A Session only accepts one call to Run,
// Start or Shell.
func (s *Session) Run(cmd string) error {
err := s.Start(cmd)
if err != nil {
return err return err
} }
return s.Wait() return s.Wait()
} }
// Shell starts a login shell on the remote host. A Session only // Shell starts a login shell on the remote host. A Session only
// accepts one call to Exec or Shell. // accepts one call to Run, Start or Shell.
func (s *Session) Shell() error { func (s *Session) Shell() error {
if s.started { if s.started {
return errors.New("ssh: session already started") return errors.New("ssh: session already started")
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment