1 /* 2 * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.io; 27 28 import java.util.*; 29 import java.nio.charset.Charset; 30 import sun.nio.cs.StreamDecoder; 31 import sun.nio.cs.StreamEncoder; 32 33 /** 34 * Methods to access the character-based console device, if any, associated 35 * with the current Java virtual machine. 36 * 37 * <p> Whether a virtual machine has a console is dependent upon the 38 * underlying platform and also upon the manner in which the virtual 39 * machine is invoked. If the virtual machine is started from an 40 * interactive command line without redirecting the standard input and 41 * output streams then its console will exist and will typically be 42 * connected to the keyboard and display from which the virtual machine 43 * was launched. If the virtual machine is started automatically, for 44 * example by a background job scheduler, then it will typically not 45 * have a console. 46 * <p> 47 * If this virtual machine has a console then it is represented by a 48 * unique instance of this class which can be obtained by invoking the 49 * {@link java.lang.System#console()} method. If no console device is 50 * available then an invocation of that method will return <tt>null</tt>. 51 * <p> 52 * Read and write operations are synchronized to guarantee the atomic 53 * completion of critical operations; therefore invoking methods 54 * {@link #readLine()}, {@link #readPassword()}, {@link #format format()}, 55 * {@link #printf printf()} as well as the read, format and write operations 56 * on the objects returned by {@link #reader()} and {@link #writer()} may 57 * block in multithreaded scenarios. 58 * <p> 59 * Invoking <tt>close()</tt> on the objects returned by the {@link #reader()} 60 * and the {@link #writer()} will not close the underlying stream of those 61 * objects. 62 * <p> 63 * The console-read methods return <tt>null</tt> when the end of the 64 * console input stream is reached, for example by typing control-D on 65 * Unix or control-Z on Windows. Subsequent read operations will succeed 66 * if additional characters are later entered on the console's input 67 * device. 68 * <p> 69 * Unless otherwise specified, passing a <tt>null</tt> argument to any method 70 * in this class will cause a {@link NullPointerException} to be thrown. 71 * <p> 72 * <b>Security note:</b> 73 * If an application needs to read a password or other secure data, it should 74 * use {@link #readPassword()} or {@link #readPassword(String, Object...)} and 75 * manually zero the returned character array after processing to minimize the 76 * lifetime of sensitive data in memory. 77 * 78 * <blockquote><pre>{@code 79 * Console cons; 80 * char[] passwd; 81 * if ((cons = System.console()) != null && 82 * (passwd = cons.readPassword("[%s]", "Password:")) != null) { 83 * ... 84 * java.util.Arrays.fill(passwd, ' '); 85 * } 86 * }</pre></blockquote> 87 * 88 * @author Xueming Shen 89 * @since 1.6 90 */ 91 92 public final class Console implements Flushable 93 { 94 /** 95 * Retrieves the unique {@link java.io.PrintWriter PrintWriter} object 96 * associated with this console. 97 * 98 * @return The printwriter associated with this console 99 */ 100 public PrintWriter writer() { 101 return pw; 102 } 103 104 /** 105 * Retrieves the unique {@link java.io.Reader Reader} object associated 106 * with this console. 107 * <p> 108 * This method is intended to be used by sophisticated applications, for 109 * example, a {@link java.util.Scanner} object which utilizes the rich 110 * parsing/scanning functionality provided by the <tt>Scanner</tt>: 111 * <blockquote><pre> 112 * Console con = System.console(); 113 * if (con != null) { 114 * Scanner sc = new Scanner(con.reader()); 115 * ... 116 * } 117 * </pre></blockquote> 118 * <p> 119 * For simple applications requiring only line-oriented reading, use 120 * <tt>{@link #readLine}</tt>. 121 * <p> 122 * The bulk read operations {@link java.io.Reader#read(char[]) read(char[]) }, 123 * {@link java.io.Reader#read(char[], int, int) read(char[], int, int) } and 124 * {@link java.io.Reader#read(java.nio.CharBuffer) read(java.nio.CharBuffer)} 125 * on the returned object will not read in characters beyond the line 126 * bound for each invocation, even if the destination buffer has space for 127 * more characters. A line bound is considered to be any one of a line feed 128 * (<tt>'\n'</tt>), a carriage return (<tt>'\r'</tt>), a carriage return 129 * followed immediately by a linefeed, or an end of stream. 130 * 131 * @return The reader associated with this console 132 */ 133 public Reader reader() { 134 return reader; 135 } 136 137 /** 138 * Writes a formatted string to this console's output stream using 139 * the specified format string and arguments. 140 * 141 * @param fmt 142 * A format string as described in <a 143 * href="../util/Formatter.html#syntax">Format string syntax</a> 144 * 145 * @param args 146 * Arguments referenced by the format specifiers in the format 147 * string. If there are more arguments than format specifiers, the 148 * extra arguments are ignored. The number of arguments is 149 * variable and may be zero. The maximum number of arguments is 150 * limited by the maximum dimension of a Java array as defined by 151 * <cite>The Java™ Virtual Machine Specification</cite>. 152 * The behaviour on a 153 * <tt>null</tt> argument depends on the <a 154 * href="../util/Formatter.html#syntax">conversion</a>. 155 * 156 * @throws IllegalFormatException 157 * If a format string contains an illegal syntax, a format 158 * specifier that is incompatible with the given arguments, 159 * insufficient arguments given the format string, or other 160 * illegal conditions. For specification of all possible 161 * formatting errors, see the <a 162 * href="../util/Formatter.html#detail">Details</a> section 163 * of the formatter class specification. 164 * 165 * @return This console 166 */ 167 public Console format(String fmt, Object ...args) { 168 formatter.format(fmt, args).flush(); 169 return this; 170 } 171 172 /** 173 * A convenience method to write a formatted string to this console's 174 * output stream using the specified format string and arguments. 175 * 176 * <p> An invocation of this method of the form <tt>con.printf(format, 177 * args)</tt> behaves in exactly the same way as the invocation of 178 * <pre>con.format(format, args)</pre>. 179 * 180 * @param format 181 * A format string as described in <a 182 * href="../util/Formatter.html#syntax">Format string syntax</a>. 183 * 184 * @param args 185 * Arguments referenced by the format specifiers in the format 186 * string. If there are more arguments than format specifiers, the 187 * extra arguments are ignored. The number of arguments is 188 * variable and may be zero. The maximum number of arguments is 189 * limited by the maximum dimension of a Java array as defined by 190 * <cite>The Java™ Virtual Machine Specification</cite>. 191 * The behaviour on a 192 * <tt>null</tt> argument depends on the <a 193 * href="../util/Formatter.html#syntax">conversion</a>. 194 * 195 * @throws IllegalFormatException 196 * If a format string contains an illegal syntax, a format 197 * specifier that is incompatible with the given arguments, 198 * insufficient arguments given the format string, or other 199 * illegal conditions. For specification of all possible 200 * formatting errors, see the <a 201 * href="../util/Formatter.html#detail">Details</a> section of the 202 * formatter class specification. 203 * 204 * @return This console 205 */ 206 public Console printf(String format, Object ... args) { 207 return format(format, args); 208 } 209 210 /** 211 * Provides a formatted prompt, then reads a single line of text from the 212 * console. 213 * 214 * @param fmt 215 * A format string as described in <a 216 * href="../util/Formatter.html#syntax">Format string syntax</a>. 217 * 218 * @param args 219 * Arguments referenced by the format specifiers in the format 220 * string. If there are more arguments than format specifiers, the 221 * extra arguments are ignored. The maximum number of arguments is 222 * limited by the maximum dimension of a Java array as defined by 223 * <cite>The Java™ Virtual Machine Specification</cite>. 224 * 225 * @throws IllegalFormatException 226 * If a format string contains an illegal syntax, a format 227 * specifier that is incompatible with the given arguments, 228 * insufficient arguments given the format string, or other 229 * illegal conditions. For specification of all possible 230 * formatting errors, see the <a 231 * href="../util/Formatter.html#detail">Details</a> section 232 * of the formatter class specification. 233 * 234 * @throws IOError 235 * If an I/O error occurs. 236 * 237 * @return A string containing the line read from the console, not 238 * including any line-termination characters, or <tt>null</tt> 239 * if an end of stream has been reached. 240 */ 241 public String readLine(String fmt, Object ... args) { 242 String line = null; 243 synchronized (writeLock) { 244 synchronized(readLock) { 245 if (fmt.length() != 0) 246 pw.format(fmt, args); 247 try { 248 char[] ca = readline(false); 249 if (ca != null) 250 line = new String(ca); 251 } catch (IOException x) { 252 throw new IOError(x); 253 } 254 } 255 } 256 return line; 257 } 258 259 /** 260 * Reads a single line of text from the console. 261 * 262 * @throws IOError 263 * If an I/O error occurs. 264 * 265 * @return A string containing the line read from the console, not 266 * including any line-termination characters, or <tt>null</tt> 267 * if an end of stream has been reached. 268 */ 269 public String readLine() { 270 return readLine(""); 271 } 272 273 /** 274 * Provides a formatted prompt, then reads a password or passphrase from 275 * the console with echoing disabled. 276 * 277 * @param fmt 278 * A format string as described in <a 279 * href="../util/Formatter.html#syntax">Format string syntax</a> 280 * for the prompt text. 281 * 282 * @param args 283 * Arguments referenced by the format specifiers in the format 284 * string. If there are more arguments than format specifiers, the 285 * extra arguments are ignored. The maximum number of arguments is 286 * limited by the maximum dimension of a Java array as defined by 287 * <cite>The Java™ Virtual Machine Specification</cite>. 288 * 289 * @throws IllegalFormatException 290 * If a format string contains an illegal syntax, a format 291 * specifier that is incompatible with the given arguments, 292 * insufficient arguments given the format string, or other 293 * illegal conditions. For specification of all possible 294 * formatting errors, see the <a 295 * href="../util/Formatter.html#detail">Details</a> 296 * section of the formatter class specification. 297 * 298 * @throws IOError 299 * If an I/O error occurs. 300 * 301 * @return A character array containing the password or passphrase read 302 * from the console, not including any line-termination characters, 303 * or <tt>null</tt> if an end of stream has been reached. 304 */ 305 public char[] readPassword(String fmt, Object ... args) { 306 char[] passwd = null; 307 synchronized (writeLock) { 308 synchronized(readLock) { 309 try { 310 echoOff = echo(false); 311 } catch (IOException x) { 312 throw new IOError(x); 313 } 314 IOError ioe = null; 315 try { 316 if (fmt.length() != 0) 317 pw.format(fmt, args); 318 passwd = readline(true); 319 } catch (IOException x) { 320 ioe = new IOError(x); 321 } finally { 322 try { 323 echoOff = echo(true); 324 } catch (IOException x) { 325 if (ioe == null) 326 ioe = new IOError(x); 327 else 328 ioe.addSuppressed(x); 329 } 330 if (ioe != null) 331 throw ioe; 332 } 333 pw.println(); 334 } 335 } 336 return passwd; 337 } 338 339 /** 340 * Reads a password or passphrase from the console with echoing disabled 341 * 342 * @throws IOError 343 * If an I/O error occurs. 344 * 345 * @return A character array containing the password or passphrase read 346 * from the console, not including any line-termination characters, 347 * or <tt>null</tt> if an end of stream has been reached. 348 */ 349 public char[] readPassword() { 350 return readPassword(""); 351 } 352 353 /** 354 * Flushes the console and forces any buffered output to be written 355 * immediately . 356 */ 357 public void flush() { 358 pw.flush(); 359 } 360 361 private Object readLock; 362 private Object writeLock; 363 private Reader reader; 364 private Writer out; 365 private PrintWriter pw; 366 private Formatter formatter; 367 private Charset cs; 368 private char[] rcb; 369 private static native String encoding(); 370 private static native boolean echo(boolean on) throws IOException; 371 private static boolean echoOff; 372 373 private char[] readline(boolean zeroOut) throws IOException { 374 int len = reader.read(rcb, 0, rcb.length); 375 if (len < 0) 376 return null; //EOL 377 if (rcb[len-1] == '\r') 378 len--; //remove CR at end; 379 else if (rcb[len-1] == '\n') { 380 len--; //remove LF at end; 381 if (len > 0 && rcb[len-1] == '\r') 382 len--; //remove the CR, if there is one 383 } 384 char[] b = new char[len]; 385 if (len > 0) { 386 System.arraycopy(rcb, 0, b, 0, len); 387 if (zeroOut) { 388 Arrays.fill(rcb, 0, len, ' '); 389 } 390 } 391 return b; 392 } 393 394 private char[] grow() { 395 assert Thread.holdsLock(readLock); 396 char[] t = new char[rcb.length * 2]; 397 System.arraycopy(rcb, 0, t, 0, rcb.length); 398 rcb = t; 399 return rcb; 400 } 401 402 class LineReader extends Reader { 403 private Reader in; 404 private char[] cb; 405 private int nChars, nextChar; 406 boolean leftoverLF; 407 LineReader(Reader in) { 408 this.in = in; 409 cb = new char[1024]; 410 nextChar = nChars = 0; 411 leftoverLF = false; 412 } 413 public void close () {} 414 public boolean ready() throws IOException { 415 //in.ready synchronizes on readLock already 416 return in.ready(); 417 } 418 419 public int read(char cbuf[], int offset, int length) 420 throws IOException 421 { 422 int off = offset; 423 int end = offset + length; 424 if (offset < 0 || offset > cbuf.length || length < 0 || 425 end < 0 || end > cbuf.length) { 426 throw new IndexOutOfBoundsException(); 427 } 428 synchronized(readLock) { 429 boolean eof = false; 430 char c = 0; 431 for (;;) { 432 if (nextChar >= nChars) { //fill 433 int n = 0; 434 do { 435 n = in.read(cb, 0, cb.length); 436 } while (n == 0); 437 if (n > 0) { 438 nChars = n; 439 nextChar = 0; 440 if (n < cb.length && 441 cb[n-1] != '\n' && cb[n-1] != '\r') { 442 /* 443 * we're in canonical mode so each "fill" should 444 * come back with an eol. if there no lf or nl at 445 * the end of returned bytes we reached an eof. 446 */ 447 eof = true; 448 } 449 } else { /*EOF*/ 450 if (off - offset == 0) 451 return -1; 452 return off - offset; 453 } 454 } 455 if (leftoverLF && cbuf == rcb && cb[nextChar] == '\n') { 456 /* 457 * if invoked by our readline, skip the leftover, otherwise 458 * return the LF. 459 */ 460 nextChar++; 461 } 462 leftoverLF = false; 463 while (nextChar < nChars) { 464 c = cbuf[off++] = cb[nextChar]; 465 cb[nextChar++] = 0; 466 if (c == '\n') { 467 return off - offset; 468 } else if (c == '\r') { 469 if (off == end) { 470 /* no space left even the next is LF, so return 471 * whatever we have if the invoker is not our 472 * readLine() 473 */ 474 if (cbuf == rcb) { 475 cbuf = grow(); 476 end = cbuf.length; 477 } else { 478 leftoverLF = true; 479 return off - offset; 480 } 481 } 482 if (nextChar == nChars && in.ready()) { 483 /* 484 * we have a CR and we reached the end of 485 * the read in buffer, fill to make sure we 486 * don't miss a LF, if there is one, it's possible 487 * that it got cut off during last round reading 488 * simply because the read in buffer was full. 489 */ 490 nChars = in.read(cb, 0, cb.length); 491 nextChar = 0; 492 } 493 if (nextChar < nChars && cb[nextChar] == '\n') { 494 cbuf[off++] = '\n'; 495 nextChar++; 496 } 497 return off - offset; 498 } else if (off == end) { 499 if (cbuf == rcb) { 500 cbuf = grow(); 501 end = cbuf.length; 502 } else { 503 return off - offset; 504 } 505 } 506 } 507 if (eof) 508 return off - offset; 509 } 510 } 511 } 512 } 513 514 // Set up JavaIOAccess in SharedSecrets 515 static { 516 try { 517 // Add a shutdown hook to restore console's echo state should 518 // it be necessary. 519 sun.misc.SharedSecrets.getJavaLangAccess() 520 .registerShutdownHook(0 /* shutdown hook invocation order */, 521 false /* only register if shutdown is not in progress */, 522 new Runnable() { 523 public void run() { 524 try { 525 if (echoOff) { 526 echo(true); 527 } 528 } catch (IOException x) { } 529 } 530 }); 531 } catch (IllegalStateException e) { 532 // shutdown is already in progress and console is first used 533 // by a shutdown hook 534 } 535 536 sun.misc.SharedSecrets.setJavaIOAccess(new sun.misc.JavaIOAccess() { 537 public Console console() { 538 if (istty()) { 539 if (cons == null) 540 cons = new Console(); 541 return cons; 542 } 543 return null; 544 } 545 546 public Charset charset() { 547 // This method is called in sun.security.util.Password, 548 // cons already exists when this method is called 549 return cons.cs; 550 } 551 }); 552 } 553 private static Console cons; 554 private native static boolean istty(); 555 private Console() { 556 readLock = new Object(); 557 writeLock = new Object(); 558 String csname = encoding(); 559 if (csname != null) { 560 try { 561 cs = Charset.forName(csname); 562 } catch (Exception x) {} 563 } 564 if (cs == null) 565 cs = Charset.defaultCharset(); 566 out = StreamEncoder.forOutputStreamWriter( 567 new FileOutputStream(FileDescriptor.out), 568 writeLock, 569 cs); 570 pw = new PrintWriter(out, true) { public void close() {} }; 571 formatter = new Formatter(out); 572 reader = new LineReader(StreamDecoder.forInputStreamReader( 573 new FileInputStream(FileDescriptor.in), 574 readLock, 575 cs)); 576 rcb = new char[1024]; 577 } 578 }