/* * 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("<>", "execute"); final Permission rxPermission
= new FilePermission("<>", "read,execute"); final Permission wxPermission
= new FilePermission("<>", "write,execute"); final Permission rwxPermission
= new FilePermission("<>", "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); }
//---------------------------------------------------------------- // If round-trip conversion works, check envvar pass-through to child
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.74 Sekunden
(vorverarbeitet)
¤
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 ist noch experimentell.