Home | History | Annotate | Download | only in channel
      1 /*
      2  * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
      3  * Please refer to the LICENSE.txt for licensing details.
      4  */
      5 package ch.ethz.ssh2.channel;
      6 
      7 /**
      8  * Channel.
      9  *
     10  * @author Christian Plattner
     11  * @version $Id: Channel.java 14 2011-05-27 14:28:21Z dkocher (at) sudo.ch $
     12  */
     13 public class Channel
     14 {
     15 	/*
     16 		  * OK. Here is an important part of the JVM Specification:
     17 		  * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
     18 		  *
     19 		  * Any association between locks and variables is purely conventional.
     20 		  * Locking any lock conceptually flushes all variables from a thread's
     21 		  * working memory, and unlocking any lock forces the writing out to main
     22 		  * memory of all variables that the thread has assigned. That a lock may be
     23 		  * associated with a particular object or a class is purely a convention.
     24 		  * (...)
     25 		  *
     26 		  * If a thread uses a particular shared variable only after locking a
     27 		  * particular lock and before the corresponding unlocking of that same lock,
     28 		  * then the thread will read the shared value of that variable from main
     29 		  * memory after the lock operation, if necessary, and will copy back to main
     30 		  * memory the value most recently assigned to that variable before the
     31 		  * unlock operation.
     32 		  *
     33 		  * This, in conjunction with the mutual exclusion rules for locks, suffices
     34 		  * to guarantee that values are correctly transmitted from one thread to
     35 		  * another through shared variables.
     36 		  *
     37 		  * ====> Always keep that in mind when modifying the Channel/ChannelManger
     38 		  * code.
     39 		  *
     40 		  */
     41 
     42 	public static final int STATE_OPENING = 1;
     43 	public static final int STATE_OPEN = 2;
     44 	public static final int STATE_CLOSED = 4;
     45 
     46 	static final int CHANNEL_BUFFER_SIZE = 32 * 1024 * 3 * 2;
     47 
     48 	/*
     49 		  * To achieve correctness, the following rules have to be respected when
     50 		  * accessing this object:
     51 		  */
     52 
     53 	// These fields can always be read
     54 	final ChannelManager cm;
     55 	final ChannelOutputStream stdinStream;
     56 	final ChannelInputStream stdoutStream;
     57 	final ChannelInputStream stderrStream;
     58 
     59 	// These two fields will only be written while the Channel is in state
     60 	// STATE_OPENING.
     61 	// The code makes sure that the two fields are written out when the state is
     62 	// changing to STATE_OPEN.
     63 	// Therefore, if you know that the Channel is in state STATE_OPEN, then you
     64 	// can read these two fields without synchronizing on the Channel. However, make
     65 	// sure that you get the latest values (e.g., flush caches by synchronizing on any
     66 	// object). However, to be on the safe side, you can lock the channel.
     67 
     68 	int localID = -1;
     69 	int remoteID = -1;
     70 
     71 	/*
     72 		  * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
     73 		  * msg.
     74 		  *
     75 		  * This is a little bit complicated, but we have to do it in that way, since
     76 		  * we cannot keep a lock on the Channel during the send operation (this
     77 		  * would block sometimes the receiver thread, and, in extreme cases, can
     78 		  * lead to a deadlock on both sides of the connection (senders are blocked
     79 		  * since the receive buffers on the other side are full, and receiver
     80 		  * threads wait for the senders to finish). It all depends on the
     81 		  * implementation on the other side. But we cannot make any assumptions, we
     82 		  * have to assume the worst case. Confused? Just believe me.
     83 		  */
     84 
     85 	/*
     86 		  * If you send a message on a channel, then you have to aquire the
     87 		  * "channelSendLock" and check the "closeMessageSent" flag (this variable
     88 		  * may only be accessed while holding the "channelSendLock" !!!
     89 		  *
     90 		  * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
     91 		  * above.
     92 		  */
     93 
     94 	final Object channelSendLock = new Object();
     95 	boolean closeMessageSent = false;
     96 
     97 	/*
     98 		  * Stop memory fragmentation by allocating this often used buffer.
     99 		  * May only be used while holding the channelSendLock
    100 		  */
    101 
    102 	final byte[] msgWindowAdjust = new byte[9];
    103 
    104 	// If you access (read or write) any of the following fields, then you have
    105 	// to synchronize on the channel.
    106 
    107 	int state = STATE_OPENING;
    108 
    109 	boolean closeMessageRecv = false;
    110 
    111 	/* This is a stupid implementation. At the moment we can only wait
    112 		  * for one pending request per channel.
    113 		  */
    114 	int successCounter = 0;
    115 	int failedCounter = 0;
    116 
    117 	int localWindow = 0; /* locally, we use a small window, < 2^31 */
    118 	long remoteWindow = 0; /* long for readable  2^32 - 1 window support */
    119 
    120 	int localMaxPacketSize = -1;
    121 	int remoteMaxPacketSize = -1;
    122 
    123 	final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
    124 	final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
    125 
    126 	int stdoutReadpos = 0;
    127 	int stdoutWritepos = 0;
    128 	int stderrReadpos = 0;
    129 	int stderrWritepos = 0;
    130 
    131 	boolean EOF = false;
    132 
    133 	Integer exit_status;
    134 
    135 	String exit_signal;
    136 
    137 	// we keep the x11 cookie so that this channel can be closed when this
    138 	// specific x11 forwarding gets stopped
    139 
    140 	String hexX11FakeCookie;
    141 
    142 	// reasonClosed is special, since we sometimes need to access it
    143 	// while holding the channelSendLock.
    144 	// We protect it with a private short term lock.
    145 
    146 	private final Object reasonClosedLock = new Object();
    147 	private String reasonClosed = null;
    148 
    149 	public Channel(ChannelManager cm)
    150 	{
    151 		this.cm = cm;
    152 
    153 		this.localWindow = CHANNEL_BUFFER_SIZE;
    154 		this.localMaxPacketSize = 32 * 1024;
    155 
    156 		this.stdinStream = new ChannelOutputStream(this);
    157 		this.stdoutStream = new ChannelInputStream(this, false);
    158 		this.stderrStream = new ChannelInputStream(this, true);
    159 	}
    160 
    161 	/* Methods to allow access from classes outside of this package */
    162 
    163 	public ChannelInputStream getStderrStream()
    164 	{
    165 		return stderrStream;
    166 	}
    167 
    168 	public ChannelOutputStream getStdinStream()
    169 	{
    170 		return stdinStream;
    171 	}
    172 
    173 	public ChannelInputStream getStdoutStream()
    174 	{
    175 		return stdoutStream;
    176 	}
    177 
    178 	public String getExitSignal()
    179 	{
    180 		synchronized (this)
    181 		{
    182 			return exit_signal;
    183 		}
    184 	}
    185 
    186 	public Integer getExitStatus()
    187 	{
    188 		synchronized (this)
    189 		{
    190 			return exit_status;
    191 		}
    192 	}
    193 
    194 	public String getReasonClosed()
    195 	{
    196 		synchronized (reasonClosedLock)
    197 		{
    198 			return reasonClosed;
    199 		}
    200 	}
    201 
    202 	public void setReasonClosed(String reasonClosed)
    203 	{
    204 		synchronized (reasonClosedLock)
    205 		{
    206 			if (this.reasonClosed == null)
    207 			{
    208 				this.reasonClosed = reasonClosed;
    209 			}
    210 		}
    211 	}
    212 
    213 	public int getState()
    214 	{
    215 		return this.state;
    216 	}
    217 }
    218