• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
Channel(ChannelManager cm)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 
getStderrStream()163 	public ChannelInputStream getStderrStream()
164 	{
165 		return stderrStream;
166 	}
167 
getStdinStream()168 	public ChannelOutputStream getStdinStream()
169 	{
170 		return stdinStream;
171 	}
172 
getStdoutStream()173 	public ChannelInputStream getStdoutStream()
174 	{
175 		return stdoutStream;
176 	}
177 
getExitSignal()178 	public String getExitSignal()
179 	{
180 		synchronized (this)
181 		{
182 			return exit_signal;
183 		}
184 	}
185 
getExitStatus()186 	public Integer getExitStatus()
187 	{
188 		synchronized (this)
189 		{
190 			return exit_status;
191 		}
192 	}
193 
getReasonClosed()194 	public String getReasonClosed()
195 	{
196 		synchronized (reasonClosedLock)
197 		{
198 			return reasonClosed;
199 		}
200 	}
201 
setReasonClosed(String reasonClosed)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 
getState()213 	public int getState()
214 	{
215 		return this.state;
216 	}
217 }
218