Mercurial > 510Connectbot
diff src/ch/ethz/ssh2/channel/Channel.java @ 273:91a31873c42a ganymed
start conversion from trilead to ganymed
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Fri, 18 Jul 2014 11:21:46 -0700 |
parents | |
children | 071eccdff8ea |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ch/ethz/ssh2/channel/Channel.java Fri Jul 18 11:21:46 2014 -0700 @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2.channel; + +import java.io.IOException; + +import ch.ethz.ssh2.transport.TransportManager; + +/** + * Channel. + * + * @author Christian Plattner + * @version $Id: Channel.java 123 2014-04-12 21:11:47Z dkocher@sudo.ch $ + */ +public class Channel +{ + /* + * OK. Here is an important part of the JVM Specification: + * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214) + * + * Any association between locks and variables is purely conventional. + * Locking any lock conceptually flushes all variables from a thread's + * working memory, and unlocking any lock forces the writing out to main + * memory of all variables that the thread has assigned. That a lock may be + * associated with a particular object or a class is purely a convention. + * (...) + * + * If a thread uses a particular shared variable only after locking a + * particular lock and before the corresponding unlocking of that same lock, + * then the thread will read the shared value of that variable from main + * memory after the lock operation, if necessary, and will copy back to main + * memory the value most recently assigned to that variable before the + * unlock operation. + * + * This, in conjunction with the mutual exclusion rules for locks, suffices + * to guarantee that values are correctly transmitted from one thread to + * another through shared variables. + * + * ====> Always keep that in mind when modifying the Channel/ChannelManger + * code. + * + */ + + public static final int STATE_OPENING = 1; + public static final int STATE_OPEN = 2; + public static final int STATE_CLOSED = 4; + + static final int CHANNEL_BUFFER_SIZE = 32 * 1024 * 3 * 2; + + /* + * To achieve correctness, the following rules have to be respected when + * accessing this object: + */ + + // These fields can always be read + final ChannelManager cm; + final ChannelOutputStream stdinStream; + final ChannelInputStream stdoutStream; + final ChannelInputStream stderrStream; + + // In case this channel belongs to a server-side session. + ServerSessionImpl ss; + + // These two fields will only be written while the Channel is in state + // STATE_OPENING. + // The code makes sure that the two fields are written out when the state is + // changing to STATE_OPEN. + // Therefore, if you know that the Channel is in state STATE_OPEN, then you + // can read these two fields without synchronizing on the Channel. However, make + // sure that you get the latest values (e.g., flush caches by synchronizing on any + // object). However, to be on the safe side, you can lock the channel. + + int localID = -1; + int remoteID = -1; + + /* + * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE + * msg. + * + * This is a little bit complicated, but we have to do it in that way, since + * we cannot keep a lock on the Channel during the send operation (this + * would block sometimes the receiver thread, and, in extreme cases, can + * lead to a deadlock on both sides of the connection (senders are blocked + * since the receive buffers on the other side are full, and receiver + * threads wait for the senders to finish). It all depends on the + * implementation on the other side. But we cannot make any assumptions, we + * have to assume the worst case. Confused? Just believe me. + */ + + /* + * If you send a message on a channel, then you have to aquire the + * "channelSendLock" and check the "closeMessageSent" flag (this variable + * may only be accessed while holding the "channelSendLock" !!! + * + * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation + * above. + */ + + final Object channelSendLock = new Object(); + boolean closeMessageSent = false; + + /* + * Stop memory fragmentation by allocating this often used buffer. + * May only be used while holding the channelSendLock + */ + + final byte[] msgWindowAdjust = new byte[9]; + + // If you access (read or write) any of the following fields, then you have + // to synchronize on the channel. + + int state = STATE_OPENING; + + boolean closeMessageRecv = false; + + /* This is a stupid implementation. At the moment we can only wait + * for one pending request per channel. + */ + int successCounter = 0; + int failedCounter = 0; + + int localWindow = 0; /* locally, we use a small window, < 2^31 */ + long remoteWindow = 0; /* long for readable 2^32 - 1 window support */ + + int localMaxPacketSize = -1; + int remoteMaxPacketSize = -1; + + final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE]; + final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE]; + + int stdoutReadpos = 0; + int stdoutWritepos = 0; + int stderrReadpos = 0; + int stderrWritepos = 0; + + boolean EOF = false; + + Integer exit_status; + + String exit_signal; + + // we keep the x11 cookie so that this channel can be closed when this + // specific x11 forwarding gets stopped + + String hexX11FakeCookie; + + // reasonClosed is special, since we sometimes need to access it + // while holding the channelSendLock. + // We protect it with a private short term lock. + + private final Object reasonClosedLock = new Object(); + private IOException reasonClosed = null; + + public Channel(ChannelManager cm) + { + this.cm = cm; + + this.localWindow = CHANNEL_BUFFER_SIZE; + this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE; + + this.stdinStream = new ChannelOutputStream(this); + this.stdoutStream = new ChannelInputStream(this, false); + this.stderrStream = new ChannelInputStream(this, true); + } + + /* Methods to allow access from classes outside of this package */ + + public ChannelInputStream getStderrStream() + { + return stderrStream; + } + + public ChannelOutputStream getStdinStream() + { + return stdinStream; + } + + public ChannelInputStream getStdoutStream() + { + return stdoutStream; + } + + public String getExitSignal() + { + synchronized (this) + { + return exit_signal; + } + } + + public Integer getExitStatus() + { + synchronized (this) + { + return exit_status; + } + } + + public IOException getReasonClosed() + { + synchronized (reasonClosedLock) + { + return reasonClosed; + } + } + + public void setReasonClosed(IOException e) + { + synchronized (reasonClosedLock) + { + this.reasonClosed = e; + } + } + + public int getState() + { + return this.state; + } +}