1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. Oracle designates this 9 * particular file as subject to the "Classpath" exception as provided 10 * by Oracle in the LICENSE file that accompanied this code. 11 * 12 * This code is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15 * version 2 for more details (a copy is included in the LICENSE file that 16 * accompanied this code). 17 * 18 * You should have received a copy of the GNU General Public License version 19 * 2 along with this work; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 21 * 22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 23 * or visit www.oracle.com if you need additional information or have any 24 * questions. 25 */ 26 27 package java.io; 28 29 import java.util.*; 30 import java.nio.charset.Charset; 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 // Android-removed: The hook is a no-op (but causes trouble when it's turned on). 316 // installShutdownHook(); 317 try { 318 restoreEcho = echo(false); 319 } catch (IOException x) { 320 throw new IOError(x); 321 } 322 IOError ioe = null; 323 try { 324 if (!fmt.isEmpty()) 325 pw.format(fmt, args); 326 passwd = readline(true); 327 } catch (IOException x) { 328 ioe = new IOError(x); 329 } finally { 330 try { 331 if (restoreEcho) 332 restoreEcho = echo(true); 333 } catch (IOException x) { 334 if (ioe == null) 335 ioe = new IOError(x); 336 else 337 ioe.addSuppressed(x); 338 } 339 if (ioe != null) 340 throw ioe; 341 } 342 pw.println(); 343 } 344 } 345 return passwd; 346 } 347 348 349 // Android-removed: The hook is a no-op (but causes trouble when it's turned on). 350 /* 351 private void installShutdownHook() { 352 if (shutdownHookInstalled) 353 return; 354 try { 355 // Add a shutdown hook to restore console's echo state should 356 // it be necessary. 357 SharedSecrets.getJavaLangAccess() 358 .registerShutdownHook(0 /* shutdown hook invocation order *//*, 359 false /* only register if shutdown is not in progress *//*, 360 new Runnable() { 361 public void run() { 362 try { 363 if (restoreEcho) { 364 echo(true); 365 } 366 } catch (IOException x) { } 367 } 368 }); 369 } catch (IllegalStateException e) { 370 // shutdown is already in progress and readPassword is first used 371 // by a shutdown hook 372 } 373 shutdownHookInstalled = true; 374 } 375 */ 376 377 /** 378 * Reads a password or passphrase from the console with echoing disabled 379 * 380 * @throws IOError 381 * If an I/O error occurs. 382 * 383 * @return A character array containing the password or passphrase read 384 * from the console, not including any line-termination characters, 385 * or {@code null} if an end of stream has been reached. 386 */ readPassword()387 public char[] readPassword() { 388 return readPassword(""); 389 } 390 391 /** 392 * Flushes the console and forces any buffered output to be written 393 * immediately . 394 */ flush()395 public void flush() { 396 pw.flush(); 397 } 398 399 400 /** 401 * Returns the {@link java.nio.charset.Charset Charset} object used for 402 * the {@code Console}. 403 * <p> 404 * The returned charset corresponds to the input and output source 405 * (e.g., keyboard and/or display) specified by the host environment or user. 406 * It may not necessarily be the same as the default charset returned from 407 * {@link java.nio.charset.Charset#defaultCharset() Charset.defaultCharset()}. 408 * 409 * @return a {@link java.nio.charset.Charset Charset} object used for the 410 * {@code Console} 411 * @since 17 412 */ charset()413 public Charset charset() { 414 assert CHARSET != null : "charset() should not return null"; 415 return CHARSET; 416 } 417 418 private Object readLock; 419 private Object writeLock; 420 private Reader reader; 421 private Writer out; 422 private PrintWriter pw; 423 private Formatter formatter; 424 private char[] rcb; 425 private boolean restoreEcho; 426 private boolean shutdownHookInstalled; encoding()427 private static native String encoding(); 428 /* 429 * Sets the console echo status to {@code on} and returns the previous 430 * console on/off status. 431 * @param on the echo status to set to. {@code true} for echo on and 432 * {@code false} for echo off 433 * @return true if the previous console echo status is on 434 */ echo(boolean on)435 private static native boolean echo(boolean on) throws IOException; 436 readline(boolean zeroOut)437 private char[] readline(boolean zeroOut) throws IOException { 438 int len = reader.read(rcb, 0, rcb.length); 439 if (len < 0) 440 return null; //EOL 441 if (rcb[len-1] == '\r') 442 len--; //remove CR at end; 443 else if (rcb[len-1] == '\n') { 444 len--; //remove LF at end; 445 if (len > 0 && rcb[len-1] == '\r') 446 len--; //remove the CR, if there is one 447 } 448 char[] b = new char[len]; 449 if (len > 0) { 450 System.arraycopy(rcb, 0, b, 0, len); 451 if (zeroOut) { 452 Arrays.fill(rcb, 0, len, ' '); 453 } 454 } 455 return b; 456 } 457 grow()458 private char[] grow() { 459 assert Thread.holdsLock(readLock); 460 char[] t = new char[rcb.length * 2]; 461 System.arraycopy(rcb, 0, t, 0, rcb.length); 462 rcb = t; 463 return rcb; 464 } 465 466 class LineReader extends Reader { 467 private Reader in; 468 private char[] cb; 469 private int nChars, nextChar; 470 boolean leftoverLF; LineReader(Reader in)471 LineReader(Reader in) { 472 this.in = in; 473 cb = new char[1024]; 474 nextChar = nChars = 0; 475 leftoverLF = false; 476 } close()477 public void close () {} ready()478 public boolean ready() throws IOException { 479 //in.ready synchronizes on readLock already 480 return in.ready(); 481 } 482 read(char cbuf[], int offset, int length)483 public int read(char cbuf[], int offset, int length) 484 throws IOException 485 { 486 int off = offset; 487 int end = offset + length; 488 if (offset < 0 || offset > cbuf.length || length < 0 || 489 end < 0 || end > cbuf.length) { 490 throw new IndexOutOfBoundsException(); 491 } 492 synchronized(readLock) { 493 boolean eof = false; 494 char c = 0; 495 for (;;) { 496 if (nextChar >= nChars) { //fill 497 int n = 0; 498 do { 499 n = in.read(cb, 0, cb.length); 500 } while (n == 0); 501 if (n > 0) { 502 nChars = n; 503 nextChar = 0; 504 if (n < cb.length && 505 cb[n-1] != '\n' && cb[n-1] != '\r') { 506 /* 507 * we're in canonical mode so each "fill" should 508 * come back with an eol. if there no lf or nl at 509 * the end of returned bytes we reached an eof. 510 */ 511 eof = true; 512 } 513 } else { /*EOF*/ 514 if (off - offset == 0) 515 return -1; 516 return off - offset; 517 } 518 } 519 if (leftoverLF && cbuf == rcb && cb[nextChar] == '\n') { 520 /* 521 * if invoked by our readline, skip the leftover, otherwise 522 * return the LF. 523 */ 524 nextChar++; 525 } 526 leftoverLF = false; 527 while (nextChar < nChars) { 528 c = cbuf[off++] = cb[nextChar]; 529 cb[nextChar++] = 0; 530 if (c == '\n') { 531 return off - offset; 532 } else if (c == '\r') { 533 if (off == end) { 534 /* no space left even the next is LF, so return 535 * whatever we have if the invoker is not our 536 * readLine() 537 */ 538 if (cbuf == rcb) { 539 cbuf = grow(); 540 end = cbuf.length; 541 } else { 542 leftoverLF = true; 543 return off - offset; 544 } 545 } 546 if (nextChar == nChars && in.ready()) { 547 /* 548 * we have a CR and we reached the end of 549 * the read in buffer, fill to make sure we 550 * don't miss a LF, if there is one, it's possible 551 * that it got cut off during last round reading 552 * simply because the read in buffer was full. 553 */ 554 nChars = in.read(cb, 0, cb.length); 555 nextChar = 0; 556 } 557 if (nextChar < nChars && cb[nextChar] == '\n') { 558 cbuf[off++] = '\n'; 559 nextChar++; 560 } 561 return off - offset; 562 } else if (off == end) { 563 if (cbuf == rcb) { 564 cbuf = grow(); 565 end = cbuf.length; 566 } else { 567 return off - offset; 568 } 569 } 570 } 571 if (eof) 572 return off - offset; 573 } 574 } 575 } 576 } 577 578 private static final Charset CHARSET; 579 static { 580 String csname = encoding(); 581 Charset cs = null; 582 // Android-changed: libcore doesn't traditionally support sun.stdout.encoding property. 583 /* 584 if (csname == null) { 585 csname = GetPropertyAction.privilegedGetProperty("sun.stdout.encoding"); 586 } 587 */ 588 if (csname != null) { 589 try { 590 cs = Charset.forName(csname); 591 } catch (Exception ignored) { 592 } 593 } 594 CHARSET = cs == null ? Charset.defaultCharset() : cs; 595 596 // Set up JavaIOAccess in SharedSecrets 597 // Android-removed: SharedSecrets setup and also the shutdown hook. 598 /* 599 SharedSecrets.setJavaIOAccess(new JavaIOAccess() { 600 public Console console() { 601 if (istty()) { 602 if (cons == null) 603 cons = new Console(); 604 return cons; 605 } 606 return null; 607 } 608 609 public Charset charset() { 610 return CHARSET; 611 } 612 }); 613 */ 614 } 615 616 // Android-changed: Use @hide rather than sun.misc.SharedSecrets to expose console(). 617 /** @hide */ console()618 public static Console console() { 619 if (istty()) { 620 if (cons == null) 621 cons = new Console(); 622 return cons; 623 } 624 return null; 625 } 626 private static Console cons; istty()627 private static native boolean istty(); Console()628 private Console() { 629 // BEGIN Android-changed: Support custom in/out streams for testing. 630 this(new FileInputStream(FileDescriptor.in), new FileOutputStream(FileDescriptor.out)); 631 } 632 633 // Constructor for tests Console(InputStream inStream, OutputStream outStream)634 private Console(InputStream inStream, OutputStream outStream) { 635 // END Android-changed: Support custom in/out streams for testing. 636 readLock = new Object(); 637 writeLock = new Object(); 638 out = StreamEncoder.forOutputStreamWriter( 639 outStream, 640 writeLock, 641 CHARSET); 642 pw = new PrintWriter(out, true) { public void close() {} }; 643 formatter = new Formatter(out); 644 reader = new LineReader(StreamDecoder.forInputStreamReader( 645 inStream, 646 readLock, 647 CHARSET)); 648 rcb = new char[1024]; 649 } 650 } 651