Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 73 additions & 3 deletions src/java.base/share/classes/java/io/Console.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
package java.io;

import java.util.*;
import java.lang.annotation.Native;
import java.nio.charset.Charset;
import java.util.concurrent.atomic.AtomicReference;
import jdk.internal.misc.JavaIOAccess;
import jdk.internal.misc.SharedSecrets;
import sun.nio.cs.StreamDecoder;
Expand Down Expand Up @@ -308,6 +310,51 @@ public String readLine() {
* or {@code null} if an end of stream has been reached.
*/
public char[] readPassword(String fmt, Object ... args) {
return readPassword0(false, fmt, args);
}

// These two methods are intended for sun.security.util.Password, so tools like keytool can
// use Console even when standard output is redirected. The Password class should first
// check if `System.console()` returns a Console instance and use it if available. Otherwise,
// it should call this method to obtain a Console. This ensures only one Console
// instance exists in the Java runtime.
private static final AtomicReference<Optional<Console>> INSTANCE = new AtomicReference<>();
private static Optional<Console> passwordConsole() {
Optional<Console> result = INSTANCE.get();
if (result != null) {
return result;
}

synchronized (Console.class) {
result = INSTANCE.get();
if (result != null) {
return result;
}

// If there's already a proper console, throw an exception
if (System.console() != null) {
throw new IllegalStateException("Can't create a dedicated password " +
"console since a real console already exists");
}

// If stdin is NOT redirected, return an Optional containing a Console
// instance, otherwise an empty Optional.
result = isStdinTty() ?
Optional.of(
new Console()) :
Optional.empty();

INSTANCE.set(result);
return result;
}
}

// Dedicated entry for sun.security.util.Password when stdout is redirected.
private char[] readPasswordNoNewLine() {
return readPassword0(true, "");
}

private char[] readPassword0(boolean noNewLine, String fmt, Object ... args) {
char[] passwd = null;
synchronized (writeLock) {
synchronized(readLock) {
Expand Down Expand Up @@ -347,7 +394,9 @@ public char[] readPassword(String fmt, Object ... args) {
throw ioe;
}
}
pw.println();
if (!noNewLine) {
pw.println();
}
}
}
return passwd;
Expand Down Expand Up @@ -411,6 +460,12 @@ public void flush() {
private boolean restoreEcho;
private boolean shutdownHookInstalled;
private static native String encoding();
@Native private static final int TTY_STDIN_MASK = 0x00000001;
@Native private static final int TTY_STDOUT_MASK = 0x00000002;
@Native private static final int TTY_STDERR_MASK = 0x00000004;
// ttyStatus() returns bit patterns above, a bit is set if the corresponding file
// descriptor is a character device
private static final int ttyStatus = ttyStatus();
/*
* Sets the console echo status to {@code on} and returns the previous
* console on/off status.
Expand Down Expand Up @@ -575,7 +630,7 @@ public int read(char cbuf[], int offset, int length)
static {
SharedSecrets.setJavaIOAccess(new JavaIOAccess() {
public Console console() {
if (istty()) {
if (isStdinTty() && isStdoutTty()) {
if (cons == null)
cons = new Console();
return cons;
Expand All @@ -588,10 +643,15 @@ public Charset charset() {
// cons already exists when this method is called
return cons.cs;
}
public Optional<Console> passwordConsole() {
return Console.passwordConsole();
}
public char[] readPasswordNoNewLine(Console c) {
return c.readPasswordNoNewLine();
}
});
}
private static Console cons;
private static native boolean istty();
private Console() {
readLock = new Object();
writeLock = new Object();
Expand All @@ -615,4 +675,14 @@ private Console() {
cs));
rcb = new char[1024];
}
private static boolean isStdinTty() {
return (ttyStatus & TTY_STDIN_MASK) != 0;
}
private static boolean isStdoutTty() {
return (ttyStatus & TTY_STDOUT_MASK) != 0;
}
private static boolean isStderrTty() {
return (ttyStatus & TTY_STDERR_MASK) != 0;
}
private static native int ttyStatus();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -27,8 +27,11 @@

import java.io.Console;
import java.nio.charset.Charset;
import java.util.Optional;

public interface JavaIOAccess {
public Console console();
public Charset charset();
public Optional<Console> passwordConsole();
public char[] readPasswordNoNewLine(Console c);
}
19 changes: 15 additions & 4 deletions src/java.base/unix/native/libjava/Console_md.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -32,10 +32,21 @@
#include <unistd.h>
#include <termios.h>

JNIEXPORT jboolean JNICALL
Java_java_io_Console_istty(JNIEnv *env, jclass cls)
JNIEXPORT jint JNICALL
Java_java_io_Console_ttyStatus(JNIEnv *env, jclass cls)
{
return isatty(fileno(stdin)) && isatty(fileno(stdout));
jint ret = 0;

if (isatty(fileno(stdin))) {
ret |= java_io_Console_TTY_STDIN_MASK;
}
if (isatty(fileno(stdout))) {
ret |= java_io_Console_TTY_STDOUT_MASK;
}
if (isatty(fileno(stderr))) {
ret |= java_io_Console_TTY_STDERR_MASK;
}
return ret;
}

JNIEXPORT jstring JNICALL
Expand Down
38 changes: 23 additions & 15 deletions src/java.base/windows/native/libjava/Console_md.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -31,23 +31,30 @@
#include <stdlib.h>
#include <Wincon.h>

static HANDLE hStdOut = INVALID_HANDLE_VALUE;
static HANDLE hStdIn = INVALID_HANDLE_VALUE;
JNIEXPORT jboolean JNICALL
Java_java_io_Console_istty(JNIEnv *env, jclass cls)
JNIEXPORT jint JNICALL
Java_java_io_Console_ttyStatus(JNIEnv *env, jclass cls)
{
if (hStdIn == INVALID_HANDLE_VALUE &&
(hStdIn = GetStdHandle(STD_INPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
return JNI_FALSE;
jint ret = 0;
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);

if (hStdIn != INVALID_HANDLE_VALUE &&
GetFileType(hStdIn) == FILE_TYPE_CHAR) {
ret |= java_io_Console_TTY_STDIN_MASK;
}

if (hStdOut != INVALID_HANDLE_VALUE &&
GetFileType(hStdOut) == FILE_TYPE_CHAR) {
ret |= java_io_Console_TTY_STDOUT_MASK;
}
if (hStdOut == INVALID_HANDLE_VALUE &&
(hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)) == INVALID_HANDLE_VALUE) {
return JNI_FALSE;

if (hStdErr != INVALID_HANDLE_VALUE &&
GetFileType(hStdErr) == FILE_TYPE_CHAR) {
ret |= java_io_Console_TTY_STDERR_MASK;
}
if (GetFileType(hStdIn) != FILE_TYPE_CHAR ||
GetFileType(hStdOut) != FILE_TYPE_CHAR)
return JNI_FALSE;
return JNI_TRUE;

return ret;
}

JNIEXPORT jstring JNICALL
Expand All @@ -67,6 +74,7 @@ Java_java_io_Console_echo(JNIEnv *env, jclass cls, jboolean on)
{
DWORD fdwMode;
jboolean old;
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
if (! GetConsoleMode(hStdIn, &fdwMode)) {
JNU_ThrowIOExceptionWithLastError(env, "GetConsoleMode failed");
return !on;
Expand Down