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