undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
321
test/jdk/java/lang/ProcessBuilder/ArgCheck.java
Normal file
321
test/jdk/java/lang/ProcessBuilder/ArgCheck.java
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8282008
|
||||
* @requires (os.family == "windows")
|
||||
* @run main/othervm ArgCheck
|
||||
* @summary Check invocation of exe and non-exe programs using ProcessBuilder
|
||||
* and arguments with spaces, backslashes, and simple quoting.
|
||||
*/
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Class to check invocation of java, .cmd, and vbs scripts with arguments and various quote cases.
|
||||
* Can be run standalone to compare results with other Java versions.
|
||||
*/
|
||||
public class ArgCheck {
|
||||
|
||||
private static final Path SRC_DIR = Paths.get(System.getProperty("test.src", "."));
|
||||
private static final Path WORK_DIR = Paths.get(System.getProperty("user.dir", "."));
|
||||
private static final Path TEST_CLASSES = Paths.get(System.getProperty("test.classes", "."));
|
||||
|
||||
private static final String ECHO_CMD_PATH = WORK_DIR.resolve("EchoArguments.cmd").toString();
|
||||
private static final String ECHO_VBS_PATH = WORK_DIR.resolve("EchoArguments.vbs").toString();
|
||||
|
||||
// Test argument containing both a space and a trailing backslash
|
||||
// Depending on the mode the final backslash may act as an escape that may turn an added quote to a literal quote
|
||||
private static final String SPACE_AND_BACKSLASH = "SPACE AND BACKSLASH\\";
|
||||
private static final char DOUBLE_QUOTE = '"';
|
||||
private static final char BACKSLASH = '\\';
|
||||
|
||||
private static final String AMBIGUOUS_PROP_NAME = "jdk.lang.Process.allowAmbiguousCommands";
|
||||
private static final String AMBIGUOUS_PROP_VALUE = System.getProperty(AMBIGUOUS_PROP_NAME);
|
||||
private static final Boolean AMBIGUOUS_PROP_BOOLEAN = AMBIGUOUS_PROP_VALUE == null ? null :
|
||||
Boolean.valueOf(!"false".equals(AMBIGUOUS_PROP_VALUE));
|
||||
|
||||
private static final List<String> ECHO_JAVA_ARGS = Arrays.asList("java", "-classpath", TEST_CLASSES.toString(), "ArgCheck");
|
||||
private static final List<String> ECHO_CMD_ARGS = Arrays.asList(ECHO_CMD_PATH);
|
||||
private static final List<String> ECHO_VBS_ARGS = Arrays.asList("CScript", "/b", ECHO_VBS_PATH);
|
||||
|
||||
/**
|
||||
* If zero arguments are supplied, run the test cases, by launching each as a child process.
|
||||
* If there are arguments, then this is a child Java process that prints each argument to stdout.
|
||||
* The test can be run manually with -Djdk.lang.Process.allowAmbiguousCommands={"true", "false", ""}
|
||||
* to run a matching subset of the tests.
|
||||
*/
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length > 0) {
|
||||
// Echo supplied arguments and exit
|
||||
for (String arg : args)
|
||||
System.out.println(arg);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Java Version: " + System.getProperty("java.version"));
|
||||
|
||||
createFiles();
|
||||
|
||||
int errors = 0;
|
||||
int success = 0;
|
||||
int skipped = 0;
|
||||
|
||||
for (CMD cmd : CASES) {
|
||||
// If System property jdk.lang.process.allowAmbiguousCommands matches the case, test it
|
||||
// If undefined, test them all
|
||||
if (AMBIGUOUS_PROP_BOOLEAN == null ||
|
||||
AMBIGUOUS_PROP_BOOLEAN.booleanValue() == cmd.allowAmbiguous) {
|
||||
try {
|
||||
testCommand(cmd);
|
||||
success++;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
errors++;
|
||||
}
|
||||
} else {
|
||||
// skip unmatched cases
|
||||
skipped++;
|
||||
}
|
||||
}
|
||||
if (skipped > 0) {
|
||||
System.out.printf("%d cases skipped, they did not match the tests with jdk.lang.Process.allowAmbiguousCommands: %s%n",
|
||||
skipped, AMBIGUOUS_PROP_BOOLEAN);
|
||||
}
|
||||
System.out.printf("\nSuccess: %d, errors: %d%n", success, errors);
|
||||
if (errors > 0) {
|
||||
throw new RuntimeException("Errors: " + errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A CMD holds the parameters and the expected result of invoking a process with the parameters.
|
||||
*/
|
||||
static class CMD {
|
||||
/**
|
||||
* Construct a test case.
|
||||
* @param allowAmbiguous true/false to set property jdk.lang.Process.allowAmbiguousCommands
|
||||
* @param command list of command parameters to invoke the executable or script
|
||||
* @param arguments list of arguments (appended to the command)
|
||||
* @param expected expected lines of output from invoked command
|
||||
*/
|
||||
CMD(boolean allowAmbiguous, List<String> command, List<String> arguments, List<String> expected) {
|
||||
this.allowAmbiguous = allowAmbiguous;
|
||||
this.command = command;
|
||||
this.arguments = arguments;
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
final boolean allowAmbiguous;
|
||||
final List<String> command;
|
||||
final List<String> arguments;
|
||||
final List<String> expected;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of cases with the command, arguments, allowAmbiguous setting, and the expected results
|
||||
*/
|
||||
static final List<CMD> CASES = Arrays.asList(
|
||||
|
||||
// allowAmbiguousCommands = false, without application supplied double-quotes.
|
||||
// The space in the argument requires it to be quoted, the final backslash
|
||||
// must not be allowed to turn the quote that is added into a literal
|
||||
// instead of closing the quote.
|
||||
new CMD(false,
|
||||
ECHO_JAVA_ARGS,
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_1"),
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_1")),
|
||||
new CMD(false,
|
||||
ECHO_CMD_ARGS,
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_2"),
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_2")),
|
||||
new CMD(false,
|
||||
ECHO_VBS_ARGS,
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_3"),
|
||||
Arrays.asList(SPACE_AND_BACKSLASH + BACKSLASH, "ARG_3")),
|
||||
|
||||
// allowAmbiguousCommands = false, WITH application supplied double-quotes around the argument
|
||||
// The argument has surrounding quotes so does not need further quoting.
|
||||
// However, for exe commands, the final backslash must not be allowed to turn the quote
|
||||
// into a literal instead of closing the quote.
|
||||
new CMD(false,
|
||||
ECHO_JAVA_ARGS,
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_11"),
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_11")),
|
||||
new CMD(false,
|
||||
ECHO_CMD_ARGS,
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_12"),
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_12")),
|
||||
new CMD(false,
|
||||
ECHO_VBS_ARGS,
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_13"),
|
||||
Arrays.asList(SPACE_AND_BACKSLASH + BACKSLASH, "ARG_13")),
|
||||
|
||||
// Legacy mode tests; allowAmbiguousCommands = true; no application supplied quotes
|
||||
// The space in the argument requires it to be quoted, the final backslash
|
||||
// must not be allowed to turn the quote that is added into a literal
|
||||
// instead of closing the quote.
|
||||
new CMD(true,
|
||||
ECHO_JAVA_ARGS,
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_21"),
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_21")),
|
||||
new CMD(true,
|
||||
ECHO_CMD_ARGS,
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_22"),
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + BACKSLASH + DOUBLE_QUOTE, "ARG_22")),
|
||||
new CMD(true,
|
||||
ECHO_VBS_ARGS,
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_23"),
|
||||
Arrays.asList(SPACE_AND_BACKSLASH + BACKSLASH, "ARG_23")),
|
||||
|
||||
// allowAmbiguousCommands = true, WITH application supplied double-quotes around the argument
|
||||
// The argument has surrounding quotes so does not need further quoting.
|
||||
// The backslash before the final quote is ignored and is interpreted differently for each command.
|
||||
new CMD(true,
|
||||
ECHO_JAVA_ARGS,
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_31"),
|
||||
Arrays.asList("SPACE AND BACKSLASH\" ARG_31")),
|
||||
new CMD(true,
|
||||
ECHO_CMD_ARGS,
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_32"),
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_32")),
|
||||
new CMD(true,
|
||||
ECHO_VBS_ARGS,
|
||||
Arrays.asList(DOUBLE_QUOTE + SPACE_AND_BACKSLASH + DOUBLE_QUOTE, "ARG_33"),
|
||||
Arrays.asList(SPACE_AND_BACKSLASH, "ARG_33"))
|
||||
);
|
||||
|
||||
/**
|
||||
* Common function to Invoke a process with the commands and check the result.
|
||||
*
|
||||
* @param cmd a CMD test case with arguments, allowAmbiguousCommands mode, and expected output
|
||||
*/
|
||||
private static void testCommand(CMD cmd) throws Exception {
|
||||
System.setProperty(AMBIGUOUS_PROP_NAME, Boolean.toString(cmd.allowAmbiguous));
|
||||
List<String> actual = null;
|
||||
List<String> arguments = new ArrayList<>(cmd.command);
|
||||
arguments.addAll(cmd.arguments);
|
||||
try {
|
||||
// Launch the process and wait for termination
|
||||
ProcessBuilder pb = new ProcessBuilder(arguments);
|
||||
Process process = pb.start();
|
||||
try (InputStream is = process.getInputStream()) {
|
||||
String str = readAllBytesAsString(is);
|
||||
str = str.replace("\r", "");
|
||||
actual = Arrays.asList(str.split("\n"));
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException(ioe.getMessage(), ioe);
|
||||
}
|
||||
int exitCode = process.waitFor();
|
||||
if (exitCode != 0) {
|
||||
actual = new ArrayList(actual);
|
||||
actual.add("Exit code: " + exitCode);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
actual = Arrays.asList(ioe.getMessage().replace(arguments.get(0), "CMD"));
|
||||
} catch (Exception ex) {
|
||||
actual = Arrays.asList(ex.getMessage()); // Use exception message as output
|
||||
}
|
||||
if (!Objects.equals(actual, cmd.expected)) {
|
||||
System.out.println("Invoking(" + cmd.allowAmbiguous + "): " + arguments);
|
||||
if (actual.size() != cmd.expected.size()) {
|
||||
System.out.println("Args Length: actual: " + actual.size() + " expected: " + cmd.expected.size());
|
||||
}
|
||||
System.out.println("Actual: " + actual);
|
||||
System.out.println("Expected: " + cmd.expected);
|
||||
System.out.println();
|
||||
throw new RuntimeException("Unexpected output");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private method to readAllBytes as a String.
|
||||
* (InputStream.readAllBytes is not supported by the JDK until 9)
|
||||
* @param is an InputStream
|
||||
* @return a String with the contents
|
||||
* @throws IOException if an error occurs
|
||||
*/
|
||||
private static String readAllBytesAsString(InputStream is) throws IOException {
|
||||
final int BUF_SIZE = 8192;
|
||||
byte[] bytes = new byte[BUF_SIZE];
|
||||
int off = 0;
|
||||
int len;
|
||||
while ((len = is.read(bytes, off, bytes.length - off)) > 0) {
|
||||
off += len;
|
||||
if (off >= bytes.length) {
|
||||
// no space in buffer, reallocate larger
|
||||
bytes = Arrays.copyOf(bytes, bytes.length + BUF_SIZE);
|
||||
}
|
||||
}
|
||||
return new String(bytes, 0, off, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize .cmd and .vbs scripts.
|
||||
*
|
||||
* @throws Error if an exception occurs
|
||||
*/
|
||||
private static void createFiles() throws IOException {
|
||||
Files.write(Paths.get(ECHO_CMD_PATH), EchoArgumentsCmd.getBytes(StandardCharsets.UTF_8));
|
||||
Files.write(Paths.get(ECHO_VBS_PATH), EchoArgumentsVbs.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Self contained .cmd to echo each argument on a separate line.
|
||||
*/
|
||||
static final String EchoArgumentsCmd = "@echo off\n" +
|
||||
"set p1=\n" +
|
||||
"set p2=\n" +
|
||||
"\n" +
|
||||
"if not [%1]==[] set p1=%1\n" +
|
||||
"if not [%2]==[] set p2=%2\n" +
|
||||
"if not [%3]==[] set p3=%3\n" +
|
||||
"if defined p1 echo %p1%\n" +
|
||||
"if defined p2 echo %p2%\n" +
|
||||
"if defined p3 echo %p3%\n" +
|
||||
"exit /b 0\n";
|
||||
|
||||
|
||||
/**
|
||||
* Self contained .vbs to echo each argument on a separate line.
|
||||
*/
|
||||
static final String EchoArgumentsVbs = "Option Explicit\n" +
|
||||
"Dim arg\n" +
|
||||
"for each arg in WScript.Arguments\n" +
|
||||
" WScript.StdOut.WriteLine(arg)\n" +
|
||||
"Next\n";
|
||||
}
|
||||
2764
test/jdk/java/lang/ProcessBuilder/Basic.java
Normal file
2764
test/jdk/java/lang/ProcessBuilder/Basic.java
Normal file
File diff suppressed because it is too large
Load diff
103
test/jdk/java/lang/ProcessBuilder/BigFork.java
Normal file
103
test/jdk/java/lang/ProcessBuilder/BigFork.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2009 Google Inc. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A manual test that demonstrates the ability to start a subprocess
|
||||
* on Linux without getting ENOMEM. Run this test like:
|
||||
*
|
||||
* java -Xmx7000m BigFork
|
||||
*
|
||||
* providing a -Xmx flag suitable for your operating environment.
|
||||
* Here's the bad old behavior:
|
||||
*
|
||||
* ==> java -Xmx7000m -esa -ea BigFork
|
||||
* -------
|
||||
* CommitLimit: 6214700 kB
|
||||
* Committed_AS: 2484452 kB
|
||||
* -------
|
||||
* size=4.6GB
|
||||
* -------
|
||||
* CommitLimit: 6214700 kB
|
||||
* Committed_AS: 7219680 kB
|
||||
* -------
|
||||
* Exception in thread "main" java.io.IOException: Cannot run program "/bin/true": java.io.IOException: error=12, Cannot allocate memory
|
||||
* at java.lang.ProcessBuilder.start(ProcessBuilder.java:1018)
|
||||
* at BigFork.main(BigFork.java:79)
|
||||
* Caused by: java.io.IOException: java.io.IOException: error=12, Cannot allocate memory
|
||||
* at java.lang.UNIXProcess.<init>(UNIXProcess.java:190)
|
||||
* at java.lang.ProcessImpl.start(ProcessImpl.java:128)
|
||||
* at java.lang.ProcessBuilder.start(ProcessBuilder.java:1010)
|
||||
* ... 1 more
|
||||
*/
|
||||
public class BigFork {
|
||||
static final Random rnd = new Random();
|
||||
static void touchPages(byte[] chunk) {
|
||||
final int pageSize = 4096;
|
||||
for (int i = 0; i < chunk.length; i+= pageSize) {
|
||||
chunk[i] = (byte) rnd.nextInt();
|
||||
}
|
||||
}
|
||||
|
||||
static void showCommittedMemory() throws IOException {
|
||||
BufferedReader r =
|
||||
new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream("/proc/meminfo")));
|
||||
System.out.println("-------");
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("Commit")) {
|
||||
System.out.printf("%s%n", line);
|
||||
}
|
||||
}
|
||||
System.out.println("-------");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
showCommittedMemory();
|
||||
|
||||
final int chunkSize = 1024 * 1024 * 100;
|
||||
List<byte[]> chunks = new ArrayList<byte[]>(100);
|
||||
try {
|
||||
for (;;) {
|
||||
byte[] chunk = new byte[chunkSize];
|
||||
touchPages(chunk);
|
||||
chunks.add(chunk);
|
||||
}
|
||||
} catch (OutOfMemoryError e) {
|
||||
chunks.set(0, null); // Free up one chunk
|
||||
System.gc();
|
||||
int size = chunks.size();
|
||||
System.out.printf("size=%.2gGB%n", (double)size/10);
|
||||
|
||||
showCommittedMemory();
|
||||
|
||||
// Can we fork/exec in our current bloated state?
|
||||
Process p = new ProcessBuilder("/bin/true").start();
|
||||
p.waitFor();
|
||||
}
|
||||
}
|
||||
}
|
||||
188
test/jdk/java/lang/ProcessBuilder/CloseRace.java
Normal file
188
test/jdk/java/lang/ProcessBuilder/CloseRace.java
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2024, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=Default
|
||||
* @bug 8024521 8315721
|
||||
* @summary Closing ProcessPipeInputStream at the time the process exits is racy
|
||||
* and leads to data corruption. Run this test manually (as
|
||||
* an ordinary java program) with -Xmx8M to repro bug 8024521.
|
||||
* @requires vm.gc != "Z"
|
||||
* @comment Don't allow -Xcomp, it disturbs the timing
|
||||
* @requires (vm.compMode != "Xcomp")
|
||||
* @run main/othervm -Xmx8M -Dtest.duration=2 CloseRace
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=Z
|
||||
* @comment Turn up heap size to lower amount of GCs
|
||||
* @requires vm.gc.Z
|
||||
* @comment Don't allow -Xcomp, it disturbs the timing
|
||||
* @requires (vm.compMode != "Xcomp")
|
||||
* @run main/othervm -XX:+UseZGC -Xmx32M -Dtest.duration=2 CloseRace
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class CloseRace {
|
||||
private static final String BIG_FILE = "bigfile";
|
||||
|
||||
private static final int[] procFDs = new int[6];
|
||||
|
||||
/** default value sufficient to repro bug 8024521. */
|
||||
private static final int testDurationSeconds
|
||||
= Integer.getInteger("test.duration", 600);
|
||||
|
||||
private static final CountDownLatch threadsStarted
|
||||
= new CountDownLatch(2);
|
||||
|
||||
static boolean fdInUse(int i) {
|
||||
return new File("/proc/self/fd/" + i).exists();
|
||||
}
|
||||
|
||||
static boolean[] procFDsInUse() {
|
||||
boolean[] inUse = new boolean[procFDs.length];
|
||||
for (int i = 0; i < procFDs.length; i++)
|
||||
inUse[i] = fdInUse(procFDs[i]);
|
||||
return inUse;
|
||||
}
|
||||
|
||||
static int count(boolean[] bits) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < bits.length; i++)
|
||||
count += bits[i] ? 1 : 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
static void dumpAllStacks() {
|
||||
System.err.println("Start of dump");
|
||||
final Map<Thread, StackTraceElement[]> allStackTraces
|
||||
= Thread.getAllStackTraces();
|
||||
for (Thread thread : allStackTraces.keySet()) {
|
||||
System.err.println("Thread " + thread.getName());
|
||||
for (StackTraceElement element : allStackTraces.get(thread))
|
||||
System.err.println("\t" + element);
|
||||
}
|
||||
System.err.println("End of dump");
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
if (!(new File("/proc/self/fd").isDirectory()))
|
||||
return;
|
||||
|
||||
// Catch Errors from process reaper
|
||||
Thread.setDefaultUncaughtExceptionHandler
|
||||
((t, e) -> { e.printStackTrace(); System.exit(1); });
|
||||
|
||||
try (RandomAccessFile f = new RandomAccessFile(BIG_FILE, "rw")) {
|
||||
f.setLength(Runtime.getRuntime().maxMemory()); // provoke OOME
|
||||
}
|
||||
|
||||
for (int i = 0, j = 0; j < procFDs.length; i++)
|
||||
if (!fdInUse(i))
|
||||
procFDs[j++] = i;
|
||||
|
||||
Thread[] threads = {
|
||||
new Thread(new OpenLoop()),
|
||||
new Thread(new ExecLoop()),
|
||||
};
|
||||
for (Thread thread : threads)
|
||||
thread.start();
|
||||
|
||||
threadsStarted.await();
|
||||
Thread.sleep(testDurationSeconds * 1000);
|
||||
|
||||
for (Thread thread : threads)
|
||||
thread.interrupt();
|
||||
for (Thread thread : threads) {
|
||||
thread.join(10_000);
|
||||
if (thread.isAlive()) {
|
||||
dumpAllStacks();
|
||||
throw new Error("At least one child thread ("
|
||||
+ thread.getName()
|
||||
+ ") failed to finish gracefully");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class OpenLoop implements Runnable {
|
||||
public void run() {
|
||||
threadsStarted.countDown();
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
// wait for ExecLoop to finish creating process
|
||||
do {
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
} while (count(procFDsInUse()) != 3);
|
||||
List<InputStream> iss = new ArrayList<>(4);
|
||||
|
||||
// eat up three "holes" (closed ends of pipe fd pairs)
|
||||
for (int i = 0; i < 3; i++)
|
||||
iss.add(new FileInputStream(BIG_FILE));
|
||||
do {
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
} while (count(procFDsInUse()) == procFDs.length);
|
||||
// hopefully this will racily occupy empty fd slot
|
||||
iss.add(new FileInputStream(BIG_FILE));
|
||||
Thread.sleep(1); // Widen race window
|
||||
for (InputStream is : iss)
|
||||
is.close();
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class ExecLoop implements Runnable {
|
||||
public void run() {
|
||||
threadsStarted.countDown();
|
||||
ProcessBuilder builder = new ProcessBuilder("/bin/true");
|
||||
while (!Thread.interrupted()) {
|
||||
try {
|
||||
// wait for OpenLoop to finish
|
||||
do {
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
} while (count(procFDsInUse()) > 0);
|
||||
Process process = builder.start();
|
||||
InputStream is = process.getInputStream();
|
||||
process.waitFor();
|
||||
is.close();
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026, IBM Corp.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=POSIX_SPAWN
|
||||
* @bug 8377907
|
||||
* @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem
|
||||
* @requires os.family != "windows"
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm/manual -Djdk.lang.Process.launchMechanism=POSIX_SPAWN ConcNativeForkTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=FORK
|
||||
* @bug 8377907
|
||||
* @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem
|
||||
* @requires os.family != "windows"
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm/manual -Djdk.lang.Process.launchMechanism=FORK ConcNativeForkTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=VFORK
|
||||
* @bug 8377907
|
||||
* @summary Test that demonstrates the hanging-parent-on-native-concurrent-forks problem
|
||||
* @requires os.family == "linux"
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm/manual -Djdk.lang.Process.launchMechanism=VFORK ConcNativeForkTest
|
||||
*/
|
||||
|
||||
public class ConcNativeForkTest {
|
||||
|
||||
// How this works:
|
||||
// - We start a child process via ProcessBuilder. Does not matter what, we just call "/bin/true".
|
||||
// - Concurrently, we continuously (up to a limit) fork natively; these forks will all exec "sleep 30".
|
||||
// - If the natively forked child process forks off at the right (wrong) moment, it will catch the open pipe from
|
||||
// the "/bin/true" child process, and forcing the parent process (this test) to wait in ProcessBuilder.start()
|
||||
// (inside forkAndExec()) until the natively forked child releases the pipe file descriptors it inherited.
|
||||
|
||||
// Notes:
|
||||
//
|
||||
// Obviously, this is racy and depends on scheduler timings of the underlying OS. The test succeeding is
|
||||
// no proof the bug does not exist (see PipesCloseOnExecTest as a complimentary test that is more reliable, but
|
||||
// only works on Linux).
|
||||
// That said, in tests it reliably reproduces the bug on Linux x64 and MacOS Arm.
|
||||
//
|
||||
// This test is not well suited for automatic test execution, since the test essentially
|
||||
// fork-bombs itself, and that may run into issues in containerized CI/CD environments.
|
||||
|
||||
native static boolean prepareNativeForkerThread(int numForks);
|
||||
native static void releaseNativeForkerThread();
|
||||
native static void stopNativeForkerThread();
|
||||
|
||||
private static final int numIterations = 20;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
System.out.println("jdk.lang.Process.launchMechanism=" +
|
||||
System.getProperty("jdk.lang.Process.launchMechanism"));
|
||||
|
||||
System.loadLibrary("ConcNativeFork");
|
||||
|
||||
// A very simple program returning immediately (/bin/true)
|
||||
ProcessBuilder pb = new ProcessBuilder("true").inheritIO();
|
||||
final int numJavaProcesses = 10;
|
||||
final int numNativeProcesses = 250;
|
||||
Process[] processes = new Process[numJavaProcesses];
|
||||
|
||||
for (int iteration = 0; iteration < numIterations; iteration ++) {
|
||||
|
||||
if (!prepareNativeForkerThread(numNativeProcesses)) {
|
||||
throw new RuntimeException("Failed to start native forker thread (see stdout)");
|
||||
}
|
||||
|
||||
long[] durations = new long[numJavaProcesses];
|
||||
|
||||
releaseNativeForkerThread();
|
||||
|
||||
for (int np = 0; np < numJavaProcesses; np ++) {
|
||||
long t1 = System.currentTimeMillis();
|
||||
try (Process p = pb.start()) {
|
||||
durations[np] = System.currentTimeMillis() - t1;
|
||||
processes[np] = p;
|
||||
}
|
||||
}
|
||||
|
||||
stopNativeForkerThread();
|
||||
|
||||
long longestDuration = 0;
|
||||
for (int np = 0; np < numJavaProcesses; np ++) {
|
||||
processes[np].waitFor();
|
||||
System.out.printf("Duration: %dms%n", durations[np]);
|
||||
longestDuration = Math.max(durations[np], longestDuration);
|
||||
}
|
||||
|
||||
System.out.printf("Longest startup time: %dms%n", longestDuration);
|
||||
|
||||
if (longestDuration >= 30000) {
|
||||
throw new RuntimeException("Looks like we blocked on native fork");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026, IBM Corp.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <jni.h>
|
||||
#include "testlib_thread_barriers.h"
|
||||
|
||||
static void trc(const char* fmt, ...) {
|
||||
char buf [1024];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
printf("(pid: %d): %s\n", (int)getpid(), buf);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static pthread_t tid_forker;
|
||||
|
||||
static pthread_barrier_t start_barrier;
|
||||
static atomic_bool stop_now = false;
|
||||
|
||||
static void* forkerLoop(void* info) {
|
||||
|
||||
const int numForks = (int)(intptr_t)info;
|
||||
pid_t* pids = calloc(numForks, sizeof(pid_t));
|
||||
|
||||
trc("Forker: Waiting for Go.");
|
||||
|
||||
pthread_barrier_wait(&start_barrier);
|
||||
|
||||
for (int i = 0; i < numForks; i++) {
|
||||
const pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
/* Exec sleep. Properly opened file descriptors in parents (tagged CLOEXEC) should be released now.
|
||||
* Note that we use bash to not have to deal with path resolution. For our case, it does not matter if
|
||||
* sleep is a builtin or not. */
|
||||
char* env[] = { "PATH=/usr/bin:/bin", NULL };
|
||||
char* argv[] = { "sh", "-c", "sleep 30", NULL };
|
||||
execve("/bin/sh", argv, env);
|
||||
trc("Native child: sleep exec failed? %d", errno);
|
||||
/* The simplest way to handle this is to just wait here; this *will* cause the test to fail. */
|
||||
sleep(120);
|
||||
trc("Native child: exiting");
|
||||
exit(0);
|
||||
} else {
|
||||
pids[i] = pid;
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
trc("Forker: All native child processes started.");
|
||||
|
||||
/* Wait for test to signal end */
|
||||
while (!atomic_load(&stop_now)) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
trc("Forker: Cleaning up.");
|
||||
|
||||
/* Reap children */
|
||||
for (int i = 0; i < numForks; i ++) {
|
||||
if (pids[i] != 0) {
|
||||
kill(pids[i], SIGKILL); /* if still running */
|
||||
waitpid(pids[i], NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
trc("Forker: Done.");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_ConcNativeForkTest_prepareNativeForkerThread(JNIEnv* env, jclass cls, jint numForks)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
int rc = 0;
|
||||
|
||||
const int cap = 1000;
|
||||
const int numForksCapped = numForks > cap ? cap : numForks;
|
||||
if (numForks > numForksCapped) {
|
||||
trc("Main: Capping max. number of forks at %d", numForksCapped); /* don't forkbomb me */
|
||||
}
|
||||
|
||||
if (pthread_barrier_init(&start_barrier, NULL, 2) != 0) {
|
||||
trc("Main: pthread_barrier_init failed (%d)", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
if (pthread_create(&tid_forker, &attr, forkerLoop, (void*)(intptr_t)numForksCapped) != 0) {
|
||||
trc("Main: pthread_create failed (%d)", errno);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
trc("Main: Prepared native forker thread");
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ConcNativeForkTest_releaseNativeForkerThread(JNIEnv* env, jclass cls)
|
||||
{
|
||||
pthread_barrier_wait(&start_barrier);
|
||||
trc("Main: signaled GO");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_ConcNativeForkTest_stopNativeForkerThread(JNIEnv* env, jclass cls)
|
||||
{
|
||||
atomic_store(&stop_now, true);
|
||||
pthread_join(tid_forker, NULL);
|
||||
pthread_barrier_destroy(&start_barrier);
|
||||
}
|
||||
134
test/jdk/java/lang/ProcessBuilder/DestroyTest.java
Normal file
134
test/jdk/java/lang/ProcessBuilder/DestroyTest.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2020, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 4244896
|
||||
* @summary Test for the various platform specific implementations of
|
||||
* destroyForcibly.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
abstract class ProcessTest implements Runnable {
|
||||
ProcessBuilder bldr;
|
||||
Process p;
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
String line;
|
||||
BufferedReader is =
|
||||
new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
while ((line = is.readLine()) != null)
|
||||
System.err.println("ProcessTrap: " + line);
|
||||
} catch(IOException e) {
|
||||
if (!e.getMessage().matches("[Ss]tream [Cc]losed")) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void runTest() throws Exception {
|
||||
// The destroy() method is not tested because
|
||||
// the process streams are closed by the destroy() call.
|
||||
// After a destroy() call, the process terminates with a
|
||||
// SIGPIPE even if it was trapping SIGTERM.
|
||||
// So skip the trap test and go straight to destroyForcibly().
|
||||
p.destroyForcibly();
|
||||
p.waitFor();
|
||||
if (p.isAlive())
|
||||
throw new RuntimeException("Problem terminating the process.");
|
||||
}
|
||||
}
|
||||
|
||||
class UnixTest extends ProcessTest {
|
||||
public UnixTest(File script) throws IOException {
|
||||
script.deleteOnExit();
|
||||
createScript(script);
|
||||
bldr = new ProcessBuilder(script.getCanonicalPath());
|
||||
bldr.redirectErrorStream(true);
|
||||
bldr.directory(new File("."));
|
||||
p = bldr.start();
|
||||
}
|
||||
|
||||
void createScript(File processTrapScript) throws IOException {
|
||||
processTrapScript.deleteOnExit();
|
||||
try (FileWriter fstream = new FileWriter(processTrapScript);
|
||||
BufferedWriter out = new BufferedWriter(fstream)) {
|
||||
out.write("#!/bin/bash\n" +
|
||||
"echo \\\"ProcessTrap.sh started\\\"\n" +
|
||||
"while :\n" +
|
||||
"do\n" +
|
||||
" sleep 1;\n" +
|
||||
"done\n");
|
||||
}
|
||||
processTrapScript.setExecutable(true, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WindowsTest extends ProcessTest {
|
||||
public WindowsTest() throws IOException {
|
||||
bldr = new ProcessBuilder("ftp");
|
||||
bldr.redirectErrorStream(true);
|
||||
bldr.directory(new File("."));
|
||||
p = bldr.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DestroyTest {
|
||||
|
||||
public static ProcessTest getTest() throws Exception {
|
||||
String osName = System.getProperty("os.name");
|
||||
if (osName.startsWith("Windows")) {
|
||||
return new WindowsTest();
|
||||
} else {
|
||||
File userDir = new File(System.getProperty("user.dir", "."));
|
||||
File tempFile = File.createTempFile("ProcessTrap-", ".sh", userDir);
|
||||
if (osName.startsWith("Linux")
|
||||
|| osName.startsWith("Mac OS")
|
||||
|| osName.equals("AIX")) {
|
||||
return new UnixTest(tempFile);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
ProcessTest test = getTest();
|
||||
if (test == null) {
|
||||
throw new RuntimeException("Unrecognised OS");
|
||||
} else {
|
||||
new Thread(test).start();
|
||||
Thread.sleep(1000);
|
||||
test.runTest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
72
test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java
Normal file
72
test/jdk/java/lang/ProcessBuilder/FDLeakTest/FDLeakTest.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2025, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @test id=posix_spawn
|
||||
* @summary Check that we don't leak FDs to child processes
|
||||
* @requires os.family != "windows"
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=posix_spawn -agentlib:FDLeaker FDLeakTest
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=fork
|
||||
* @summary Check that we don't leak FDs to child processes
|
||||
* @requires os.family != "windows"
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=fork -agentlib:FDLeaker FDLeakTest
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=vfork
|
||||
* @summary Check that we don't leak FDs to child processes
|
||||
* @requires os.family == "linux"
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=vfork -agentlib:FDLeaker FDLeakTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
public class FDLeakTest {
|
||||
// This test has two native parts:
|
||||
// - a library invoked with -agentlib that ensures that, in the parent JVM, we open
|
||||
// a native fd without setting FD_CLOEXEC (libFDLeaker.c). This is necessary because
|
||||
// there is no way to do this from Java: if Java functions correctly, all files the
|
||||
// user could open via its APIs should be marked with FD_CLOEXEC.
|
||||
// - a small native executable that tests - without using /proc - whether any file
|
||||
// descriptors other than stdin/out/err are open.
|
||||
//
|
||||
// What should happen: In the child process, between the initial fork and the exec of
|
||||
// the target binary, we should close all filedescriptors that are not stdin/out/err.
|
||||
// If that works, the child process should not see any other file descriptors save
|
||||
// those three.
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createNativeTestProcessBuilder("FDLeakTester");
|
||||
pb.inheritIO();
|
||||
Process p = pb.start();
|
||||
p.waitFor();
|
||||
if (p.exitValue() != 0) {
|
||||
throw new RuntimeException("Failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Check if any fd past stderr is valid; if true, print warning on stderr and return -1
|
||||
*
|
||||
* Note: check without accessing /proc since:
|
||||
* - non-portable
|
||||
* - may cause creation of temporary file descriptors
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
int errors = 0;
|
||||
int rc = 0;
|
||||
char buf[128];
|
||||
int max_fd = (int)sysconf(_SC_OPEN_MAX);
|
||||
if (max_fd == -1) {
|
||||
snprintf(buf, sizeof(buf), "*** sysconf(_SC_OPEN_MAX) failed? (%d) ***\n", errno);
|
||||
rc = write(2, buf, strlen(buf));
|
||||
max_fd = 10000;
|
||||
}
|
||||
// We start after stderr fd
|
||||
for (int fd = 3; fd < max_fd; fd++) {
|
||||
if (fcntl(fd, F_GETFD, 0) >= 0) {
|
||||
// Error: found valid file descriptor
|
||||
errors++;
|
||||
snprintf(buf, sizeof(buf), "*** Parent leaked file descriptor %d ***\n", fd);
|
||||
rc = write(2, buf, strlen(buf));
|
||||
}
|
||||
}
|
||||
return errors > 0 ? -1 : 0;
|
||||
}
|
||||
75
test/jdk/java/lang/ProcessBuilder/FDLeakTest/libFDLeaker.c
Normal file
75
test/jdk/java/lang/ProcessBuilder/FDLeakTest/libFDLeaker.c
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include "jvmti.h"
|
||||
|
||||
static jint limit_num_fds();
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
// Lower the number of possible open files to make the test go faster
|
||||
jint ret = limit_num_fds();
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "Failed to limit number of fds: %s", strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* filename = "./testfile_FDLeaker.txt";
|
||||
FILE* f = fopen(filename, "w");
|
||||
if (f == NULL) {
|
||||
fprintf(stderr, "Failed to open file: %s", strerror(errno));
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
printf("Opened and leaked %s (%d)", filename, fileno(f));
|
||||
return JNI_OK;
|
||||
}
|
||||
|
||||
static jint limit_num_fds() {
|
||||
struct rlimit rl;
|
||||
|
||||
// Fetch the current limit
|
||||
int ret = getrlimit(RLIMIT_NOFILE, &rl);
|
||||
if (ret != 0) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// Use a lower value unless it is already low
|
||||
rlim_t limit = 100;
|
||||
if (limit < rl.rlim_cur) {
|
||||
rl.rlim_cur = limit;
|
||||
}
|
||||
|
||||
// Lower the value
|
||||
int ret2 = setrlimit(RLIMIT_NOFILE, &rl);
|
||||
if (ret2 != 0) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
78
test/jdk/java/lang/ProcessBuilder/FeelingLucky.java
Normal file
78
test/jdk/java/lang/ProcessBuilder/FeelingLucky.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* A test for
|
||||
* 6469606: (process) Process.destroy() can kill wrong process (Unix)
|
||||
* that is not suitable for use as a regression test, because it must
|
||||
* create 32768 processes, and depends on perl.
|
||||
*/
|
||||
import java.io.*;
|
||||
|
||||
public class FeelingLucky {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
final Runtime rt = Runtime.getRuntime();
|
||||
final String[] pidPrinter = {"/bin/sh", "-c", "echo $$"};
|
||||
final Process minedProcess = rt.exec(pidPrinter);
|
||||
int minedPid = 0;
|
||||
final InputStream is = minedProcess.getInputStream();
|
||||
int c;
|
||||
while ((c = is.read()) >= '0' && c <= '9')
|
||||
minedPid = 10 * minedPid + (c - '0');
|
||||
System.out.printf("minedPid=%d%n", minedPid);
|
||||
minedProcess.waitFor();
|
||||
|
||||
final String[] magnum = {
|
||||
"perl", "-e",
|
||||
"my $punk = getppid();" +
|
||||
"open TTY, '> /dev/tty';" +
|
||||
"print TTY \"punk=$punk\\n\";" +
|
||||
"for (my $i = 0; $i < 32768; $i++) {" +
|
||||
" my $pid = fork();" +
|
||||
" if ($pid == 0) {" +
|
||||
" if ($$ == " + minedPid + ") {" +
|
||||
" print TTY \"MATCH $$ $punk\\n\";" +
|
||||
" $SIG{TERM} = sub {" +
|
||||
" print TTY \"Got TERM $$ $punk\\n\";" +
|
||||
" kill 9, $punk;" +
|
||||
" exit; };" +
|
||||
" $| = 1; print 'Go ahead. Make my day.';" +
|
||||
" sleep 100;" +
|
||||
" }" +
|
||||
" exit;" +
|
||||
" } else { " +
|
||||
" waitpid($pid,0);" +
|
||||
" break if $pid == " + minedPid + ";" +
|
||||
" }" +
|
||||
"}"
|
||||
};
|
||||
|
||||
Process p = rt.exec(magnum);
|
||||
if (p.getInputStream().read() != -1) {
|
||||
System.out.println("got a char");
|
||||
minedProcess.destroy();
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
test/jdk/java/lang/ProcessBuilder/InheritIOClosed.java
Normal file
71
test/jdk/java/lang/ProcessBuilder/InheritIOClosed.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary child process should not hang even if parent process has closed System.out/err
|
||||
* @bug 8366736
|
||||
* @run main/othervm InheritIOClosed close dontclose
|
||||
* @run main/othervm InheritIOClosed dontclose close
|
||||
* @run main/othervm InheritIOClosed close close
|
||||
*/
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class InheritIOClosed {
|
||||
private final static Path JAVA_EXE =
|
||||
Path.of(System.getProperty("java.home"), "bin", "java");
|
||||
|
||||
private static final String s = "1234567890".repeat(10);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length == 2) {
|
||||
// Main test process
|
||||
if (args[0].equals("close")) {
|
||||
System.out.close();
|
||||
}
|
||||
if (args[1].equals("close")) {
|
||||
System.err.close();
|
||||
}
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder().inheritIO()
|
||||
.command(JAVA_EXE.toString(),
|
||||
"-cp",
|
||||
System.getProperty("java.class.path"),
|
||||
InheritIOClosed.class.getName());
|
||||
Process process = pb.start();
|
||||
process.waitFor();
|
||||
|
||||
System.out.println("Done");
|
||||
} else {
|
||||
// Child process -- print to System.out/err. Without the fix in
|
||||
// JDK-8366736, this process will hang on Windows.
|
||||
for (int i = 0; i < 100; i++) {
|
||||
System.out.println(s);
|
||||
}
|
||||
for (int i = 0; i < 100; i++) {
|
||||
System.err.println(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
166
test/jdk/java/lang/ProcessBuilder/InheritIOEHandle.java
Normal file
166
test/jdk/java/lang/ProcessBuilder/InheritIOEHandle.java
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 7147084
|
||||
* @run main/othervm InheritIOEHandle
|
||||
* @summary inherit IOE handles and MS CreateProcess limitations (kb315939)
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class InheritIOEHandle {
|
||||
private static enum APP {
|
||||
A, B, C;
|
||||
}
|
||||
|
||||
private static File stopC = new File("StopC.txt");
|
||||
private static String SIGNAL = "After call child process";
|
||||
private static String JAVA_EXE = System.getProperty("java.home")
|
||||
+ File.separator + "bin"
|
||||
+ File.separator + "java";
|
||||
|
||||
private static String[] getCommandArray(String processName) {
|
||||
String[] cmdArray = {
|
||||
JAVA_EXE,
|
||||
"-cp",
|
||||
System.getProperty("java.class.path"),
|
||||
InheritIOEHandle.class.getName(),
|
||||
processName
|
||||
};
|
||||
return cmdArray;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (!System.getProperty("os.name").startsWith("Windows")) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP app = (args.length > 0) ? APP.valueOf(args[0]) : APP.A;
|
||||
switch (app) {
|
||||
case A:
|
||||
performA();
|
||||
break;
|
||||
case B:
|
||||
performB();
|
||||
break;
|
||||
case C:
|
||||
performC();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void performA() {
|
||||
try {
|
||||
stopC.delete();
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(
|
||||
getCommandArray(APP.B.name()));
|
||||
builder.redirectErrorStream(true);
|
||||
|
||||
Process process = builder.start();
|
||||
|
||||
process.getOutputStream().close();
|
||||
process.getErrorStream().close();
|
||||
|
||||
boolean isSignalReceived = false;
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||
process.getInputStream(), "utf-8")))
|
||||
{
|
||||
String result;
|
||||
while ((result = in.readLine()) != null) {
|
||||
if (SIGNAL.equals(result)) {
|
||||
isSignalReceived = true;
|
||||
break;
|
||||
} else {
|
||||
throw new RuntimeException("Catastrophe in process B! Bad output.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!isSignalReceived) {
|
||||
throw new RuntimeException("Signal from B was not received");
|
||||
}
|
||||
|
||||
// If JDK-7147084 is not fixed that point is unreachable.
|
||||
System.out.println("Received signal from B, creating file StopC");
|
||||
// write signal file
|
||||
boolean isFileStopC = stopC.createNewFile();
|
||||
if (!isFileStopC) {
|
||||
throw new RuntimeException("Signal file StopC.txt was not created. TEST or INFRA bug");
|
||||
}
|
||||
|
||||
process.waitFor();
|
||||
|
||||
System.err.println("Read stream finished.");
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Catastrophe in process A!", ex);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException("A was interrupted while waiting for B", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void performB() {
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(
|
||||
getCommandArray(APP.C.name()));
|
||||
|
||||
Process process = builder.start();
|
||||
|
||||
process.getInputStream().close();
|
||||
process.getOutputStream().close();
|
||||
process.getErrorStream().close();
|
||||
|
||||
System.out.println(SIGNAL);
|
||||
process.waitFor();
|
||||
|
||||
// JDK-7147084 subject:
|
||||
// Process C inherits the [System.out] handle and
|
||||
// handle close in B does not finalize the streaming for A.
|
||||
// (handle reference count > 1).
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Catastrophe in process B!", ex);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException("B was interrupted while waiting for C", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void performC() {
|
||||
// If JDK-7147084 is not fixed the loop is 5min long.
|
||||
for (int i = 0; i < 5 * 60; ++i) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ex) {
|
||||
// that is ok. Longer sleep - better effect.
|
||||
}
|
||||
// check for success
|
||||
if (stopC.exists()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
test/jdk/java/lang/ProcessBuilder/InheritIOTest.java
Normal file
85
test/jdk/java/lang/ProcessBuilder/InheritIOTest.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Unit test for java.lang.ProcessBuilder inheritance of standard output and standard error streams
|
||||
* @bug 8023130 8166026
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.process.*
|
||||
* @run junit InheritIOTest
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import static java.lang.ProcessBuilder.Redirect.INHERIT;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
public class InheritIOTest {
|
||||
|
||||
private static final String EXIT_VALUE_TEMPLATE = "exit value: %d";
|
||||
private static final String EXPECTED_RESULT_STDOUT = "message";
|
||||
private static final String EXPECTED_RESULT_STDERR = EXIT_VALUE_TEMPLATE.formatted(0);
|
||||
|
||||
public static List<Arguments> testCases() {
|
||||
return List.of(
|
||||
Arguments.of(List.of("InheritIOTest$TestInheritIO", "printf", EXPECTED_RESULT_STDOUT)),
|
||||
Arguments.of(List.of("InheritIOTest$TestRedirectInherit", "printf", EXPECTED_RESULT_STDOUT))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("testCases")
|
||||
public void testInheritWithoutRedirect(List<String> arguments) throws Throwable {
|
||||
ProcessBuilder processBuilder = ProcessTools.createLimitedTestJavaProcessBuilder(arguments);
|
||||
OutputAnalyzer outputAnalyzer = ProcessTools.executeCommand(processBuilder);
|
||||
outputAnalyzer.shouldHaveExitValue(0);
|
||||
assertEquals(EXPECTED_RESULT_STDOUT, outputAnalyzer.getStdout());
|
||||
assertEquals(EXPECTED_RESULT_STDERR, outputAnalyzer.getStderr());
|
||||
}
|
||||
|
||||
public static class TestInheritIO {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
int err = new ProcessBuilder(args).inheritIO().start().waitFor();
|
||||
System.err.printf(EXIT_VALUE_TEMPLATE, err);
|
||||
System.exit(err);
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestRedirectInherit {
|
||||
public static void main(String[] args) throws Throwable {
|
||||
int err = new ProcessBuilder(args)
|
||||
.redirectInput(INHERIT)
|
||||
.redirectOutput(INHERIT)
|
||||
.redirectError(INHERIT)
|
||||
.start().waitFor();
|
||||
System.err.printf(EXIT_VALUE_TEMPLATE, err);
|
||||
System.exit(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
62
test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java
Normal file
62
test/jdk/java/lang/ProcessBuilder/InvalidWorkDir.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=FORK
|
||||
* @bug 8379967
|
||||
* @summary Check that passing an invalid work dir yields a corresponding IOE text.
|
||||
* @requires (os.family != "windows")
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm -Xmx64m -Djdk.lang.Process.launchMechanism=FORK InvalidWorkDir
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=POSIX_SPAWN
|
||||
* @bug 8379967
|
||||
* @summary Check that passing an invalid work dir yields a corresponding IOE text.
|
||||
* @requires (os.family != "windows")
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm -Xmx64m -Djdk.lang.Process.launchMechanism=FORK InvalidWorkDir
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class InvalidWorkDir {
|
||||
|
||||
public static void main(String[] args) {
|
||||
ProcessBuilder bld = new ProcessBuilder("ls").directory(new File("./doesnotexist"));
|
||||
try(Process p = bld.start()) {
|
||||
throw new RuntimeException("IOE expected");
|
||||
} catch (IOException e) {
|
||||
if (!e.getMessage().matches(".*Failed to access working directory.*No such file or directory.*")) {
|
||||
throw new RuntimeException(String.format("got IOE but with different text (%s)", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
245
test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java
Normal file
245
test/jdk/java/lang/ProcessBuilder/JspawnhelperProtocol.java
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright Amazon.com Inc. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8307990
|
||||
* @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac")
|
||||
* @requires vm.debug
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm/timeout=300 JspawnhelperProtocol
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class JspawnhelperProtocol {
|
||||
// Timout in seconds
|
||||
private static final int TIMEOUT = 60;
|
||||
// Base error code to communicate various error states from the parent process to the top-level test
|
||||
private static final int ERROR = 10;
|
||||
private static final String[] CMD = { "pwd" };
|
||||
private static final String ENV_KEY = "JTREG_JSPAWNHELPER_PROTOCOL_TEST";
|
||||
|
||||
private static final String SPAWNHELPER_FAILURE_MSG = "Possible reasons:";
|
||||
|
||||
private static void parentCode(String arg) throws IOException, InterruptedException {
|
||||
System.out.println("Recursively executing 'JspawnhelperProtocol " + arg + "'");
|
||||
Process p = null;
|
||||
try {
|
||||
// Route any stdout from the child process - be it jspawnhelper error messages or the output of "/bin/pwd" -
|
||||
// through to the parent process.
|
||||
p = new ProcessBuilder(CMD).inheritIO().start();
|
||||
} catch (Exception e) {
|
||||
// Check that exception contains rich message on failure.
|
||||
e.printStackTrace(System.out);
|
||||
if (e instanceof IOException && e.getMessage().contains(SPAWNHELPER_FAILURE_MSG)) {
|
||||
System.exit(ERROR);
|
||||
} else {
|
||||
System.exit(ERROR + 3);
|
||||
}
|
||||
}
|
||||
if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
System.out.println("Child process timed out");
|
||||
System.exit(ERROR + 1);
|
||||
}
|
||||
if (p.exitValue() == 0) {
|
||||
System.out.println(" Successfully executed '" + CMD[0] + "'");
|
||||
System.exit(0);
|
||||
} else {
|
||||
System.out.println(" Failed to executed '" + CMD[0] + "' (exitValue=" + p.exitValue() + ")");
|
||||
System.exit(ERROR + 3);
|
||||
}
|
||||
}
|
||||
|
||||
private static void normalExec() throws Exception {
|
||||
ProcessBuilder pb;
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Djdk.lang.Process.launchMechanism=posix_spawn",
|
||||
"JspawnhelperProtocol",
|
||||
"normalExec");
|
||||
Process p = pb.start();
|
||||
if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
throw new Exception("Parent process timed out");
|
||||
}
|
||||
if (p.exitValue() != 0) {
|
||||
throw new Exception("Parent process exited with " + p.exitValue());
|
||||
}
|
||||
OutputAnalyzer output = new OutputAnalyzer(p);
|
||||
output.shouldContain("Recursively executing 'JspawnhelperProtocol normalExec'");
|
||||
String realPwd = Path.of("").toAbsolutePath().toString();
|
||||
output.shouldContain(realPwd);
|
||||
}
|
||||
|
||||
private static void simulateCrashInChild(int stage) throws Exception {
|
||||
ProcessBuilder pb;
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Djdk.lang.Process.launchMechanism=posix_spawn",
|
||||
"JspawnhelperProtocol",
|
||||
"simulateCrashInChild" + stage);
|
||||
pb.environment().put(ENV_KEY, Integer.toString(stage));
|
||||
Process p = pb.start();
|
||||
|
||||
boolean foundCrashInfo = false;
|
||||
try (BufferedReader br = p.inputReader()) {
|
||||
String line = br.readLine();
|
||||
while (line != null) {
|
||||
System.out.println(line);
|
||||
if (line.equals("posix_spawn:0")) {
|
||||
foundCrashInfo = true;
|
||||
}
|
||||
line = br.readLine();
|
||||
}
|
||||
}
|
||||
if (!foundCrashInfo) {
|
||||
throw new Exception("Wrong output from child process");
|
||||
}
|
||||
if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
throw new Exception("Parent process timed out");
|
||||
}
|
||||
|
||||
int ret = p.exitValue();
|
||||
if (ret == 0) {
|
||||
throw new Exception("Expected error during child execution");
|
||||
}
|
||||
System.out.println("Parent exit code: " + ret);
|
||||
}
|
||||
|
||||
private static void simulateCrashInParent(int stage) throws Exception {
|
||||
ProcessBuilder pb;
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Djdk.lang.Process.launchMechanism=posix_spawn",
|
||||
"JspawnhelperProtocol",
|
||||
"simulateCrashInParent" + stage);
|
||||
pb.environment().put(ENV_KEY, Integer.toString(stage));
|
||||
Process p = pb.start();
|
||||
|
||||
String line = null;
|
||||
try (BufferedReader br = p.inputReader()) {
|
||||
line = br.readLine();
|
||||
while (line != null && !line.startsWith("posix_spawn:")) {
|
||||
System.out.println("parent stdout:" + line);
|
||||
line = br.readLine();
|
||||
}
|
||||
}
|
||||
if (line == null) {
|
||||
throw new Exception("Wrong output from parent process");
|
||||
}
|
||||
System.out.println(line);
|
||||
long childPid = Integer.parseInt(line.substring(line.indexOf(':') + 1));
|
||||
|
||||
if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
throw new Exception("Parent process timed out");
|
||||
}
|
||||
|
||||
Optional<ProcessHandle> oph = ProcessHandle.of(childPid);
|
||||
if (!oph.isEmpty()) {
|
||||
ProcessHandle ph = oph.get();
|
||||
try {
|
||||
// Give jspawnhelper a chance to exit gracefully
|
||||
ph.onExit().get(TIMEOUT, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException te) {
|
||||
Optional<String> cmd = ph.info().command();
|
||||
if (cmd.isPresent() && cmd.get().endsWith("jspawnhelper")) {
|
||||
throw new Exception("jspawnhelper still alive after parent Java process terminated");
|
||||
}
|
||||
}
|
||||
}
|
||||
int ret = p.exitValue();
|
||||
if (ret != stage) {
|
||||
throw new Exception("Expected exit code " + stage + " but got " + ret);
|
||||
}
|
||||
System.out.println("Parent exit code: " + ret);
|
||||
}
|
||||
|
||||
private static void simulateTruncatedWriteInParent(int stage) throws Exception {
|
||||
ProcessBuilder pb;
|
||||
pb = ProcessTools.createLimitedTestJavaProcessBuilder("-Djdk.lang.Process.launchMechanism=posix_spawn",
|
||||
"JspawnhelperProtocol",
|
||||
"simulateTruncatedWriteInParent" + stage);
|
||||
pb.environment().put(ENV_KEY, Integer.toString(stage));
|
||||
Process p = pb.start();
|
||||
|
||||
BufferedReader br = p.inputReader();
|
||||
String line = br.readLine();
|
||||
while (line != null && !line.startsWith("posix_spawn:")) {
|
||||
System.out.println(line);
|
||||
line = br.readLine();
|
||||
}
|
||||
if (line == null) {
|
||||
throw new Exception("Wrong output from parent process");
|
||||
}
|
||||
System.out.println(line);
|
||||
|
||||
if (!p.waitFor(TIMEOUT, TimeUnit.SECONDS)) {
|
||||
throw new Exception("Parent process timed out");
|
||||
}
|
||||
line = br.readLine();
|
||||
while (line != null) {
|
||||
System.out.println(line);
|
||||
line = br.readLine();
|
||||
}
|
||||
|
||||
int ret = p.exitValue();
|
||||
if (ret != ERROR) {
|
||||
throw new Exception("Expected exit code " + ERROR + " but got " + ret);
|
||||
}
|
||||
System.out.println("Parent exit code: " + ret);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// This test works as follows:
|
||||
// - jtreg executes the test class `JspawnhelperProtocol` without arguments.
|
||||
// This is the initial "grandparent" process.
|
||||
// - For each sub-test (i.e. `normalExec()`, `simulateCrashInParent()` and
|
||||
// `simulateCrashInChild()`), a new sub-process (called the "parent") will be
|
||||
// forked which executes `JspawnhelperProtocol` recursively with a corresponding
|
||||
// command line argument.
|
||||
// - The forked `JspawnhelperProtocol` process (i.e. the "parent") runs
|
||||
// `JspawnhelperProtocol::parentCode()` which forks off yet another sub-process
|
||||
// (called the "child").
|
||||
// - The sub-tests in the "grandparent" check that various abnormal program
|
||||
// terminations in the "parent" or the "child" process are handled gracefully and
|
||||
// don't lead to deadlocks or zombie processes.
|
||||
if (args.length > 0) {
|
||||
// Entry point for recursive execution in the "parent" process
|
||||
parentCode(args[0]);
|
||||
} else {
|
||||
// Main test entry for execution from jtreg
|
||||
normalExec();
|
||||
simulateCrashInParent(1);
|
||||
simulateCrashInParent(2);
|
||||
simulateCrashInParent(3);
|
||||
simulateCrashInChild(4);
|
||||
simulateCrashInChild(5);
|
||||
simulateCrashInChild(6);
|
||||
simulateTruncatedWriteInParent(99);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java
Normal file
101
test/jdk/java/lang/ProcessBuilder/JspawnhelperWarnings.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2024, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=badargs
|
||||
* @bug 8325567 8325621 8379967
|
||||
* @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac")
|
||||
* @library /test/lib
|
||||
* @run driver JspawnhelperWarnings badargs
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=badversion
|
||||
* @bug 8325567 8325621 8379967
|
||||
* @requires (os.family == "linux") | (os.family == "aix") | (os.family == "mac")
|
||||
* @library /test/lib
|
||||
* @run driver JspawnhelperWarnings badversion
|
||||
*/
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
public class JspawnhelperWarnings {
|
||||
|
||||
// See childproc_errorcodes.h
|
||||
static final int ESTEP_JSPAWN_ARG_ERROR = 1;
|
||||
static final int ESTEP_JSPAWN_VERSION_ERROR = 2;
|
||||
|
||||
// See exitCodeFromErrorCode() in childproc_errorcodes.c
|
||||
static final int EXITCODE_OFFSET = 0x10;
|
||||
|
||||
private static void tryWithNArgs(int nArgs) throws Exception {
|
||||
System.out.println("Running jspawnhelper with " + nArgs + " args");
|
||||
String[] args = new String[nArgs + 1];
|
||||
Arrays.fill(args, "1");
|
||||
args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString();
|
||||
Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args));
|
||||
OutputAnalyzer oa = new OutputAnalyzer(p);
|
||||
oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_ARG_ERROR);
|
||||
oa.shouldContain("jspawnhelper fail: (1-0-0)");
|
||||
oa.shouldContain("This command is not for general use");
|
||||
if (nArgs != 1) {
|
||||
oa.shouldContain("Incorrect number of arguments");
|
||||
} else {
|
||||
oa.shouldContain("Incorrect Java version");
|
||||
}
|
||||
}
|
||||
|
||||
private static void testVersion() throws Exception {
|
||||
String[] args = new String[2];
|
||||
args[0] = Paths.get(System.getProperty("java.home"), "lib", "jspawnhelper").toString();
|
||||
args[1] = "wrongVersion";
|
||||
Process p = ProcessTools.startProcess("jspawnhelper", new ProcessBuilder(args));
|
||||
OutputAnalyzer oa = new OutputAnalyzer(p);
|
||||
oa.shouldHaveExitValue(EXITCODE_OFFSET + ESTEP_JSPAWN_VERSION_ERROR);
|
||||
oa.shouldContain("jspawnhelper fail: (2-0-0)");
|
||||
oa.shouldContain("This command is not for general use");
|
||||
oa.shouldContain("Incorrect Java version: wrongVersion");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (args.length != 1) {
|
||||
throw new RuntimeException("test argument error");
|
||||
}
|
||||
switch (args[0]) {
|
||||
case "badargs" -> {
|
||||
for (int nArgs = 0; nArgs < 10; nArgs++) {
|
||||
if (nArgs != 1) {
|
||||
tryWithNArgs(nArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
case "badversion" -> {
|
||||
testVersion();
|
||||
}
|
||||
default -> throw new RuntimeException("test argument error");
|
||||
}
|
||||
}
|
||||
}
|
||||
62
test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java
Normal file
62
test/jdk/java/lang/ProcessBuilder/LinuxFDInfo.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Accessor for Native calls to get information about File Descriptors.
|
||||
* If it's a pipe and its Inode number.
|
||||
*/
|
||||
public class LinuxFDInfo {
|
||||
|
||||
static {
|
||||
System.loadLibrary("LinuxFDInfo");
|
||||
}
|
||||
|
||||
// Maximum file descriptor to probe for being a pipe,
|
||||
private final static int MAX_FD = 100;
|
||||
|
||||
// A simple main to print the pipes in this process and their Inode value
|
||||
public static void main() {
|
||||
for (int fd = 0; fd < MAX_FD; fd++) {
|
||||
long inode = getPipeInodeNum(fd);
|
||||
if (inode != 0) {
|
||||
System.out.printf("fd: %d, inode: 0x%08x\n", fd, inode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the output from main into a long array of the fd, and Inode.
|
||||
public static FdAndInode parseFdAndInode(String s) {
|
||||
String[] args = s.split(",");
|
||||
return new FdAndInode(Integer.parseUnsignedInt(args[0].split(":")[1].trim()),
|
||||
Long.parseUnsignedLong(args[1].split(":")[1].trim().substring(2), 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inode number for the FD, if it is a pipe.
|
||||
* @param fd file descriptor
|
||||
* @return the Inode number.
|
||||
*/
|
||||
public static native long getPipeInodeNum(int fd);
|
||||
|
||||
public record FdAndInode(int fd, long inode) {}
|
||||
}
|
||||
182
test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java
Normal file
182
test/jdk/java/lang/ProcessBuilder/NonPipelineLeaksFD.java
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026, IBM Corp.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/*
|
||||
* @test id=FORK
|
||||
* @summary Check that we don't accumulate leaked FDs in the parent process
|
||||
* @requires os.family == "linux"
|
||||
* @library /test/lib
|
||||
* @run main/othervm -Djdk.lang.Process.launchMechanism=fork NonPipelineLeaksFD
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=VFORK
|
||||
* @summary Check that we don't accumulate leaked FDs in the parent process
|
||||
* @requires os.family == "linux"
|
||||
* @library /test/lib
|
||||
* @run main/othervm -Djdk.lang.Process.launchMechanism=vfork NonPipelineLeaksFD
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=POSIX_SPAWN
|
||||
* @summary Check that we don't accumulate leaked FDs in the parent process
|
||||
* @requires os.family == "linux"
|
||||
* @library /test/lib
|
||||
* @run main/othervm -Djdk.lang.Process.launchMechanism=posix_spawn NonPipelineLeaksFD
|
||||
*/
|
||||
|
||||
public class NonPipelineLeaksFD {
|
||||
|
||||
final static int repeatCount = 50;
|
||||
|
||||
// Similar to PilelineLeaksFD, but where PilelineLeaksFD checks that the parent process
|
||||
// does not leak file descriptors when invoking a pipeline, here we check that we don't
|
||||
// leak FDs when executing simple (non-pipelined) programs but we test a wider span of
|
||||
// redirection modes in both successful and failing variants.
|
||||
|
||||
// How this works:
|
||||
//
|
||||
// We execute a mix of failing and succeeding child process starts with various
|
||||
// flavors of IO redirections many times; we observe the open file descriptors
|
||||
// before and afterwards. Test fails if we have significantly more file descriptors
|
||||
// open afterwards than before.
|
||||
|
||||
static int countNumberOfOpenFileDescriptors() {
|
||||
return new File("/proc/self/fd").list().length;
|
||||
}
|
||||
|
||||
static void printOpenFileDescriptors() {
|
||||
long mypid = ProcessHandle.current().pid();
|
||||
try(Process p = new ProcessBuilder("lsof", "-p", Long.toString(mypid))
|
||||
.inheritIO().start()) {
|
||||
p.waitFor();
|
||||
} catch (InterruptedException | IOException ignored) {
|
||||
// Quietly swallow; it was just an attempt.
|
||||
}
|
||||
}
|
||||
|
||||
static String readFirstLineOf(File f) {
|
||||
String result;
|
||||
try (BufferedReader b = new BufferedReader(new FileReader(f))){
|
||||
result = b.readLine();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void runThisExpectSuccess(ProcessBuilder bld) {
|
||||
try(Process p = bld.start()) {
|
||||
p.waitFor();
|
||||
if (p.exitValue() != 0) {
|
||||
throw new RuntimeException("Unexpected exitcode");
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void runThisExpectError(ProcessBuilder bld) {
|
||||
boolean failed = false;
|
||||
try(Process p = bld.start()) {
|
||||
p.waitFor();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
failed = true;
|
||||
}
|
||||
if (!failed) {
|
||||
throw new RuntimeException("Expected Error");
|
||||
}
|
||||
}
|
||||
|
||||
static void runPosWithPipes() {
|
||||
ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo");
|
||||
runThisExpectSuccess(bld);
|
||||
}
|
||||
|
||||
static void runPosWithInheritIO() {
|
||||
ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo").inheritIO();
|
||||
runThisExpectSuccess(bld);
|
||||
}
|
||||
|
||||
static void runPosWithRedirectToFile() {
|
||||
File fo = new File("test.out");
|
||||
ProcessBuilder bld = new ProcessBuilder("sh", "-c", "echo hallo");
|
||||
bld.redirectOutput(ProcessBuilder.Redirect.to(fo));
|
||||
runThisExpectSuccess(bld);
|
||||
if (!readFirstLineOf(fo).equals("hallo")) {
|
||||
throw new RuntimeException("mismatch");
|
||||
}
|
||||
}
|
||||
|
||||
static void runNegWithPipes() {
|
||||
ProcessBuilder bld = new ProcessBuilder("doesnotexist");
|
||||
runThisExpectError(bld);
|
||||
}
|
||||
|
||||
static void runNegWithInheritIO() {
|
||||
ProcessBuilder bld = new ProcessBuilder("doesnotexist").inheritIO();
|
||||
runThisExpectError(bld);
|
||||
}
|
||||
|
||||
static void runNegWithRedirectToFile() {
|
||||
File fo = new File("test.out");
|
||||
ProcessBuilder bld = new ProcessBuilder("doesnotexist");
|
||||
bld.redirectOutput(ProcessBuilder.Redirect.to(fo));
|
||||
runThisExpectError(bld);
|
||||
}
|
||||
|
||||
static void doTestNTimesAndCountFDs(Runnable runnable, String name) {
|
||||
System.out.println(name);
|
||||
int c1 = countNumberOfOpenFileDescriptors();
|
||||
for (int i = 0; i < repeatCount; i++) {
|
||||
runnable.run();
|
||||
}
|
||||
int c2 = countNumberOfOpenFileDescriptors();
|
||||
System.out.printf("%d->%d", c1, c2);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("jdk.lang.Process.launchMechanism=" +
|
||||
System.getProperty("jdk.lang.Process.launchMechanism"));
|
||||
int c1 = countNumberOfOpenFileDescriptors();
|
||||
doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithPipes, "runPosWithPipes");
|
||||
doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithInheritIO, "runPosWithInheritIO");
|
||||
doTestNTimesAndCountFDs(NonPipelineLeaksFD::runPosWithRedirectToFile, "runPosWithRedirectToFile");
|
||||
doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithPipes, "runNegWithPipes");
|
||||
doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithInheritIO, "runNegWithInheritIO");
|
||||
doTestNTimesAndCountFDs(NonPipelineLeaksFD::runNegWithRedirectToFile, "runNegWithRedirectToFile");
|
||||
int c2 = countNumberOfOpenFileDescriptors();
|
||||
|
||||
System.out.printf("All tests: %d->%d", c1, c2);
|
||||
printOpenFileDescriptors();
|
||||
|
||||
final int fudge = 10;
|
||||
if (c2 > (c1 + fudge)) {
|
||||
throw new RuntimeException(
|
||||
String.format("Leak suspected (%d->%d) - see lsof output", c1, c2));
|
||||
}
|
||||
}
|
||||
}
|
||||
244
test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java
Normal file
244
test/jdk/java/lang/ProcessBuilder/PipelineLeaksFD.java
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/*
|
||||
* @test id=FORK
|
||||
* @bug 8289643 8291760 8291986
|
||||
* @requires os.family == "mac" | os.family == "linux"
|
||||
* @summary File descriptor leak detection with ProcessBuilder.startPipeline
|
||||
* @library /test/lib
|
||||
* @run junit/othervm/timeout=240 -Djdk.lang.Process.launchMechanism=FORK PipelineLeaksFD
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=POSIX_SPAWN
|
||||
* @bug 8289643 8291760 8291986 8379182
|
||||
* @requires os.family == "mac" | os.family == "linux"
|
||||
* @summary File descriptor leak detection with ProcessBuilder.startPipeline
|
||||
* @library /test/lib
|
||||
* @run junit/othervm/native/timeout=240 -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipelineLeaksFD
|
||||
*/
|
||||
|
||||
public class PipelineLeaksFD {
|
||||
|
||||
private static final String TEST_JDK = System.getProperty("test.jdk");
|
||||
|
||||
private static final String JAVA_LIBRARY_PATH = System.getProperty("java.library.path");
|
||||
|
||||
private static final long MY_PID = ProcessHandle.current().pid();
|
||||
|
||||
// Maximum file descriptor to probe for being a pipe,
|
||||
private static final int MAX_FD = 100;
|
||||
|
||||
// Test cases for pipelines with a number of pipeline sequences
|
||||
public static Object[][] builders() {
|
||||
return new Object[][]{
|
||||
{List.of(new ProcessBuilder("cat"))},
|
||||
{List.of(new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat"))},
|
||||
{List.of(new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat"))},
|
||||
};
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("builders")
|
||||
void checkForLeaks(List<ProcessBuilder> builders) throws IOException {
|
||||
|
||||
System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism"));
|
||||
Set<PipeRecord> beforePipes = pipesFromSelf();
|
||||
if (beforePipes.size() < 3) {
|
||||
fail("There should be at least 3 pipes before, (0, 1, 2): is " +
|
||||
beforePipes.size());
|
||||
}
|
||||
printPipes(beforePipes, "Before start");
|
||||
|
||||
List<Process> processes = ProcessBuilder.startPipeline(builders);
|
||||
|
||||
// Write something through the pipeline
|
||||
final String text = "xyz";
|
||||
try (Writer out = processes.getFirst().outputWriter()) {
|
||||
out.write(text);
|
||||
}
|
||||
|
||||
// Read, check, and close all streams
|
||||
for (int i = 0; i < processes.size(); i++) {
|
||||
final Process p = processes.get(i);
|
||||
String expectedOut = (i == processes.size() - 1) ? text : null;
|
||||
String expectedErr = null; // EOF
|
||||
try (BufferedReader inputStream = p.inputReader();
|
||||
BufferedReader errorStream = p.errorReader()) {
|
||||
String outActual = inputStream.readLine();
|
||||
assertEquals(expectedOut, outActual, "stdout, process[ " + i + "]: " + p);
|
||||
|
||||
String errActual = errorStream.readLine();
|
||||
assertEquals(expectedErr, errActual, "stderr, process[ " + i + "]: " + p);
|
||||
}
|
||||
}
|
||||
|
||||
processes.forEach(PipelineLeaksFD::waitForQuiet);
|
||||
|
||||
Set<PipeRecord> afterPipes = pipesFromSelf();
|
||||
if (!beforePipes.equals(afterPipes)) {
|
||||
Set<PipeRecord> missing = new HashSet<>(beforePipes);
|
||||
missing.removeAll(afterPipes);
|
||||
printPipes(missing, "Missing from beforePipes()");
|
||||
Set<PipeRecord> extra = new HashSet<>(afterPipes);
|
||||
extra.removeAll(beforePipes);
|
||||
printPipes(extra, "Extra pipes in afterPipes()");
|
||||
fail("More or fewer pipes than expected");
|
||||
}
|
||||
}
|
||||
|
||||
// Test redirectErrorStream, both true and false
|
||||
public static Object[][] redirectCases() {
|
||||
return new Object[][] {
|
||||
{true},
|
||||
{false},
|
||||
};
|
||||
}
|
||||
|
||||
// Test redirectErrorStream (true/false) has the right number of pipes in use.
|
||||
// Spawn the child to report its pipe inode info.
|
||||
@ParameterizedTest()
|
||||
@MethodSource("redirectCases")
|
||||
void checkRedirectErrorStream(boolean redirectError) {
|
||||
|
||||
System.out.println("Using:" + System.getProperty("jdk.lang.Process.launchMechanism"));
|
||||
|
||||
try (Process p = new ProcessBuilder(TEST_JDK + "/bin/java",
|
||||
"--enable-preview",
|
||||
"-Djava.library.path=" + JAVA_LIBRARY_PATH,
|
||||
"--enable-native-access=ALL-UNNAMED", "LinuxFDInfo")
|
||||
.redirectErrorStream(redirectError)
|
||||
.start()) {
|
||||
|
||||
final Set<PipeRecord> pipes = pipesFromSelf();
|
||||
printPipes(pipes, "Parent pipes");
|
||||
|
||||
List<String> lines = p.inputReader().readAllLines();
|
||||
Set<PipeRecord> childPipes = lines.stream().map(s -> {
|
||||
var fdAndInode = LinuxFDInfo.parseFdAndInode(s);
|
||||
return PipeRecord.lookup(fdAndInode.fd(), "0x%08x".formatted(fdAndInode.inode()), p.pid());
|
||||
})
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
printPipes(childPipes, "child pipes");
|
||||
int uniquePipes = childPipes.stream().map(PipeRecord::myKey).collect(Collectors.toSet()).size();
|
||||
// Number of pipe references depending on whether stderr is redirected to stdout
|
||||
long expected = redirectError ? 2 : 3;
|
||||
assertEquals(expected, uniquePipes, "Wrong number of unique pipes");
|
||||
|
||||
String expectedTypeName = redirectError
|
||||
? "java.lang.ProcessBuilder$NullInputStream"
|
||||
: "java.lang.ProcessImpl$ProcessPipeInputStream";
|
||||
assertEquals(expectedTypeName, p.getErrorStream().getClass().getName(),
|
||||
"errorStream type is incorrect");
|
||||
|
||||
final Set<PipeRecord> afterPipes = pipesFromSelf();
|
||||
if (!pipes.equals(afterPipes)) {
|
||||
printPipes(afterPipes, "Parent pipes after are different");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
fail("Process start", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
static void printPipes(Set<PipeRecord> pipes, String label) {
|
||||
System.err.printf("%s: [%d]%n", label, pipes.size());
|
||||
pipes.forEach(System.err::println);
|
||||
}
|
||||
|
||||
static void waitForQuiet(Process p) {
|
||||
try {
|
||||
int st = p.waitFor();
|
||||
if (st != 0) {
|
||||
System.err.println("non-zero exit status: " + p);
|
||||
}
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
static Set<PipeRecord> pipesFromSelf() {
|
||||
Set<PipeRecord> pipes = new LinkedHashSet<>(MAX_FD);
|
||||
for (int fd = 0; fd < MAX_FD; fd++) {
|
||||
long inode = LinuxFDInfo.getPipeInodeNum(fd);
|
||||
if (inode != 0) {
|
||||
pipes.add(PipeRecord.lookup(fd, "0x%08x".formatted(inode), MY_PID));
|
||||
}
|
||||
}
|
||||
return pipes;
|
||||
}
|
||||
|
||||
// Identify a pipe by pid, fd, and a key (unique across processes)
|
||||
// Mac OS X has separate keys for read and write sides, both are matched to the same "name"
|
||||
record PipeRecord(long pid, int fd, KeyedString myKey) {
|
||||
static PipeRecord lookup(int fd, String myKey, long pid) {
|
||||
return new PipeRecord(pid, fd, KeyedString.getKey(myKey));
|
||||
}
|
||||
}
|
||||
|
||||
// A unique name for a string with a count of uses
|
||||
// Used to associate pipe between parent and child.
|
||||
static class KeyedString {
|
||||
private static final HashMap<String, KeyedString> map = new HashMap<>();
|
||||
private static int nextInt = 1;
|
||||
private final String key;
|
||||
private final String name;
|
||||
KeyedString(String key, String name) {
|
||||
this.key = key;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
KeyedString(String s) {
|
||||
String k = "p" + nextInt++;
|
||||
this(s, k);
|
||||
}
|
||||
|
||||
static KeyedString getKey(String key) {
|
||||
return map.computeIfAbsent(key, KeyedString::new);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name + ": osInfo: " + key;
|
||||
}
|
||||
}
|
||||
}
|
||||
306
test/jdk/java/lang/ProcessBuilder/PipelineTest.java
Normal file
306
test/jdk/java/lang/ProcessBuilder/PipelineTest.java
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* Copyright (c) 2015, 2019, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/*
|
||||
* @test PipelineTest
|
||||
* @bug 8211844
|
||||
* @summary Tests for ProcessBuilder.startPipeline
|
||||
*/
|
||||
|
||||
public class PipelineTest {
|
||||
|
||||
private static void realMain(String[] args) throws Throwable {
|
||||
t1_simplePipeline();
|
||||
t2_translatePipeline();
|
||||
t3_redirectErrorStream();
|
||||
t4_failStartPipeline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the varargs arguments.
|
||||
* @param args elements to include in the list
|
||||
* @param <T> the type of the elements
|
||||
* @return a {@code List<T>} of the arguments
|
||||
*/
|
||||
@SafeVarargs
|
||||
@SuppressWarnings("varargs")
|
||||
static <T> List<T> asList(T... args) {
|
||||
return Arrays.asList(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* T1 - simple copy between two processes
|
||||
*/
|
||||
static void t1_simplePipeline() {
|
||||
try {
|
||||
String s1 = "Now is the time to check!";
|
||||
verify(s1, s1,
|
||||
asList(new ProcessBuilder("cat")));
|
||||
verify(s1, s1,
|
||||
asList(new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat")));
|
||||
verify(s1, s1,
|
||||
asList(new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat"),
|
||||
new ProcessBuilder("cat")));
|
||||
} catch (Throwable t) {
|
||||
unexpected(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pipeline that modifies the content.
|
||||
*/
|
||||
static void t2_translatePipeline() {
|
||||
try {
|
||||
String s2 = "Now is the time to check!";
|
||||
String r2 = s2.replace('e', 'E').replace('o', 'O');
|
||||
verify(s2, r2,
|
||||
asList(new ProcessBuilder("tr", "e", "E"),
|
||||
new ProcessBuilder("tr", "o", "O")));
|
||||
} catch (Throwable t) {
|
||||
unexpected(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that redirectErrorStream sends standard error of the first process
|
||||
* to the standard output. The standard error of the first process should be empty.
|
||||
* The standard output of the 2nd should contain the error message including the bad file name.
|
||||
*/
|
||||
static void t3_redirectErrorStream() {
|
||||
try {
|
||||
File p1err = new File("p1-test.err");
|
||||
File p2out = new File("p2-test.out");
|
||||
|
||||
List<Process> processes = ProcessBuilder.startPipeline(
|
||||
asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE")
|
||||
.redirectErrorStream(true)
|
||||
.redirectError(p1err),
|
||||
new ProcessBuilder("cat").redirectOutput(p2out)));
|
||||
waitForAll(processes);
|
||||
|
||||
check("".equals(fileContents(p1err)), "The first process standard error should be empty");
|
||||
String p2contents = fileContents(p2out);
|
||||
check(p2contents.contains("NON-EXISTENT-FILE"),
|
||||
"The error from the first process should be in the output of the second: " + p2contents);
|
||||
} catch (Throwable t) {
|
||||
unexpected(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that no processes are left after a failed startPipeline.
|
||||
* Test illegal combinations of redirects.
|
||||
*/
|
||||
static void t4_failStartPipeline() {
|
||||
File p1err = new File("p1-test.err");
|
||||
File p2out = new File("p2-test.out");
|
||||
|
||||
THROWS(IllegalArgumentException.class,
|
||||
() -> {
|
||||
// Test that output redirect != PIPE throws IAE
|
||||
List<Process> processes = ProcessBuilder.startPipeline(
|
||||
asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE1")
|
||||
.redirectOutput(p1err),
|
||||
new ProcessBuilder("cat")));
|
||||
},
|
||||
() -> {
|
||||
// Test that input redirect != PIPE throws IAE
|
||||
List<Process> processes = ProcessBuilder.startPipeline(
|
||||
asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE2"),
|
||||
new ProcessBuilder("cat").redirectInput(p2out)));
|
||||
}
|
||||
);
|
||||
|
||||
THROWS(NullPointerException.class,
|
||||
() -> {
|
||||
List<Process> processes = ProcessBuilder.startPipeline(
|
||||
asList(new ProcessBuilder("cat", "a"), null));
|
||||
},
|
||||
() -> {
|
||||
List<Process> processes = ProcessBuilder.startPipeline(
|
||||
asList(null, new ProcessBuilder("cat", "b")));
|
||||
}
|
||||
);
|
||||
|
||||
THROWS(IOException.class,
|
||||
() -> {
|
||||
List<Process> processes = ProcessBuilder.startPipeline(
|
||||
asList(new ProcessBuilder("cat", "c"),
|
||||
new ProcessBuilder("NON-EXISTENT-COMMAND")));
|
||||
});
|
||||
|
||||
// Check no subprocess are left behind
|
||||
ProcessHandle.current().children().forEach(PipelineTest::print);
|
||||
ProcessHandle.current().children()
|
||||
.filter(p -> p.info().command().orElse("").contains("cat"))
|
||||
.forEach(p -> fail("process should have been destroyed: " + p));
|
||||
}
|
||||
|
||||
static void verify(String input, String expected, List<ProcessBuilder> builders) throws IOException {
|
||||
File infile = new File("test.in");
|
||||
File outfile = new File("test.out");
|
||||
setFileContents(infile, input);
|
||||
for (int i = 0; i < builders.size(); i++) {
|
||||
ProcessBuilder b = builders.get(i);
|
||||
if (i == 0) {
|
||||
b.redirectInput(infile);
|
||||
}
|
||||
if (i == builders.size() - 1) {
|
||||
b.redirectOutput(outfile);
|
||||
}
|
||||
}
|
||||
List<Process> processes = ProcessBuilder.startPipeline(builders);
|
||||
verifyProcesses(processes);
|
||||
waitForAll(processes);
|
||||
String result = fileContents(outfile);
|
||||
check(result.equals(expected),
|
||||
"result not as expected: " + result + ", expected: " + expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for each of the processes to be done.
|
||||
*
|
||||
* @param processes the list of processes to check
|
||||
*/
|
||||
static void waitForAll(List<Process> processes) {
|
||||
processes.forEach(p -> {
|
||||
try {
|
||||
int status = p.waitFor();
|
||||
} catch (InterruptedException ie) {
|
||||
unexpected(ie);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void print(ProcessBuilder pb) {
|
||||
if (pb != null) {
|
||||
System.out.printf(" pb: %s%n", pb);
|
||||
System.out.printf(" cmd: %s%n", pb.command());
|
||||
}
|
||||
}
|
||||
|
||||
static void print(ProcessHandle p) {
|
||||
System.out.printf("process: pid: %d, info: %s%n",
|
||||
p.pid(), p.info());
|
||||
}
|
||||
|
||||
// Check various aspects of the processes
|
||||
static void verifyProcesses(List<Process> processes) {
|
||||
for (int i = 0; i < processes.size(); i++) {
|
||||
Process p = processes.get(i);
|
||||
|
||||
if (i != 0) {
|
||||
verifyNullStream(p.getOutputStream(), "getOutputStream");
|
||||
}
|
||||
if (i <= processes.size() - 1) {
|
||||
verifyNullStream(p.getInputStream(), "getInputStream");
|
||||
}
|
||||
if (i == processes.size() - 1) {
|
||||
verifyNullStream(p.getErrorStream(), "getErrorStream");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyNullStream(OutputStream s, String msg) {
|
||||
try {
|
||||
s.write(0xff);
|
||||
fail("Stream should have been a NullStream: " + msg);
|
||||
} catch (IOException ie) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
static void verifyNullStream(InputStream s, String msg) {
|
||||
try {
|
||||
int len = s.read();
|
||||
check(len == -1, "Stream should have been a NullStream: " + msg);
|
||||
} catch (IOException ie) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
static void setFileContents(File file, String contents) {
|
||||
try {
|
||||
Writer w = new FileWriter(file);
|
||||
w.write(contents);
|
||||
w.close();
|
||||
} catch (Throwable t) { unexpected(t); }
|
||||
}
|
||||
|
||||
static String fileContents(File file) {
|
||||
try {
|
||||
Reader r = new FileReader(file);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
char[] buffer = new char[1024];
|
||||
int n;
|
||||
while ((n = r.read(buffer)) != -1)
|
||||
sb.append(buffer,0,n);
|
||||
r.close();
|
||||
return new String(sb);
|
||||
} catch (Throwable t) { unexpected(t); return ""; }
|
||||
}
|
||||
|
||||
//--------------------- Infrastructure ---------------------------
|
||||
static volatile int passed = 0, failed = 0;
|
||||
static void pass() {passed++;}
|
||||
static void fail() {failed++; new Exception("Stack trace").printStackTrace(System.out);}
|
||||
static void fail(String msg) {
|
||||
System.out.println(msg); failed++;
|
||||
new Exception("Stack trace: " + msg).printStackTrace(System.out);
|
||||
}
|
||||
static void unexpected(Throwable t) {failed++; t.printStackTrace(System.out);}
|
||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
||||
static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
|
||||
static void equal(Object x, Object y) {
|
||||
if (x == null ? y == null : x.equals(y)) pass();
|
||||
else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||
if (failed > 0) throw new AssertionError("Some tests failed");
|
||||
}
|
||||
interface Fun {void f() throws Throwable;}
|
||||
static void THROWS(Class<? extends Throwable> k, Fun... fs) {
|
||||
for (Fun f : fs)
|
||||
try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
|
||||
catch (Throwable t) {
|
||||
if (k.isAssignableFrom(t.getClass())) pass();
|
||||
else unexpected(t);}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026, IBM Corp.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=FORK
|
||||
* @bug 8377907
|
||||
* @summary Check that we don't open pipes without CLOEXCEC
|
||||
* @requires os.family == "linux"
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=FORK PipesCloseOnExecTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=VFORK
|
||||
* @bug 8377907
|
||||
* @summary Check that we don't open pipes without CLOEXCEC
|
||||
* @requires os.family == "linux"
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=VFORK PipesCloseOnExecTest
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=POSIX_SPAWN
|
||||
* @bug 8377907
|
||||
* @summary Check that we don't open pipes without CLOEXCEC
|
||||
* @requires os.family == "linux"
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=POSIX_SPAWN PipesCloseOnExecTest
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalTime;
|
||||
|
||||
public class PipesCloseOnExecTest {
|
||||
|
||||
// How this works:
|
||||
// - We start a child process A. Does not matter what, we just call "/bin/date".
|
||||
// - Concurrently, we (natively, continuously) iterate all pipe file descriptors in
|
||||
// the process and count all those that are not tagged with CLOEXEC. Finding one
|
||||
// counts as error.
|
||||
|
||||
// Note that this test can only reliably succeed with Linux and the xxxBSDs, where
|
||||
// we have pipe2(2).
|
||||
//
|
||||
// On MacOs and AIX, we emulate pipe2(2) with pipe(2) and fcntl(2); therefore we
|
||||
// have a tiny time window in which a concurrent thread can can observe pipe
|
||||
// filedescriptors without CLOEXEC. Furthermore, on MacOS, we also have to employ
|
||||
// the double-dup-trick to workaround a the buggy MacOS implementation of posix_spawn.
|
||||
// Therefore, on these platforms, the test would (correctly) spot "bad" file descriptors.
|
||||
|
||||
native static boolean startTester();
|
||||
native static boolean stopTester();
|
||||
|
||||
static final int num_tries = 100;
|
||||
|
||||
static void printOpenFileDescriptors() {
|
||||
long mypid = ProcessHandle.current().pid();
|
||||
try(Process p = new ProcessBuilder("lsof", "-p", Long.toString(mypid))
|
||||
.inheritIO().start()) {
|
||||
p.waitFor();
|
||||
} catch (InterruptedException | IOException ignored) {
|
||||
// Quietly swallow; it was just an attempt.
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
System.out.println("jdk.lang.Process.launchMechanism=" +
|
||||
System.getProperty("jdk.lang.Process.launchMechanism"));
|
||||
|
||||
System.loadLibrary("PipesCloseOnExec");
|
||||
|
||||
if (!startTester()) {
|
||||
throw new RuntimeException("Failed to start testers (see stdout)");
|
||||
}
|
||||
|
||||
System.out.println(LocalTime.now() + ": Call ProcessBuilder.start...");
|
||||
|
||||
for (int i = 0; i < num_tries; i ++) {
|
||||
ProcessBuilder pb = new ProcessBuilder("true").inheritIO();
|
||||
new OutputAnalyzer(pb.start()).shouldHaveExitValue(0);
|
||||
}
|
||||
|
||||
if (!stopTester()) {
|
||||
printOpenFileDescriptors();
|
||||
throw new RuntimeException("Catched FDs without CLOEXEC? Check output.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026, IBM Corp.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <jni.h>
|
||||
#include "testlib_thread_barriers.h"
|
||||
|
||||
static void trc(const char* fmt, ...) {
|
||||
char buf [1024];
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
printf("%s\n", buf);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* Set 1 to restrict this test to pipes, 0 to test all file descriptors.
|
||||
* (for now, we ignore regular files opened with CLOEXEC since loaded jars seem not tagged as CLOEXEc.
|
||||
* We should probably fix that eventually. */
|
||||
#define TEST_PIPES_ONLY 1
|
||||
|
||||
/* stdin/out/err file descriptors are usually not CLOEXEC */
|
||||
#define IGNORE_BELOW 4
|
||||
|
||||
/* Only query file descriptors up to this point */
|
||||
#define MAX_FD 1024
|
||||
|
||||
static pthread_t tid_tester;
|
||||
|
||||
static pthread_barrier_t start_barrier;
|
||||
static atomic_bool stop_now = false;
|
||||
|
||||
/* Mainly to prevent tracing the same fd over and over again:
|
||||
* 1 - present, 2 - present, cloexec */
|
||||
static unsigned fd_state[MAX_FD];
|
||||
|
||||
static bool is_pipe(int fd) {
|
||||
struct stat mystat;
|
||||
if (fstat(fd, &mystat) == -1) {
|
||||
return false;
|
||||
}
|
||||
return mystat.st_mode & S_IFIFO;
|
||||
}
|
||||
|
||||
static void print_fd_details(int fd, char* out, size_t outlen) {
|
||||
const char* type = "unknown";
|
||||
char link[1024] = { 0 };
|
||||
char procfd[129];
|
||||
struct stat mystat;
|
||||
|
||||
out[0] = '\0';
|
||||
|
||||
if (fstat(fd, &mystat) == -1) {
|
||||
snprintf(out, outlen, "%s", errno == EBADF ? "EBADF" : "???");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mystat.st_mode & S_IFMT) {
|
||||
case S_IFBLK: type = "blk"; break;
|
||||
case S_IFCHR: type = "char"; break;
|
||||
case S_IFDIR: type = "dir"; break;
|
||||
case S_IFIFO: type = "fifo"; break;
|
||||
case S_IFLNK: type = "lnk"; break;
|
||||
case S_IFREG: type = "reg"; break;
|
||||
case S_IFSOCK: type = "sock"; break;
|
||||
}
|
||||
|
||||
snprintf(procfd, sizeof(procfd) - 1, "/proc/self/fd/%d", fd);
|
||||
int linklen = readlink(procfd, link, sizeof(link) - 1);
|
||||
if (linklen > 0) {
|
||||
link[linklen] = '\0';
|
||||
snprintf(out, outlen, "%s (%s)", type, link);
|
||||
} else {
|
||||
snprintf(out, outlen, "%s", type);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true for error */
|
||||
static bool testFD(int fd) {
|
||||
|
||||
int rc = fcntl(fd, F_GETFD);
|
||||
if (rc == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool has_cloexec = (rc & FD_CLOEXEC);
|
||||
const bool is_a_pipe = is_pipe(fd);
|
||||
const unsigned state = has_cloexec ? 2 : 1;
|
||||
bool had_error = false;
|
||||
|
||||
if (fd_state[fd] != state) {
|
||||
fd_state[fd] = state;
|
||||
char buf[1024];
|
||||
print_fd_details(fd, buf, sizeof(buf));
|
||||
if (has_cloexec) {
|
||||
trc("%d: %s", fd, buf);
|
||||
} else {
|
||||
if (fd < IGNORE_BELOW) {
|
||||
trc("%d: %s ** CLOEXEC MISSING ** (ignored - below scanned range)", fd, buf);
|
||||
} else if (TEST_PIPES_ONLY && !is_a_pipe) {
|
||||
trc("%d: %s ** CLOEXEC MISSING ** (ignored - not a pipe)", fd, buf);
|
||||
} else {
|
||||
trc("%d: %s ** CLOEXEC MISSING ** (ERROR)", fd, buf);
|
||||
had_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return had_error;
|
||||
}
|
||||
|
||||
static void* testerLoop(void* dummy) {
|
||||
|
||||
pthread_barrier_wait(&start_barrier);
|
||||
|
||||
trc("Tester is alive");
|
||||
|
||||
bool had_error = false;
|
||||
|
||||
while (!atomic_load(&stop_now)) {
|
||||
for (int fd = 0; fd < MAX_FD; fd++) {
|
||||
bool rc = testFD(fd);
|
||||
had_error = had_error || rc;
|
||||
}
|
||||
}
|
||||
|
||||
trc("Tester dies");
|
||||
|
||||
return had_error ? (void*)1 : NULL;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_PipesCloseOnExecTest_startTester(JNIEnv* env, jclass cls)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
int rc = 0;
|
||||
|
||||
if (pthread_barrier_init(&start_barrier, NULL, 2) != 0) {
|
||||
trc("pthread_barrier_init failed (%d)", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
if (pthread_create(&tid_tester, &attr, testerLoop, NULL) != 0) {
|
||||
trc("pthread_create failed (%d)", errno);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
pthread_barrier_wait(&start_barrier);
|
||||
|
||||
trc("Started tester");
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_PipesCloseOnExecTest_stopTester(JNIEnv* env, jclass cls)
|
||||
{
|
||||
atomic_store(&stop_now, true);
|
||||
|
||||
void* retval = NULL;
|
||||
pthread_join(tid_tester, &retval);
|
||||
pthread_barrier_destroy(&start_barrier);
|
||||
|
||||
return retval == NULL ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
############################################################
|
||||
# Enable logging java.lang.ProcessBuilder to the console
|
||||
############################################################
|
||||
|
||||
handlers= java.util.logging.ConsoleHandler
|
||||
|
||||
java.util.logging.ConsoleHandler.level = ALL
|
||||
java.lang.ProcessBuilder.level = FINE
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
############################################################
|
||||
# Enable logging java.lang.ProcessBuilder to the console
|
||||
############################################################
|
||||
|
||||
handlers= java.util.logging.ConsoleHandler
|
||||
|
||||
java.util.logging.ConsoleHandler.level = ALL
|
||||
java.lang.ProcessBuilder.level = FINER
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
############################################################
|
||||
# Enable logging java.lang.ProcessBuilder to the console
|
||||
############################################################
|
||||
|
||||
handlers= java.util.logging.ConsoleHandler
|
||||
|
||||
java.util.logging.ConsoleHandler.level = ALL
|
||||
java.lang.ProcessBuilder.level = INFO
|
||||
64
test/jdk/java/lang/ProcessBuilder/ProcessReaperCCL.java
Normal file
64
test/jdk/java/lang/ProcessBuilder/ProcessReaperCCL.java
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary verify that Process Reaper threads have a null CCL
|
||||
* @bug 8269488
|
||||
* @run junit ProcessReaperCCL
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ProcessReaperCCL {
|
||||
|
||||
@Test
|
||||
void test() throws Exception {
|
||||
// create a class loader
|
||||
File dir = new File(".");
|
||||
URL[] urls = new URL[] {dir.toURI().toURL()};
|
||||
ClassLoader cl = new URLClassLoader(urls);
|
||||
Thread.currentThread().setContextClassLoader(cl);
|
||||
|
||||
// Invoke a subprocess with processBuilder
|
||||
ProcessBuilder pb = new ProcessBuilder(List.of("echo", "abc", "xyz"));
|
||||
Process p = pb.start();
|
||||
CompletableFuture<Process> cf = p.onExit();
|
||||
int exitValue = cf.get().exitValue();
|
||||
Assertions.assertEquals(0, exitValue, "error exit value");
|
||||
|
||||
// Verify all "Process Reaper" threads have a null CCL
|
||||
for (Thread th : Thread.getAllStackTraces().keySet()) {
|
||||
if (th.getName().startsWith("process reaper")) {
|
||||
Assertions.assertNull(th.getContextClassLoader(), "CCL not null");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
189
test/jdk/java/lang/ProcessBuilder/ProcessStartLoggingTest.java
Normal file
189
test/jdk/java/lang/ProcessBuilder/ProcessStartLoggingTest.java
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.logging.StreamHandler;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary verify logging of ProcessBuilder.start()
|
||||
* @requires vm.flagless
|
||||
* @run junit/othervm ProcessStartLoggingTest
|
||||
*/
|
||||
public class ProcessStartLoggingTest {
|
||||
|
||||
private final static int NORMAL_STATUS = 0;
|
||||
private final static int ERROR_STATUS = 1;
|
||||
|
||||
private static final String TEST_JDK = System.getProperty("test.jdk");
|
||||
private static final String TEST_SRC = System.getProperty("test.src");
|
||||
|
||||
private static Object HOLD_LOGGER;
|
||||
|
||||
/**
|
||||
* Launch a process with the arguments.
|
||||
* @param args 1 or strings passed directly to ProcessBuilder as command and arguments.
|
||||
*/
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
if (System.getProperty("ThrowingHandler") != null) {
|
||||
HOLD_LOGGER = ProcessStartLoggingTest.ThrowingHandler.installHandler();
|
||||
}
|
||||
String directory = System.getProperty("directory");
|
||||
try {
|
||||
ProcessBuilder pb = new ProcessBuilder(args);
|
||||
pb.directory((directory == null) ? null : new File(directory));
|
||||
Process p = pb.start();
|
||||
int status = p.waitFor();
|
||||
if (status != 0) {
|
||||
System.out.println("exitValue: " + status);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
System.out.println("ProcessBuilder.start() threw IOException: " + ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test various log level settings, and none.
|
||||
* @return a stream of arguments for parameterized test
|
||||
*/
|
||||
private static Stream<Arguments> logParamProvider() {
|
||||
|
||||
return Stream.of(
|
||||
// Logging enabled with level TRACE
|
||||
Arguments.of(List.of("-Djava.util.logging.config.file=" +
|
||||
Path.of(TEST_SRC, "ProcessLogging-FINER.properties").toString(),
|
||||
"-Ddirectory=."),
|
||||
List.of("echo", "echo0"),
|
||||
NORMAL_STATUS,
|
||||
"dir: ., cmd: \"echo\" \"echo0\""),
|
||||
// Logging enabled with level DEBUG
|
||||
Arguments.of(List.of("-Djava.util.logging.config.file=" +
|
||||
Path.of(TEST_SRC, "ProcessLogging-FINE.properties").toString(),
|
||||
"-Ddirectory=."),
|
||||
List.of("echo", "echo1"),
|
||||
NORMAL_STATUS,
|
||||
"dir: ., cmd: \"echo\""),
|
||||
// Logging disabled due to level INFO
|
||||
Arguments.of(List.of("-Djava.util.logging.config.file=" +
|
||||
Path.of(TEST_SRC, "ProcessLogging-INFO.properties").toString()),
|
||||
List.of("echo", "echo2"),
|
||||
NORMAL_STATUS,
|
||||
""),
|
||||
// Console logger DEBUG
|
||||
Arguments.of(List.of("--limit-modules", "java.base",
|
||||
"-Djdk.system.logger.level=DEBUG"),
|
||||
List.of("echo", "echo3"),
|
||||
NORMAL_STATUS,
|
||||
"dir: null, cmd: \"echo\""),
|
||||
// Console logger TRACE
|
||||
Arguments.of(List.of("--limit-modules", "java.base",
|
||||
"-Djdk.system.logger.level=TRACE",
|
||||
"-Ddirectory=."),
|
||||
List.of("echo", "echo4"),
|
||||
NORMAL_STATUS,
|
||||
"dir: ., cmd: \"echo\" \"echo4\""),
|
||||
// No Logging configured
|
||||
Arguments.of(List.of(),
|
||||
List.of("echo", "echo5"),
|
||||
NORMAL_STATUS,
|
||||
""),
|
||||
// Throwing Handler
|
||||
Arguments.of(List.of("-DThrowingHandler",
|
||||
"-Djava.util.logging.config.file=" +
|
||||
Path.of(TEST_SRC, "ProcessLogging-FINE.properties").toString()),
|
||||
List.of("echo", "echo6"),
|
||||
ERROR_STATUS,
|
||||
"Exception in thread \"main\" java.lang.RuntimeException: Exception in publish")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the logger output of a launched process contains the expected message.
|
||||
*
|
||||
* @param logArgs Arguments to configure logging in the java test process
|
||||
* @param childArgs the args passed to the child to be invoked as a Process
|
||||
* @param expectMessage log should contain the message
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("logParamProvider")
|
||||
public void checkLogger(List<String> logArgs, List<String> childArgs,
|
||||
int expectedStatus, String expectMessage) {
|
||||
ProcessBuilder pb = new ProcessBuilder();
|
||||
pb.redirectErrorStream(true);
|
||||
|
||||
List<String> cmd = pb.command();
|
||||
cmd.add(Path.of(TEST_JDK,"bin", "java").toString());
|
||||
cmd.addAll(logArgs);
|
||||
cmd.add(this.getClass().getName());
|
||||
cmd.addAll(childArgs);
|
||||
try {
|
||||
Process process = pb.start();
|
||||
try (BufferedReader reader = process.inputReader()) {
|
||||
List<String> lines = reader.lines().toList();
|
||||
boolean match = (expectMessage.isEmpty())
|
||||
? lines.size() == 0
|
||||
: lines.stream().filter(s -> s.contains(expectMessage)).findFirst().isPresent();
|
||||
if (!match) {
|
||||
// Output lines for debug
|
||||
System.err.println("Expected> \"" + expectMessage + "\"");
|
||||
lines.forEach(l -> System.err.println("Actual> \"" + l+ "\""));
|
||||
fail("Unexpected log contents");
|
||||
}
|
||||
}
|
||||
int result = process.waitFor();
|
||||
assertEquals(expectedStatus, result, "Exit status");
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
fail(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A LoggingHandler that throws an Exception.
|
||||
*/
|
||||
public static class ThrowingHandler extends StreamHandler {
|
||||
|
||||
// Install this handler for java.lang.ProcessBuilder
|
||||
public static Logger installHandler() {
|
||||
Logger logger = Logger.getLogger("java.lang.ProcessBuilder");
|
||||
logger.addHandler(new ProcessStartLoggingTest.ThrowingHandler());
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void publish(LogRecord record) {
|
||||
throw new RuntimeException("Exception in publish");
|
||||
}
|
||||
}
|
||||
}
|
||||
365
test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java
Normal file
365
test/jdk/java/lang/ProcessBuilder/ReaderWriterTest.java
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jtreg.SkippedException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @requires vm.flagless
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.process.ProcessTools
|
||||
* @run junit ReaderWriterTest
|
||||
*/
|
||||
|
||||
public class ReaderWriterTest {
|
||||
|
||||
static final String ASCII = "ASCII: \u0000_A-Z_a-Z_\u007C_\u007D_\u007E_\u007F_;";
|
||||
static final String ISO_8859_1 = " Symbols: \u00AB_\u00BB_\u00fc_\u00fd_\u00fe_\u00ff;";
|
||||
static final String FRACTIONS = " Fractions: \u00bc_\u00bd_\u00be_\u00bf;";
|
||||
|
||||
public static final String TESTCHARS = "OneWay: " + ASCII + ISO_8859_1 + FRACTIONS;
|
||||
public static final String ROUND_TRIP_TESTCHARS = "RoundTrip: " + ASCII + ISO_8859_1 + FRACTIONS;
|
||||
|
||||
static Stream<Arguments> charsetCases() {
|
||||
return Stream.of(
|
||||
Arguments.of("UTF-8"),
|
||||
Arguments.of("ISO8859-1"),
|
||||
Arguments.of("US-ASCII")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the defaults case of native.encoding. No extra command line flags or switches.
|
||||
*/
|
||||
@Test
|
||||
void testCaseNativeEncoding() throws IOException {
|
||||
String nativeEncoding = System.getProperty("native.encoding");
|
||||
Charset cs = Charset.forName(nativeEncoding);
|
||||
System.out.println("Native.encoding Charset: " + cs);
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("ReaderWriterTest$ChildWithCharset");
|
||||
Process p = pb.start();
|
||||
writeTestChars(p.outputWriter());
|
||||
checkReader(p.inputReader(), cs, "Out");
|
||||
checkReader(p.errorReader(), cs, "Err");
|
||||
try {
|
||||
int exitValue = p.waitFor();
|
||||
if (exitValue != 0)
|
||||
System.out.println("exitValue: " + exitValue);
|
||||
} catch (InterruptedException ie) {
|
||||
fail("waitFor interrupted");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that redirects of input and error streams result in Readers that are empty.
|
||||
* Test that when the output to a process is redirected, the writer acts as
|
||||
* a null stream and throws an exception as expected for a null output stream
|
||||
* as specified by ProcessBuilder.
|
||||
*/
|
||||
@Test
|
||||
void testRedirects() throws IOException {
|
||||
String nativeEncoding = System.getProperty("native.encoding");
|
||||
Charset cs = Charset.forName(nativeEncoding);
|
||||
System.out.println("Native.encoding Charset: " + cs);
|
||||
|
||||
Path inPath = Path.of("InFile.tmp");
|
||||
BufferedWriter inWriter = Files.newBufferedWriter(inPath);
|
||||
inWriter.close();
|
||||
|
||||
Path outPath = Path.of("OutFile.tmp");
|
||||
Path errorPath = Path.of("ErrFile.tmp");
|
||||
|
||||
for (int errType = 1; errType < 4; errType++) {
|
||||
// Three cases to test for which the error stream is empty
|
||||
// 1: redirectErrorStream(false); redirect of errorOutput to a file
|
||||
// 2: redirectErrorStream(true); no redirect of errorOutput
|
||||
// 3: redirectErrorStream(true); redirect of errorOutput to a file
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder("ReaderWriterTest$ChildWithCharset");
|
||||
pb.redirectInput(inPath.toFile());
|
||||
pb.redirectOutput(outPath.toFile());
|
||||
if (errType == 1 || errType == 3) {
|
||||
pb.redirectError(errorPath.toFile());
|
||||
}
|
||||
if (errType == 2 || errType == 3) {
|
||||
pb.redirectErrorStream(true);
|
||||
}
|
||||
try (Process p = pb.start()) {
|
||||
// Output has been redirected to a null stream; success is IOException on the write
|
||||
BufferedWriter wr = p.outputWriter();
|
||||
assertThrows(IOException.class, () -> {
|
||||
wr.write("X");
|
||||
wr.flush();
|
||||
});
|
||||
|
||||
// InputReader should be empty; and at EOF
|
||||
BufferedReader inputReader = p.inputReader();
|
||||
int ch = inputReader.read();
|
||||
assertEquals(-1, ch, "inputReader not at EOF: ch: " + (char) ch);
|
||||
|
||||
// InputReader should be empty; and at EOF
|
||||
BufferedReader errorReader = p.errorReader();
|
||||
ch = errorReader.read();
|
||||
assertEquals(-1, ch, "errorReader not at EOF: ch: " + (char) ch);
|
||||
|
||||
int exitValue = p.waitFor();
|
||||
if (exitValue != 0) System.out.println("exitValue: " + exitValue);
|
||||
} catch (InterruptedException ie) {
|
||||
fail("waitFor interrupted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the test characters to the child using the Process.outputWriter.
|
||||
* @param writer the Writer
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
private static void writeTestChars(Writer writer) throws IOException {
|
||||
// Write the test data to the child
|
||||
try (writer) {
|
||||
writer.append(ROUND_TRIP_TESTCHARS);
|
||||
writer.append(System.lineSeparator());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a child with a character set.
|
||||
* A Process is spawned; characters are written to and read from the child
|
||||
* using the character set and compared.
|
||||
*
|
||||
* @param encoding a charset name
|
||||
*/
|
||||
@ParameterizedTest
|
||||
@MethodSource("charsetCases")
|
||||
void testCase(String encoding) throws IOException {
|
||||
Charset cs = null;
|
||||
try {
|
||||
cs = Charset.forName(encoding);
|
||||
System.out.println("Charset: " + cs);
|
||||
} catch (UnsupportedCharsetException use) {
|
||||
throw new SkippedException("Charset not supported: " + encoding);
|
||||
}
|
||||
String cleanCSName = cleanCharsetName(cs);
|
||||
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"-Dsun.stdout.encoding=" + cleanCSName, // Encode in the child using the charset
|
||||
"-Dsun.stderr.encoding=" + cleanCSName,
|
||||
"ReaderWriterTest$ChildWithCharset");
|
||||
|
||||
Process p = pb.start();
|
||||
// Write the test data to the child
|
||||
writeTestChars(p.outputWriter(cs));
|
||||
checkReader(p.inputReader(cs), cs, "Out");
|
||||
checkReader(p.errorReader(cs), cs, "Err");
|
||||
try {
|
||||
int exitValue = p.waitFor();
|
||||
if (exitValue != 0)
|
||||
System.out.println("exitValue: " + exitValue);
|
||||
} catch (InterruptedException ie) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test passing null when a charset is expected
|
||||
* @throws IOException if an I/O error occurs; not expected
|
||||
*/
|
||||
@Test
|
||||
void testNullCharsets() throws IOException {
|
||||
// Launch a child; its behavior is not interesting and is ignored
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"ReaderWriterTest$ChildWithCharset");
|
||||
|
||||
try (Process p = pb.start()) {
|
||||
assertThrows(NullPointerException.class, () -> writeTestChars(p.outputWriter(null)));
|
||||
assertThrows(NullPointerException.class, () -> checkReader(p.inputReader(null), null, "Out"));
|
||||
assertThrows(NullPointerException.class, () -> checkReader(p.errorReader(null), null, "Err"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test passing different charset on multiple calls when the same charset is expected.
|
||||
* @throws IOException if an I/O error occurs; not expected
|
||||
*/
|
||||
@Test
|
||||
void testIllegalArgCharsets() throws IOException {
|
||||
String nativeEncoding = System.getProperty("native.encoding");
|
||||
Charset cs = Charset.forName(nativeEncoding);
|
||||
System.out.println("Native.encoding Charset: " + cs);
|
||||
Charset otherCharset = cs.equals(StandardCharsets.UTF_8)
|
||||
? StandardCharsets.ISO_8859_1
|
||||
: StandardCharsets.UTF_8;
|
||||
|
||||
// Launch a child; its behavior is not interesting and is ignored
|
||||
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
|
||||
"ReaderWriterTest$ChildWithCharset");
|
||||
|
||||
Process p = pb.start();
|
||||
try {
|
||||
var writer = p.outputWriter(cs);
|
||||
writer = p.outputWriter(cs); // try again with same
|
||||
writer = p.outputWriter(otherCharset); // this should throw
|
||||
fail("Process.outputWriter(otherCharset) did not throw IllegalStateException");
|
||||
} catch (IllegalStateException ile) {
|
||||
// expected, ignore
|
||||
System.out.println(ile);
|
||||
}
|
||||
try {
|
||||
var reader = p.inputReader(cs);
|
||||
reader = p.inputReader(cs); // try again with same
|
||||
reader = p.inputReader(otherCharset); // this should throw
|
||||
fail("Process.inputReader(otherCharset) did not throw IllegalStateException");
|
||||
} catch (IllegalStateException ile) {
|
||||
// expected, ignore
|
||||
System.out.println(ile);
|
||||
}
|
||||
try {
|
||||
var reader = p.errorReader(cs);
|
||||
reader = p.errorReader(cs); // try again with same
|
||||
reader = p.errorReader(otherCharset); // this should throw
|
||||
fail("Process.errorReader(otherCharset) did not throw IllegalStateException");
|
||||
} catch (IllegalStateException ile) {
|
||||
// expected, ignore
|
||||
System.out.println(ile);
|
||||
}
|
||||
|
||||
p.destroyForcibly();
|
||||
try {
|
||||
// Collect the exit status to cleanup after the process; but ignore it
|
||||
p.waitFor();
|
||||
} catch (InterruptedException ie) {
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkReader(BufferedReader reader, Charset cs, String label) throws IOException {
|
||||
try (BufferedReader in = reader) {
|
||||
String prefix = " " + label + ": ";
|
||||
String firstline = in.readLine();
|
||||
System.out.append(prefix).println(firstline);
|
||||
String secondline = in.readLine();
|
||||
System.out.append(prefix).println(secondline);
|
||||
for (String line = in.readLine(); line != null; line = in.readLine()) {
|
||||
System.out.append(prefix).append(line);
|
||||
System.out.println();
|
||||
}
|
||||
ByteBuffer bb = cs.encode(TESTCHARS);
|
||||
String reencoded = cs.decode(bb).toString();
|
||||
if (!firstline.equals(reencoded))
|
||||
diffStrings(firstline, reencoded);
|
||||
assertEquals(reencoded, firstline, label + " Test Chars");
|
||||
|
||||
bb = cs.encode(ROUND_TRIP_TESTCHARS);
|
||||
reencoded = cs.decode(bb).toString();
|
||||
if (!secondline.equals(reencoded))
|
||||
diffStrings(secondline, reencoded);
|
||||
assertEquals(reencoded, secondline, label + " Round Trip Test Chars");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cleaned up Charset name that is suitable for Linux LANG environment variable.
|
||||
* If there are two '-'s the first one is removed.
|
||||
* @param cs a Charset
|
||||
* @return the cleanedup Charset name
|
||||
*/
|
||||
private static String cleanCharsetName(Charset cs) {
|
||||
String name = cs.name();
|
||||
int ndx = name.indexOf('-');
|
||||
if (ndx >= 0 && name.indexOf('-', ndx + 1) >= 0) {
|
||||
name = name.substring(0, ndx) + name.substring(ndx + 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private static void diffStrings(String actual, String expected) {
|
||||
if (actual.equals(expected))
|
||||
return;
|
||||
int lenDiff = expected.length() - actual.length();
|
||||
if (lenDiff != 0)
|
||||
System.out.println("String lengths: " + actual.length() + " != " + expected.length());
|
||||
int first; // find first mismatched character
|
||||
for (first = 0; first < Math.min(actual.length(), expected.length()); first++) {
|
||||
if (actual.charAt(first) != expected.charAt(first))
|
||||
break;
|
||||
}
|
||||
int last;
|
||||
for (last = actual.length() - 1; last >= 0 && (last + lenDiff) >= 0; last--) {
|
||||
if (actual.charAt(last) != expected.charAt(last + lenDiff))
|
||||
break; // last mismatched character
|
||||
}
|
||||
System.out.printf("actual vs expected[%3d]: 0x%04x != 0x%04x%n", first, (int)actual.charAt(first), (int)expected.charAt(first));
|
||||
System.out.printf("actual vs expected[%3d]: 0x%04x != 0x%04x%n", last, (int)actual.charAt(last), (int)expected.charAt(last));
|
||||
System.out.printf("actual [%3d-%3d]: %s%n", first, last, actual.substring(first, last+1));
|
||||
System.out.printf("expected[%3d-%3d]: %s%n", first, last, expected.substring(first, last + lenDiff + 1));
|
||||
}
|
||||
|
||||
static class ChildWithCharset {
|
||||
public static void main(String[] args) {
|
||||
String nativeEncoding = System.getProperty("native.encoding");
|
||||
System.out.println(TESTCHARS);
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
bytes = System.in.readAllBytes();
|
||||
System.out.write(bytes); // echo bytes back to parent on stdout
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace(); // Seen by the parent
|
||||
}
|
||||
System.out.println("native.encoding: " + nativeEncoding);
|
||||
System.out.println("sun.stdout.encoding: " + System.getProperty("sun.stdout.encoding"));
|
||||
System.out.println("LANG: " + System.getenv().get("LANG"));
|
||||
|
||||
System.err.println(TESTCHARS);
|
||||
try {
|
||||
System.err.write(bytes); // echo bytes back to parent on stderr
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace(); // Seen by the parent
|
||||
}
|
||||
System.err.println("native.encoding: " + nativeEncoding);
|
||||
System.err.println("sun.stderr.encoding: " + System.getProperty("sun.stderr.encoding"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2015 SAP SE. 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8072611
|
||||
* @requires (os.family == "windows")
|
||||
* @summary ProcessBuilder Redirect to file appending on Windows should work with long file names
|
||||
* @author Thomas Stuefe
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ProcessBuilder.Redirect;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class RedirectWithLongFilename {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
// Redirect ProcessBuilder output to a file whose pathlen is > 255.
|
||||
Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir"));
|
||||
File dir2 = null;
|
||||
File longFileName = null;
|
||||
|
||||
try {
|
||||
dir2 = Files.createTempDirectory(tmpDir, "RedirectWithLongFilename").toFile();
|
||||
dir2.mkdirs();
|
||||
longFileName = new File(dir2,
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"012345678901234567890123456789012345678901234567890123456789" +
|
||||
"0123456789");
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder("hostname.exe");
|
||||
pb.redirectOutput(Redirect.appendTo(longFileName));
|
||||
Process p = pb.start();
|
||||
p.waitFor();
|
||||
|
||||
if (longFileName.exists()) {
|
||||
System.out.println("OK");
|
||||
} else {
|
||||
throw new RuntimeException("Test failed.");
|
||||
}
|
||||
|
||||
} finally {
|
||||
longFileName.delete();
|
||||
dir2.delete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
228
test/jdk/java/lang/ProcessBuilder/SiblingIOEHandle.java
Normal file
228
test/jdk/java/lang/ProcessBuilder/SiblingIOEHandle.java
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 6921885
|
||||
* @run main/othervm SiblingIOEHandle
|
||||
* @summary inherit IOE handles and MS CreateProcess limitations (kb315939)
|
||||
* @requires (os.family == "windows")
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
|
||||
public class SiblingIOEHandle {
|
||||
private static enum APP {
|
||||
A, B, C;
|
||||
}
|
||||
|
||||
private static File stopC = new File(".\\StopCs.txt");
|
||||
private static String SIGNAL = "B child reported.";
|
||||
private static String JAVA_EXE = System.getProperty("java.home")
|
||||
+ File.separator + "bin"
|
||||
+ File.separator + "java";
|
||||
|
||||
private static String[] getCommandArray(String processName) {
|
||||
String[] cmdArray = {
|
||||
JAVA_EXE,
|
||||
"-cp",
|
||||
System.getProperty("java.class.path"),
|
||||
SiblingIOEHandle.class.getName(),
|
||||
processName
|
||||
};
|
||||
return cmdArray;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
APP app = (args.length > 0) ? APP.valueOf(args[0]) : APP.A;
|
||||
switch (app) {
|
||||
case A:
|
||||
performA(true);
|
||||
performA(false);
|
||||
break;
|
||||
case B:
|
||||
performB();
|
||||
break;
|
||||
case C:
|
||||
performC();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean procClaunched = false;
|
||||
|
||||
private static void waitAbit() {
|
||||
try {
|
||||
Thread.sleep(0);
|
||||
} catch (InterruptedException ex) {
|
||||
// that was long enough
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean waitBarrier(CyclicBarrier barrier) {
|
||||
while (true) try {
|
||||
barrier.await();
|
||||
return true;
|
||||
} catch (InterruptedException ex) {
|
||||
continue;
|
||||
} catch (BrokenBarrierException ex) {
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ProcessC implements Runnable {
|
||||
private CyclicBarrier barrier;
|
||||
private Process processC;
|
||||
|
||||
public ProcessC(CyclicBarrier barrier) {
|
||||
this.barrier = barrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (waitBarrier(barrier)) {
|
||||
waitAbit();
|
||||
// Run process C next to B ASAP to make an attempt
|
||||
// to capture the B-process IOE handles in C process.
|
||||
ProcessBuilder builderC = new ProcessBuilder(
|
||||
getCommandArray(APP.C.name()));
|
||||
processC = builderC.start();
|
||||
procClaunched = true;
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void waitFor() throws InterruptedException {
|
||||
processC.waitFor();
|
||||
}
|
||||
}
|
||||
|
||||
private static void performA(boolean fileOut) {
|
||||
try {
|
||||
stopC.delete();
|
||||
ProcessBuilder builderB = new ProcessBuilder(
|
||||
getCommandArray(APP.B.name()));
|
||||
|
||||
File outB = null;
|
||||
if (fileOut) {
|
||||
outB = new File("outB.txt");
|
||||
builderB.redirectOutput(outB);
|
||||
}
|
||||
builderB.redirectErrorStream(true);
|
||||
|
||||
final CyclicBarrier barrier = new CyclicBarrier(2);
|
||||
//Create process C in a new thread
|
||||
ProcessC processC = new ProcessC(barrier);
|
||||
Thread procCRunner = new Thread(processC);
|
||||
procCRunner.start();
|
||||
|
||||
if (!waitBarrier(barrier)) {
|
||||
throw new RuntimeException("Catastrophe in process A! Synchronization failed.");
|
||||
}
|
||||
// Run process B first.
|
||||
Process processB = builderB.start();
|
||||
|
||||
while (true) try {
|
||||
procCRunner.join();
|
||||
break;
|
||||
} catch (InterruptedException ex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!procClaunched) {
|
||||
throw new RuntimeException("Catastrophe in process A! C was not launched.");
|
||||
}
|
||||
|
||||
processB.getOutputStream().close();
|
||||
processB.getErrorStream().close();
|
||||
|
||||
if (fileOut) {
|
||||
try {
|
||||
processB.waitFor();
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException("Catastrophe in process B! B hung up.");
|
||||
}
|
||||
System.err.println("Trying to delete [outB.txt].");
|
||||
if (!outB.delete()) {
|
||||
throw new RuntimeException("Greedy brother C deadlock! File share.");
|
||||
}
|
||||
System.err.println("Succeeded in delete [outB.txt].");
|
||||
} else {
|
||||
System.err.println("Read stream start.");
|
||||
boolean isSignalReceived = false;
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||
processB.getInputStream(), "utf-8"))) {
|
||||
String result;
|
||||
while ((result = in.readLine()) != null) {
|
||||
if (SIGNAL.equals(result)) {
|
||||
isSignalReceived = true;
|
||||
} else {
|
||||
throw new RuntimeException("Catastrophe in process B! Bad output.");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isSignalReceived) {
|
||||
throw new RuntimeException("Signal from B was not received");
|
||||
}
|
||||
System.err.println("Read stream finished.");
|
||||
}
|
||||
// If JDK-6921885 is not fixed that point is unreachable.
|
||||
// Test timeout exception.
|
||||
|
||||
// write signal file to stop C process.
|
||||
stopC.createNewFile();
|
||||
processC.waitFor();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Catastrophe in process A!", ex);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException("Process A was interrupted while waiting for C", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static void performB() {
|
||||
System.out.println(SIGNAL);
|
||||
}
|
||||
|
||||
private static void performC() {
|
||||
// If JDK-7147084 is not fixed the loop is 5min long.
|
||||
for (int i = 0; i < 5 * 60; ++i) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
// check for sucess
|
||||
if (stopC.exists())
|
||||
break;
|
||||
} catch (InterruptedException ex) {
|
||||
// that is ok. Longer sleep - better effect.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
176
test/jdk/java/lang/ProcessBuilder/SkipTest.java
Normal file
176
test/jdk/java/lang/ProcessBuilder/SkipTest.java
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2023, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8155808
|
||||
* @requires vm.flagless
|
||||
* @run main SkipTest
|
||||
* @summary verify skip method of Process Input Stream
|
||||
*/
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class SkipTest {
|
||||
private static final String javaExe =
|
||||
System.getProperty("java.home") +
|
||||
File.separator + "bin" + File.separator + "java";
|
||||
|
||||
private static final String classpath =
|
||||
System.getProperty("java.class.path");
|
||||
|
||||
static final int BLOCK_SIZE = 10000;
|
||||
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
|
||||
// Start a Process to generate the test data to stdout and stderr
|
||||
ProcessBuilder pb = new ProcessBuilder(javaExe, "-classpath", classpath, "SkipTest$GenerateData");
|
||||
System.out.printf("cmd: %s%n", pb.command());
|
||||
Process process = pb.start();
|
||||
|
||||
/*
|
||||
* Verify the data expected is received mixing reads and skip.
|
||||
*/
|
||||
try (InputStream in = process.getInputStream()) {
|
||||
|
||||
// Note: Process.getInputStream() actually returns a BufferedInputStream whose
|
||||
// skip() method works perfectly, partially obscuring the bug. Only when the
|
||||
// BufferedInputStream's buffer is drained, and it passes the skip() call to
|
||||
// the underlying native stream, does the problem occur.
|
||||
|
||||
long n = in.skip(-1);
|
||||
if (n != 0) {
|
||||
throw new AssertionError("skip(-1) should return 0");
|
||||
}
|
||||
n = in.skip(0);
|
||||
if (n != 0) {
|
||||
throw new AssertionError("skip(0) should return 0");
|
||||
}
|
||||
|
||||
// Now iterate all the data blocks
|
||||
int header;
|
||||
for (int expectedHeader = 'A'; (header = in.read()) != -1; expectedHeader++) {
|
||||
// The header byte should be simple 'A' to 'Z'.
|
||||
// When the bug hits, we will get lowercase letters instead.
|
||||
if (header != expectedHeader) {
|
||||
throw new AssertionError("header char wrong, expected: " +
|
||||
expectedHeader + ", actual: " + header);
|
||||
}
|
||||
|
||||
// Handle the data bytes.
|
||||
// If the correct number of bytes are not skipped,
|
||||
// then subsequent reads become out-of-sync;
|
||||
int remaining = BLOCK_SIZE;
|
||||
do {
|
||||
remaining -= in.skip(remaining);
|
||||
} while (remaining != 0);
|
||||
}
|
||||
n = in.skip(1);
|
||||
if (n != 0) {
|
||||
throw new AssertionError("skip(1) at eof should return 0");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the same for the standard error stream.
|
||||
*/
|
||||
try (InputStream in = process.getErrorStream()) {
|
||||
long n = in.skip(-1);
|
||||
if (n != 0) {
|
||||
throw new AssertionError("skip(-1) should return 0");
|
||||
}
|
||||
n = in.skip(0);
|
||||
if (n != 0) {
|
||||
throw new AssertionError("skip(0) should return 0");
|
||||
}
|
||||
|
||||
// Now iterate all the data blocks
|
||||
int header;
|
||||
for (int expectedHeader = 'A'; (header = in.read()) != -1; expectedHeader++) {
|
||||
// The header byte should be simple 'A' to 'Z'.
|
||||
// When the bug hits, we will get lowercase letters instead.
|
||||
if (header != expectedHeader) {
|
||||
throw new AssertionError("header char wrong, expected: " +
|
||||
expectedHeader + ", actual: " + header);
|
||||
}
|
||||
|
||||
// Handle the data bytes.
|
||||
// If the correct number of bytes are not skipped,
|
||||
// then subsequent reads become out-of-sync;
|
||||
int remaining = BLOCK_SIZE;
|
||||
do {
|
||||
remaining -= in.skip(remaining);
|
||||
} while (remaining != 0);
|
||||
}
|
||||
n = in.skip(1);
|
||||
if (n != 0) {
|
||||
throw new AssertionError("skip(1) at eof should return 0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alternate main to generate the test data to standard output
|
||||
* and standard error.
|
||||
*/
|
||||
static class GenerateData {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Generate test data containing a series of data blocks of length BLOCK_SIZE,
|
||||
// each with a one-byte header. For example's sake, the "header" is a capital letter,
|
||||
// and the "data" is the lowercase version of that letter repeated BLOCK_SIZE times:
|
||||
try (OutputStream out = new BufferedOutputStream(System.out)) {
|
||||
for (int header = 'A'; header <= 'Z'; header++) {
|
||||
out.write(header);
|
||||
|
||||
int data = Character.toLowerCase(header);
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
out.write(data);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
// Generate the same data to the error output
|
||||
try (OutputStream err = new BufferedOutputStream(System.err)) {
|
||||
for (int header = 'A'; header <= 'Z'; header++) {
|
||||
err.write(header);
|
||||
|
||||
int data = Character.toLowerCase(header);
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
err.write(data);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
test/jdk/java/lang/ProcessBuilder/TEST.properties
Normal file
1
test/jdk/java/lang/ProcessBuilder/TEST.properties
Normal file
|
|
@ -0,0 +1 @@
|
|||
maxOutputSize=12000000
|
||||
66
test/jdk/java/lang/ProcessBuilder/UnblockSignals.java
Normal file
66
test/jdk/java/lang/ProcessBuilder/UnblockSignals.java
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* @test id=posix_spawn
|
||||
* @summary Verify Signal mask is cleared by ProcessBuilder start when using posix_spawn mode
|
||||
* @bug 8234262
|
||||
* @requires (os.family == "linux" | os.family == "mac")
|
||||
* @comment Don't allow -Xcomp, it disturbs the relative timing of the sleep and kill commands
|
||||
* @requires (vm.compMode != "Xcomp")
|
||||
* @run main/othervm/timeout=480 -Djdk.lang.Process.launchMechanism=POSIX_SPAWN UnblockSignals
|
||||
* @run main/othervm/timeout=480 -Djdk.lang.Process.launchMechanism=POSIX_SPAWN -Xrs UnblockSignals
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=fork
|
||||
* @summary Verify Signal mask is cleared by ProcessBuilder start when using fork mode
|
||||
* @bug 8357683
|
||||
* @requires (os.family == "linux" | os.family == "mac")
|
||||
* @comment Don't allow -Xcomp, it disturbs the relative timing of the sleep and kill commands
|
||||
* @requires (vm.compMode != "Xcomp")
|
||||
* @run main/othervm/timeout=480 -Djdk.lang.Process.launchMechanism=FORK UnblockSignals
|
||||
* @run main/othervm/timeout=480 -Djdk.lang.Process.launchMechanism=FORK -Xrs UnblockSignals
|
||||
*/
|
||||
|
||||
public class UnblockSignals {
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
// Check that SIGQUIT is not masked, in previous releases it was masked
|
||||
final ProcessBuilder pb = new ProcessBuilder("sleep", "30").inheritIO();
|
||||
Process p = pb.start();
|
||||
System.out.printf("Child %d, %s%n", p.pid(), pb.command());
|
||||
ProcessBuilder killpb = new ProcessBuilder("kill", "-s", "QUIT", Long.toString(p.pid()));
|
||||
Process killp = killpb.start();
|
||||
System.out.printf("Child %d, %s%n", killp.pid(), killpb.command());
|
||||
int killst = killp.waitFor();
|
||||
if (killst != 0) {
|
||||
throw new RuntimeException("Kill process failed, exit status: " + killst);
|
||||
}
|
||||
int sleepStatus = p.waitFor();
|
||||
if (sleepStatus == 0) {
|
||||
throw new RuntimeException("Child not killed");
|
||||
}
|
||||
}
|
||||
}
|
||||
45
test/jdk/java/lang/ProcessBuilder/WindowsExitValue.java
Normal file
45
test/jdk/java/lang/ProcessBuilder/WindowsExitValue.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Verify that when a child returns exit value 259, Process.waitFor does not throw
|
||||
* @bug 8294899
|
||||
* @requires (os.family == "windows")
|
||||
* @run junit WindowsExitValue
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class WindowsExitValue {
|
||||
|
||||
@Test
|
||||
public void checkExit259() throws IOException, InterruptedException {
|
||||
Process process = new ProcessBuilder("cmd", "/c", "exit /b 259").start();
|
||||
int exitValue = process.waitFor();
|
||||
assertEquals(exitValue, 259);
|
||||
}
|
||||
}
|
||||
89
test/jdk/java/lang/ProcessBuilder/Zombies.java
Normal file
89
test/jdk/java/lang/ProcessBuilder/Zombies.java
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 2017, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @run main/othervm Zombies
|
||||
* @bug 6474073 6180151
|
||||
* @summary Make sure zombies don't get created on Unix
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class Zombies {
|
||||
|
||||
static final String os = System.getProperty("os.name");
|
||||
|
||||
static final String TrueCommand = os.contains("OS X")?
|
||||
"/usr/bin/true" : "/bin/true";
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
if (! new File("/usr/bin/perl").canExecute() ||
|
||||
! new File("/bin/ps").canExecute())
|
||||
return;
|
||||
System.out.println("Looks like a Unix system.");
|
||||
long mypid = ProcessHandle.current().pid();
|
||||
System.out.printf("mypid: %d%n", mypid);
|
||||
|
||||
final Runtime rt = Runtime.getRuntime();
|
||||
|
||||
try {
|
||||
String[] cmd = {"no-such-file"};
|
||||
rt.exec(cmd);
|
||||
throw new Error("expected IOException not thrown");
|
||||
} catch (IOException expected) {/* OK */}
|
||||
|
||||
try {
|
||||
String[] cmd = {"."};
|
||||
rt.exec(cmd);
|
||||
throw new Error("expected IOException not thrown");
|
||||
} catch (IOException expected) {/* OK */}
|
||||
|
||||
try {
|
||||
String[] cmd = {TrueCommand};
|
||||
rt.exec(cmd, null, new File("no-such-dir"));
|
||||
throw new Error("expected IOException not thrown");
|
||||
} catch (IOException expected) {/* OK */}
|
||||
|
||||
Process p = rt.exec(TrueCommand);
|
||||
ProcessHandle pp = p.toHandle().parent().orElse(null);
|
||||
System.out.printf("%s pid: %d, parent: %s%n", TrueCommand, p.pid(), pp);
|
||||
p.waitFor();
|
||||
|
||||
// Count all the zombies that are children of this Java process
|
||||
final String[] zombieCounter = {
|
||||
"/usr/bin/perl", "-e",
|
||||
"$a=`/bin/ps -eo ppid,pid,s,command`;" +
|
||||
"print @b=$a=~/^ *@{[getppid]} +[0-9]+ +Z.*$/mog;" +
|
||||
"exit @b"
|
||||
};
|
||||
|
||||
ProcessBuilder pb = new ProcessBuilder(zombieCounter);
|
||||
pb.inheritIO();
|
||||
int zombies = pb.start().waitFor();
|
||||
if (zombies != 0) {
|
||||
throw new Error(zombies + " zombies!");
|
||||
}
|
||||
}
|
||||
}
|
||||
142
test/jdk/java/lang/ProcessBuilder/checkHandles/CheckHandles.java
Normal file
142
test/jdk/java/lang/ProcessBuilder/checkHandles/CheckHandles.java
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.ProcessHandle;
|
||||
|
||||
import jdk.test.lib.util.FileUtils;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8239893
|
||||
* @summary Verify that handles for processes that terminate do not accumulate
|
||||
* @requires ((os.family == "windows") & (vm.compMode != "Xcomp"))
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Xint CheckHandles
|
||||
*/
|
||||
public class CheckHandles {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("mypid: " + ProcessHandle.current().pid());
|
||||
|
||||
// Warmup the process launch mechanism and vm to stabilize the number of handles in use
|
||||
int MAX_WARMUP = 20;
|
||||
long prevCount = FileUtils.getProcessHandleCount();
|
||||
for (int i = 0; i < MAX_WARMUP; i++) {
|
||||
oneProcess();
|
||||
System.gc(); // an opportunity to close unreferenced handles
|
||||
sleep(10);
|
||||
|
||||
long count = FileUtils.getProcessHandleCount();
|
||||
if (count < 0)
|
||||
throw new AssertionError("getProcessHandleCount failed");
|
||||
System.out.println("warmup handle delta: " + (count - prevCount));
|
||||
prevCount = count;
|
||||
}
|
||||
System.out.println("Warmup done");
|
||||
System.out.println();
|
||||
|
||||
prevCount = FileUtils.getProcessHandleCount();
|
||||
long startHandles = prevCount;
|
||||
long maxHandles = startHandles;
|
||||
int MAX_SPAWN = 50;
|
||||
for (int i = 0; i < MAX_SPAWN; i++) {
|
||||
oneProcess();
|
||||
System.gc(); // an opportunity to close unreferenced handles
|
||||
sleep(10);
|
||||
|
||||
long count = FileUtils.getProcessHandleCount();
|
||||
if (count < 0)
|
||||
throw new AssertionError("getProcessHandleCount failed");
|
||||
System.out.println("handle delta: " + (count - prevCount));
|
||||
prevCount = count;
|
||||
maxHandles = Math.max(maxHandles, count);
|
||||
}
|
||||
|
||||
System.out.println("Processes started: " + MAX_SPAWN);
|
||||
System.out.println("startHandles: " + startHandles);
|
||||
System.out.println("maxHandles: " + maxHandles);
|
||||
|
||||
final float ERROR_PERCENT = 10.0f; // allowable extra handles
|
||||
final long ERROR_THRESHOLD = startHandles + Math.round(startHandles * ERROR_PERCENT / 100.0f);
|
||||
if (maxHandles >= ERROR_THRESHOLD) {
|
||||
throw new AssertionError("Handle use increased by more than " + ERROR_PERCENT + " percent.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a single process and consume its output.
|
||||
*/
|
||||
private static void oneProcess() {
|
||||
try {
|
||||
|
||||
Process testProcess = new ProcessBuilder("cmd", "/c", "dir").start();
|
||||
|
||||
Thread outputConsumer = new Thread(() -> consumeStream(testProcess.getInputStream()));
|
||||
outputConsumer.setDaemon(true);
|
||||
outputConsumer.start();
|
||||
Thread errorConsumer = new Thread(() -> consumeStream(testProcess.getErrorStream()));
|
||||
errorConsumer.setDaemon(true);
|
||||
errorConsumer.start();
|
||||
|
||||
testProcess.waitFor();
|
||||
outputConsumer.join();
|
||||
errorConsumer.join();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void consumeStream(InputStream inputStream) {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
int lines = 0;
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
while (reader.readLine() != null) {
|
||||
lines++;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void sleep(long millis) {
|
||||
try {
|
||||
Thread.sleep(millis);
|
||||
} catch (InterruptedException ie) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=posix_spawn
|
||||
* @bug 8364611
|
||||
* @summary Check that childs start with SIG_DFL as SIGPIPE disposition
|
||||
* @requires os.family != "windows"
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=posix_spawn -agentlib:ChangeSignalDisposition TestChildSignalDisposition
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=fork
|
||||
* @bug 8364611
|
||||
* @summary Check that childs start with SIG_DFL as SIGPIPE disposition
|
||||
* @requires os.family != "windows"
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=fork -agentlib:ChangeSignalDisposition TestChildSignalDisposition
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test id=vfork
|
||||
* @bug 8364611
|
||||
* @summary Check that childs start with SIG_DFL as SIGPIPE disposition
|
||||
* @requires os.family == "linux"
|
||||
* @library /test/lib
|
||||
* @run main/othervm/native -Djdk.lang.Process.launchMechanism=vfork -agentlib:ChangeSignalDisposition TestChildSignalDisposition
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
public class TestChildSignalDisposition {
|
||||
// This test has two native parts:
|
||||
// - a library injected into the JVM with -agentlib changes signal disposition of the VM process for SIGPIPE to
|
||||
// SIG_IGN
|
||||
// - a small native executable that prints out, in its main function, all signal handler dispositions, to be executed
|
||||
// as a child process.
|
||||
//
|
||||
// What should happen: In child process, SIGPIPE should be set to default.
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createNativeTestProcessBuilder("PrintSignalDisposition");
|
||||
OutputAnalyzer output = ProcessTools.executeProcess(pb);
|
||||
output.shouldHaveExitValue(0);
|
||||
output.shouldNotMatch("SIGPIPE: +ignore");
|
||||
output.shouldNotMatch("SIGPIPE: +block");
|
||||
output.shouldMatch("SIGPIPE: +default");
|
||||
output.reportDiagnosticSummary();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "jvmti.h"
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static const struct { int sig; const char* name; } signals[] = {
|
||||
{ SIGABRT, "SIGABRT" }, { SIGALRM, "SIGALRM" }, { SIGBUS, "SIGBUS" }, { SIGCHLD, "SIGCHLD" }, { SIGCONT, "SIGCONT" },
|
||||
{ SIGFPE, "SIGFPE" }, { SIGHUP, "SIGHUP" }, { SIGILL, "SIGILL" }, { SIGINT, "SIGINT" }, { SIGKILL, "SIGKILL" },
|
||||
{ SIGPIPE, "SIGPIPE" }, { SIGQUIT, "SIGQUIT" }, { SIGSEGV, "SIGSEGV" }, { SIGSTOP, "SIGSTOP" }, { SIGTERM, "SIGTERM" },
|
||||
{ SIGTSTP, "SIGTSTP" }, { SIGTTIN, "SIGTTIN" }, { SIGTTOU, "SIGTTOU" }, { SIGUSR1, "SIGUSR1" }, { SIGUSR2, "SIGUSR2" },
|
||||
#ifdef SIGPOLL
|
||||
{ SIGPOLL, "SIGPOLL" },
|
||||
#endif
|
||||
{ SIGPROF, "SIGPROF" }, { SIGSYS, "SIGSYS" }, { SIGTRAP, "SIGTRAP" }, { SIGURG, "SIGURG" }, { SIGVTALRM, "SIGVTALRM" },
|
||||
{ SIGXCPU, "SIGXCPU" }, { SIGXFSZ, "SIGXFSZ" }, { -1, NULL }
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
printf("PID: %d\n", getpid());
|
||||
|
||||
sigset_t current_mask;
|
||||
sigemptyset(¤t_mask);
|
||||
if (sigprocmask(SIG_BLOCK /* ignored */, NULL, ¤t_mask) != 0) {
|
||||
printf("sigprocmask %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int n = 0; signals[n].sig != -1; n++) {
|
||||
printf("%s: ", signals[n].name);
|
||||
if (sigismember(¤t_mask, signals[n].sig)) {
|
||||
printf("blocked ");
|
||||
}
|
||||
struct sigaction act;
|
||||
if (sigaction(signals[n].sig, NULL, &act) != 0) {
|
||||
printf("sigaction %d\n", errno);
|
||||
printf("\n");
|
||||
continue;
|
||||
}
|
||||
const void* const handler = (act.sa_flags & SA_SIGINFO ?
|
||||
(void*)act.sa_sigaction : (void*)act.sa_handler);
|
||||
if (handler == (void*)SIG_DFL) {
|
||||
printf("default ");
|
||||
} else if (handler == (void*)SIG_IGN) {
|
||||
printf("ignore ");
|
||||
} else if (handler == (void*)SIG_HOLD) {
|
||||
printf("hold ");
|
||||
} else {
|
||||
printf("%p ", handler);
|
||||
}
|
||||
printf("%X\n", act.sa_flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include "jvmti.h"
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
|
||||
|
||||
if (signal(SIGPIPE, SIG_IGN) != SIG_ERR) {
|
||||
printf("changed signal disposition for SIGPIPE to SIG_IGN\n");
|
||||
} else {
|
||||
printf("FAILED to change signal disposition for SIGPIPE to SIG_IGN (%d)\n", errno);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_OK;
|
||||
}
|
||||
54
test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c
Normal file
54
test/jdk/java/lang/ProcessBuilder/exeBasicSleep.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
/**
|
||||
* Command line program to sleep at least given number of seconds.
|
||||
* The behavior should equivalent to the Unix sleep command.
|
||||
* Actual time sleeping may vary if interrupted, the remaining time
|
||||
* returned from sleep has limited accuracy.
|
||||
*
|
||||
* Note: the file name prefix "exe" identifies the source should be built into BasicSleep(.exe).
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
int seconds;
|
||||
|
||||
if (argc < 2 || (seconds = atoi(argv[1])) < 0) {
|
||||
fprintf(stderr, "usage: BasicSleep <non-negative seconds>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
Sleep(seconds * 1000);
|
||||
#else
|
||||
while ((seconds = sleep(seconds)) > 0) {
|
||||
// until no more to sleep
|
||||
}
|
||||
#endif
|
||||
}
|
||||
37
test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c
Normal file
37
test/jdk/java/lang/ProcessBuilder/libLinuxFDInfo.c
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2026, 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
// If the file descriptor is a PIPE, return the INode.
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_LinuxFDInfo_getPipeInodeNum(JNIEnv *env, jclass cls, jint fd) {
|
||||
#ifdef S_ISFIFO
|
||||
struct stat buf;
|
||||
if (fstat(fd, &buf) != -1 && S_ISFIFO(buf.st_mode)) {
|
||||
return buf.st_ino;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue