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