/* * Copyright (c) 2003, 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.
*/
/* used for Windows only */ staticfinal String systemRoot = System.getenv("SystemRoot");
/* used for Mac OS X only */ staticfinal String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING");
/* used for AIX only */ staticfinal String libpath = System.getenv("LIBPATH");
/* Used for regex String matching for long error messages */ staticfinal String PERMISSION_DENIED_ERROR_MSG = "(Permission denied|error=13)"; staticfinal String NO_SUCH_FILE_ERROR_MSG = "(No such file|error=2)";
/** * Returns the number of milliseconds since time given by * startNanoTime, which must have been previously returned from a * call to {@link System#nanoTime()}.
*/ privatestaticlong millisElapsedSince(long startNanoTime) { return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
}
privatestatic String commandOutput(Reader r) throws Throwable {
StringBuilder sb = new StringBuilder(); int c; while ((c = r.read()) > 0) if (c != '\r')
sb.append((char) c); return sb.toString();
}
privatestatic String commandOutput(Process p) throws Throwable {
check(p.getInputStream() == p.getInputStream());
check(p.getOutputStream() == p.getOutputStream());
check(p.getErrorStream() == p.getErrorStream());
Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");
String output = commandOutput(r);
equal(p.waitFor(), 0);
equal(p.exitValue(), 0); // The debug/fastdebug versions of the VM may write some warnings to stdout // (i.e. "Warning: Cannot open log file: hotspot.log" if the VM is started // in a directory without write permissions). These warnings will confuse tests // which match the entire output of the child process so better filter them out. return output.replaceAll("Warning:.*\\n", "");
}
privatestatic String absolutifyPath(String path) {
StringBuilder sb = new StringBuilder(); for (String file : path.split(File.pathSeparator)) { if (sb.length() != 0)
sb.append(File.pathSeparator);
sb.append(new File(file).getAbsolutePath());
} return sb.toString();
}
// compare windows-style, by canonicalizing to upper case, // not lower case as String.compareToIgnoreCase does privatestaticclass WindowsComparator implements Comparator<String> { publicint compare(String x, String y) { return x.toUpperCase(Locale.US)
.compareTo(y.toUpperCase(Locale.US));
}
}
privatestatic String sortedLines(String lines) {
String[] arr = lines.split("\n");
List<String> ls = new ArrayList<String>(); for (String s : arr)
ls.add(s);
Collections.sort(ls, new WindowsComparator());
StringBuilder sb = new StringBuilder(); for (String s : ls)
sb.append(s + "\n"); return sb.toString();
}
for (final ProcessBuilder pb : new ProcessBuilder[] {pb1, pb2, pb3}) { try { // Not on PATH at all; directories don't exist try {
pb.start();
fail("Expected IOException not thrown");
} catch (IOException e) {
String m = e.getMessage(); if (EnglishUnix.is() &&
! matches(m, NO_SUCH_FILE_ERROR_MSG))
unexpected(e);
} catch (Throwable t) { unexpected(t); }
// Not on PATH at all; directories exist new File("dir1").mkdirs(); new File("dir2").mkdirs(); try {
pb.start();
fail("Expected IOException not thrown");
} catch (IOException e) {
String m = e.getMessage(); if (EnglishUnix.is() &&
! matches(m, NO_SUCH_FILE_ERROR_MSG))
unexpected(e);
} catch (Throwable t) { unexpected(t); }
// Can't execute a directory -- permission denied // Report EACCES errno new File("dir1/prog").mkdirs();
checkPermissionDenied(pb);
// continue searching if EACCES
copy(TrueExe.path(), "dir2/prog");
equal(run(pb).exitValue(), True.exitValue()); new File("dir1/prog").delete(); new File("dir2/prog").delete();
new File("dir2/prog").mkdirs();
copy(TrueExe.path(), "dir1/prog");
equal(run(pb).exitValue(), True.exitValue());
// Check empty PATH component means current directory. // // While we're here, let's test different kinds of // Unix executables, and PATH vs explicit searching. new File("dir1/prog").delete(); new File("dir2/prog").delete(); for (String[] command : new String[][] { new String[] {"./prog"},
cmd}) {
pb.command(command);
File prog = new File("./prog"); // "Normal" binaries
copy(TrueExe.path(), "./prog");
equal(run(pb).exitValue(), True.exitValue());
copy(FalseExe.path(), "./prog");
equal(run(pb).exitValue(), False.exitValue());
prog.delete(); // Interpreter scripts with #!
setFileContents(prog, "#!/bin/true\n");
prog.setExecutable(true);
equal(run(pb).exitValue(), True.exitValue());
prog.delete();
setFileContents(prog, "#!/bin/false\n");
prog.setExecutable(true);
equal(run(pb).exitValue(), False.exitValue()); // Traditional shell scripts without #! if (!(Platform.isLinux() && Platform.isMusl())) {
setFileContents(prog, "exec /bin/true\n");
prog.setExecutable(true);
equal(run(pb).exitValue(), True.exitValue());
prog.delete();
setFileContents(prog, "exec /bin/false\n");
prog.setExecutable(true);
equal(run(pb).exitValue(), False.exitValue());
}
prog.delete();
}
// Test traditional shell scripts without #! if (!(Platform.isLinux() && Platform.isMusl())) {
setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
pb.command(new String[] {"prog", "hello", "world"});
checkPermissionDenied(pb);
dir1Prog.setExecutable(true);
equal(run(pb).out(), "hello world\n");
equal(run(pb).exitValue(), True.exitValue());
dir1Prog.delete();
pb.command(cmd);
}
// If prog found on both parent and child's PATH, // parent's is used. new File("dir1/prog").delete(); new File("dir2/prog").delete(); new File("prog").delete(); new File("dir3").mkdirs();
copy(TrueExe.path(), "dir1/prog");
copy(FalseExe.path(), "dir3/prog");
pb.environment().put("PATH","dir3");
equal(run(pb).exitValue(), True.exitValue());
copy(TrueExe.path(), "dir3/prog");
copy(FalseExe.path(), "dir1/prog");
equal(run(pb).exitValue(), False.exitValue());
} finally { // cleanup new File("dir1/prog").delete(); new File("dir2/prog").delete(); new File("dir3/prog").delete(); new File("dir1").delete(); new File("dir2").delete(); new File("dir3").delete(); new File("prog").delete();
}
}
// On Alpine Linux, /bin/true and /bin/false are just links to /bin/busybox. // Some tests copy /bin/true and /bin/false to files with a different filename. // However, copying the busbox executable into a file with a different name // won't result in the expected return codes. As workaround, we create // executable files that can be copied and produce the expected return // values.
/* Only used for Mac OS X -- * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also * be set in Mac OS X. * Remove them both from the list of env variables
*/ privatestatic String removeMacExpectedVars(String vars) { // Check for __CF_USER_TEXT_ENCODING
String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="
+cfUserTextEncoding+",",""); // Check for JAVA_MAIN_CLASS_<pid>
String javaMainClassStr
= matchAndExtract(cleanedVars, "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,"); return cleanedVars.replace(javaMainClassStr,"");
}
/* Only used for AIX -- * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment. * Remove it from the list of env variables
*/ privatestatic String removeAixExpectedVars(String vars) { return vars.replace("AIXTHREAD_GUARDPAGES=0,", "");
}
privatestatic String sortByLinesWindowsly(String text) {
String[] lines = text.split("\n");
Arrays.sort(lines, new WindowsComparator());
StringBuilder sb = new StringBuilder(); for (String line : lines)
sb.append(line).append("\n"); return sb.toString();
}
static String fileContents(File file) { try {
Reader r = new FileReader(file);
StringBuilder sb = new StringBuilder(); char[] buffer = newchar[1024]; int n; while ((n = r.read(buffer)) != -1)
sb.append(buffer,0,n);
r.close(); returnnew String(sb);
} catch (Throwable t) { unexpected(t); return""; }
}
@SuppressWarnings("removal") staticvoid testIORedirection() throws Throwable { final File ifile = new File("ifile"); final File ofile = new File("ofile"); final File efile = new File("efile");
ifile.delete();
ofile.delete();
efile.delete();
//---------------------------------------------------------------- // Check mutual inequality of different types of Redirect //----------------------------------------------------------------
Redirect[] redirects =
{ PIPE,
INHERIT,
DISCARD,
Redirect.from(ifile),
Redirect.to(ifile),
Redirect.appendTo(ifile),
Redirect.from(ofile),
Redirect.to(ofile),
Redirect.appendTo(ofile),
}; for (int i = 0; i < redirects.length; i++) for (int j = 0; j < redirects.length; j++)
equal(redirects[i].equals(redirects[j]), (i == j));
//---------------------------------------------------------------- // Check basic properties of different types of Redirect //----------------------------------------------------------------
equal(PIPE.type(), Redirect.Type.PIPE);
equal(PIPE.toString(), "PIPE");
equal(PIPE.file(), null);
//---------------------------------------------------------------- // DISCARDing output and merging error into output //----------------------------------------------------------------
{
setFileContents(ifile, "standard input");
setFileContents(ofile, "ofile-contents");
setFileContents(efile, "efile-contents");
pb.redirectOutput(DISCARD);
pb.redirectErrorStream(true);
pb.redirectError(efile);
ProcessResults r = run(pb);
equal(r.exitValue(), 0);
equal(fileContents(ofile), "ofile-contents"); // untouched
equal(fileContents(efile), ""); // empty
equal(r.out(), "");
equal(r.err(), "");
ifile.delete();
ofile.delete();
efile.delete();
pb.redirectErrorStream(false); // reset for next test
}
//---------------------------------------------------------------- // Testing INHERIT is harder. // Note that this requires __FOUR__ nested JVMs involved in one test, // if you count the harness JVM. //---------------------------------------------------------------- for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {
redirectIO(pb, PIPE, PIPE, PIPE);
List<String> command = pb.command();
command.set(command.size() - 1, testName);
Process p = pb.start(); new PrintStream(p.getOutputStream()).print("standard input");
p.getOutputStream().close();
ProcessResults r = run(p);
equal(r.exitValue(), 0);
equal(r.out(), "standard output");
equal(r.err(), "standard error");
}
//---------------------------------------------------------------- // Test security implications of I/O redirection //----------------------------------------------------------------
// Read access to current directory is always granted; // So create a tmpfile for input instead. final File tmpFile = File.createTempFile("Basic", "tmp");
setFileContents(tmpFile, "standard input");
final Policy policy = new Policy();
Policy.setPolicy(policy);
System.setSecurityManager(new SecurityManager()); try { final Permission xPermission
= new FilePermission("<<ALL FILES>>", "execute"); final Permission rxPermission
= new FilePermission("<<ALL FILES>>", "read,execute"); final Permission wxPermission
= new FilePermission("<<ALL FILES>>", "write,execute"); final Permission rwxPermission
= new FilePermission("<<ALL FILES>>", "read,write,execute");
staticvoid checkProcessPid() {
ProcessBuilder pb = new ProcessBuilder();
List<String> list = new ArrayList<String>(javaChildArgs);
list.add("pid");
pb.command(list); try {
Process p = pb.start();
String s = commandOutput(p); long actualPid = Long.valueOf(s.trim()); long expectedPid = p.pid();
equal(actualPid, expectedPid);
} catch (Throwable t) {
unexpected(t);
}
// Test the default implementation of Process.getPid
DelegatingProcess p = new DelegatingProcess(null); THROWS(UnsupportedOperationException.class,
() -> p.pid(),
() -> p.toHandle(),
() -> p.supportsNormalTermination(),
() -> p.children(),
() -> p.descendants());
}
@SuppressWarnings("removal") privatestaticvoid realMain(String[] args) throws Throwable { if (Windows.is())
System.out.println("This appears to be a Windows system."); if (Unix.is())
System.out.println("This appears to be a Unix system."); if (UnicodeOS.is())
System.out.println("This appears to be a Unicode-based OS.");
try {
Collection<Map.Entry<String,String>> c = getenv().entrySet(); if (! c.isEmpty()) try {
c.iterator().next().setValue("foo");
fail("Expected UnsupportedOperationException not thrown");
} catch (UnsupportedOperationException e) {} // OK
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // System.getenv() always returns the same object in our implementation. //---------------------------------------------------------------- try {
check(System.getenv() == System.getenv());
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // You can't create an env var name containing "=", // or an env var name or value containing NUL. //----------------------------------------------------------------
{ final Map<String,String> m = new ProcessBuilder().environment(); THROWS(IllegalArgumentException.class,
() -> m.put("FOO=","BAR"),
() -> m.put("FOO\u0000","BAR"),
() -> m.put("FOO","BAR\u0000"));
}
//---------------------------------------------------------------- // Commands must never be null. //---------------------------------------------------------------- THROWS(NullPointerException.class,
() -> new ProcessBuilder((List<String>)null),
() -> new ProcessBuilder().command((List<String>)null));
//---------------------------------------------------------------- // Put in a command; get the same one back out. //---------------------------------------------------------------- try {
List<String> command = new ArrayList<String>();
ProcessBuilder pb = new ProcessBuilder(command);
check(pb.command() == command);
List<String> command2 = new ArrayList<String>(2);
command2.add("foo");
command2.add("bar");
pb.command(command2);
check(pb.command() == command2);
pb.command("foo", "bar");
check(pb.command() != command2 && pb.command().equals(command2));
pb.command(command2);
command2.add("baz");
equal(pb.command().get(2), "baz");
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Commands must contain at least one element. //---------------------------------------------------------------- THROWS(IndexOutOfBoundsException.class,
() -> new ProcessBuilder().start(),
() -> new ProcessBuilder(new ArrayList<String>()).start(),
() -> Runtime.getRuntime().exec(new String[]{}));
//---------------------------------------------------------------- // Commands must not contain null elements at start() time. //---------------------------------------------------------------- THROWS(NullPointerException.class,
() -> new ProcessBuilder("foo",null,"bar").start(),
() -> new ProcessBuilder((String)null).start(),
() -> new ProcessBuilder(new String[]{null}).start(),
() -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());
//---------------------------------------------------------------- // Command lists are growable. //---------------------------------------------------------------- try { new ProcessBuilder().command().add("foo"); new ProcessBuilder("bar").command().add("foo"); new ProcessBuilder(new String[]{"1","2"}).command().add("3");
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Nulls in environment updates generate NullPointerException //---------------------------------------------------------------- try { final Map<String,String> env = new ProcessBuilder().environment(); THROWS(NullPointerException.class,
() -> env.put("foo",null),
() -> env.put(null,"foo"),
() -> env.remove(null),
() -> { for (Map.Entry<String,String> e : env.entrySet())
e.setValue(null);},
() -> Runtime.getRuntime().exec(new String[]{"foo"}, new String[]{null}));
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Put in a directory; get the same one back out. //---------------------------------------------------------------- try {
ProcessBuilder pb = new ProcessBuilder();
File foo = new File("foo");
equal(pb.directory(), null);
equal(pb.directory(foo).directory(), foo);
equal(pb.directory(null).directory(), null);
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // A surprisingly large number of ways to delete an environment var. //----------------------------------------------------------------
testVariableDeleter(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) {
environ.remove("Foo");}});
testVariableDeleter(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) { // Legally fabricate a ProcessEnvironment.StringEntry, // even though it's private.
Map<String,String> environ2
= new ProcessBuilder().environment();
environ2.clear();
environ2.put("Foo","BAAR"); // Subtlety alert.
Map.Entry<String,String> e
= environ2.entrySet().iterator().next();
environ.entrySet().remove(e);}});
testVariableDeleter(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) {
Map.Entry<String,String> victim = null; for (Map.Entry<String,String> e : environ.entrySet()) if (e.getKey().equals("Foo"))
victim = e; if (victim != null)
environ.entrySet().remove(victim);}});
testVariableDeleter(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) {
Iterator<String> it = environ.keySet().iterator(); while (it.hasNext()) {
String val = it.next(); if (val.equals("Foo"))
it.remove();}}});
testVariableDeleter(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) {
Iterator<Map.Entry<String,String>> it
= environ.entrySet().iterator(); while (it.hasNext()) {
Map.Entry<String,String> e = it.next(); if (e.getKey().equals("Foo"))
it.remove();}}});
testVariableDeleter(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) {
Iterator<String> it = environ.values().iterator(); while (it.hasNext()) {
String val = it.next(); if (val.equals("BAAR"))
it.remove();}}});
//---------------------------------------------------------------- // A surprisingly small number of ways to add an environment var. //----------------------------------------------------------------
testVariableAdder(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) {
environ.put("Foo","Bahrein");}});
//---------------------------------------------------------------- // A few ways to modify an environment var. //----------------------------------------------------------------
testVariableModifier(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) {
environ.put("Foo","NewValue");}});
testVariableModifier(new EnvironmentFrobber() { publicvoid doIt(Map<String,String> environ) { for (Map.Entry<String,String> e : environ.entrySet()) if (e.getKey().equals("Foo"))
e.setValue("NewValue");}});
//---------------------------------------------------------------- // Fiddle with environment sizes //---------------------------------------------------------------- try {
Map<String,String> environ = new ProcessBuilder().environment(); int size = environ.size();
checkSizes(environ, size);
environ = new ProcessBuilder().environment();
environ.keySet().clear();
checkSizes(environ, 0);
environ = new ProcessBuilder().environment();
environ.entrySet().clear();
checkSizes(environ, 0);
environ = new ProcessBuilder().environment();
environ.values().clear();
checkSizes(environ, 0);
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Check that various map invariants hold //----------------------------------------------------------------
checkMapSanity(new ProcessBuilder().environment());
checkMapSanity(System.getenv());
checkMapEquality(new ProcessBuilder().environment(), new ProcessBuilder().environment());
//---------------------------------------------------------------- // Check effects on external "env" command. //---------------------------------------------------------------- try {
Set<String> env1 = new HashSet<String>
(Arrays.asList(nativeEnv((String[])null).split("\n")));
ProcessBuilder pb = new ProcessBuilder();
pb.environment().put("QwErTyUiOp","AsDfGhJk");
Set<String> env2 = new HashSet<String>
(Arrays.asList(nativeEnv(pb).split("\n")));
//---------------------------------------------------------------- // Test Runtime.exec(...envp...) // Check for sort order of environment variables on Windows. //---------------------------------------------------------------- try {
String systemRoot = "SystemRoot=" + System.getenv("SystemRoot"); // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'
String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", "+=+", "_=_", "~=~", systemRoot};
String output = nativeEnv(envp);
String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n"; // On Windows, Java must keep the environment sorted. // Order is random on Unix, so this test does the sort. if (! Windows.is())
output = sortByLinesWindowsly(output);
equal(output, expected);
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Test Runtime.exec(...envp...) // and check SystemRoot gets set automatically on Windows //---------------------------------------------------------------- try { if (Windows.is()) {
String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", "+=+", "_=_", "~=~"};
String output = nativeEnv(envp);
String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
equal(output, expected);
}
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // System.getenv() must be consistent with System.getenv(String) //---------------------------------------------------------------- try { for (Map.Entry<String,String> e : getenv().entrySet())
equal(getenv(e.getKey()), e.getValue());
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Fiddle with working directory in child //---------------------------------------------------------------- try {
String canonicalUserDir = new File(System.getProperty("user.dir")).getCanonicalPath();
String[] sdirs = new String[]
{".", "..", "/", "/bin", "C:", "c:", "C:/", "c:\\", "\\", "\\bin", "c:\\windows ", "c:\\Program Files", "c:\\Program Files\\" }; for (String sdir : sdirs) {
File dir = new File(sdir); if (! (dir.isDirectory() && dir.exists())) continue;
out.println("Testing directory " + dir); //dir = new File(dir.getCanonicalPath());
ProcessBuilder pb = new ProcessBuilder();
equal(pb.directory(), null);
equal(pwdInChild(pb), canonicalUserDir);
//---------------------------------------------------------------- // Working directory with Unicode in child //---------------------------------------------------------------- try { if (UnicodeOS.is()) {
File dir = new File(System.getProperty("test.dir", "."), "ProcessBuilderDir\u4e00\u4e02"); try { if (!dir.exists())
dir.mkdir();
out.println("Testing Unicode directory:" + dir);
ProcessBuilder pb = new ProcessBuilder();
pb.directory(dir);
equal(pwdInChild(pb), dir.getCanonicalPath());
} finally { if (dir.exists())
dir.delete();
}
}
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // OOME in child allocating maximally sized array // Test for hotspot/jvmti bug 6850957 //---------------------------------------------------------------- try {
List<String> list = new ArrayList<String>(javaChildArgs);
list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",
javaExe));
list.add("ArrayOOME");
ProcessResults r = run(new ProcessBuilder(list));
check(r.err().contains("java.lang.OutOfMemoryError:"));
check(r.err().contains(javaExe));
check(r.err().contains(System.getProperty("java.version")));
equal(r.exitValue(), 1);
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Windows has tricky semi-case-insensitive semantics //---------------------------------------------------------------- if (Windows.is()) try {
out.println("Running case insensitve variable tests"); for (String[] namePair : new String[][]
{ new String[]{"PATH","PaTh"}, new String[]{"home","HOME"}, new String[]{"SYSTEMROOT","SystemRoot"}}) {
check((getenv(namePair[0]) == null &&
getenv(namePair[1]) == null)
||
getenv(namePair[0]).equals(getenv(namePair[1])), "Windows environment variables are not case insensitive");
}
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Test proper Unicode child environment transfer //---------------------------------------------------------------- if (UnicodeOS.is()) try {
ProcessBuilder pb = new ProcessBuilder();
pb.environment().put("\u1234","\u5678");
pb.environment().remove("PATH");
equal(getenvInChild1234(pb), "\u5678");
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Parent's, not child's PATH is used //---------------------------------------------------------------- try { new File("suBdiR").mkdirs();
copy(TrueExe.path(), "suBdiR/unliKely"); final ProcessBuilder pb = new ProcessBuilder(new String[]{"unliKely"});
pb.environment().put("PATH", "suBdiR"); THROWS(IOException.class, () -> pb.start());
} catch (Throwable t) { unexpected(t);
} finally { new File("suBdiR/unliKely").delete(); new File("suBdiR").delete();
}
}
//---------------------------------------------------------------- // Attempt to start bogus program "" //---------------------------------------------------------------- try { new ProcessBuilder("").start();
fail("Expected IOException not thrown");
} catch (IOException e) {
String m = e.getMessage(); if (EnglishUnix.is() &&
! matches(m, NO_SUCH_FILE_ERROR_MSG))
unexpected(e);
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Check that attempt to execute program name with funny // characters throws an exception containing those characters. //---------------------------------------------------------------- for (String programName : new String[] {"\u00f0", "\u01f0"}) try { new ProcessBuilder(programName).start();
fail("Expected IOException not thrown");
} catch (IOException e) {
String m = e.getMessage();
Pattern p = Pattern.compile(programName); if (! matches(m, programName)
|| (EnglishUnix.is() &&
! matches(m, NO_SUCH_FILE_ERROR_MSG)))
unexpected(e);
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Attempt to start process in nonexistent directory fails. //---------------------------------------------------------------- try { new ProcessBuilder("echo")
.directory(new File("UnLiKeLY"))
.start();
fail("Expected IOException not thrown");
} catch (IOException e) {
String m = e.getMessage(); if (! matches(m, "in directory")
|| (EnglishUnix.is() &&
! matches(m, NO_SUCH_FILE_ERROR_MSG)))
unexpected(e);
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Attempt to write 4095 bytes to the pipe buffer without a // reader to drain it would deadlock, if not for the fact that // interprocess pipe buffers are at least 4096 bytes. // // Also, check that available reports all the bytes expected // in the pipe buffer, and that I/O operations do the expected // things. //---------------------------------------------------------------- try {
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("print4095"); finalint SIZE = 4095; final Process p = new ProcessBuilder(childArgs).start();
print4095(p.getOutputStream(), (byte) '!'); // Might hang!
p.waitFor(); // Might hang!
equal(SIZE, p.getInputStream().available());
equal(SIZE, p.getErrorStream().available()); THROWS(IOException.class,
() -> { p.getOutputStream().write((byte) '!');
p.getOutputStream().flush();});
finalbyte[] bytes = newbyte[SIZE + 1];
equal(SIZE, p.getInputStream().read(bytes)); for (int i = 0; i < SIZE; i++)
equal((byte) '!', bytes[i]);
equal((byte) 0, bytes[SIZE]);
equal(SIZE, p.getErrorStream().read(bytes)); for (int i = 0; i < SIZE; i++)
equal((byte) 'E', bytes[i]);
equal((byte) 0, bytes[SIZE]);
//---------------------------------------------------------------- // Check that reads which are pending when Process.destroy is // called, get EOF, or IOException("Stream closed"). //---------------------------------------------------------------- try { finalint cases = 4; for (int i = 0; i < cases; i++) { finalint action = i;
List<String> childArgs = getSleepArgs(); final ProcessBuilder pb = new ProcessBuilder(childArgs); finalbyte[] bytes = newbyte[10]; final Process p = pb.start(); final CountDownLatch latch = new CountDownLatch(1); final InputStream s; switch (action & 0x1) { case 0: s = p.getInputStream(); break; case 1: s = p.getErrorStream(); break; default: thrownew Error();
} finalThreadthread = newThread() { publicvoid run() { try { int r;
latch.countDown(); switch (action & 0x2) { case 0: r = s.read(); break; case 2: r = s.read(bytes); break; default: thrownew Error();
} if (r >= 0) { // The child sent unexpected output; print it to diagnose
System.out.println("Unexpected child output, to: " +
((action & 0x1) == 0 ? "getInputStream" : "getErrorStream"));
System.out.println("Child args: " + childArgs); if ((action & 0x2) == 0) {
System.out.write(r); // Single character
} else {
System.out.write(bytes, 0, r);
} for (int c = s.read(); c >= 0; c = s.read())
System.out.write(c);
System.out.println("\nEND Child output.");
}
equal(-1, r);
} catch (IOException ioe) { if (!ioe.getMessage().equals("Stream closed")) { // BufferedInputStream may throw IOE("Stream closed").
unexpected(ioe);
}
} catch (Throwable t) { unexpected(t); }}};
thread.start();
latch.await(); Thread.sleep(30);
if (s instanceof BufferedInputStream) { // Wait until after the s.read occurs in "thread" by // checking when the input stream monitor is acquired // (BufferedInputStream.read is synchronized) while (!isLocked((BufferedInputStream) s)) { Thread.sleep(100);
}
}
p.destroy(); thread.join();
}
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Check that subprocesses which create subprocesses of their // own do not cause parent to hang waiting for file // descriptors to be closed. //---------------------------------------------------------------- try { if (Unix.is()
&& new File("/bin/bash").exists()
&& new File("/bin/sleep").exists()) { // Notice that we only destroy the process created by us (i.e. // our child) but not our grandchild (i.e. '/bin/sleep'). So // pay attention that the grandchild doesn't run too long to // avoid polluting the process space with useless processes. // Running the grandchild for 59s should be more than enough. // A unique (59s) time is needed to avoid killing other sleep processes. final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 59)" }; final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 59\")" }; final ProcessBuilder pb = new ProcessBuilder(cmd); final Process p = pb.start(); final InputStream stdout = p.getInputStream(); final InputStream stderr = p.getErrorStream(); final OutputStream stdin = p.getOutputStream(); finalThread reader = newThread() { publicvoid run() { try { stdout.read(); } catch (IOException e) { // Check that reader failed because stream was // asynchronously closed. // e.printStackTrace();
String msg = e.getMessage(); if (EnglishUnix.is() &&
! (msg.matches(".*Bad file.*") ||
msg.matches(".*Stream closed.*")))
unexpected(e);
} catch (Throwable t) { unexpected(t); }}};
reader.setDaemon(true);
reader.start(); Thread.sleep(100);
p.destroy();
check(p.waitFor() != 0);
check(p.exitValue() != 0); // Subprocess is now dead, but file descriptors remain open. // Make sure the test will fail if we don't manage to close // the open streams within 30 seconds. Notice that this time // must be shorter than the sleep time of the grandchild.
Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);
t.schedule(new TimerTask() { publicvoid run() {
fail("Subprocesses which create subprocesses of " + "their own caused the parent to hang while " + "waiting for file descriptors to be closed.");
System.exit(-1);
}
}, 30000);
stdout.close();
stderr.close();
stdin.close(); new ProcessBuilder(cmdkill).start(); // All streams successfully closed so we can cancel the timer.
t.cancel(); //---------------------------------------------------------- // There remain unsolved issues with asynchronous close. // Here's a highly non-portable experiment to demonstrate: //---------------------------------------------------------- if (Boolean.getBoolean("wakeupJeff!")) {
System.out.println("wakeupJeff!"); // Initialize signal handler for INTERRUPT_SIGNAL. new FileInputStream("/bin/sleep").getChannel().close(); // Send INTERRUPT_SIGNAL to every thread in this java.
String[] wakeupJeff = { "/bin/bash", "-c", "/bin/ps --noheaders -Lfp $PPID | " + "/usr/bin/perl -nale 'print $F[3]' | " + // INTERRUPT_SIGNAL == 62 on my machine du jour. "/usr/bin/xargs kill -62"
}; new ProcessBuilder(wakeupJeff).start().waitFor(); // If wakeupJeff worked, reader probably got EBADF.
reader.join();
}
}
//---------------------------------------------------------------- // Check the Process toString() method //----------------------------------------------------------------
{
List<String> childArgs = new ArrayList<String>(javaChildArgs);
childArgs.add("testIO");
ProcessBuilder pb = new ProcessBuilder(childArgs);
pb.redirectInput(Redirect.PIPE);
pb.redirectOutput(DISCARD);
pb.redirectError(DISCARD); final Process p = pb.start(); // Child process waits until it gets input
String s = p.toString();
check(s.contains("not exited"));
check(s.contains("pid=" + p.pid() + ","));
new PrintStream(p.getOutputStream()).print("standard input");
p.getOutputStream().close();
// Check the toString after it exits int exitValue = p.waitFor();
s = p.toString();
check(s.contains("pid=" + p.pid() + ","));
check(s.contains("exitValue=" + exitValue) &&
!s.contains("not exited"));
}
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Attempt to start process with insufficient permissions fails. //---------------------------------------------------------------- try { new File("emptyCommand").delete(); new FileOutputStream("emptyCommand").close(); new File("emptyCommand").setExecutable(false); new ProcessBuilder("./emptyCommand").start();
fail("Expected IOException not thrown");
} catch (IOException e) { new File("./emptyCommand").delete();
String m = e.getMessage(); if (EnglishUnix.is() &&
! matches(m, PERMISSION_DENIED_ERROR_MSG))
unexpected(e);
} catch (Throwable t) { unexpected(t); }
new File("emptyCommand").delete();
//---------------------------------------------------------------- // Check for correct security permission behavior //---------------------------------------------------------------- final Policy policy = new Policy();
Policy.setPolicy(policy);
System.setSecurityManager(new SecurityManager());
try { // No permissions required to CREATE a ProcessBuilder
policy.setPermissions(/* Nothing */); new ProcessBuilder("env").directory(null).directory(); new ProcessBuilder("env").directory(new File("dir")).directory(); new ProcessBuilder("env").command("??").command();
} catch (Throwable t) { unexpected(t); }
final Permission execPermission
= new FilePermission("<<ALL FILES>>", "execute");
THROWS(SecurityException.class,
() -> { // environment permission by itself insufficient
policy.setPermissions(new RuntimePermission("getenv.*"));
ProcessBuilder pb = new ProcessBuilder("env");
pb.environment().put("foo","bar");
pb.start();},
() -> { // exec permission by itself insufficient
policy.setPermissions(execPermission);
ProcessBuilder pb = new ProcessBuilder("env");
pb.environment().put("foo","bar");
pb.start();});
try { // Both permissions? OK.
policy.setPermissions(new RuntimePermission("getenv.*"),
execPermission);
ProcessBuilder pb = new ProcessBuilder("env");
pb.environment().put("foo","bar");
Process p = pb.start();
closeStreams(p);
} catch (IOException e) { // OK
} catch (Throwable t) { unexpected(t); }
try { // Don't need environment permission unless READING environment
policy.setPermissions(execPermission);
Runtime.getRuntime().exec("env", new String[]{});
} catch (IOException e) { // OK
} catch (Throwable t) { unexpected(t); }
try { // Don't need environment permission unless READING environment
policy.setPermissions(execPermission); new ProcessBuilder("env").start();
} catch (IOException e) { // OK
} catch (Throwable t) { unexpected(t); }
// Restore "normal" state without a security manager
policy.setPermissions(new RuntimePermission("setSecurityManager"));
System.setSecurityManager(null);
//---------------------------------------------------------------- // Check that Process.isAlive() & // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected. //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); long start = System.nanoTime(); if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
fail("Test failed: Process exited prematurely");
} long end = System.nanoTime(); // give waitFor(timeout) a wide berth (2s)
System.out.printf(" waitFor process: delta: %d%n",(end - start) );
if ((end - start) > TimeUnit.SECONDS.toNanos(2))
fail("Test failed: waitFor took too long (" + (end - start) + "ns)");
p.destroy();
p.waitFor();
if (p.isAlive() ||
!p.waitFor(0, TimeUnit.MILLISECONDS))
{
fail("Test failed: Process still alive - please terminate " +
p.toString() + " manually");
}
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) // works as expected. //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); long start = System.nanoTime();
if (p.waitFor(10, TimeUnit.MILLISECONDS)) { var msg = "External sleep process terminated early: exitValue: %d, (%dns)%n"
.formatted(p.exitValue(), (System.nanoTime() - start));
fail(msg);
} else { long end = System.nanoTime(); if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))
fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
}
p.destroy();
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) // interrupt works as expected, if interrupted while waiting. //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); finallong start = System.nanoTime(); final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
//---------------------------------------------------------------- // Check that Process.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS) // interrupt works as expected, if interrupted while waiting. //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); finallong start = System.nanoTime(); final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
//---------------------------------------------------------------- // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) // interrupt works as expected, if interrupted before waiting. //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); finallong start = System.nanoTime(); final CountDownLatch threadStarted = new CountDownLatch(1);
finalThreadthread = newThread() { publicvoid run() { try {
threadStarted.countDown(); do { Thread.yield(); } while (!Thread.currentThread().isInterrupted()); boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);
fail("waitFor() wasn't interrupted, its return value was: " + result);
} catch (InterruptedException success) {
} catch (Throwable t) { unexpected(t); }
}
};
//---------------------------------------------------------------- // Check that Process.waitFor(timeout, null) throws NPE. //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process p = new ProcessBuilder(childArgs).start(); THROWS(NullPointerException.class,
() -> p.waitFor(10L, null)); THROWS(NullPointerException.class,
() -> p.waitFor(0L, null)); THROWS(NullPointerException.class,
() -> p.waitFor(-1L, null)); // Terminate process and recheck after it exits
p.destroy();
p.waitFor(); THROWS(NullPointerException.class,
() -> p.waitFor(10L, null)); THROWS(NullPointerException.class,
() -> p.waitFor(0L, null)); THROWS(NullPointerException.class,
() -> p.waitFor(-1L, null));
} catch (Throwable t) { unexpected(t); }
//---------------------------------------------------------------- // Check that default implementation of Process.waitFor(timeout, null) throws NPE. //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process proc = new ProcessBuilder(childArgs).start(); final DelegatingProcess p = new DelegatingProcess(proc);
//---------------------------------------------------------------- // Check the default implementation for // Process.waitFor(long, TimeUnit) //---------------------------------------------------------------- try {
List<String> childArgs = getSleepArgs(); final Process proc = new ProcessBuilder(childArgs).start();
DelegatingProcess p = new DelegatingProcess(proc); long start = System.nanoTime();
if (p.waitFor(1000, TimeUnit.MILLISECONDS)) { var msg = "External sleep process terminated early: exitValue: %02x, (%dns)"
.formatted(p.exitValue(), (System.nanoTime() - start));
fail(msg);
} else { long end = System.nanoTime(); if ((end - start) < 500000000)
fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
}
p.destroy();
// Path to native executables, if any privatestaticfinal String TEST_NATIVEPATH = System.getProperty("test.nativepath");
// Path where "sleep" program may be found" or null privatestaticfinal Path SLEEP_PATH = initSleepPath();
/** * Compute the Path to a sleep executable. * @return a Path to sleep or BasicSleep(.exe) or null if none
*/ privatestatic Path initSleepPath() { if (Windows.is() && TEST_NATIVEPATH != null) { // exeBasicSleep is equivalent to sleep on Unix
Path exePath = Path.of(TEST_NATIVEPATH).resolve("BasicSleep.exe"); if (Files.isExecutable(exePath)) { return exePath;
}
}
List<String> binPaths = List.of("/bin", "/usr/bin"); for (String dir : binPaths) {
Path exePath = Path.of(dir).resolve("sleep"); if (Files.isExecutable(exePath)) { return exePath;
}
} returnnull;
}
/** * Return the list of process arguments for a child to sleep 10 minutes (600 seconds). * * @return A list of process arguments to sleep 10 minutes.
*/ privatestatic List<String> getSleepArgs() {
List<String> childArgs = null; if (SLEEP_PATH != null) {
childArgs = List.of(SLEEP_PATH.toString(), "600");
} else { // Fallback to the JavaChild ; its 'sleep' command is for 10 minutes. // The fallback the Java$Child is used if the test is run without building // the BasicSleep native executable (for Windows).
childArgs = new ArrayList<>(javaChildArgs);
childArgs.add("sleep");
System.out.println("Sleep not found, fallback to JavaChild: " + childArgs);
} return childArgs;
}
//---------------------------------------------------------------- // A Policy class designed to make permissions fiddling very easy. //----------------------------------------------------------------
@SuppressWarnings("removal") privatestaticclass Policy extends java.security.Policy { staticfinal java.security.Policy DEFAULT_POLICY = java.security.Policy.getPolicy();
private Permissions perms;
publicvoid setPermissions(Permission...permissions) {
perms = new Permissions(); for (Permission permission : permissions)
perms.add(permission);
}
public Policy() { setPermissions(/* Nothing */); }
public PermissionCollection getPermissions(CodeSource cs) { return perms;
}
public PermissionCollection getPermissions(ProtectionDomain pd) { return perms;
}
StreamAccumulator outAccumulator = new StreamAccumulator(p.getInputStream());
StreamAccumulator errAccumulator = new StreamAccumulator(p.getErrorStream());
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.38Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.