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;
+	}
+}