diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-12 01:31:45 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2012-01-12 01:31:45 +0000 |
commit | 9a0e3259f44ad2de9c65f14f756dab01b3598391 (patch) | |
tree | 86a3b8019380d5fad53258c4baba3dd9e1e7c736 /libgo/go/exp/ssh/client.go | |
parent | c6135f063335419e4b5df0b4e1caf145882c8a4b (diff) | |
download | gcc-9a0e3259f44ad2de9c65f14f756dab01b3598391.tar.gz |
libgo: Update to weekly.2011-12-14.
From-SVN: r183118
Diffstat (limited to 'libgo/go/exp/ssh/client.go')
-rw-r--r-- | libgo/go/exp/ssh/client.go | 92 |
1 files changed, 70 insertions, 22 deletions
diff --git a/libgo/go/exp/ssh/client.go b/libgo/go/exp/ssh/client.go index d89b908cdcf..7c862078b7e 100644 --- a/libgo/go/exp/ssh/client.go +++ b/libgo/go/exp/ssh/client.go @@ -187,10 +187,10 @@ func (c *ClientConn) mainLoop() { if err != nil { break } - // TODO(dfc) A note on blocking channel use. - // The msg, win, data and dataExt channels of a clientChan can - // cause this loop to block indefinately if the consumer does - // not service them. + // TODO(dfc) A note on blocking channel use. + // The msg, win, data and dataExt channels of a clientChan can + // cause this loop to block indefinately if the consumer does + // not service them. switch packet[0] { case msgChannelData: if len(packet) < 9 { @@ -200,7 +200,7 @@ func (c *ClientConn) mainLoop() { peersId := uint32(packet[1])<<24 | uint32(packet[2])<<16 | uint32(packet[3])<<8 | uint32(packet[4]) if length := int(packet[5])<<24 | int(packet[6])<<16 | int(packet[7])<<8 | int(packet[8]); length > 0 { packet = packet[9:] - c.getChan(peersId).stdout.data <- packet[:length] + c.getChan(peersId).stdout.handleData(packet[:length]) } case msgChannelExtendedData: if len(packet) < 13 { @@ -211,11 +211,11 @@ func (c *ClientConn) mainLoop() { datatype := uint32(packet[5])<<24 | uint32(packet[6])<<16 | uint32(packet[7])<<8 | uint32(packet[8]) if length := int(packet[9])<<24 | int(packet[10])<<16 | int(packet[11])<<8 | int(packet[12]); length > 0 { packet = packet[13:] - // RFC 4254 5.2 defines data_type_code 1 to be data destined + // RFC 4254 5.2 defines data_type_code 1 to be data destined // for stderr on interactive sessions. Other data types are // silently discarded. if datatype == 1 { - c.getChan(peersId).stderr.data <- packet[:length] + c.getChan(peersId).stderr.handleData(packet[:length]) } } default: @@ -228,12 +228,22 @@ func (c *ClientConn) mainLoop() { c.getChan(msg.PeersId).msg <- msg case *channelCloseMsg: ch := c.getChan(msg.PeersId) + ch.theyClosed = true close(ch.stdin.win) - close(ch.stdout.data) - close(ch.stderr.data) + ch.stdout.eof() + ch.stderr.eof() + close(ch.msg) + if !ch.weClosed { + ch.weClosed = true + ch.sendClose() + } c.chanlist.remove(msg.PeersId) case *channelEOFMsg: - c.getChan(msg.PeersId).msg <- msg + ch := c.getChan(msg.PeersId) + ch.stdout.eof() + // RFC 4254 is mute on how EOF affects dataExt messages but + // it is logical to signal EOF at the same time. + ch.stderr.eof() case *channelRequestSuccessMsg: c.getChan(msg.PeersId).msg <- msg case *channelRequestFailureMsg: @@ -242,6 +252,8 @@ func (c *ClientConn) mainLoop() { c.getChan(msg.PeersId).msg <- msg case *windowAdjustMsg: c.getChan(msg.PeersId).stdin.win <- int(msg.AdditionalBytes) + case *disconnectMsg: + break default: fmt.Printf("mainLoop: unhandled message %T: %v\n", msg, msg) } @@ -249,7 +261,7 @@ func (c *ClientConn) mainLoop() { } } -// Dial connects to the given network address using net.Dial and +// Dial connects to the given network address using net.Dial and // then initiates a SSH handshake, returning the resulting client connection. func Dial(network, addr string, config *ClientConfig) (*ClientConn, error) { conn, err := net.Dial(network, addr) @@ -259,18 +271,18 @@ func Dial(network, addr string, config *ClientConfig) (*ClientConn, error) { return Client(conn, config) } -// A ClientConfig structure is used to configure a ClientConn. After one has +// A ClientConfig structure is used to configure a ClientConn. After one has // been passed to an SSH function it must not be modified. type ClientConfig struct { - // Rand provides the source of entropy for key exchange. If Rand is - // nil, the cryptographic random reader in package crypto/rand will + // Rand provides the source of entropy for key exchange. If Rand is + // nil, the cryptographic random reader in package crypto/rand will // be used. Rand io.Reader // The username to authenticate. User string - // A slice of ClientAuth methods. Only the first instance + // A slice of ClientAuth methods. Only the first instance // of a particular RFC 4252 method will be used during authentication. Auth []ClientAuth @@ -285,7 +297,7 @@ func (c *ClientConfig) rand() io.Reader { return c.Rand } -// A clientChan represents a single RFC 4254 channel that is multiplexed +// A clientChan represents a single RFC 4254 channel that is multiplexed // over a single SSH connection. type clientChan struct { packetWriter @@ -294,10 +306,13 @@ type clientChan struct { stdout *chanReader // receives the payload of channelData messages stderr *chanReader // receives the payload of channelExtendedData messages msg chan interface{} // incoming messages + + theyClosed bool // indicates the close msg has been received from the remote side + weClosed bool // incidates the close msg has been sent from our side } // newClientChan returns a partially constructed *clientChan -// using the local id provided. To be usable clientChan.peersId +// using the local id provided. To be usable clientChan.peersId // needs to be assigned once known. func newClientChan(t *transport, id uint32) *clientChan { c := &clientChan{ @@ -320,8 +335,8 @@ func newClientChan(t *transport, id uint32) *clientChan { return c } -// waitForChannelOpenResponse, if successful, fills out -// the peerId and records any initial window advertisement. +// waitForChannelOpenResponse, if successful, fills out +// the peerId and records any initial window advertisement. func (c *clientChan) waitForChannelOpenResponse() error { switch msg := (<-c.msg).(type) { case *channelOpenConfirmMsg: @@ -335,13 +350,29 @@ func (c *clientChan) waitForChannelOpenResponse() error { return errors.New("unexpected packet") } -// Close closes the channel. This does not close the underlying connection. -func (c *clientChan) Close() error { +// sendEOF sends EOF to the server. RFC 4254 Section 5.3 +func (c *clientChan) sendEOF() error { + return c.writePacket(marshal(msgChannelEOF, channelEOFMsg{ + PeersId: c.peersId, + })) +} + +// sendClose signals the intent to close the channel. +func (c *clientChan) sendClose() error { return c.writePacket(marshal(msgChannelClose, channelCloseMsg{ PeersId: c.peersId, })) } +// Close closes the channel. This does not close the underlying connection. +func (c *clientChan) Close() error { + if !c.weClosed { + c.weClosed = true + return c.sendClose() + } + return nil +} + // Thread safe channel list. type chanlist struct { // protects concurrent access to chans @@ -413,7 +444,7 @@ func (w *chanWriter) Write(data []byte) (n int, err error) { } func (w *chanWriter) Close() error { - return w.clientChan.writePacket(marshal(msgChannelEOF, channelEOFMsg{w.clientChan.peersId})) + return w.clientChan.sendEOF() } // A chanReader represents stdout or stderr of a remote process. @@ -422,10 +453,27 @@ type chanReader struct { // If writes to this channel block, they will block mainLoop, making // it unable to receive new messages from the remote side. data chan []byte // receives data from remote + dataClosed bool // protects data from being closed twice clientChan *clientChan // the channel backing this reader buf []byte } +// eof signals to the consumer that there is no more data to be received. +func (r *chanReader) eof() { + if !r.dataClosed { + r.dataClosed = true + close(r.data) + } +} + +// handleData sends buf to the reader's consumer. If r.data is closed +// the data will be silently discarded +func (r *chanReader) handleData(buf []byte) { + if !r.dataClosed { + r.data <- buf + } +} + // Read reads data from the remote process's stdout or stderr. func (r *chanReader) Read(data []byte) (int, error) { var ok bool |