2626package java .io ;
2727
2828import java .util .*;
29+ import java .lang .annotation .Native ;
2930import java .nio .charset .Charset ;
31+ import java .util .Optional ;
32+ import java .util .concurrent .atomic .AtomicReference ;
3033import jdk .internal .access .JavaIOAccess ;
3134import jdk .internal .access .SharedSecrets ;
35+ import jdk .internal .util .StaticProperty ;
3236import sun .nio .cs .StreamDecoder ;
3337import sun .nio .cs .StreamEncoder ;
38+ import sun .nio .cs .UTF_8 ;
3439import sun .security .action .GetPropertyAction ;
3540
3641/**
@@ -280,7 +285,7 @@ public String readLine() {
280285 * Provides a formatted prompt, then reads a password or passphrase from
281286 * the console with echoing disabled.
282287 *
283- * @param fmt
288+ * @param format
284289 * A format string as described in <a
285290 * href="../util/Formatter.html#syntax">Format string syntax</a>
286291 * for the prompt text.
@@ -308,7 +313,68 @@ public String readLine() {
308313 * from the console, not including any line-termination characters,
309314 * or {@code null} if an end of stream has been reached.
310315 */
311- public char [] readPassword (String fmt , Object ... args ) {
316+ public char [] readPassword (String format , Object ... args ) {
317+ return readPassword0 (false , format , args );
318+ }
319+
320+ // These two methods are intended for sun.security.util.Password, so tools like keytool can
321+ // use Console even when standard output is redirected. The Password class should first
322+ // check if `System.console()` returns a Console instance and use it if available. Otherwise,
323+ // it should call this method to obtain a Console. This ensures only one Console
324+ // instance exists in the Java runtime.
325+ private static final AtomicReference <Optional <Console >> INSTANCE = new AtomicReference <>();
326+ /**
327+ * Returns a Console to be used by sun.security.util.Password, so tools like keytool can
328+ * use Console even when standard output is redirected.
329+ *
330+ * @return Returns a console.
331+ */
332+ public static Optional <Console > passwordConsole () {
333+ Optional <Console > result = INSTANCE .get ();
334+ if (result != null ) {
335+ return result ;
336+ }
337+
338+ synchronized (Console .class ) {
339+ result = INSTANCE .get ();
340+ if (result != null ) {
341+ return result ;
342+ }
343+
344+ // If there's already a proper console, throw an exception
345+ if (System .console () != null ) {
346+ throw new IllegalStateException ("Can't create a dedicated password " +
347+ "console since a real console already exists" );
348+ }
349+
350+ // If stdin is NOT redirected, return an Optional containing a Console
351+ // instance, otherwise an empty Optional.
352+ result = SharedSecrets .getJavaIOAccess ().isStdinTty () ?
353+ Optional .of (
354+ new Console ()) :
355+ Optional .empty ();
356+
357+ INSTANCE .set (result );
358+ return result ;
359+ }
360+ }
361+
362+ // Dedicated entry for sun.security.util.Password when stdout is redirected.
363+ // This method strictly avoids producing any output by using noNewLine = true
364+ // and an empty format string.
365+ /**
366+ * This method strictly avoids producing any output by using noNewLine = true
367+ * and an empty format string.
368+ *
369+ * @return A character array containing the password or passphrase read
370+ * from the console, not including any line-termination characters,
371+ * or {@code null} if an end of stream has been reached.
372+ */
373+ public char [] readPasswordNoNewLine () {
374+ return readPassword0 (true , "" );
375+ }
376+
377+ private char [] readPassword0 (boolean noNewLine , String fmt , Object ... args ) {
312378 char [] passwd = null ;
313379 synchronized (writeLock ) {
314380 synchronized (readLock ) {
@@ -347,7 +413,9 @@ public char[] readPassword(String fmt, Object ... args) {
347413 throw ioe ;
348414 }
349415 }
350- pw .println ();
416+ if (!noNewLine ) {
417+ pw .println ();
418+ }
351419 }
352420 }
353421 return passwd ;
@@ -587,6 +655,12 @@ public int read(char cbuf[], int offset, int length)
587655 }
588656 }
589657
658+ @ Native static final int TTY_STDIN_MASK = 0x00000001 ;
659+ @ Native static final int TTY_STDOUT_MASK = 0x00000002 ;
660+ @ Native static final int TTY_STDERR_MASK = 0x00000004 ;
661+ // ttyStatus() returns bit patterns above, a bit is set if the corresponding file
662+ // descriptor is a character device
663+ private static final int ttyStatus = ttyStatus ();
590664 private static final Charset CHARSET ;
591665 static {
592666 String csname = encoding ();
@@ -604,7 +678,7 @@ public int read(char cbuf[], int offset, int length)
604678 // Set up JavaIOAccess in SharedSecrets
605679 SharedSecrets .setJavaIOAccess (new JavaIOAccess () {
606680 public Console console () {
607- if (istty ()) {
681+ if (isStdinTty () && isStdoutTty ()) {
608682 if (cons == null )
609683 cons = new Console ();
610684 return cons ;
@@ -615,10 +689,12 @@ public Console console() {
615689 public Charset charset () {
616690 return CHARSET ;
617691 }
692+ public boolean isStdinTty () {
693+ return Console .isStdinTty ();
694+ }
618695 });
619696 }
620697 private static Console cons ;
621- private static native boolean istty ();
622698 private Console () {
623699 readLock = new Object ();
624700 writeLock = new Object ();
@@ -634,4 +710,14 @@ private Console() {
634710 CHARSET ));
635711 rcb = new char [1024 ];
636712 }
713+ private static boolean isStdinTty () {
714+ return (ttyStatus & TTY_STDIN_MASK ) != 0 ;
715+ }
716+ private static boolean isStdoutTty () {
717+ return (ttyStatus & TTY_STDOUT_MASK ) != 0 ;
718+ }
719+ private static boolean isStderrTty () {
720+ return (ttyStatus & TTY_STDERR_MASK ) != 0 ;
721+ }
722+ private static native int ttyStatus ();
637723}
0 commit comments