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