Skip to content

Commit e30e060

Browse files
committed
backport 8366261 from 21
1 parent e476901 commit e30e060

4 files changed

Lines changed: 130 additions & 25 deletions

File tree

src/java.base/share/classes/java/io/Console.java

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,16 @@
2626
package java.io;
2727

2828
import java.util.*;
29+
import java.lang.annotation.Native;
2930
import java.nio.charset.Charset;
31+
import java.util.Optional;
32+
import java.util.concurrent.atomic.AtomicReference;
3033
import jdk.internal.access.JavaIOAccess;
3134
import jdk.internal.access.SharedSecrets;
35+
import jdk.internal.util.StaticProperty;
3236
import sun.nio.cs.StreamDecoder;
3337
import sun.nio.cs.StreamEncoder;
38+
import sun.nio.cs.UTF_8;
3439
import 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
}

src/java.base/share/classes/jdk/internal/access/JavaIOAccess.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -31,4 +31,5 @@
3131
public interface JavaIOAccess {
3232
Console console();
3333
Charset charset();
34+
boolean isStdinTty();
3435
}

src/java.base/unix/native/libjava/Console_md.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,10 +32,21 @@
3232
#include <unistd.h>
3333
#include <termios.h>
3434

35-
JNIEXPORT jboolean JNICALL
36-
Java_java_io_Console_istty(JNIEnv *env, jclass cls)
35+
JNIEXPORT jint JNICALL
36+
Java_java_io_Console_ttyStatus(JNIEnv *env, jclass cls)
3737
{
38-
return isatty(fileno(stdin)) && isatty(fileno(stdout));
38+
jint ret = 0;
39+
40+
if (isatty(fileno(stdin))) {
41+
ret |= java_io_Console_TTY_STDIN_MASK;
42+
}
43+
if (isatty(fileno(stdout))) {
44+
ret |= java_io_Console_TTY_STDOUT_MASK;
45+
}
46+
if (isatty(fileno(stderr))) {
47+
ret |= java_io_Console_TTY_STDERR_MASK;
48+
}
49+
return ret;
3950
}
4051

4152
JNIEXPORT jstring JNICALL

src/java.base/windows/native/libjava/Console_md.c

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -31,23 +31,30 @@
3131
#include <stdlib.h>
3232
#include <Wincon.h>
3333

34-
static HANDLE hStdOut = INVALID_HANDLE_VALUE;
35-
static HANDLE hStdIn = INVALID_HANDLE_VALUE;
36-
JNIEXPORT jboolean JNICALL
37-
Java_java_io_Console_istty(JNIEnv *env, jclass cls)
34+
JNIEXPORT jint JNICALL
35+
Java_java_io_Console_ttyStatus(JNIEnv *env, jclass cls)
3836
{
39-
if (hStdIn == INVALID_HANDLE_VALUE &&
40-
(hStdIn = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
41-
return JNI_FALSE;
37+
jint ret = 0;
38+
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
39+
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
40+
HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
41+
42+
if (hStdIn != INVALID_HANDLE_VALUE &&
43+
GetFileType(hStdIn) == FILE_TYPE_CHAR) {
44+
ret |= java_io_Console_TTY_STDIN_MASK;
45+
}
46+
47+
if (hStdOut != INVALID_HANDLE_VALUE &&
48+
GetFileType(hStdOut) == FILE_TYPE_CHAR) {
49+
ret |= java_io_Console_TTY_STDOUT_MASK;
4250
}
43-
if (hStdOut == INVALID_HANDLE_VALUE &&
44-
(hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
45-
return JNI_FALSE;
51+
52+
if (hStdErr != INVALID_HANDLE_VALUE &&
53+
GetFileType(hStdErr) == FILE_TYPE_CHAR) {
54+
ret |= java_io_Console_TTY_STDERR_MASK;
4655
}
47-
if (GetFileType(hStdIn) != FILE_TYPE_CHAR ||
48-
GetFileType(hStdOut) != FILE_TYPE_CHAR)
49-
return JNI_FALSE;
50-
return JNI_TRUE;
56+
57+
return ret;
5158
}
5259

5360
JNIEXPORT jstring JNICALL

0 commit comments

Comments
 (0)