aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/exp/ssh/session.go
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2012-01-12 01:31:45 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2012-01-12 01:31:45 +0000
commit9a0e3259f44ad2de9c65f14f756dab01b3598391 (patch)
tree86a3b8019380d5fad53258c4baba3dd9e1e7c736 /libgo/go/exp/ssh/session.go
parentc6135f063335419e4b5df0b4e1caf145882c8a4b (diff)
downloadgcc-9a0e3259f44ad2de9c65f14f756dab01b3598391.zip
gcc-9a0e3259f44ad2de9c65f14f756dab01b3598391.tar.gz
gcc-9a0e3259f44ad2de9c65f14f756dab01b3598391.tar.bz2
libgo: Update to weekly.2011-12-14.
From-SVN: r183118
Diffstat (limited to 'libgo/go/exp/ssh/session.go')
-rw-r--r--libgo/go/exp/ssh/session.go134
1 files changed, 119 insertions, 15 deletions
diff --git a/libgo/go/exp/ssh/session.go b/libgo/go/exp/ssh/session.go
index 23ea18c..bf9a88e 100644
--- a/libgo/go/exp/ssh/session.go
+++ b/libgo/go/exp/ssh/session.go
@@ -34,6 +34,20 @@ const (
SIGUSR2 Signal = "USR2"
)
+var signals = map[Signal]int{
+ SIGABRT: 6,
+ SIGALRM: 14,
+ SIGFPE: 8,
+ SIGHUP: 1,
+ SIGILL: 4,
+ SIGINT: 2,
+ SIGKILL: 9,
+ SIGPIPE: 13,
+ SIGQUIT: 3,
+ SIGSEGV: 11,
+ SIGTERM: 15,
+}
+
// A Session represents a connection to a remote command or shell.
type Session struct {
// Stdin specifies the remote process's standard input.
@@ -170,10 +184,17 @@ func (s *Session) Start(cmd string) error {
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.
+// Run 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.
+//
+// The returned error is nil if the command runs, has no problems
+// copying stdin, stdout, and stderr, and exits with a zero exit
+// status.
+//
+// If the command fails to run or doesn't complete successfully, the
+// error is of type *ExitError. Other error types may be
+// returned for I/O problems.
func (s *Session) Run(cmd string) error {
err := s.Start(cmd)
if err != nil {
@@ -233,6 +254,14 @@ func (s *Session) start() error {
}
// Wait waits for the remote command to exit.
+//
+// The returned error is nil if the command runs, has no problems
+// copying stdin, stdout, and stderr, and exits with a zero exit
+// status.
+//
+// If the command fails to run or doesn't complete successfully, the
+// error is of type *ExitError. Other error types may be
+// returned for I/O problems.
func (s *Session) Wait() error {
if !s.started {
return errors.New("ssh: session not started")
@@ -255,21 +284,40 @@ func (s *Session) Wait() error {
}
func (s *Session) wait() error {
- for {
- switch msg := (<-s.msg).(type) {
+ wm := Waitmsg{status: -1}
+
+ // Wait for msg channel to be closed before returning.
+ for msg := range s.msg {
+ switch msg := msg.(type) {
case *channelRequestMsg:
- // TODO(dfc) improve this behavior to match os.Waitmsg
switch msg.Request {
case "exit-status":
d := msg.RequestSpecificData
- status := int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
- if status > 0 {
- return fmt.Errorf("remote process exited with %d", status)
- }
- return nil
+ wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
case "exit-signal":
- // TODO(dfc) make a more readable error message
- return fmt.Errorf("%v", msg.RequestSpecificData)
+ signal, rest, ok := parseString(msg.RequestSpecificData)
+ if !ok {
+ return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
+ }
+ wm.signal = safeString(string(signal))
+
+ // skip coreDumped bool
+ if len(rest) == 0 {
+ return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
+ }
+ rest = rest[1:]
+
+ errmsg, rest, ok := parseString(rest)
+ if !ok {
+ return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
+ }
+ wm.msg = safeString(string(errmsg))
+
+ lang, _, ok := parseString(rest)
+ if !ok {
+ return fmt.Errorf("wait: could not parse request data: %v", msg.RequestSpecificData)
+ }
+ wm.lang = safeString(string(lang))
default:
return fmt.Errorf("wait: unexpected channel request: %v", msg)
}
@@ -277,7 +325,20 @@ func (s *Session) wait() error {
return fmt.Errorf("wait: unexpected packet %T received: %v", msg, msg)
}
}
- panic("unreachable")
+ if wm.status == 0 {
+ return nil
+ }
+ if wm.status == -1 {
+ // exit-status was never sent from server
+ if wm.signal == "" {
+ return errors.New("wait: remote command exited without exit status or exit signal")
+ }
+ wm.status = 128
+ if _, ok := signals[Signal(wm.signal)]; ok {
+ wm.status += signals[Signal(wm.signal)]
+ }
+ }
+ return &ExitError{wm}
}
func (s *Session) stdin() error {
@@ -391,3 +452,46 @@ func (c *ClientConn) NewSession() (*Session, error) {
clientChan: ch,
}, nil
}
+
+// An ExitError reports unsuccessful completion of a remote command.
+type ExitError struct {
+ Waitmsg
+}
+
+func (e *ExitError) Error() string {
+ return e.Waitmsg.String()
+}
+
+// Waitmsg stores the information about an exited remote command
+// as reported by Wait.
+type Waitmsg struct {
+ status int
+ signal string
+ msg string
+ lang string
+}
+
+// ExitStatus returns the exit status of the remote command.
+func (w Waitmsg) ExitStatus() int {
+ return w.status
+}
+
+// Signal returns the exit signal of the remote command if
+// it was terminated violently.
+func (w Waitmsg) Signal() string {
+ return w.signal
+}
+
+// Msg returns the exit message given by the remote command
+func (w Waitmsg) Msg() string {
+ return w.msg
+}
+
+// Lang returns the language tag. See RFC 3066
+func (w Waitmsg) Lang() string {
+ return w.lang
+}
+
+func (w Waitmsg) String() string {
+ return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal)
+}