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