From c6691d1fb4e59ba620dd7f4fdb3ea24e8a0ab404 Mon Sep 17 00:00:00 2001 From: Gustav Paul Date: Tue, 29 Nov 2011 12:26:39 -0500 Subject: [PATCH] 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 --- src/pkg/exp/ssh/doc.go | 4 +-- src/pkg/exp/ssh/session.go | 64 +++++++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/pkg/exp/ssh/doc.go b/src/pkg/exp/ssh/doc.go index 248b2fec4f8..480f877191a 100644 --- a/src/pkg/exp/ssh/doc.go +++ b/src/pkg/exp/ssh/doc.go @@ -92,9 +92,9 @@ Each ClientConn can support multiple interactive sessions, represented by a Sess session, err := client.NewSession() 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()) } reader := bufio.NewReader(session.Stdin) diff --git a/src/pkg/exp/ssh/session.go b/src/pkg/exp/ssh/session.go index cafa38cf509..dab0113f4b5 100644 --- a/src/pkg/exp/ssh/session.go +++ b/src/pkg/exp/ssh/session.go @@ -15,6 +15,25 @@ import ( "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. type Session struct { // Stdin specifies the remote process's standard input. @@ -35,7 +54,7 @@ type Session struct { *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 errch chan error // one send per copyFunc } @@ -50,7 +69,7 @@ type setenvRequest struct { } // 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 { req := setenvRequest{ PeersId: s.peersId, @@ -100,6 +119,26 @@ func (s *Session) RequestPty(term string, h, w int) error { 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. type execMsg struct { PeersId uint32 @@ -108,10 +147,10 @@ type execMsg struct { Command string } -// Exec runs cmd on the remote host. Typically, the remote -// server passes cmd to the shell for interpretation. -// A Session only accepts one call to Exec or Shell. -func (s *Session) Exec(cmd string) error { +// Start runs cmd on the remote host. 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) Start(cmd string) error { if s.started { return errors.New("ssh: session already started") } @@ -127,14 +166,23 @@ func (s *Session) Exec(cmd string) error { if err := s.waitForResponse(); err != nil { 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 s.Wait() } // 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 { if s.started { return errors.New("ssh: session already started")