001: package ch.ethz.ssh2.channel;
002:
003: /**
004: * Channel.
005: *
006: * @author Christian Plattner, plattner@inf.ethz.ch
007: * @version $Id: Channel.java,v 1.7 2005/12/07 10:25:48 cplattne Exp $
008: */
009: public class Channel {
010: /*
011: * OK. Here is an important part of the JVM Specification:
012: * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
013: *
014: * Any association between locks and variables is purely conventional.
015: * Locking any lock conceptually flushes all variables from a thread's
016: * working memory, and unlocking any lock forces the writing out to main
017: * memory of all variables that the thread has assigned. That a lock may be
018: * associated with a particular object or a class is purely a convention.
019: * (...)
020: *
021: * If a thread uses a particular shared variable only after locking a
022: * particular lock and before the corresponding unlocking of that same lock,
023: * then the thread will read the shared value of that variable from main
024: * memory after the lock operation, if necessary, and will copy back to main
025: * memory the value most recently assigned to that variable before the
026: * unlock operation.
027: *
028: * This, in conjunction with the mutual exclusion rules for locks, suffices
029: * to guarantee that values are correctly transmitted from one thread to
030: * another through shared variables.
031: *
032: * ====> Always keep that in mind when modifying the Channel/ChannelManger
033: * code.
034: *
035: */
036:
037: static final int STATE_OPENING = 1;
038: static final int STATE_OPEN = 2;
039: static final int STATE_CLOSED = 4;
040:
041: static final int CHANNEL_BUFFER_SIZE = 30000;
042:
043: /*
044: * To achieve correctness, the following rules have to be respected when
045: * accessing this object:
046: */
047:
048: // These fields can always be read
049: final ChannelManager cm;
050: final ChannelOutputStream stdinStream;
051: final ChannelInputStream stdoutStream;
052: final ChannelInputStream stderrStream;
053:
054: // These two fields will only be written while the Channel is in state
055: // STATE_OPENING.
056: // The code makes sure that the two fields are written out when the state is
057: // changing to STATE_OPEN.
058: // Therefore, if you know that the Channel is in state STATE_OPEN, then you
059: // can read these two fields without synchronizing on the Channel. However, make
060: // sure that you get the latest values (e.g., flush caches by synchronizing on any
061: // object). However, to be on the safe side, you can lock the channel.
062:
063: int localID = -1;
064: int remoteID = -1;
065:
066: /*
067: * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
068: * msg.
069: *
070: * This is a little bit complicated, but we have to do it in that way, since
071: * we cannot keep a lock on the Channel during the send operation (this
072: * would block sometimes the receiver thread, and, in extreme cases, can
073: * lead to a deadlock on both sides of the connection (senders are blocked
074: * since the receive buffers on the other side are full, and receiver
075: * threads wait for the senders to finish). It all depends on the
076: * implementation on the other side. But we cannot make any assumptions, we
077: * have to assume the worst case. Confused? Just believe me.
078: */
079:
080: /*
081: * If you send a message on a channel, then you have to aquire the
082: * "channelSendLock" and check the "closeMessageSent" flag (this variable
083: * may only be accessed while holding the "channelSendLock" !!!
084: *
085: * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
086: * above.
087: */
088:
089: final Object channelSendLock = new Object();
090: boolean closeMessageSent = false;
091:
092: /*
093: * Stop memory fragmentation by allocating this often used buffer.
094: * May only be used while holding the channelSendLock
095: */
096:
097: final byte[] msgWindowAdjust = new byte[9];
098:
099: // If you access (read or write) any of the following fields, then you have
100: // to synchronize on the channel.
101:
102: int state = STATE_OPENING;
103:
104: boolean closeMessageRecv = false;
105:
106: /* This is a stupid implementation. At the moment we can only wait
107: * for one pending request per channel.
108: */
109: int successCounter = 0;
110: int failedCounter = 0;
111:
112: int localWindow = 0; /* locally, we use a small window, < 2^31 */
113: long remoteWindow = 0; /* long for readable 2^32 - 1 window support */
114:
115: int localMaxPacketSize = -1;
116: int remoteMaxPacketSize = -1;
117:
118: final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
119: final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
120:
121: int stdoutReadpos = 0;
122: int stdoutWritepos = 0;
123: int stderrReadpos = 0;
124: int stderrWritepos = 0;
125:
126: boolean EOF = false;
127:
128: Integer exit_status;
129:
130: String exit_signal;
131:
132: // we keep the x11 cookie so that this channel can be closed when this
133: // specific x11 forwarding gets stopped
134:
135: String hexX11FakeCookie;
136:
137: // reasonClosed is special, since we sometimes need to access it
138: // while holding the channelSendLock.
139: // We protect it with a private short term lock.
140:
141: private final Object reasonClosedLock = new Object();
142: private String reasonClosed = null;
143:
144: public Channel(ChannelManager cm) {
145: this .cm = cm;
146:
147: this .localWindow = CHANNEL_BUFFER_SIZE;
148: this .localMaxPacketSize = 35000 - 1024; // leave enough slack
149:
150: this .stdinStream = new ChannelOutputStream(this );
151: this .stdoutStream = new ChannelInputStream(this , false);
152: this .stderrStream = new ChannelInputStream(this , true);
153: }
154:
155: /* Methods to allow access from classes outside of this package */
156:
157: public ChannelInputStream getStderrStream() {
158: return stderrStream;
159: }
160:
161: public ChannelOutputStream getStdinStream() {
162: return stdinStream;
163: }
164:
165: public ChannelInputStream getStdoutStream() {
166: return stdoutStream;
167: }
168:
169: public String getExitSignal() {
170: synchronized (this ) {
171: return exit_signal;
172: }
173: }
174:
175: public Integer getExitStatus() {
176: synchronized (this ) {
177: return exit_status;
178: }
179: }
180:
181: public String getReasonClosed() {
182: synchronized (reasonClosedLock) {
183: return reasonClosed;
184: }
185: }
186:
187: public void setReasonClosed(String reasonClosed) {
188: synchronized (reasonClosedLock) {
189: if (this.reasonClosed == null)
190: this.reasonClosed = reasonClosed;
191: }
192: }
193: }
|