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