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