Mercurial > 510Connectbot
comparison src/ch/ethz/ssh2/Session.java @ 308:42b15aaa7ac7 ganymed
merge
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Wed, 30 Jul 2014 14:21:50 -0700 |
parents | 071eccdff8ea |
children |
comparison
equal
deleted
inserted
replaced
306:90e47d99ea54 | 308:42b15aaa7ac7 |
---|---|
20 * a session. However, multiple sessions can be active simultaneously. | 20 * a session. However, multiple sessions can be active simultaneously. |
21 * | 21 * |
22 * @author Christian Plattner | 22 * @author Christian Plattner |
23 * @version $Id: Session.java 96 2014-04-08 15:14:37Z dkocher@sudo.ch $ | 23 * @version $Id: Session.java 96 2014-04-08 15:14:37Z dkocher@sudo.ch $ |
24 */ | 24 */ |
25 public class Session | 25 public class Session { |
26 { | 26 private ChannelManager cm; |
27 private ChannelManager cm; | 27 private Channel cn; |
28 private Channel cn; | 28 |
29 | 29 private boolean flag_pty_requested = false; |
30 private boolean flag_pty_requested = false; | 30 private boolean flag_x11_requested = false; |
31 private boolean flag_x11_requested = false; | 31 private boolean flag_execution_started = false; |
32 private boolean flag_execution_started = false; | 32 private boolean flag_closed = false; |
33 private boolean flag_closed = false; | 33 |
34 | 34 private String x11FakeCookie = null; |
35 private String x11FakeCookie = null; | 35 |
36 | 36 private final SecureRandom rnd; |
37 private final SecureRandom rnd; | 37 |
38 | 38 protected Session(ChannelManager cm, SecureRandom rnd) throws IOException { |
39 protected Session(ChannelManager cm, SecureRandom rnd) throws IOException | 39 this.cm = cm; |
40 { | 40 this.cn = cm.openSessionChannel(); |
41 this.cm = cm; | 41 this.rnd = rnd; |
42 this.cn = cm.openSessionChannel(); | 42 } |
43 this.rnd = rnd; | 43 |
44 } | 44 /** |
45 | 45 * Basically just a wrapper for lazy people - identical to calling |
46 /** | 46 * <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>. |
47 * Basically just a wrapper for lazy people - identical to calling | 47 * |
48 * <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>. | 48 * @throws IOException |
49 * | 49 */ |
50 * @throws IOException | 50 public void requestDumbPTY() throws IOException { |
51 */ | 51 requestPTY("dumb", 0, 0, 0, 0, null); |
52 public void requestDumbPTY() throws IOException | 52 } |
53 { | 53 |
54 requestPTY("dumb", 0, 0, 0, 0, null); | 54 /** |
55 } | 55 * Basically just another wrapper for lazy people - identical to calling |
56 | 56 * <code>requestPTY(term, 0, 0, 0, 0, null)</code>. |
57 /** | 57 * |
58 * Basically just another wrapper for lazy people - identical to calling | 58 * @throws IOException |
59 * <code>requestPTY(term, 0, 0, 0, 0, null)</code>. | 59 */ |
60 * | 60 public void requestPTY(String term) throws IOException { |
61 * @throws IOException | 61 requestPTY(term, 0, 0, 0, 0, null); |
62 */ | 62 } |
63 public void requestPTY(String term) throws IOException | 63 |
64 { | 64 /** |
65 requestPTY(term, 0, 0, 0, 0, null); | 65 * Allocate a pseudo-terminal for this session. |
66 } | 66 * <p/> |
67 | 67 * This method may only be called before a program or shell is started in |
68 /** | 68 * this session. |
69 * Allocate a pseudo-terminal for this session. | 69 * <p/> |
70 * <p/> | 70 * Different aspects can be specified: |
71 * This method may only be called before a program or shell is started in | 71 * <p/> |
72 * this session. | 72 * <ul> |
73 * <p/> | 73 * <li>The TERM environment variable value (e.g., vt100)</li> |
74 * Different aspects can be specified: | 74 * <li>The terminal's dimensions.</li> |
75 * <p/> | 75 * <li>The encoded terminal modes.</li> |
76 * <ul> | 76 * </ul> |
77 * <li>The TERM environment variable value (e.g., vt100)</li> | 77 * Zero dimension parameters are ignored. The character/row dimensions |
78 * <li>The terminal's dimensions.</li> | 78 * override the pixel dimensions (when nonzero). Pixel dimensions refer to |
79 * <li>The encoded terminal modes.</li> | 79 * the drawable area of the window. The dimension parameters are only |
80 * </ul> | 80 * informational. The encoding of terminal modes (parameter |
81 * Zero dimension parameters are ignored. The character/row dimensions | 81 * <code>terminal_modes</code>) is described in RFC4254. |
82 * override the pixel dimensions (when nonzero). Pixel dimensions refer to | 82 * |
83 * the drawable area of the window. The dimension parameters are only | 83 * @param term The TERM environment variable value (e.g., vt100) |
84 * informational. The encoding of terminal modes (parameter | 84 * @param term_width_characters terminal width, characters (e.g., 80) |
85 * <code>terminal_modes</code>) is described in RFC4254. | 85 * @param term_height_characters terminal height, rows (e.g., 24) |
86 * | 86 * @param term_width_pixels terminal width, pixels (e.g., 640) |
87 * @param term The TERM environment variable value (e.g., vt100) | 87 * @param term_height_pixels terminal height, pixels (e.g., 480) |
88 * @param term_width_characters terminal width, characters (e.g., 80) | 88 * @param terminal_modes encoded terminal modes (may be <code>null</code>) |
89 * @param term_height_characters terminal height, rows (e.g., 24) | 89 * @throws IOException |
90 * @param term_width_pixels terminal width, pixels (e.g., 640) | 90 */ |
91 * @param term_height_pixels terminal height, pixels (e.g., 480) | 91 public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels, |
92 * @param terminal_modes encoded terminal modes (may be <code>null</code>) | 92 int term_height_pixels, byte[] terminal_modes) throws IOException { |
93 * @throws IOException | 93 if (term == null) |
94 */ | 94 throw new IllegalArgumentException("TERM cannot be null."); |
95 public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels, | 95 |
96 int term_height_pixels, byte[] terminal_modes) throws IOException | 96 if ((terminal_modes != null) && (terminal_modes.length > 0)) { |
97 { | 97 if (terminal_modes[terminal_modes.length - 1] != 0) |
98 if (term == null) | 98 throw new IOException("Illegal terminal modes description, does not end in zero byte"); |
99 throw new IllegalArgumentException("TERM cannot be null."); | 99 } |
100 | 100 else |
101 if ((terminal_modes != null) && (terminal_modes.length > 0)) | 101 terminal_modes = new byte[] {0}; |
102 { | 102 |
103 if (terminal_modes[terminal_modes.length - 1] != 0) | 103 synchronized (this) { |
104 throw new IOException("Illegal terminal modes description, does not end in zero byte"); | 104 /* The following is just a nicer error, we would catch it anyway later in the channel code */ |
105 } | 105 if (flag_closed) |
106 else | 106 throw new IOException("This session is closed."); |
107 terminal_modes = new byte[]{0}; | 107 |
108 | 108 if (flag_pty_requested) |
109 synchronized (this) | 109 throw new IOException("A PTY was already requested."); |
110 { | 110 |
111 /* The following is just a nicer error, we would catch it anyway later in the channel code */ | 111 if (flag_execution_started) |
112 if (flag_closed) | 112 throw new IOException( |
113 throw new IOException("This session is closed."); | 113 "Cannot request PTY at this stage anymore, a remote execution has already started."); |
114 | 114 |
115 if (flag_pty_requested) | 115 flag_pty_requested = true; |
116 throw new IOException("A PTY was already requested."); | 116 } |
117 | 117 |
118 if (flag_execution_started) | 118 cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, |
119 throw new IOException( | 119 terminal_modes); |
120 "Cannot request PTY at this stage anymore, a remote execution has already started."); | 120 } |
121 | |
122 flag_pty_requested = true; | |
123 } | |
124 | |
125 cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels, | |
126 terminal_modes); | |
127 } | |
128 | 121 |
129 /** | 122 /** |
130 * Tells the server that the size of the terminal has changed. | 123 * Tells the server that the size of the terminal has changed. |
131 * | 124 * |
132 * See {@link #requestPTY(String, int, int, int, int, byte[])} for more details about how parameters are interpreted. | 125 * See {@link #requestPTY(String, int, int, int, int, byte[])} for more details about how parameters are interpreted. |
133 * | 126 * |
134 * @param term_width_characters | 127 * @param term_width_characters |
135 * terminal width, characters (e.g., 80) | 128 * terminal width, characters (e.g., 80) |
136 * @param term_height_characters | 129 * @param term_height_characters |
137 * terminal height, rows (e.g., 24) | 130 * terminal height, rows (e.g., 24) |
138 * @param term_width_pixels | 131 * @param term_width_pixels |
139 * terminal width, pixels (e.g., 640) | 132 * terminal width, pixels (e.g., 640) |
140 * @param term_height_pixels | 133 * @param term_height_pixels |
141 * terminal height, pixels (e.g., 480) | 134 * terminal height, pixels (e.g., 480) |
142 * @throws IOException | 135 * @throws IOException |
143 */ | 136 */ |
144 public void resizePTY(int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels) throws IOException { | 137 public void resizePTY(int term_width_characters, int term_height_characters, int term_width_pixels, int term_height_pixels) throws IOException { |
145 requestWindowChange(term_width_characters, term_height_characters, term_width_pixels, term_height_pixels); | 138 requestWindowChange(term_width_characters, term_height_characters, term_width_pixels, term_height_pixels); |
146 } | 139 } |
147 | 140 |
148 public void requestWindowChange(int term_width_characters, int term_height_characters, int term_width_pixels, | 141 public void requestWindowChange(int term_width_characters, int term_height_characters, int term_width_pixels, |
149 int term_height_pixels) throws IOException | 142 int term_height_pixels) throws IOException { |
150 { | 143 synchronized (this) { |
151 synchronized (this) | 144 /* The following is just a nicer error, we would catch it anyway later in the channel code */ |
152 { | 145 if (flag_closed) |
153 /* The following is just a nicer error, we would catch it anyway later in the channel code */ | 146 throw new IOException("This session is closed."); |
154 if (flag_closed) | 147 |
155 throw new IOException("This session is closed."); | 148 if (!flag_pty_requested) |
156 | 149 throw new IOException("A PTY was not requested."); |
157 if (!flag_pty_requested) | 150 } |
158 throw new IOException("A PTY was not requested."); | 151 |
159 } | 152 cm.requestWindowChange(cn, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels); |
160 | 153 } |
161 cm.requestWindowChange(cn, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels); | 154 |
162 } | 155 /** |
163 | 156 * Request X11 forwarding for the current session. |
164 /** | 157 * <p/> |
165 * Request X11 forwarding for the current session. | 158 * You have to supply the name and port of your X-server. |
166 * <p/> | 159 * <p/> |
167 * You have to supply the name and port of your X-server. | 160 * This method may only be called before a program or shell is started in |
168 * <p/> | 161 * this session. |
169 * This method may only be called before a program or shell is started in | 162 * |
170 * this session. | 163 * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) |
171 * | 164 * @param port the port of the real (target) X11 server (e.g., 6010) |
172 * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1) | 165 * @param cookie if non-null, then present this cookie to the real X11 server |
173 * @param port the port of the real (target) X11 server (e.g., 6010) | 166 * @param singleConnection if true, then the server is instructed to only forward one single |
174 * @param cookie if non-null, then present this cookie to the real X11 server | 167 * connection, no more connections shall be forwarded after first, or after the session |
175 * @param singleConnection if true, then the server is instructed to only forward one single | 168 * channel has been closed |
176 * connection, no more connections shall be forwarded after first, or after the session | 169 * @throws IOException |
177 * channel has been closed | 170 */ |
178 * @throws IOException | 171 public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) |
179 */ | 172 throws IOException { |
180 public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection) | 173 if (hostname == null) |
181 throws IOException | 174 throw new IllegalArgumentException("hostname argument may not be null"); |
182 { | 175 |
183 if (hostname == null) | 176 synchronized (this) { |
184 throw new IllegalArgumentException("hostname argument may not be null"); | 177 /* The following is just a nicer error, we would catch it anyway later in the channel code */ |
185 | 178 if (flag_closed) |
186 synchronized (this) | 179 throw new IOException("This session is closed."); |
187 { | 180 |
188 /* The following is just a nicer error, we would catch it anyway later in the channel code */ | 181 if (flag_x11_requested) |
189 if (flag_closed) | 182 throw new IOException("X11 forwarding was already requested."); |
190 throw new IOException("This session is closed."); | 183 |
191 | 184 if (flag_execution_started) |
192 if (flag_x11_requested) | 185 throw new IOException( |
193 throw new IOException("X11 forwarding was already requested."); | 186 "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); |
194 | 187 |
195 if (flag_execution_started) | 188 flag_x11_requested = true; |
196 throw new IOException( | 189 } |
197 "Cannot request X11 forwarding at this stage anymore, a remote execution has already started."); | 190 |
198 | 191 /* X11ServerData - used to store data about the target X11 server */ |
199 flag_x11_requested = true; | 192 X11ServerData x11data = new X11ServerData(); |
200 } | 193 x11data.hostname = hostname; |
201 | 194 x11data.port = port; |
202 /* X11ServerData - used to store data about the target X11 server */ | 195 x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ |
203 | 196 /* Generate fake cookie - this one is used between remote clients and the ganymed proxy */ |
204 X11ServerData x11data = new X11ServerData(); | 197 byte[] fakeCookie = new byte[16]; |
205 | 198 String hexEncodedFakeCookie; |
206 x11data.hostname = hostname; | 199 |
207 x11data.port = port; | 200 /* Make sure that this fake cookie is unique for this connection */ |
208 x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */ | 201 |
209 | 202 while (true) { |
210 /* Generate fake cookie - this one is used between remote clients and the ganymed proxy */ | 203 rnd.nextBytes(fakeCookie); |
211 | 204 /* Generate also hex representation of fake cookie */ |
212 byte[] fakeCookie = new byte[16]; | 205 StringBuilder tmp = new StringBuilder(32); |
213 String hexEncodedFakeCookie; | 206 |
214 | 207 for (int i = 0; i < fakeCookie.length; i++) { |
215 /* Make sure that this fake cookie is unique for this connection */ | 208 String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); |
216 | 209 tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); |
217 while (true) | 210 } |
218 { | 211 |
219 rnd.nextBytes(fakeCookie); | 212 hexEncodedFakeCookie = tmp.toString(); |
220 | 213 |
221 /* Generate also hex representation of fake cookie */ | 214 /* Well, yes, chances are low, but we want to be on the safe side */ |
222 | 215 |
223 StringBuilder tmp = new StringBuilder(32); | 216 if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) |
224 for (int i = 0; i < fakeCookie.length; i++) | 217 break; |
225 { | 218 } |
226 String digit2 = Integer.toHexString(fakeCookie[i] & 0xff); | 219 |
227 tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2); | 220 /* Ask for X11 forwarding */ |
228 } | 221 cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); |
229 hexEncodedFakeCookie = tmp.toString(); | 222 |
230 | 223 /* OK, that went fine, get ready to accept X11 connections... */ |
231 /* Well, yes, chances are low, but we want to be on the safe side */ | 224 /* ... but only if the user has not called close() in the meantime =) */ |
232 | 225 |
233 if (cm.checkX11Cookie(hexEncodedFakeCookie) == null) | 226 synchronized (this) { |
234 break; | 227 if (flag_closed == false) { |
235 } | 228 this.x11FakeCookie = hexEncodedFakeCookie; |
236 | 229 cm.registerX11Cookie(hexEncodedFakeCookie, x11data); |
237 /* Ask for X11 forwarding */ | 230 } |
238 | 231 } |
239 cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0); | 232 |
240 | 233 /* Now it is safe to start remote X11 programs */ |
241 /* OK, that went fine, get ready to accept X11 connections... */ | 234 } |
242 /* ... but only if the user has not called close() in the meantime =) */ | 235 |
243 | 236 /** |
244 synchronized (this) | 237 * Execute a command on the remote machine. |
245 { | 238 * |
246 if (flag_closed == false) | 239 * @param cmd The command to execute on the remote host. |
247 { | 240 * @throws IOException |
248 this.x11FakeCookie = hexEncodedFakeCookie; | 241 */ |
249 cm.registerX11Cookie(hexEncodedFakeCookie, x11data); | 242 public void execCommand(String cmd) throws IOException { |
250 } | 243 this.execCommand(cmd, null); |
251 } | 244 } |
252 | 245 |
253 /* Now it is safe to start remote X11 programs */ | 246 /** |
254 } | 247 * Execute a command on the remote machine. |
255 | 248 * |
256 /** | 249 * @param cmd The command to execute on the remote host. |
257 * Execute a command on the remote machine. | 250 * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings |
258 * | 251 * @throws IOException |
259 * @param cmd The command to execute on the remote host. | 252 */ |
260 * @throws IOException | 253 public void execCommand(String cmd, String charsetName) throws IOException { |
261 */ | 254 if (cmd == null) |
262 public void execCommand(String cmd) throws IOException | 255 throw new IllegalArgumentException("cmd argument may not be null"); |
263 { | 256 |
264 this.execCommand(cmd, null); | 257 synchronized (this) { |
265 } | 258 /* The following is just a nicer error, we would catch it anyway later in the channel code */ |
266 | 259 if (flag_closed) |
267 /** | 260 throw new IOException("This session is closed."); |
268 * Execute a command on the remote machine. | 261 |
269 * | 262 if (flag_execution_started) |
270 * @param cmd The command to execute on the remote host. | 263 throw new IOException("A remote execution has already started."); |
271 * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings | 264 |
272 * @throws IOException | 265 flag_execution_started = true; |
273 */ | 266 } |
274 public void execCommand(String cmd, String charsetName) throws IOException | 267 |
275 { | 268 cm.requestExecCommand(cn, cmd, charsetName); |
276 if (cmd == null) | 269 } |
277 throw new IllegalArgumentException("cmd argument may not be null"); | 270 |
278 | 271 /** |
279 synchronized (this) | 272 * Start a shell on the remote machine. |
280 { | 273 * |
281 /* The following is just a nicer error, we would catch it anyway later in the channel code */ | 274 * @throws IOException |
282 if (flag_closed) | 275 */ |
283 throw new IOException("This session is closed."); | 276 public void startShell() throws IOException { |
284 | 277 synchronized (this) { |
285 if (flag_execution_started) | 278 /* The following is just a nicer error, we would catch it anyway later in the channel code */ |
286 throw new IOException("A remote execution has already started."); | 279 if (flag_closed) |
287 | 280 throw new IOException("This session is closed."); |
288 flag_execution_started = true; | 281 |
289 } | 282 if (flag_execution_started) |
290 | 283 throw new IOException("A remote execution has already started."); |
291 cm.requestExecCommand(cn, cmd, charsetName); | 284 |
292 } | 285 flag_execution_started = true; |
293 | 286 } |
294 /** | 287 |
295 * Start a shell on the remote machine. | 288 cm.requestShell(cn); |
296 * | 289 } |
297 * @throws IOException | 290 |
298 */ | 291 /** |
299 public void startShell() throws IOException | 292 * Start a subsystem on the remote machine. |
300 { | 293 * Unless you know what you are doing, you will never need this. |
301 synchronized (this) | 294 * |
302 { | 295 * @param name the name of the subsystem. |
303 /* The following is just a nicer error, we would catch it anyway later in the channel code */ | 296 * @throws IOException |
304 if (flag_closed) | 297 */ |
305 throw new IOException("This session is closed."); | 298 public void startSubSystem(String name) throws IOException { |
306 | 299 if (name == null) |
307 if (flag_execution_started) | 300 throw new IllegalArgumentException("name argument may not be null"); |
308 throw new IOException("A remote execution has already started."); | 301 |
309 | 302 synchronized (this) { |
310 flag_execution_started = true; | 303 /* The following is just a nicer error, we would catch it anyway later in the channel code */ |
311 } | 304 if (flag_closed) |
312 | 305 throw new IOException("This session is closed."); |
313 cm.requestShell(cn); | 306 |
314 } | 307 if (flag_execution_started) |
315 | 308 throw new IOException("A remote execution has already started."); |
316 /** | 309 |
317 * Start a subsystem on the remote machine. | 310 flag_execution_started = true; |
318 * Unless you know what you are doing, you will never need this. | 311 } |
319 * | 312 |
320 * @param name the name of the subsystem. | 313 cm.requestSubSystem(cn, name); |
321 * @throws IOException | 314 } |
322 */ | |
323 public void startSubSystem(String name) throws IOException | |
324 { | |
325 if (name == null) | |
326 throw new IllegalArgumentException("name argument may not be null"); | |
327 | |
328 synchronized (this) | |
329 { | |
330 /* The following is just a nicer error, we would catch it anyway later in the channel code */ | |
331 if (flag_closed) | |
332 throw new IOException("This session is closed."); | |
333 | |
334 if (flag_execution_started) | |
335 throw new IOException("A remote execution has already started."); | |
336 | |
337 flag_execution_started = true; | |
338 } | |
339 | |
340 cm.requestSubSystem(cn, name); | |
341 } | |
342 | 315 |
343 /** | 316 /** |
344 * Request authentication agent forwarding. | 317 * Request authentication agent forwarding. |
345 * @param agent object that implements the callbacks | 318 * @param agent object that implements the callbacks |
346 * | 319 * |
358 } | 331 } |
359 | 332 |
360 cm.requestChannelAgentForwarding(cn, agent); | 333 cm.requestChannelAgentForwarding(cn, agent); |
361 } | 334 } |
362 | 335 |
363 public int getState() | 336 public int getState() { |
364 { | 337 return cn.getState(); |
365 return cn.getState(); | 338 } |
366 } | 339 |
367 | 340 public InputStream getStdout() { |
368 public InputStream getStdout() | 341 return cn.getStdoutStream(); |
369 { | 342 } |
370 return cn.getStdoutStream(); | 343 |
371 } | 344 public InputStream getStderr() { |
372 | 345 return cn.getStderrStream(); |
373 public InputStream getStderr() | 346 } |
374 { | 347 |
375 return cn.getStderrStream(); | 348 public OutputStream getStdin() { |
376 } | 349 return cn.getStdinStream(); |
377 | 350 } |
378 public OutputStream getStdin() | 351 |
379 { | 352 /** |
380 return cn.getStdinStream(); | 353 * This method blocks until there is more data available on either the |
381 } | 354 * stdout or stderr InputStream of this <code>Session</code>. Very useful |
382 | 355 * if you do not want to use two parallel threads for reading from the two |
383 /** | 356 * InputStreams. One can also specify a timeout. NOTE: do NOT call this |
384 * This method blocks until there is more data available on either the | 357 * method if you use concurrent threads that operate on either of the two |
385 * stdout or stderr InputStream of this <code>Session</code>. Very useful | 358 * InputStreams of this <code>Session</code> (otherwise this method may |
386 * if you do not want to use two parallel threads for reading from the two | 359 * block, even though more data is available). |
387 * InputStreams. One can also specify a timeout. NOTE: do NOT call this | 360 * |
388 * method if you use concurrent threads that operate on either of the two | 361 * @param timeout The (non-negative) timeout in <code>ms</code>. <code>0</code> means no |
389 * InputStreams of this <code>Session</code> (otherwise this method may | 362 * timeout, the call may block forever. |
390 * block, even though more data is available). | 363 * @return <ul> |
391 * | 364 * <li><code>0</code> if no more data will arrive.</li> |
392 * @param timeout The (non-negative) timeout in <code>ms</code>. <code>0</code> means no | 365 * <li><code>1</code> if more data is available.</li> |
393 * timeout, the call may block forever. | 366 * <li><code>-1</code> if a timeout occurred.</li> |
394 * @return <ul> | 367 * </ul> |
395 * <li><code>0</code> if no more data will arrive.</li> | 368 * @throws IOException |
396 * <li><code>1</code> if more data is available.</li> | 369 * @deprecated This method has been replaced with a much more powerful wait-for-condition |
397 * <li><code>-1</code> if a timeout occurred.</li> | 370 * interface and therefore acts only as a wrapper. |
398 * </ul> | 371 */ |
399 * @throws IOException | 372 public int waitUntilDataAvailable(long timeout) throws IOException { |
400 * @deprecated This method has been replaced with a much more powerful wait-for-condition | 373 if (timeout < 0) |
401 * interface and therefore acts only as a wrapper. | 374 throw new IllegalArgumentException("timeout must not be negative!"); |
402 */ | 375 |
403 public int waitUntilDataAvailable(long timeout) throws IOException | 376 int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA |
404 { | 377 | ChannelCondition.EOF); |
405 if (timeout < 0) | 378 |
406 throw new IllegalArgumentException("timeout must not be negative!"); | 379 if ((conditions & ChannelCondition.TIMEOUT) != 0) |
407 | 380 return -1; |
408 int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA | 381 |
409 | ChannelCondition.EOF); | 382 if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0) |
410 | 383 return 1; |
411 if ((conditions & ChannelCondition.TIMEOUT) != 0) | 384 |
412 return -1; | 385 /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ |
413 | 386 |
414 if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0) | 387 if ((conditions & ChannelCondition.EOF) != 0) |
415 return 1; | 388 return 0; |
416 | 389 |
417 /* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */ | 390 throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); |
418 | 391 } |
419 if ((conditions & ChannelCondition.EOF) != 0) | 392 |
420 return 0; | 393 /** |
421 | 394 * This method blocks until certain conditions hold true on the underlying SSH-2 channel. |
422 throw new IllegalStateException("Unexpected condition result (" + conditions + ")"); | 395 * <p/> |
423 } | 396 * This method returns as soon as one of the following happens: |
424 | 397 * <ul> |
425 /** | 398 * <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li> |
426 * This method blocks until certain conditions hold true on the underlying SSH-2 channel. | 399 * <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a> |
427 * <p/> | 400 * <li>the underlying channel was closed (CLOSED will be set in result conditions)</a> |
428 * This method returns as soon as one of the following happens: | 401 * </ul> |
429 * <ul> | 402 * <p/> |
430 * <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li> | 403 * In any case, the result value contains ALL current conditions, which may be more |
431 * <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a> | 404 * than the specified condition set (i.e., never use the "==" operator to test for conditions |
432 * <li>the underlying channel was closed (CLOSED will be set in result conditions)</a> | 405 * in the bitmask, see also comments in {@link ChannelCondition}). |
433 * </ul> | 406 * <p/> |
434 * <p/> | 407 * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and |
435 * In any case, the result value contains ALL current conditions, which may be more | 408 * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two |
436 * than the specified condition set (i.e., never use the "==" operator to test for conditions | 409 * InputStreams of this <code>Session</code> (otherwise this method may |
437 * in the bitmask, see also comments in {@link ChannelCondition}). | 410 * block, even though more data is available in the StreamGobblers). |
438 * <p/> | 411 * |
439 * Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and | 412 * @param condition_set a bitmask based on {@link ChannelCondition} values |
440 * there are concurrent threads (e.g., StreamGobblers) that operate on either of the two | 413 * @param timeout non-negative timeout in ms, <code>0</code> means no timeout |
441 * InputStreams of this <code>Session</code> (otherwise this method may | 414 * @return all bitmask specifying all current conditions that are true |
442 * block, even though more data is available in the StreamGobblers). | 415 */ |
443 * | 416 |
444 * @param condition_set a bitmask based on {@link ChannelCondition} values | 417 public int waitForCondition(int condition_set, long timeout) throws IOException { |
445 * @param timeout non-negative timeout in ms, <code>0</code> means no timeout | 418 if (timeout < 0) |
446 * @return all bitmask specifying all current conditions that are true | 419 throw new IllegalArgumentException("timeout must be non-negative!"); |
447 */ | 420 |
448 | 421 return cm.waitForCondition(cn, timeout, condition_set); |
449 public int waitForCondition(int condition_set, long timeout) throws IOException { | 422 } |
450 if (timeout < 0) | 423 |
451 throw new IllegalArgumentException("timeout must be non-negative!"); | 424 /** |
452 | 425 * Get the exit code/status from the remote command - if available. Be |
453 return cm.waitForCondition(cn, timeout, condition_set); | 426 * careful - not all server implementations return this value. It is |
454 } | 427 * generally a good idea to call this method only when all data from the |
455 | 428 * remote side has been consumed (see also the <code<WaitForCondition</code> method). |
456 /** | 429 * |
457 * Get the exit code/status from the remote command - if available. Be | 430 * @return An <code>Integer</code> holding the exit code, or |
458 * careful - not all server implementations return this value. It is | 431 * <code>null</code> if no exit code is (yet) available. |
459 * generally a good idea to call this method only when all data from the | 432 */ |
460 * remote side has been consumed (see also the <code<WaitForCondition</code> method). | 433 public Integer getExitStatus() { |
461 * | 434 return cn.getExitStatus(); |
462 * @return An <code>Integer</code> holding the exit code, or | 435 } |
463 * <code>null</code> if no exit code is (yet) available. | 436 |
464 */ | 437 /** |
465 public Integer getExitStatus() | 438 * Get the name of the signal by which the process on the remote side was |
466 { | 439 * stopped - if available and applicable. Be careful - not all server |
467 return cn.getExitStatus(); | 440 * implementations return this value. |
468 } | 441 * |
469 | 442 * @return An <code>String</code> holding the name of the signal, or |
470 /** | 443 * <code>null</code> if the process exited normally or is still |
471 * Get the name of the signal by which the process on the remote side was | 444 * running (or if the server forgot to send this information). |
472 * stopped - if available and applicable. Be careful - not all server | 445 */ |
473 * implementations return this value. | 446 public String getExitSignal() { |
474 * | 447 return cn.getExitSignal(); |
475 * @return An <code>String</code> holding the name of the signal, or | 448 } |
476 * <code>null</code> if the process exited normally or is still | 449 |
477 * running (or if the server forgot to send this information). | 450 /** |
478 */ | 451 * Close this session. NEVER forget to call this method to free up resources - |
479 public String getExitSignal() | 452 * even if you got an exception from one of the other methods (or when |
480 { | 453 * getting an Exception on the Input- or OutputStreams). Sometimes these other |
481 return cn.getExitSignal(); | 454 * methods may throw an exception, saying that the underlying channel is |
482 } | 455 * closed (this can happen, e.g., if the other server sent a close message.) |
483 | 456 * However, as long as you have not called the <code>close()</code> |
484 /** | 457 * method, you may be wasting (local) resources. |
485 * Close this session. NEVER forget to call this method to free up resources - | 458 */ |
486 * even if you got an exception from one of the other methods (or when | 459 public void close() { |
487 * getting an Exception on the Input- or OutputStreams). Sometimes these other | 460 synchronized (this) { |
488 * methods may throw an exception, saying that the underlying channel is | 461 if (flag_closed) |
489 * closed (this can happen, e.g., if the other server sent a close message.) | 462 return; |
490 * However, as long as you have not called the <code>close()</code> | 463 |
491 * method, you may be wasting (local) resources. | 464 flag_closed = true; |
492 */ | 465 |
493 public void close() | 466 if (x11FakeCookie != null) |
494 { | 467 cm.unRegisterX11Cookie(x11FakeCookie, true); |
495 synchronized (this) | 468 |
496 { | 469 try { |
497 if (flag_closed) | 470 cm.closeChannel(cn, "Closed due to user request", true); |
498 return; | 471 } |
499 | 472 catch (IOException ignored) { |
500 flag_closed = true; | 473 } |
501 | 474 } |
502 if (x11FakeCookie != null) | 475 } |
503 cm.unRegisterX11Cookie(x11FakeCookie, true); | |
504 | |
505 try | |
506 { | |
507 cm.closeChannel(cn, "Closed due to user request", true); | |
508 } | |
509 catch (IOException ignored) | |
510 { | |
511 } | |
512 } | |
513 } | |
514 } | 476 } |