Path: blob/master/test/jdk/java/lang/ProcessBuilder/Basic.java
41149 views
/*1* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 498668926* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 641331327* 6464154 6523983 6206031 4960438 6631352 6631966 6850957 685095828* 4947220 7018606 7034570 4244896 5049299 8003488 8054494 805846429* 8067796 8224905 8263729 826517330* @key intermittent31* @summary Basic tests for Process and Environment Variable code32* @modules java.base/java.lang:open33* @library /test/lib34* @run main/othervm/timeout=300 -Djava.security.manager=allow Basic35* @run main/othervm/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic36* @author Martin Buchholz37*/3839/*40* @test41* @modules java.base/java.lang:open42* @requires (os.family == "linux")43* @library /test/lib44* @run main/othervm/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=posix_spawn Basic45*/4647import java.lang.ProcessBuilder.Redirect;48import java.lang.ProcessHandle;49import static java.lang.ProcessBuilder.Redirect.*;5051import java.io.*;52import java.lang.reflect.Field;53import java.nio.file.Files;54import java.nio.file.Paths;55import java.nio.file.StandardCopyOption;56import java.util.*;57import java.util.concurrent.CountDownLatch;58import java.util.concurrent.TimeUnit;59import java.security.*;60import java.util.regex.Pattern;61import java.util.regex.Matcher;62import static java.lang.System.getenv;63import static java.lang.System.out;64import static java.lang.Boolean.TRUE;65import static java.util.AbstractMap.SimpleImmutableEntry;6667import jdk.test.lib.Platform;6869public class Basic {7071/* used for Windows only */72static final String systemRoot = System.getenv("SystemRoot");7374/* used for Mac OS X only */75static final String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING");7677/* used for AIX only */78static final String libpath = System.getenv("LIBPATH");7980/* Used for regex String matching for long error messages */81static final String PERMISSION_DENIED_ERROR_MSG = "(Permission denied|error=13)";82static final String NO_SUCH_FILE_ERROR_MSG = "(No such file|error=2)";8384/**85* Returns the number of milliseconds since time given by86* startNanoTime, which must have been previously returned from a87* call to {@link System.nanoTime()}.88*/89private static long millisElapsedSince(long startNanoTime) {90return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);91}9293private static String commandOutput(Reader r) throws Throwable {94StringBuilder sb = new StringBuilder();95int c;96while ((c = r.read()) > 0)97if (c != '\r')98sb.append((char) c);99return sb.toString();100}101102private static String commandOutput(Process p) throws Throwable {103check(p.getInputStream() == p.getInputStream());104check(p.getOutputStream() == p.getOutputStream());105check(p.getErrorStream() == p.getErrorStream());106Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");107String output = commandOutput(r);108equal(p.waitFor(), 0);109equal(p.exitValue(), 0);110// The debug/fastdebug versions of the VM may write some warnings to stdout111// (i.e. "Warning: Cannot open log file: hotspot.log" if the VM is started112// in a directory without write permissions). These warnings will confuse tests113// which match the entire output of the child process so better filter them out.114return output.replaceAll("Warning:.*\\n", "");115}116117private static String commandOutput(ProcessBuilder pb) {118try {119return commandOutput(pb.start());120} catch (Throwable t) {121String commandline = "";122for (String arg : pb.command())123commandline += " " + arg;124System.out.println("Exception trying to run process: " + commandline);125unexpected(t);126return "";127}128}129130private static String commandOutput(String...command) {131try {132return commandOutput(Runtime.getRuntime().exec(command));133} catch (Throwable t) {134String commandline = "";135for (String arg : command)136commandline += " " + arg;137System.out.println("Exception trying to run process: " + commandline);138unexpected(t);139return "";140}141}142143private static void checkCommandOutput(ProcessBuilder pb,144String expected,145String failureMsg) {146String got = commandOutput(pb);147check(got.equals(expected),148failureMsg + "\n" +149"Expected: \"" + expected + "\"\n" +150"Got: \"" + got + "\"");151}152153private static String absolutifyPath(String path) {154StringBuilder sb = new StringBuilder();155for (String file : path.split(File.pathSeparator)) {156if (sb.length() != 0)157sb.append(File.pathSeparator);158sb.append(new File(file).getAbsolutePath());159}160return sb.toString();161}162163// compare windows-style, by canonicalizing to upper case,164// not lower case as String.compareToIgnoreCase does165private static class WindowsComparator166implements Comparator<String> {167public int compare(String x, String y) {168return x.toUpperCase(Locale.US)169.compareTo(y.toUpperCase(Locale.US));170}171}172173private static String sortedLines(String lines) {174String[] arr = lines.split("\n");175List<String> ls = new ArrayList<String>();176for (String s : arr)177ls.add(s);178Collections.sort(ls, new WindowsComparator());179StringBuilder sb = new StringBuilder();180for (String s : ls)181sb.append(s + "\n");182return sb.toString();183}184185private static void compareLinesIgnoreCase(String lines1, String lines2) {186if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) {187String dashes =188"-----------------------------------------------------";189out.println(dashes);190out.print(sortedLines(lines1));191out.println(dashes);192out.print(sortedLines(lines2));193out.println(dashes);194out.println("sizes: " + sortedLines(lines1).length() +195" " + sortedLines(lines2).length());196197fail("Sorted string contents differ");198}199}200201private static final Runtime runtime = Runtime.getRuntime();202203private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"};204205private static String winEnvFilter(String env) {206return env.replaceAll("\r", "")207.replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n","");208}209210private static String unixEnvProg() {211return new File("/usr/bin/env").canExecute() ? "/usr/bin/env"212: "/bin/env";213}214215private static String nativeEnv(String[] env) {216try {217if (Windows.is()) {218return winEnvFilter219(commandOutput(runtime.exec(winEnvCommand, env)));220} else {221return commandOutput(runtime.exec(unixEnvProg(), env));222}223} catch (Throwable t) { throw new Error(t); }224}225226private static String nativeEnv(ProcessBuilder pb) {227try {228if (Windows.is()) {229pb.command(winEnvCommand);230return winEnvFilter(commandOutput(pb));231} else {232pb.command(new String[]{unixEnvProg()});233return commandOutput(pb);234}235} catch (Throwable t) { throw new Error(t); }236}237238private static void checkSizes(Map<String,String> environ, int size) {239try {240equal(size, environ.size());241equal(size, environ.entrySet().size());242equal(size, environ.keySet().size());243equal(size, environ.values().size());244245boolean isEmpty = (size == 0);246equal(isEmpty, environ.isEmpty());247equal(isEmpty, environ.entrySet().isEmpty());248equal(isEmpty, environ.keySet().isEmpty());249equal(isEmpty, environ.values().isEmpty());250} catch (Throwable t) { unexpected(t); }251}252253private interface EnvironmentFrobber {254void doIt(Map<String,String> environ);255}256257private static void testVariableDeleter(EnvironmentFrobber fooDeleter) {258try {259Map<String,String> environ = new ProcessBuilder().environment();260environ.put("Foo", "BAAR");261fooDeleter.doIt(environ);262equal(environ.get("Foo"), null);263equal(environ.remove("Foo"), null);264} catch (Throwable t) { unexpected(t); }265}266267private static void testVariableAdder(EnvironmentFrobber fooAdder) {268try {269Map<String,String> environ = new ProcessBuilder().environment();270environ.remove("Foo");271fooAdder.doIt(environ);272equal(environ.get("Foo"), "Bahrein");273} catch (Throwable t) { unexpected(t); }274}275276private static void testVariableModifier(EnvironmentFrobber fooModifier) {277try {278Map<String,String> environ = new ProcessBuilder().environment();279environ.put("Foo","OldValue");280fooModifier.doIt(environ);281equal(environ.get("Foo"), "NewValue");282} catch (Throwable t) { unexpected(t); }283}284285private static void printUTF8(String s) throws IOException {286out.write(s.getBytes("UTF-8"));287}288289private static String getenvAsString(Map<String,String> environment) {290StringBuilder sb = new StringBuilder();291environment = new TreeMap<>(environment);292for (Map.Entry<String,String> e : environment.entrySet())293// Ignore magic environment variables added by the launcher294if (! e.getKey().equals("LD_LIBRARY_PATH"))295sb.append(e.getKey())296.append('=')297.append(e.getValue())298.append(',');299return sb.toString();300}301302static void print4095(OutputStream s, byte b) throws Throwable {303byte[] bytes = new byte[4095];304Arrays.fill(bytes, b);305s.write(bytes); // Might hang!306}307308static void checkPermissionDenied(ProcessBuilder pb) {309try {310pb.start();311fail("Expected IOException not thrown");312} catch (IOException e) {313String m = e.getMessage();314if (EnglishUnix.is() &&315! matches(m, PERMISSION_DENIED_ERROR_MSG))316unexpected(e);317} catch (Throwable t) { unexpected(t); }318}319320public static class JavaChild {321public static void main(String args[]) throws Throwable {322String action = args[0];323if (action.equals("sleep")) {324Thread.sleep(10 * 60 * 1000L);325} else if (action.equals("pid")) {326System.out.println(ProcessHandle.current().pid());327} else if (action.equals("testIO")) {328String expected = "standard input";329char[] buf = new char[expected.length()+1];330int n = new InputStreamReader(System.in).read(buf,0,buf.length);331if (n != expected.length())332System.exit(5);333if (! new String(buf,0,n).equals(expected))334System.exit(5);335System.err.print("standard error");336System.out.print("standard output");337} else if (action.equals("testInheritIO")338|| action.equals("testRedirectInherit")) {339List<String> childArgs = new ArrayList<String>(javaChildArgs);340childArgs.add("testIO");341ProcessBuilder pb = new ProcessBuilder(childArgs);342if (action.equals("testInheritIO"))343pb.inheritIO();344else345redirectIO(pb, INHERIT, INHERIT, INHERIT);346ProcessResults r = run(pb);347if (! r.out().equals(""))348System.exit(7);349if (! r.err().equals(""))350System.exit(8);351if (r.exitValue() != 0)352System.exit(9);353} else if (action.equals("System.getenv(String)")) {354String val = System.getenv(args[1]);355printUTF8(val == null ? "null" : val);356} else if (action.equals("System.getenv(\\u1234)")) {357String val = System.getenv("\u1234");358printUTF8(val == null ? "null" : val);359} else if (action.equals("System.getenv()")) {360printUTF8(getenvAsString(System.getenv()));361} else if (action.equals("ArrayOOME")) {362Object dummy;363switch(new Random().nextInt(3)) {364case 0: dummy = new Integer[Integer.MAX_VALUE]; break;365case 1: dummy = new double[Integer.MAX_VALUE]; break;366case 2: dummy = new byte[Integer.MAX_VALUE][]; break;367default: throw new InternalError();368}369} else if (action.equals("pwd")) {370printUTF8(new File(System.getProperty("user.dir"))371.getCanonicalPath());372} else if (action.equals("print4095")) {373print4095(System.out, (byte) '!');374print4095(System.err, (byte) 'E');375System.exit(5);376} else if (action.equals("OutErr")) {377// You might think the system streams would be378// buffered, and in fact they are implemented using379// BufferedOutputStream, but each and every print380// causes immediate operating system I/O.381System.out.print("out");382System.err.print("err");383System.out.print("out");384System.err.print("err");385} else if (action.equals("null PATH")) {386equal(System.getenv("PATH"), null);387check(new File("/bin/true").exists());388check(new File("/bin/false").exists());389ProcessBuilder pb1 = new ProcessBuilder();390ProcessBuilder pb2 = new ProcessBuilder();391pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");392ProcessResults r;393394for (final ProcessBuilder pb :395new ProcessBuilder[] {pb1, pb2}) {396pb.command("true");397equal(run(pb).exitValue(), True.exitValue());398399pb.command("false");400equal(run(pb).exitValue(), False.exitValue());401}402403if (failed != 0) throw new Error("null PATH");404} else if (action.equals("PATH search algorithm")) {405equal(System.getenv("PATH"), "dir1:dir2:");406check(new File(TrueExe.path()).exists());407check(new File(FalseExe.path()).exists());408String[] cmd = {"prog"};409ProcessBuilder pb1 = new ProcessBuilder(cmd);410ProcessBuilder pb2 = new ProcessBuilder(cmd);411ProcessBuilder pb3 = new ProcessBuilder(cmd);412pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");413pb3.environment().remove("PATH");414415for (final ProcessBuilder pb :416new ProcessBuilder[] {pb1, pb2, pb3}) {417try {418// Not on PATH at all; directories don't exist419try {420pb.start();421fail("Expected IOException not thrown");422} catch (IOException e) {423String m = e.getMessage();424if (EnglishUnix.is() &&425! matches(m, NO_SUCH_FILE_ERROR_MSG))426unexpected(e);427} catch (Throwable t) { unexpected(t); }428429// Not on PATH at all; directories exist430new File("dir1").mkdirs();431new File("dir2").mkdirs();432try {433pb.start();434fail("Expected IOException not thrown");435} catch (IOException e) {436String m = e.getMessage();437if (EnglishUnix.is() &&438! matches(m, NO_SUCH_FILE_ERROR_MSG))439unexpected(e);440} catch (Throwable t) { unexpected(t); }441442// Can't execute a directory -- permission denied443// Report EACCES errno444new File("dir1/prog").mkdirs();445checkPermissionDenied(pb);446447// continue searching if EACCES448copy(TrueExe.path(), "dir2/prog");449equal(run(pb).exitValue(), True.exitValue());450new File("dir1/prog").delete();451new File("dir2/prog").delete();452453new File("dir2/prog").mkdirs();454copy(TrueExe.path(), "dir1/prog");455equal(run(pb).exitValue(), True.exitValue());456457// Check empty PATH component means current directory.458//459// While we're here, let's test different kinds of460// Unix executables, and PATH vs explicit searching.461new File("dir1/prog").delete();462new File("dir2/prog").delete();463for (String[] command :464new String[][] {465new String[] {"./prog"},466cmd}) {467pb.command(command);468File prog = new File("./prog");469// "Normal" binaries470copy(TrueExe.path(), "./prog");471equal(run(pb).exitValue(),472True.exitValue());473copy(FalseExe.path(), "./prog");474equal(run(pb).exitValue(),475False.exitValue());476prog.delete();477// Interpreter scripts with #!478setFileContents(prog, "#!/bin/true\n");479prog.setExecutable(true);480equal(run(pb).exitValue(),481True.exitValue());482prog.delete();483setFileContents(prog, "#!/bin/false\n");484prog.setExecutable(true);485equal(run(pb).exitValue(),486False.exitValue());487// Traditional shell scripts without #!488setFileContents(prog, "exec /bin/true\n");489prog.setExecutable(true);490equal(run(pb).exitValue(),491True.exitValue());492prog.delete();493setFileContents(prog, "exec /bin/false\n");494prog.setExecutable(true);495equal(run(pb).exitValue(),496False.exitValue());497prog.delete();498}499500// Test Unix interpreter scripts501File dir1Prog = new File("dir1/prog");502dir1Prog.delete();503pb.command(new String[] {"prog", "world"});504setFileContents(dir1Prog, "#!/bin/echo hello\n");505checkPermissionDenied(pb);506dir1Prog.setExecutable(true);507equal(run(pb).out(), "hello dir1/prog world\n");508equal(run(pb).exitValue(), True.exitValue());509dir1Prog.delete();510pb.command(cmd);511512// Test traditional shell scripts without #!513setFileContents(dir1Prog, "/bin/echo \"$@\"\n");514pb.command(new String[] {"prog", "hello", "world"});515checkPermissionDenied(pb);516dir1Prog.setExecutable(true);517equal(run(pb).out(), "hello world\n");518equal(run(pb).exitValue(), True.exitValue());519dir1Prog.delete();520pb.command(cmd);521522// If prog found on both parent and child's PATH,523// parent's is used.524new File("dir1/prog").delete();525new File("dir2/prog").delete();526new File("prog").delete();527new File("dir3").mkdirs();528copy(TrueExe.path(), "dir1/prog");529copy(FalseExe.path(), "dir3/prog");530pb.environment().put("PATH","dir3");531equal(run(pb).exitValue(), True.exitValue());532copy(TrueExe.path(), "dir3/prog");533copy(FalseExe.path(), "dir1/prog");534equal(run(pb).exitValue(), False.exitValue());535536} finally {537// cleanup538new File("dir1/prog").delete();539new File("dir2/prog").delete();540new File("dir3/prog").delete();541new File("dir1").delete();542new File("dir2").delete();543new File("dir3").delete();544new File("prog").delete();545}546}547548if (failed != 0) throw new Error("PATH search algorithm");549}550else throw new Error("JavaChild invocation error");551}552}553554private static void copy(String src, String dst) throws IOException {555Files.copy(Paths.get(src), Paths.get(dst),556StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);557}558559private static String javaChildOutput(ProcessBuilder pb, String...args) {560List<String> list = new ArrayList<String>(javaChildArgs);561for (String arg : args)562list.add(arg);563pb.command(list);564return commandOutput(pb);565}566567private static String getenvInChild(ProcessBuilder pb) {568return javaChildOutput(pb, "System.getenv()");569}570571private static String getenvInChild1234(ProcessBuilder pb) {572return javaChildOutput(pb, "System.getenv(\\u1234)");573}574575private static String getenvInChild(ProcessBuilder pb, String name) {576return javaChildOutput(pb, "System.getenv(String)", name);577}578579private static String pwdInChild(ProcessBuilder pb) {580return javaChildOutput(pb, "pwd");581}582583private static final String javaExe =584System.getProperty("java.home") +585File.separator + "bin" + File.separator + "java";586587private static final String classpath =588System.getProperty("java.class.path");589590private static final List<String> javaChildArgs =591Arrays.asList(javaExe,592"-XX:+DisplayVMOutputToStderr",593"-classpath", absolutifyPath(classpath),594"Basic$JavaChild");595596private static void testEncoding(String encoding, String tested) {597try {598// If round trip conversion works, should be able to set env vars599// correctly in child.600if (new String(tested.getBytes()).equals(tested)) {601out.println("Testing " + encoding + " environment values");602ProcessBuilder pb = new ProcessBuilder();603pb.environment().put("ASCIINAME",tested);604equal(getenvInChild(pb,"ASCIINAME"), tested);605}606} catch (Throwable t) { unexpected(t); }607}608609static class Windows {610public static boolean is() { return is; }611private static final boolean is =612System.getProperty("os.name").startsWith("Windows");613}614615static class AIX {616public static boolean is() { return is; }617private static final boolean is =618System.getProperty("os.name").equals("AIX");619}620621static class Unix {622public static boolean is() { return is; }623private static final boolean is =624(! Windows.is() &&625new File("/bin/sh").exists() &&626new File("/bin/true").exists() &&627new File("/bin/false").exists());628}629630static class UnicodeOS {631public static boolean is() { return is; }632private static final String osName = System.getProperty("os.name");633private static final boolean is =634// MacOS X would probably also qualify635osName.startsWith("Windows") &&636! osName.startsWith("Windows 9") &&637! osName.equals("Windows Me");638}639640static class MacOSX {641public static boolean is() { return is; }642private static final String osName = System.getProperty("os.name");643private static final boolean is = osName.contains("OS X");644}645646static class True {647public static int exitValue() { return 0; }648}649650private static class False {651public static int exitValue() { return exitValue; }652private static final int exitValue = exitValue0();653private static int exitValue0() {654// /bin/false returns an *unspecified* non-zero number.655try {656if (! Unix.is())657return -1;658else {659int rc = new ProcessBuilder("/bin/false")660.start().waitFor();661check(rc != 0);662return rc;663}664} catch (Throwable t) { unexpected(t); return -1; }665}666}667668// On Alpine Linux, /bin/true and /bin/false are just links to /bin/busybox.669// Some tests copy /bin/true and /bin/false to files with a different filename.670// However, copying the busbox executable into a file with a different name671// won't result in the expected return codes. As workaround, we create672// executable files that can be copied and produce the expected return673// values.674675private static class TrueExe {676public static String path() { return path; }677private static final String path = path0();678private static String path0(){679if (!Platform.isBusybox("/bin/true")) {680return "/bin/true";681} else {682File trueExe = new File("true");683setFileContents(trueExe, "#!/bin/true\n");684trueExe.setExecutable(true);685return trueExe.getAbsolutePath();686}687}688}689690private static class FalseExe {691public static String path() { return path; }692private static final String path = path0();693private static String path0(){694if (!Platform.isBusybox("/bin/false")) {695return "/bin/false";696} else {697File falseExe = new File("false");698setFileContents(falseExe, "#!/bin/false\n");699falseExe.setExecutable(true);700return falseExe.getAbsolutePath();701}702}703}704705static class EnglishUnix {706private static final Boolean is =707(! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));708709private static boolean isEnglish(String envvar) {710String val = getenv(envvar);711return (val == null) || val.matches("en.*") || val.matches("C");712}713714/** Returns true if we can expect English OS error strings */715static boolean is() { return is; }716}717718static class DelegatingProcess extends Process {719final Process p;720721DelegatingProcess(Process p) {722this.p = p;723}724725@Override726public void destroy() {727p.destroy();728}729730@Override731public int exitValue() {732return p.exitValue();733}734735@Override736public int waitFor() throws InterruptedException {737return p.waitFor();738}739740@Override741public OutputStream getOutputStream() {742return p.getOutputStream();743}744745@Override746public InputStream getInputStream() {747return p.getInputStream();748}749750@Override751public InputStream getErrorStream() {752return p.getErrorStream();753}754}755756private static boolean matches(String str, String regex) {757return Pattern.compile(regex).matcher(str).find();758}759760private static String matchAndExtract(String str, String regex) {761Matcher matcher = Pattern.compile(regex).matcher(str);762if (matcher.find()) {763return matcher.group();764} else {765return "";766}767}768769/* Only used for Mac OS X --770* Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty771* environment. The environment variable JAVA_MAIN_CLASS_<pid> may also772* be set in Mac OS X.773* Remove them both from the list of env variables774*/775private static String removeMacExpectedVars(String vars) {776// Check for __CF_USER_TEXT_ENCODING777String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="778+cfUserTextEncoding+",","");779// Check for JAVA_MAIN_CLASS_<pid>780String javaMainClassStr781= matchAndExtract(cleanedVars,782"JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");783return cleanedVars.replace(javaMainClassStr,"");784}785786/* Only used for AIX --787* AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.788* Remove it from the list of env variables789*/790private static String removeAixExpectedVars(String vars) {791return vars.replace("AIXTHREAD_GUARDPAGES=0,", "");792}793794private static String sortByLinesWindowsly(String text) {795String[] lines = text.split("\n");796Arrays.sort(lines, new WindowsComparator());797StringBuilder sb = new StringBuilder();798for (String line : lines)799sb.append(line).append("\n");800return sb.toString();801}802803private static void checkMapSanity(Map<String,String> map) {804try {805Set<String> keySet = map.keySet();806Collection<String> values = map.values();807Set<Map.Entry<String,String>> entrySet = map.entrySet();808809equal(entrySet.size(), keySet.size());810equal(entrySet.size(), values.size());811812StringBuilder s1 = new StringBuilder();813for (Map.Entry<String,String> e : entrySet)814s1.append(e.getKey() + "=" + e.getValue() + "\n");815816StringBuilder s2 = new StringBuilder();817for (String var : keySet)818s2.append(var + "=" + map.get(var) + "\n");819820equal(s1.toString(), s2.toString());821822Iterator<String> kIter = keySet.iterator();823Iterator<String> vIter = values.iterator();824Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();825826while (eIter.hasNext()) {827Map.Entry<String,String> entry = eIter.next();828String key = kIter.next();829String value = vIter.next();830check(entrySet.contains(entry));831check(keySet.contains(key));832check(values.contains(value));833check(map.containsKey(key));834check(map.containsValue(value));835equal(entry.getKey(), key);836equal(entry.getValue(), value);837}838check(!kIter.hasNext() &&839!vIter.hasNext());840841} catch (Throwable t) { unexpected(t); }842}843844private static void checkMapEquality(Map<String,String> map1,845Map<String,String> map2) {846try {847equal(map1.size(), map2.size());848equal(map1.isEmpty(), map2.isEmpty());849for (String key : map1.keySet()) {850equal(map1.get(key), map2.get(key));851check(map2.keySet().contains(key));852}853equal(map1, map2);854equal(map2, map1);855equal(map1.entrySet(), map2.entrySet());856equal(map2.entrySet(), map1.entrySet());857equal(map1.keySet(), map2.keySet());858equal(map2.keySet(), map1.keySet());859860equal(map1.hashCode(), map2.hashCode());861equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());862equal(map1.keySet().hashCode(), map2.keySet().hashCode());863} catch (Throwable t) { unexpected(t); }864}865866static void checkRedirects(ProcessBuilder pb,867Redirect in, Redirect out, Redirect err) {868equal(pb.redirectInput(), in);869equal(pb.redirectOutput(), out);870equal(pb.redirectError(), err);871}872873static void redirectIO(ProcessBuilder pb,874Redirect in, Redirect out, Redirect err) {875pb.redirectInput(in);876pb.redirectOutput(out);877pb.redirectError(err);878}879880static void setFileContents(File file, String contents) {881try {882Writer w = new FileWriter(file);883w.write(contents);884w.close();885} catch (Throwable t) { unexpected(t); }886}887888static String fileContents(File file) {889try {890Reader r = new FileReader(file);891StringBuilder sb = new StringBuilder();892char[] buffer = new char[1024];893int n;894while ((n = r.read(buffer)) != -1)895sb.append(buffer,0,n);896r.close();897return new String(sb);898} catch (Throwable t) { unexpected(t); return ""; }899}900901static void testIORedirection() throws Throwable {902final File ifile = new File("ifile");903final File ofile = new File("ofile");904final File efile = new File("efile");905ifile.delete();906ofile.delete();907efile.delete();908909//----------------------------------------------------------------910// Check mutual inequality of different types of Redirect911//----------------------------------------------------------------912Redirect[] redirects =913{ PIPE,914INHERIT,915DISCARD,916Redirect.from(ifile),917Redirect.to(ifile),918Redirect.appendTo(ifile),919Redirect.from(ofile),920Redirect.to(ofile),921Redirect.appendTo(ofile),922};923for (int i = 0; i < redirects.length; i++)924for (int j = 0; j < redirects.length; j++)925equal(redirects[i].equals(redirects[j]), (i == j));926927//----------------------------------------------------------------928// Check basic properties of different types of Redirect929//----------------------------------------------------------------930equal(PIPE.type(), Redirect.Type.PIPE);931equal(PIPE.toString(), "PIPE");932equal(PIPE.file(), null);933934equal(INHERIT.type(), Redirect.Type.INHERIT);935equal(INHERIT.toString(), "INHERIT");936equal(INHERIT.file(), null);937938equal(DISCARD.type(), Redirect.Type.WRITE);939equal(DISCARD.toString(), "WRITE");940equal(DISCARD.file(), new File((Windows.is() ? "NUL" : "/dev/null")));941942equal(Redirect.from(ifile).type(), Redirect.Type.READ);943equal(Redirect.from(ifile).toString(),944"redirect to read from file \"ifile\"");945equal(Redirect.from(ifile).file(), ifile);946equal(Redirect.from(ifile),947Redirect.from(ifile));948equal(Redirect.from(ifile).hashCode(),949Redirect.from(ifile).hashCode());950951equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);952equal(Redirect.to(ofile).toString(),953"redirect to write to file \"ofile\"");954equal(Redirect.to(ofile).file(), ofile);955equal(Redirect.to(ofile),956Redirect.to(ofile));957equal(Redirect.to(ofile).hashCode(),958Redirect.to(ofile).hashCode());959960equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);961equal(Redirect.appendTo(efile).toString(),962"redirect to append to file \"efile\"");963equal(Redirect.appendTo(efile).file(), efile);964equal(Redirect.appendTo(efile),965Redirect.appendTo(efile));966equal(Redirect.appendTo(efile).hashCode(),967Redirect.appendTo(efile).hashCode());968969//----------------------------------------------------------------970// Check initial values of redirects971//----------------------------------------------------------------972List<String> childArgs = new ArrayList<String>(javaChildArgs);973childArgs.add("testIO");974final ProcessBuilder pb = new ProcessBuilder(childArgs);975checkRedirects(pb, PIPE, PIPE, PIPE);976977//----------------------------------------------------------------978// Check inheritIO979//----------------------------------------------------------------980pb.inheritIO();981checkRedirects(pb, INHERIT, INHERIT, INHERIT);982983//----------------------------------------------------------------984// Check DISCARD for stdout,stderr985//----------------------------------------------------------------986redirectIO(pb, INHERIT, DISCARD, DISCARD);987checkRedirects(pb, INHERIT, DISCARD, DISCARD);988989//----------------------------------------------------------------990// Check setters and getters agree991//----------------------------------------------------------------992pb.redirectInput(ifile);993equal(pb.redirectInput().file(), ifile);994equal(pb.redirectInput(), Redirect.from(ifile));995996pb.redirectOutput(ofile);997equal(pb.redirectOutput().file(), ofile);998equal(pb.redirectOutput(), Redirect.to(ofile));9991000pb.redirectError(efile);1001equal(pb.redirectError().file(), efile);1002equal(pb.redirectError(), Redirect.to(efile));10031004THROWS(IllegalArgumentException.class,1005() -> pb.redirectInput(Redirect.to(ofile)),1006() -> pb.redirectOutput(Redirect.from(ifile)),1007() -> pb.redirectError(Redirect.from(ifile)),1008() -> pb.redirectInput(DISCARD));10091010THROWS(NullPointerException.class,1011() -> pb.redirectInput((File)null),1012() -> pb.redirectOutput((File)null),1013() -> pb.redirectError((File)null),1014() -> pb.redirectInput((Redirect)null),1015() -> pb.redirectOutput((Redirect)null),1016() -> pb.redirectError((Redirect)null));10171018THROWS(IOException.class,1019// Input file does not exist1020() -> pb.start());1021setFileContents(ifile, "standard input");10221023//----------------------------------------------------------------1024// Writing to non-existent files1025//----------------------------------------------------------------1026{1027ProcessResults r = run(pb);1028equal(r.exitValue(), 0);1029equal(fileContents(ofile), "standard output");1030equal(fileContents(efile), "standard error");1031equal(r.out(), "");1032equal(r.err(), "");1033ofile.delete();1034efile.delete();1035}10361037//----------------------------------------------------------------1038// Both redirectErrorStream + redirectError1039//----------------------------------------------------------------1040{1041pb.redirectErrorStream(true);1042ProcessResults r = run(pb);1043equal(r.exitValue(), 0);1044equal(fileContents(ofile),1045"standard error" + "standard output");1046equal(fileContents(efile), "");1047equal(r.out(), "");1048equal(r.err(), "");1049ofile.delete();1050efile.delete();1051}10521053//----------------------------------------------------------------1054// Appending to existing files1055//----------------------------------------------------------------1056{1057setFileContents(ofile, "ofile-contents");1058setFileContents(efile, "efile-contents");1059pb.redirectOutput(Redirect.appendTo(ofile));1060pb.redirectError(Redirect.appendTo(efile));1061pb.redirectErrorStream(false);1062ProcessResults r = run(pb);1063equal(r.exitValue(), 0);1064equal(fileContents(ofile),1065"ofile-contents" + "standard output");1066equal(fileContents(efile),1067"efile-contents" + "standard error");1068equal(r.out(), "");1069equal(r.err(), "");1070ofile.delete();1071efile.delete();1072}10731074//----------------------------------------------------------------1075// Replacing existing files1076//----------------------------------------------------------------1077{1078setFileContents(ofile, "ofile-contents");1079setFileContents(efile, "efile-contents");1080pb.redirectOutput(ofile);1081pb.redirectError(Redirect.to(efile));1082ProcessResults r = run(pb);1083equal(r.exitValue(), 0);1084equal(fileContents(ofile), "standard output");1085equal(fileContents(efile), "standard error");1086equal(r.out(), "");1087equal(r.err(), "");1088ofile.delete();1089efile.delete();1090}10911092//----------------------------------------------------------------1093// Appending twice to the same file?1094//----------------------------------------------------------------1095{1096setFileContents(ofile, "ofile-contents");1097setFileContents(efile, "efile-contents");1098Redirect appender = Redirect.appendTo(ofile);1099pb.redirectOutput(appender);1100pb.redirectError(appender);1101ProcessResults r = run(pb);1102equal(r.exitValue(), 0);1103equal(fileContents(ofile),1104"ofile-contents" +1105"standard error" +1106"standard output");1107equal(fileContents(efile), "efile-contents");1108equal(r.out(), "");1109equal(r.err(), "");1110ifile.delete();1111ofile.delete();1112efile.delete();1113}11141115//----------------------------------------------------------------1116// DISCARDing output1117//----------------------------------------------------------------1118{1119setFileContents(ifile, "standard input");1120pb.redirectOutput(DISCARD);1121pb.redirectError(DISCARD);1122ProcessResults r = run(pb);1123equal(r.exitValue(), 0);1124equal(r.out(), "");1125equal(r.err(), "");1126}11271128//----------------------------------------------------------------1129// DISCARDing output and redirecting error1130//----------------------------------------------------------------1131{1132setFileContents(ifile, "standard input");1133setFileContents(ofile, "ofile-contents");1134setFileContents(efile, "efile-contents");1135pb.redirectOutput(DISCARD);1136pb.redirectError(efile);1137ProcessResults r = run(pb);1138equal(r.exitValue(), 0);1139equal(fileContents(ofile), "ofile-contents");1140equal(fileContents(efile), "standard error");1141equal(r.out(), "");1142equal(r.err(), "");1143ofile.delete();1144efile.delete();1145}11461147//----------------------------------------------------------------1148// DISCARDing error and redirecting output1149//----------------------------------------------------------------1150{1151setFileContents(ifile, "standard input");1152setFileContents(ofile, "ofile-contents");1153setFileContents(efile, "efile-contents");1154pb.redirectOutput(ofile);1155pb.redirectError(DISCARD);1156ProcessResults r = run(pb);1157equal(r.exitValue(), 0);1158equal(fileContents(ofile), "standard output");1159equal(fileContents(efile), "efile-contents");1160equal(r.out(), "");1161equal(r.err(), "");1162ofile.delete();1163efile.delete();1164}11651166//----------------------------------------------------------------1167// DISCARDing output and merging error into output1168//----------------------------------------------------------------1169{1170setFileContents(ifile, "standard input");1171setFileContents(ofile, "ofile-contents");1172setFileContents(efile, "efile-contents");1173pb.redirectOutput(DISCARD);1174pb.redirectErrorStream(true);1175pb.redirectError(efile);1176ProcessResults r = run(pb);1177equal(r.exitValue(), 0);1178equal(fileContents(ofile), "ofile-contents"); // untouched1179equal(fileContents(efile), ""); // empty1180equal(r.out(), "");1181equal(r.err(), "");1182ifile.delete();1183ofile.delete();1184efile.delete();1185pb.redirectErrorStream(false); // reset for next test1186}11871188//----------------------------------------------------------------1189// Testing INHERIT is harder.1190// Note that this requires __FOUR__ nested JVMs involved in one test,1191// if you count the harness JVM.1192//----------------------------------------------------------------1193for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {1194redirectIO(pb, PIPE, PIPE, PIPE);1195List<String> command = pb.command();1196command.set(command.size() - 1, testName);1197Process p = pb.start();1198new PrintStream(p.getOutputStream()).print("standard input");1199p.getOutputStream().close();1200ProcessResults r = run(p);1201equal(r.exitValue(), 0);1202equal(r.out(), "standard output");1203equal(r.err(), "standard error");1204}12051206//----------------------------------------------------------------1207// Test security implications of I/O redirection1208//----------------------------------------------------------------12091210// Read access to current directory is always granted;1211// So create a tmpfile for input instead.1212final File tmpFile = File.createTempFile("Basic", "tmp");1213setFileContents(tmpFile, "standard input");12141215final Policy policy = new Policy();1216Policy.setPolicy(policy);1217System.setSecurityManager(new SecurityManager());1218try {1219final Permission xPermission1220= new FilePermission("<<ALL FILES>>", "execute");1221final Permission rxPermission1222= new FilePermission("<<ALL FILES>>", "read,execute");1223final Permission wxPermission1224= new FilePermission("<<ALL FILES>>", "write,execute");1225final Permission rwxPermission1226= new FilePermission("<<ALL FILES>>", "read,write,execute");12271228THROWS(SecurityException.class,1229() -> { policy.setPermissions(xPermission);1230redirectIO(pb, from(tmpFile), PIPE, PIPE);1231pb.start();},1232() -> { policy.setPermissions(rxPermission);1233redirectIO(pb, PIPE, to(ofile), PIPE);1234pb.start();},1235() -> { policy.setPermissions(rxPermission);1236redirectIO(pb, PIPE, PIPE, to(efile));1237pb.start();});12381239{1240policy.setPermissions(rxPermission);1241redirectIO(pb, from(tmpFile), PIPE, PIPE);1242ProcessResults r = run(pb);1243equal(r.out(), "standard output");1244equal(r.err(), "standard error");1245}12461247{1248policy.setPermissions(wxPermission);1249redirectIO(pb, PIPE, to(ofile), to(efile));1250Process p = pb.start();1251new PrintStream(p.getOutputStream()).print("standard input");1252p.getOutputStream().close();1253ProcessResults r = run(p);1254policy.setPermissions(rwxPermission);1255equal(fileContents(ofile), "standard output");1256equal(fileContents(efile), "standard error");1257}12581259{1260policy.setPermissions(rwxPermission);1261redirectIO(pb, from(tmpFile), to(ofile), to(efile));1262ProcessResults r = run(pb);1263policy.setPermissions(rwxPermission);1264equal(fileContents(ofile), "standard output");1265equal(fileContents(efile), "standard error");1266}12671268} finally {1269policy.setPermissions(new RuntimePermission("setSecurityManager"));1270System.setSecurityManager(null);1271tmpFile.delete();1272ifile.delete();1273ofile.delete();1274efile.delete();1275}1276}12771278static void checkProcessPid() {1279ProcessBuilder pb = new ProcessBuilder();1280List<String> list = new ArrayList<String>(javaChildArgs);1281list.add("pid");1282pb.command(list);1283try {1284Process p = pb.start();1285String s = commandOutput(p);1286long actualPid = Long.valueOf(s.trim());1287long expectedPid = p.pid();1288equal(actualPid, expectedPid);1289} catch (Throwable t) {1290unexpected(t);1291}129212931294// Test the default implementation of Process.getPid1295DelegatingProcess p = new DelegatingProcess(null);1296THROWS(UnsupportedOperationException.class,1297() -> p.pid(),1298() -> p.toHandle(),1299() -> p.supportsNormalTermination(),1300() -> p.children(),1301() -> p.descendants());13021303}13041305private static void realMain(String[] args) throws Throwable {1306if (Windows.is())1307System.out.println("This appears to be a Windows system.");1308if (Unix.is())1309System.out.println("This appears to be a Unix system.");1310if (UnicodeOS.is())1311System.out.println("This appears to be a Unicode-based OS.");13121313try { testIORedirection(); }1314catch (Throwable t) { unexpected(t); }13151316//----------------------------------------------------------------1317// Basic tests for getPid()1318//----------------------------------------------------------------1319checkProcessPid();13201321//----------------------------------------------------------------1322// Basic tests for setting, replacing and deleting envvars1323//----------------------------------------------------------------1324try {1325ProcessBuilder pb = new ProcessBuilder();1326Map<String,String> environ = pb.environment();13271328// New env var1329environ.put("QUUX", "BAR");1330equal(environ.get("QUUX"), "BAR");1331equal(getenvInChild(pb,"QUUX"), "BAR");13321333// Modify env var1334environ.put("QUUX","bear");1335equal(environ.get("QUUX"), "bear");1336equal(getenvInChild(pb,"QUUX"), "bear");1337checkMapSanity(environ);13381339// Remove env var1340environ.remove("QUUX");1341equal(environ.get("QUUX"), null);1342equal(getenvInChild(pb,"QUUX"), "null");1343checkMapSanity(environ);13441345// Remove non-existent env var1346environ.remove("QUUX");1347equal(environ.get("QUUX"), null);1348equal(getenvInChild(pb,"QUUX"), "null");1349checkMapSanity(environ);1350} catch (Throwable t) { unexpected(t); }13511352//----------------------------------------------------------------1353// Pass Empty environment to child1354//----------------------------------------------------------------1355try {1356ProcessBuilder pb = new ProcessBuilder();1357pb.environment().clear();1358String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";1359expected = AIX.is() ? "LIBPATH="+libpath+",": expected;1360if (Windows.is()) {1361pb.environment().put("SystemRoot", systemRoot);1362}1363if (AIX.is()) {1364pb.environment().put("LIBPATH", libpath);1365}1366String result = getenvInChild(pb);1367if (MacOSX.is()) {1368result = removeMacExpectedVars(result);1369}1370if (AIX.is()) {1371result = removeAixExpectedVars(result);1372}1373equal(result, expected);1374} catch (Throwable t) { unexpected(t); }13751376//----------------------------------------------------------------1377// System.getenv() is read-only.1378//----------------------------------------------------------------1379THROWS(UnsupportedOperationException.class,1380() -> getenv().put("FOO","BAR"),1381() -> getenv().remove("PATH"),1382() -> getenv().keySet().remove("PATH"),1383() -> getenv().values().remove("someValue"));13841385try {1386Collection<Map.Entry<String,String>> c = getenv().entrySet();1387if (! c.isEmpty())1388try {1389c.iterator().next().setValue("foo");1390fail("Expected UnsupportedOperationException not thrown");1391} catch (UnsupportedOperationException e) {} // OK1392} catch (Throwable t) { unexpected(t); }13931394//----------------------------------------------------------------1395// System.getenv() always returns the same object in our implementation.1396//----------------------------------------------------------------1397try {1398check(System.getenv() == System.getenv());1399} catch (Throwable t) { unexpected(t); }14001401//----------------------------------------------------------------1402// You can't create an env var name containing "=",1403// or an env var name or value containing NUL.1404//----------------------------------------------------------------1405{1406final Map<String,String> m = new ProcessBuilder().environment();1407THROWS(IllegalArgumentException.class,1408() -> m.put("FOO=","BAR"),1409() -> m.put("FOO\u0000","BAR"),1410() -> m.put("FOO","BAR\u0000"));1411}14121413//----------------------------------------------------------------1414// Commands must never be null.1415//----------------------------------------------------------------1416THROWS(NullPointerException.class,1417() -> new ProcessBuilder((List<String>)null),1418() -> new ProcessBuilder().command((List<String>)null));14191420//----------------------------------------------------------------1421// Put in a command; get the same one back out.1422//----------------------------------------------------------------1423try {1424List<String> command = new ArrayList<String>();1425ProcessBuilder pb = new ProcessBuilder(command);1426check(pb.command() == command);1427List<String> command2 = new ArrayList<String>(2);1428command2.add("foo");1429command2.add("bar");1430pb.command(command2);1431check(pb.command() == command2);1432pb.command("foo", "bar");1433check(pb.command() != command2 && pb.command().equals(command2));1434pb.command(command2);1435command2.add("baz");1436equal(pb.command().get(2), "baz");1437} catch (Throwable t) { unexpected(t); }14381439//----------------------------------------------------------------1440// Commands must contain at least one element.1441//----------------------------------------------------------------1442THROWS(IndexOutOfBoundsException.class,1443() -> new ProcessBuilder().start(),1444() -> new ProcessBuilder(new ArrayList<String>()).start(),1445() -> Runtime.getRuntime().exec(new String[]{}));14461447//----------------------------------------------------------------1448// Commands must not contain null elements at start() time.1449//----------------------------------------------------------------1450THROWS(NullPointerException.class,1451() -> new ProcessBuilder("foo",null,"bar").start(),1452() -> new ProcessBuilder((String)null).start(),1453() -> new ProcessBuilder(new String[]{null}).start(),1454() -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());14551456//----------------------------------------------------------------1457// Command lists are growable.1458//----------------------------------------------------------------1459try {1460new ProcessBuilder().command().add("foo");1461new ProcessBuilder("bar").command().add("foo");1462new ProcessBuilder(new String[]{"1","2"}).command().add("3");1463} catch (Throwable t) { unexpected(t); }14641465//----------------------------------------------------------------1466// Nulls in environment updates generate NullPointerException1467//----------------------------------------------------------------1468try {1469final Map<String,String> env = new ProcessBuilder().environment();1470THROWS(NullPointerException.class,1471() -> env.put("foo",null),1472() -> env.put(null,"foo"),1473() -> env.remove(null),1474() -> { for (Map.Entry<String,String> e : env.entrySet())1475e.setValue(null);},1476() -> Runtime.getRuntime().exec(new String[]{"foo"},1477new String[]{null}));1478} catch (Throwable t) { unexpected(t); }14791480//----------------------------------------------------------------1481// Non-String types in environment updates generate ClassCastException1482//----------------------------------------------------------------1483try {1484final Map<String,String> env = new ProcessBuilder().environment();1485THROWS(ClassCastException.class,1486() -> env.remove(TRUE),1487() -> env.keySet().remove(TRUE),1488() -> env.values().remove(TRUE),1489() -> env.entrySet().remove(TRUE));1490} catch (Throwable t) { unexpected(t); }14911492//----------------------------------------------------------------1493// Check query operations on environment maps1494//----------------------------------------------------------------1495try {1496List<Map<String,String>> envs =1497new ArrayList<Map<String,String>>(2);1498envs.add(System.getenv());1499envs.add(new ProcessBuilder().environment());1500for (final Map<String,String> env : envs) {1501//----------------------------------------------------------------1502// Nulls in environment queries are forbidden.1503//----------------------------------------------------------------1504THROWS(NullPointerException.class,1505() -> getenv(null),1506() -> env.get(null),1507() -> env.containsKey(null),1508() -> env.containsValue(null),1509() -> env.keySet().contains(null),1510() -> env.values().contains(null));15111512//----------------------------------------------------------------1513// Non-String types in environment queries are forbidden.1514//----------------------------------------------------------------1515THROWS(ClassCastException.class,1516() -> env.get(TRUE),1517() -> env.containsKey(TRUE),1518() -> env.containsValue(TRUE),1519() -> env.keySet().contains(TRUE),1520() -> env.values().contains(TRUE));15211522//----------------------------------------------------------------1523// Illegal String values in environment queries are (grumble) OK1524//----------------------------------------------------------------1525equal(env.get("\u0000"), null);1526check(! env.containsKey("\u0000"));1527check(! env.containsValue("\u0000"));1528check(! env.keySet().contains("\u0000"));1529check(! env.values().contains("\u0000"));1530}15311532} catch (Throwable t) { unexpected(t); }15331534try {1535final Set<Map.Entry<String,String>> entrySet =1536new ProcessBuilder().environment().entrySet();1537THROWS(NullPointerException.class,1538() -> entrySet.contains(null));1539THROWS(ClassCastException.class,1540() -> entrySet.contains(TRUE),1541() -> entrySet.contains(1542new SimpleImmutableEntry<Boolean,String>(TRUE,"")));15431544check(! entrySet.contains1545(new SimpleImmutableEntry<String,String>("", "")));1546} catch (Throwable t) { unexpected(t); }15471548//----------------------------------------------------------------1549// Put in a directory; get the same one back out.1550//----------------------------------------------------------------1551try {1552ProcessBuilder pb = new ProcessBuilder();1553File foo = new File("foo");1554equal(pb.directory(), null);1555equal(pb.directory(foo).directory(), foo);1556equal(pb.directory(null).directory(), null);1557} catch (Throwable t) { unexpected(t); }15581559//----------------------------------------------------------------1560// If round-trip conversion works, check envvar pass-through to child1561//----------------------------------------------------------------1562try {1563testEncoding("ASCII", "xyzzy");1564testEncoding("Latin1", "\u00f1\u00e1");1565testEncoding("Unicode", "\u22f1\u11e1");1566} catch (Throwable t) { unexpected(t); }15671568//----------------------------------------------------------------1569// A surprisingly large number of ways to delete an environment var.1570//----------------------------------------------------------------1571testVariableDeleter(new EnvironmentFrobber() {1572public void doIt(Map<String,String> environ) {1573environ.remove("Foo");}});15741575testVariableDeleter(new EnvironmentFrobber() {1576public void doIt(Map<String,String> environ) {1577environ.keySet().remove("Foo");}});15781579testVariableDeleter(new EnvironmentFrobber() {1580public void doIt(Map<String,String> environ) {1581environ.values().remove("BAAR");}});15821583testVariableDeleter(new EnvironmentFrobber() {1584public void doIt(Map<String,String> environ) {1585// Legally fabricate a ProcessEnvironment.StringEntry,1586// even though it's private.1587Map<String,String> environ21588= new ProcessBuilder().environment();1589environ2.clear();1590environ2.put("Foo","BAAR");1591// Subtlety alert.1592Map.Entry<String,String> e1593= environ2.entrySet().iterator().next();1594environ.entrySet().remove(e);}});15951596testVariableDeleter(new EnvironmentFrobber() {1597public void doIt(Map<String,String> environ) {1598Map.Entry<String,String> victim = null;1599for (Map.Entry<String,String> e : environ.entrySet())1600if (e.getKey().equals("Foo"))1601victim = e;1602if (victim != null)1603environ.entrySet().remove(victim);}});16041605testVariableDeleter(new EnvironmentFrobber() {1606public void doIt(Map<String,String> environ) {1607Iterator<String> it = environ.keySet().iterator();1608while (it.hasNext()) {1609String val = it.next();1610if (val.equals("Foo"))1611it.remove();}}});16121613testVariableDeleter(new EnvironmentFrobber() {1614public void doIt(Map<String,String> environ) {1615Iterator<Map.Entry<String,String>> it1616= environ.entrySet().iterator();1617while (it.hasNext()) {1618Map.Entry<String,String> e = it.next();1619if (e.getKey().equals("Foo"))1620it.remove();}}});16211622testVariableDeleter(new EnvironmentFrobber() {1623public void doIt(Map<String,String> environ) {1624Iterator<String> it = environ.values().iterator();1625while (it.hasNext()) {1626String val = it.next();1627if (val.equals("BAAR"))1628it.remove();}}});16291630//----------------------------------------------------------------1631// A surprisingly small number of ways to add an environment var.1632//----------------------------------------------------------------1633testVariableAdder(new EnvironmentFrobber() {1634public void doIt(Map<String,String> environ) {1635environ.put("Foo","Bahrein");}});16361637//----------------------------------------------------------------1638// A few ways to modify an environment var.1639//----------------------------------------------------------------1640testVariableModifier(new EnvironmentFrobber() {1641public void doIt(Map<String,String> environ) {1642environ.put("Foo","NewValue");}});16431644testVariableModifier(new EnvironmentFrobber() {1645public void doIt(Map<String,String> environ) {1646for (Map.Entry<String,String> e : environ.entrySet())1647if (e.getKey().equals("Foo"))1648e.setValue("NewValue");}});16491650//----------------------------------------------------------------1651// Fiddle with environment sizes1652//----------------------------------------------------------------1653try {1654Map<String,String> environ = new ProcessBuilder().environment();1655int size = environ.size();1656checkSizes(environ, size);16571658environ.put("UnLiKeLYeNVIROmtNam", "someVal");1659checkSizes(environ, size+1);16601661// Check for environment independence1662new ProcessBuilder().environment().clear();16631664environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");1665checkSizes(environ, size+1);16661667environ.remove("UnLiKeLYeNVIROmtNam");1668checkSizes(environ, size);16691670environ.clear();1671checkSizes(environ, 0);16721673environ.clear();1674checkSizes(environ, 0);16751676environ = new ProcessBuilder().environment();1677environ.keySet().clear();1678checkSizes(environ, 0);16791680environ = new ProcessBuilder().environment();1681environ.entrySet().clear();1682checkSizes(environ, 0);16831684environ = new ProcessBuilder().environment();1685environ.values().clear();1686checkSizes(environ, 0);1687} catch (Throwable t) { unexpected(t); }16881689//----------------------------------------------------------------1690// Check that various map invariants hold1691//----------------------------------------------------------------1692checkMapSanity(new ProcessBuilder().environment());1693checkMapSanity(System.getenv());1694checkMapEquality(new ProcessBuilder().environment(),1695new ProcessBuilder().environment());169616971698//----------------------------------------------------------------1699// Check effects on external "env" command.1700//----------------------------------------------------------------1701try {1702Set<String> env1 = new HashSet<String>1703(Arrays.asList(nativeEnv((String[])null).split("\n")));17041705ProcessBuilder pb = new ProcessBuilder();1706pb.environment().put("QwErTyUiOp","AsDfGhJk");17071708Set<String> env2 = new HashSet<String>1709(Arrays.asList(nativeEnv(pb).split("\n")));17101711check(env2.size() == env1.size() + 1);1712env1.add("QwErTyUiOp=AsDfGhJk");1713check(env1.equals(env2));1714} catch (Throwable t) { unexpected(t); }17151716//----------------------------------------------------------------1717// Test Runtime.exec(...envp...)1718// Check for sort order of environment variables on Windows.1719//----------------------------------------------------------------1720try {1721String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");1722// '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'1723String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",1724"+=+", "_=_", "~=~", systemRoot};1725String output = nativeEnv(envp);1726String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";1727// On Windows, Java must keep the environment sorted.1728// Order is random on Unix, so this test does the sort.1729if (! Windows.is())1730output = sortByLinesWindowsly(output);1731equal(output, expected);1732} catch (Throwable t) { unexpected(t); }17331734//----------------------------------------------------------------1735// Test Runtime.exec(...envp...)1736// and check SystemRoot gets set automatically on Windows1737//----------------------------------------------------------------1738try {1739if (Windows.is()) {1740String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");1741String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",1742"+=+", "_=_", "~=~"};1743String output = nativeEnv(envp);1744String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";1745equal(output, expected);1746}1747} catch (Throwable t) { unexpected(t); }17481749//----------------------------------------------------------------1750// System.getenv() must be consistent with System.getenv(String)1751//----------------------------------------------------------------1752try {1753for (Map.Entry<String,String> e : getenv().entrySet())1754equal(getenv(e.getKey()), e.getValue());1755} catch (Throwable t) { unexpected(t); }17561757//----------------------------------------------------------------1758// Fiddle with working directory in child1759//----------------------------------------------------------------1760try {1761String canonicalUserDir =1762new File(System.getProperty("user.dir")).getCanonicalPath();1763String[] sdirs = new String[]1764{".", "..", "/", "/bin",1765"C:", "c:", "C:/", "c:\\", "\\", "\\bin",1766"c:\\windows ", "c:\\Program Files", "c:\\Program Files\\" };1767for (String sdir : sdirs) {1768File dir = new File(sdir);1769if (! (dir.isDirectory() && dir.exists()))1770continue;1771out.println("Testing directory " + dir);1772//dir = new File(dir.getCanonicalPath());17731774ProcessBuilder pb = new ProcessBuilder();1775equal(pb.directory(), null);1776equal(pwdInChild(pb), canonicalUserDir);17771778pb.directory(dir);1779equal(pb.directory(), dir);1780equal(pwdInChild(pb), dir.getCanonicalPath());17811782pb.directory(null);1783equal(pb.directory(), null);1784equal(pwdInChild(pb), canonicalUserDir);17851786pb.directory(dir);1787}1788} catch (Throwable t) { unexpected(t); }17891790//----------------------------------------------------------------1791// Working directory with Unicode in child1792//----------------------------------------------------------------1793try {1794if (UnicodeOS.is()) {1795File dir = new File(System.getProperty("test.dir", "."),1796"ProcessBuilderDir\u4e00\u4e02");1797try {1798if (!dir.exists())1799dir.mkdir();1800out.println("Testing Unicode directory:" + dir);1801ProcessBuilder pb = new ProcessBuilder();1802pb.directory(dir);1803equal(pwdInChild(pb), dir.getCanonicalPath());1804} finally {1805if (dir.exists())1806dir.delete();1807}1808}1809} catch (Throwable t) { unexpected(t); }18101811//----------------------------------------------------------------1812// OOME in child allocating maximally sized array1813// Test for hotspot/jvmti bug 68509571814//----------------------------------------------------------------1815try {1816List<String> list = new ArrayList<String>(javaChildArgs);1817list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",1818javaExe));1819list.add("ArrayOOME");1820ProcessResults r = run(new ProcessBuilder(list));1821check(r.err().contains("java.lang.OutOfMemoryError:"));1822check(r.err().contains(javaExe));1823check(r.err().contains(System.getProperty("java.version")));1824equal(r.exitValue(), 1);1825} catch (Throwable t) { unexpected(t); }18261827//----------------------------------------------------------------1828// Windows has tricky semi-case-insensitive semantics1829//----------------------------------------------------------------1830if (Windows.is())1831try {1832out.println("Running case insensitve variable tests");1833for (String[] namePair :1834new String[][]1835{ new String[]{"PATH","PaTh"},1836new String[]{"home","HOME"},1837new String[]{"SYSTEMROOT","SystemRoot"}}) {1838check((getenv(namePair[0]) == null &&1839getenv(namePair[1]) == null)1840||1841getenv(namePair[0]).equals(getenv(namePair[1])),1842"Windows environment variables are not case insensitive");1843}1844} catch (Throwable t) { unexpected(t); }18451846//----------------------------------------------------------------1847// Test proper Unicode child environment transfer1848//----------------------------------------------------------------1849if (UnicodeOS.is())1850try {1851ProcessBuilder pb = new ProcessBuilder();1852pb.environment().put("\u1234","\u5678");1853pb.environment().remove("PATH");1854equal(getenvInChild1234(pb), "\u5678");1855} catch (Throwable t) { unexpected(t); }185618571858//----------------------------------------------------------------1859// Test Runtime.exec(...envp...) with envstrings with initial `='1860//----------------------------------------------------------------1861try {1862List<String> childArgs = new ArrayList<String>(javaChildArgs);1863childArgs.add("System.getenv()");1864String[] cmdp = childArgs.toArray(new String[childArgs.size()]);1865String[] envp;1866String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};1867String[] envpOth = {"=ExitValue=3", "=C:=\\"};1868if (Windows.is()) {1869envp = envpWin;1870} else {1871envp = envpOth;1872}1873Process p = Runtime.getRuntime().exec(cmdp, envp);1874String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";1875expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected;1876String commandOutput = commandOutput(p);1877if (MacOSX.is()) {1878commandOutput = removeMacExpectedVars(commandOutput);1879}1880if (AIX.is()) {1881commandOutput = removeAixExpectedVars(commandOutput);1882}1883equal(commandOutput, expected);1884if (Windows.is()) {1885ProcessBuilder pb = new ProcessBuilder(childArgs);1886pb.environment().clear();1887pb.environment().put("SystemRoot", systemRoot);1888pb.environment().put("=ExitValue", "3");1889pb.environment().put("=C:", "\\");1890equal(commandOutput(pb), expected);1891}1892} catch (Throwable t) { unexpected(t); }18931894//----------------------------------------------------------------1895// Test Runtime.exec(...envp...) with envstrings without any `='1896//----------------------------------------------------------------1897try {1898String[] cmdp = {"echo"};1899String[] envp = {"Hello", "World"}; // Yuck!1900Process p = Runtime.getRuntime().exec(cmdp, envp);1901equal(commandOutput(p), "\n");1902} catch (Throwable t) { unexpected(t); }19031904//----------------------------------------------------------------1905// Test Runtime.exec(...envp...) with envstrings containing NULs1906//----------------------------------------------------------------1907try {1908List<String> childArgs = new ArrayList<String>(javaChildArgs);1909childArgs.add("System.getenv()");1910String[] cmdp = childArgs.toArray(new String[childArgs.size()]);1911String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!1912"FO\u0000=B\u0000R"};1913String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!1914"FO\u0000=B\u0000R"};1915String[] envp;1916if (Windows.is()) {1917envp = envpWin;1918} else {1919envp = envpOth;1920}1921System.out.println ("cmdp");1922for (int i=0; i<cmdp.length; i++) {1923System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);1924}1925System.out.println ("envp");1926for (int i=0; i<envp.length; i++) {1927System.out.printf ("envp %d: %s\n", i, envp[i]);1928}1929Process p = Runtime.getRuntime().exec(cmdp, envp);1930String commandOutput = commandOutput(p);1931if (MacOSX.is()) {1932commandOutput = removeMacExpectedVars(commandOutput);1933}1934if (AIX.is()) {1935commandOutput = removeAixExpectedVars(commandOutput);1936}1937check(commandOutput.equals(Windows.is()1938? "LC_ALL=C,SystemRoot="+systemRoot+","1939: AIX.is()1940? "LC_ALL=C,LIBPATH="+libpath+","1941: "LC_ALL=C,"),1942"Incorrect handling of envstrings containing NULs");1943} catch (Throwable t) { unexpected(t); }19441945//----------------------------------------------------------------1946// Test the redirectErrorStream property1947//----------------------------------------------------------------1948try {1949ProcessBuilder pb = new ProcessBuilder();1950equal(pb.redirectErrorStream(), false);1951equal(pb.redirectErrorStream(true), pb);1952equal(pb.redirectErrorStream(), true);1953equal(pb.redirectErrorStream(false), pb);1954equal(pb.redirectErrorStream(), false);1955} catch (Throwable t) { unexpected(t); }19561957try {1958List<String> childArgs = new ArrayList<String>(javaChildArgs);1959childArgs.add("OutErr");1960ProcessBuilder pb = new ProcessBuilder(childArgs);1961{1962ProcessResults r = run(pb);1963equal(r.out(), "outout");1964equal(r.err(), "errerr");1965}1966{1967pb.redirectErrorStream(true);1968ProcessResults r = run(pb);1969equal(r.out(), "outerrouterr");1970equal(r.err(), "");1971}1972} catch (Throwable t) { unexpected(t); }19731974if (Unix.is()) {1975//----------------------------------------------------------------1976// We can find true and false when PATH is null1977//----------------------------------------------------------------1978try {1979List<String> childArgs = new ArrayList<String>(javaChildArgs);1980childArgs.add("null PATH");1981ProcessBuilder pb = new ProcessBuilder(childArgs);1982pb.environment().remove("PATH");1983ProcessResults r = run(pb);1984equal(r.out(), "");1985equal(r.err(), "");1986equal(r.exitValue(), 0);1987} catch (Throwable t) { unexpected(t); }19881989//----------------------------------------------------------------1990// PATH search algorithm on Unix1991//----------------------------------------------------------------1992try {1993List<String> childArgs = new ArrayList<String>(javaChildArgs);1994childArgs.add("PATH search algorithm");1995ProcessBuilder pb = new ProcessBuilder(childArgs);1996pb.environment().put("PATH", "dir1:dir2:");1997ProcessResults r = run(pb);1998equal(r.out(), "");1999equal(r.err(), "");2000equal(r.exitValue(), True.exitValue());2001} catch (Throwable t) { unexpected(t); }20022003//----------------------------------------------------------------2004// Parent's, not child's PATH is used2005//----------------------------------------------------------------2006try {2007new File("suBdiR").mkdirs();2008copy(TrueExe.path(), "suBdiR/unliKely");2009final ProcessBuilder pb =2010new ProcessBuilder(new String[]{"unliKely"});2011pb.environment().put("PATH", "suBdiR");2012THROWS(IOException.class, () -> pb.start());2013} catch (Throwable t) { unexpected(t);2014} finally {2015new File("suBdiR/unliKely").delete();2016new File("suBdiR").delete();2017}2018}20192020//----------------------------------------------------------------2021// Attempt to start bogus program ""2022//----------------------------------------------------------------2023try {2024new ProcessBuilder("").start();2025fail("Expected IOException not thrown");2026} catch (IOException e) {2027String m = e.getMessage();2028if (EnglishUnix.is() &&2029! matches(m, NO_SUCH_FILE_ERROR_MSG))2030unexpected(e);2031} catch (Throwable t) { unexpected(t); }20322033//----------------------------------------------------------------2034// Check that attempt to execute program name with funny2035// characters throws an exception containing those characters.2036//----------------------------------------------------------------2037for (String programName : new String[] {"\u00f0", "\u01f0"})2038try {2039new ProcessBuilder(programName).start();2040fail("Expected IOException not thrown");2041} catch (IOException e) {2042String m = e.getMessage();2043Pattern p = Pattern.compile(programName);2044if (! matches(m, programName)2045|| (EnglishUnix.is() &&2046! matches(m, NO_SUCH_FILE_ERROR_MSG)))2047unexpected(e);2048} catch (Throwable t) { unexpected(t); }20492050//----------------------------------------------------------------2051// Attempt to start process in nonexistent directory fails.2052//----------------------------------------------------------------2053try {2054new ProcessBuilder("echo")2055.directory(new File("UnLiKeLY"))2056.start();2057fail("Expected IOException not thrown");2058} catch (IOException e) {2059String m = e.getMessage();2060if (! matches(m, "in directory")2061|| (EnglishUnix.is() &&2062! matches(m, NO_SUCH_FILE_ERROR_MSG)))2063unexpected(e);2064} catch (Throwable t) { unexpected(t); }20652066//----------------------------------------------------------------2067// Attempt to write 4095 bytes to the pipe buffer without a2068// reader to drain it would deadlock, if not for the fact that2069// interprocess pipe buffers are at least 4096 bytes.2070//2071// Also, check that available reports all the bytes expected2072// in the pipe buffer, and that I/O operations do the expected2073// things.2074//----------------------------------------------------------------2075try {2076List<String> childArgs = new ArrayList<String>(javaChildArgs);2077childArgs.add("print4095");2078final int SIZE = 4095;2079final Process p = new ProcessBuilder(childArgs).start();2080print4095(p.getOutputStream(), (byte) '!'); // Might hang!2081p.waitFor(); // Might hang!2082equal(SIZE, p.getInputStream().available());2083equal(SIZE, p.getErrorStream().available());2084THROWS(IOException.class,2085() -> { p.getOutputStream().write((byte) '!');2086p.getOutputStream().flush();});20872088final byte[] bytes = new byte[SIZE + 1];2089equal(SIZE, p.getInputStream().read(bytes));2090for (int i = 0; i < SIZE; i++)2091equal((byte) '!', bytes[i]);2092equal((byte) 0, bytes[SIZE]);20932094equal(SIZE, p.getErrorStream().read(bytes));2095for (int i = 0; i < SIZE; i++)2096equal((byte) 'E', bytes[i]);2097equal((byte) 0, bytes[SIZE]);20982099equal(0, p.getInputStream().available());2100equal(0, p.getErrorStream().available());2101equal(-1, p.getErrorStream().read());2102equal(-1, p.getInputStream().read());21032104equal(p.exitValue(), 5);21052106p.getInputStream().close();2107p.getErrorStream().close();2108try { p.getOutputStream().close(); } catch (IOException flushFailed) { }21092110InputStream[] streams = { p.getInputStream(), p.getErrorStream() };2111for (final InputStream in : streams) {2112Fun[] ops = {2113() -> in.read(),2114() -> in.read(bytes),2115() -> in.available()2116};2117for (Fun op : ops) {2118try {2119op.f();2120fail();2121} catch (IOException expected) {2122check(expected.getMessage()2123.matches("[Ss]tream [Cc]losed"));2124}2125}2126}2127} catch (Throwable t) { unexpected(t); }21282129//----------------------------------------------------------------2130// Check that reads which are pending when Process.destroy is2131// called, get EOF, or IOException("Stream closed").2132//----------------------------------------------------------------2133try {2134final int cases = 4;2135for (int i = 0; i < cases; i++) {2136final int action = i;2137List<String> childArgs = new ArrayList<>(javaChildArgs);2138final ProcessBuilder pb = new ProcessBuilder(childArgs);2139{2140// Redirect any child VM error output away from the stream being tested2141// and to the log file. For background see:2142// 8231297: java/lang/ProcessBuilder/Basic.java test fails intermittently2143// Destroying the process may, depending on the timing, cause some output2144// from the child VM.2145// This test requires the thread reading from the subprocess be blocked2146// in the read from the subprocess; there should be no bytes to read.2147// Modify the argument list shared with ProcessBuilder to redirect VM output.2148assert (childArgs.get(1).equals("-XX:+DisplayVMOutputToStderr")) : "Expected arg 1 to be \"-XX:+DisplayVMOutputToStderr\"";2149switch (action & 0x1) {2150case 0:2151childArgs.set(1, "-XX:+DisplayVMOutputToStderr");2152childArgs.add(2, "-Xlog:all=warning:stderr");2153pb.redirectError(INHERIT);2154break;2155case 1:2156childArgs.set(1, "-XX:+DisplayVMOutputToStdout");2157childArgs.add(2, "-Xlog:all=warning:stdout");2158pb.redirectOutput(INHERIT);2159break;2160default:2161throw new Error();2162}2163}2164childArgs.add("sleep");2165final byte[] bytes = new byte[10];2166final Process p = pb.start();2167final CountDownLatch latch = new CountDownLatch(1);2168final InputStream s;2169switch (action & 0x1) {2170case 0: s = p.getInputStream(); break;2171case 1: s = p.getErrorStream(); break;2172default: throw new Error();2173}2174final Thread thread = new Thread() {2175public void run() {2176try {2177int r;2178latch.countDown();2179switch (action & 0x2) {2180case 0: r = s.read(); break;2181case 2: r = s.read(bytes); break;2182default: throw new Error();2183}2184if (r >= 0) {2185// The child sent unexpected output; print it to diagnose2186System.out.println("Unexpected child output:");2187if ((action & 0x2) == 0) {2188System.out.write(r); // Single character21892190} else {2191System.out.write(bytes, 0, r);2192}2193for (int c = s.read(); c >= 0; c = s.read())2194System.out.write(c);2195System.out.println("\nEND Child output.");2196}2197equal(-1, r);2198} catch (IOException ioe) {2199if (!ioe.getMessage().equals("Stream closed")) {2200// BufferedInputStream may throw IOE("Stream closed").2201unexpected(ioe);2202}2203} catch (Throwable t) { unexpected(t); }}};22042205thread.start();2206latch.await();2207Thread.sleep(10);22082209if (s instanceof BufferedInputStream) {2210// Wait until after the s.read occurs in "thread" by2211// checking when the input stream monitor is acquired2212// (BufferedInputStream.read is synchronized)2213while (!isLocked(s, 10)) {2214Thread.sleep(100);2215}2216}2217p.destroy();2218thread.join();2219}2220} catch (Throwable t) { unexpected(t); }22212222//----------------------------------------------------------------2223// Check that subprocesses which create subprocesses of their2224// own do not cause parent to hang waiting for file2225// descriptors to be closed.2226//----------------------------------------------------------------2227try {2228if (Unix.is()2229&& new File("/bin/bash").exists()2230&& new File("/bin/sleep").exists()) {2231// Notice that we only destroy the process created by us (i.e.2232// our child) but not our grandchild (i.e. '/bin/sleep'). So2233// pay attention that the grandchild doesn't run too long to2234// avoid polluting the process space with useless processes.2235// Running the grandchild for 60s should be more than enough.2236final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" };2237final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" };2238final ProcessBuilder pb = new ProcessBuilder(cmd);2239final Process p = pb.start();2240final InputStream stdout = p.getInputStream();2241final InputStream stderr = p.getErrorStream();2242final OutputStream stdin = p.getOutputStream();2243final Thread reader = new Thread() {2244public void run() {2245try { stdout.read(); }2246catch (IOException e) {2247// Check that reader failed because stream was2248// asynchronously closed.2249// e.printStackTrace();2250String msg = e.getMessage();2251if (EnglishUnix.is() &&2252! (msg.matches(".*Bad file.*") ||2253msg.matches(".*Stream closed.*")))2254unexpected(e);2255}2256catch (Throwable t) { unexpected(t); }}};2257reader.setDaemon(true);2258reader.start();2259Thread.sleep(100);2260p.destroy();2261check(p.waitFor() != 0);2262check(p.exitValue() != 0);2263// Subprocess is now dead, but file descriptors remain open.2264// Make sure the test will fail if we don't manage to close2265// the open streams within 30 seconds. Notice that this time2266// must be shorter than the sleep time of the grandchild.2267Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);2268t.schedule(new TimerTask() {2269public void run() {2270fail("Subprocesses which create subprocesses of " +2271"their own caused the parent to hang while " +2272"waiting for file descriptors to be closed.");2273System.exit(-1);2274}2275}, 30000);2276stdout.close();2277stderr.close();2278stdin.close();2279new ProcessBuilder(cmdkill).start();2280// All streams successfully closed so we can cancel the timer.2281t.cancel();2282//----------------------------------------------------------2283// There remain unsolved issues with asynchronous close.2284// Here's a highly non-portable experiment to demonstrate:2285//----------------------------------------------------------2286if (Boolean.getBoolean("wakeupJeff!")) {2287System.out.println("wakeupJeff!");2288// Initialize signal handler for INTERRUPT_SIGNAL.2289new FileInputStream("/bin/sleep").getChannel().close();2290// Send INTERRUPT_SIGNAL to every thread in this java.2291String[] wakeupJeff = {2292"/bin/bash", "-c",2293"/bin/ps --noheaders -Lfp $PPID | " +2294"/usr/bin/perl -nale 'print $F[3]' | " +2295// INTERRUPT_SIGNAL == 62 on my machine du jour.2296"/usr/bin/xargs kill -62"2297};2298new ProcessBuilder(wakeupJeff).start().waitFor();2299// If wakeupJeff worked, reader probably got EBADF.2300reader.join();2301}2302}23032304//----------------------------------------------------------------2305// Check the Process toString() method2306//----------------------------------------------------------------2307{2308List<String> childArgs = new ArrayList<String>(javaChildArgs);2309childArgs.add("testIO");2310ProcessBuilder pb = new ProcessBuilder(childArgs);2311pb.redirectInput(Redirect.PIPE);2312pb.redirectOutput(DISCARD);2313pb.redirectError(DISCARD);2314final Process p = pb.start();2315// Child process waits until it gets input2316String s = p.toString();2317check(s.contains("not exited"));2318check(s.contains("pid=" + p.pid() + ","));23192320new PrintStream(p.getOutputStream()).print("standard input");2321p.getOutputStream().close();23222323// Check the toString after it exits2324int exitValue = p.waitFor();2325s = p.toString();2326check(s.contains("pid=" + p.pid() + ","));2327check(s.contains("exitValue=" + exitValue) &&2328!s.contains("not exited"));2329}2330} catch (Throwable t) { unexpected(t); }23312332//----------------------------------------------------------------2333// Attempt to start process with insufficient permissions fails.2334//----------------------------------------------------------------2335try {2336new File("emptyCommand").delete();2337new FileOutputStream("emptyCommand").close();2338new File("emptyCommand").setExecutable(false);2339new ProcessBuilder("./emptyCommand").start();2340fail("Expected IOException not thrown");2341} catch (IOException e) {2342new File("./emptyCommand").delete();2343String m = e.getMessage();2344if (EnglishUnix.is() &&2345! matches(m, PERMISSION_DENIED_ERROR_MSG))2346unexpected(e);2347} catch (Throwable t) { unexpected(t); }23482349new File("emptyCommand").delete();23502351//----------------------------------------------------------------2352// Check for correct security permission behavior2353//----------------------------------------------------------------2354final Policy policy = new Policy();2355Policy.setPolicy(policy);2356System.setSecurityManager(new SecurityManager());23572358try {2359// No permissions required to CREATE a ProcessBuilder2360policy.setPermissions(/* Nothing */);2361new ProcessBuilder("env").directory(null).directory();2362new ProcessBuilder("env").directory(new File("dir")).directory();2363new ProcessBuilder("env").command("??").command();2364} catch (Throwable t) { unexpected(t); }23652366THROWS(SecurityException.class,2367() -> { policy.setPermissions(/* Nothing */);2368System.getenv("foo");},2369() -> { policy.setPermissions(/* Nothing */);2370System.getenv();},2371() -> { policy.setPermissions(/* Nothing */);2372new ProcessBuilder("echo").start();},2373() -> { policy.setPermissions(/* Nothing */);2374Runtime.getRuntime().exec("echo");},2375() -> { policy.setPermissions(2376new RuntimePermission("getenv.bar"));2377System.getenv("foo");});23782379try {2380policy.setPermissions(new RuntimePermission("getenv.foo"));2381System.getenv("foo");23822383policy.setPermissions(new RuntimePermission("getenv.*"));2384System.getenv("foo");2385System.getenv();2386new ProcessBuilder().environment();2387} catch (Throwable t) { unexpected(t); }238823892390final Permission execPermission2391= new FilePermission("<<ALL FILES>>", "execute");23922393THROWS(SecurityException.class,2394() -> { // environment permission by itself insufficient2395policy.setPermissions(new RuntimePermission("getenv.*"));2396ProcessBuilder pb = new ProcessBuilder("env");2397pb.environment().put("foo","bar");2398pb.start();},2399() -> { // exec permission by itself insufficient2400policy.setPermissions(execPermission);2401ProcessBuilder pb = new ProcessBuilder("env");2402pb.environment().put("foo","bar");2403pb.start();});24042405try {2406// Both permissions? OK.2407policy.setPermissions(new RuntimePermission("getenv.*"),2408execPermission);2409ProcessBuilder pb = new ProcessBuilder("env");2410pb.environment().put("foo","bar");2411Process p = pb.start();2412closeStreams(p);2413} catch (IOException e) { // OK2414} catch (Throwable t) { unexpected(t); }24152416try {2417// Don't need environment permission unless READING environment2418policy.setPermissions(execPermission);2419Runtime.getRuntime().exec("env", new String[]{});2420} catch (IOException e) { // OK2421} catch (Throwable t) { unexpected(t); }24222423try {2424// Don't need environment permission unless READING environment2425policy.setPermissions(execPermission);2426new ProcessBuilder("env").start();2427} catch (IOException e) { // OK2428} catch (Throwable t) { unexpected(t); }24292430// Restore "normal" state without a security manager2431policy.setPermissions(new RuntimePermission("setSecurityManager"));2432System.setSecurityManager(null);24332434//----------------------------------------------------------------2435// Check that Process.isAlive() &2436// Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.2437//----------------------------------------------------------------2438try {2439List<String> childArgs = new ArrayList<String>(javaChildArgs);2440childArgs.add("sleep");2441final Process p = new ProcessBuilder(childArgs).start();2442long start = System.nanoTime();2443if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {2444fail("Test failed: Process exited prematurely");2445}2446long end = System.nanoTime();2447// give waitFor(timeout) a wide berth (2s)2448System.out.printf(" waitFor process: delta: %d%n",(end - start) );24492450if ((end - start) > TimeUnit.SECONDS.toNanos(2))2451fail("Test failed: waitFor took too long (" + (end - start) + "ns)");24522453p.destroy();2454p.waitFor();24552456if (p.isAlive() ||2457!p.waitFor(0, TimeUnit.MILLISECONDS))2458{2459fail("Test failed: Process still alive - please terminate " +2460p.toString() + " manually");2461}2462} catch (Throwable t) { unexpected(t); }24632464//----------------------------------------------------------------2465// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2466// works as expected.2467//----------------------------------------------------------------2468try {2469List<String> childArgs = new ArrayList<String>(javaChildArgs);2470childArgs.add("sleep");2471final Process p = new ProcessBuilder(childArgs).start();2472long start = System.nanoTime();24732474p.waitFor(10, TimeUnit.MILLISECONDS);24752476long end = System.nanoTime();2477if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))2478fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");24792480p.destroy();2481} catch (Throwable t) { unexpected(t); }24822483//----------------------------------------------------------------2484// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2485// interrupt works as expected, if interrupted while waiting.2486//----------------------------------------------------------------2487try {2488List<String> childArgs = new ArrayList<String>(javaChildArgs);2489childArgs.add("sleep");2490final Process p = new ProcessBuilder(childArgs).start();2491final long start = System.nanoTime();2492final CountDownLatch aboutToWaitFor = new CountDownLatch(1);24932494final Thread thread = new Thread() {2495public void run() {2496try {2497aboutToWaitFor.countDown();2498Thread.currentThread().interrupt();2499boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);2500fail("waitFor() wasn't interrupted, its return value was: " + result);2501} catch (InterruptedException success) {2502} catch (Throwable t) { unexpected(t); }2503}2504};25052506thread.start();2507aboutToWaitFor.await();2508thread.interrupt();2509thread.join(10L * 1000L);2510check(millisElapsedSince(start) < 10L * 1000L);2511check(!thread.isAlive());2512p.destroy();2513} catch (Throwable t) { unexpected(t); }25142515//----------------------------------------------------------------2516// Check that Process.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS)2517// interrupt works as expected, if interrupted while waiting.2518//----------------------------------------------------------------2519try {2520List<String> childArgs = new ArrayList<String>(javaChildArgs);2521childArgs.add("sleep");2522final Process p = new ProcessBuilder(childArgs).start();2523final long start = System.nanoTime();2524final CountDownLatch aboutToWaitFor = new CountDownLatch(1);25252526final Thread thread = new Thread() {2527public void run() {2528try {2529aboutToWaitFor.countDown();2530Thread.currentThread().interrupt();2531boolean result = p.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS);2532fail("waitFor() wasn't interrupted, its return value was: " + result);2533} catch (InterruptedException success) {2534} catch (Throwable t) { unexpected(t); }2535}2536};25372538thread.start();2539aboutToWaitFor.await();2540thread.interrupt();2541thread.join(10L * 1000L);2542check(millisElapsedSince(start) < 10L * 1000L);2543check(!thread.isAlive());2544p.destroy();2545} catch (Throwable t) { unexpected(t); }25462547//----------------------------------------------------------------2548// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2549// interrupt works as expected, if interrupted before waiting.2550//----------------------------------------------------------------2551try {2552List<String> childArgs = new ArrayList<String>(javaChildArgs);2553childArgs.add("sleep");2554final Process p = new ProcessBuilder(childArgs).start();2555final long start = System.nanoTime();2556final CountDownLatch threadStarted = new CountDownLatch(1);25572558final Thread thread = new Thread() {2559public void run() {2560try {2561threadStarted.countDown();2562do { Thread.yield(); }2563while (!Thread.currentThread().isInterrupted());2564boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);2565fail("waitFor() wasn't interrupted, its return value was: " + result);2566} catch (InterruptedException success) {2567} catch (Throwable t) { unexpected(t); }2568}2569};25702571thread.start();2572threadStarted.await();2573thread.interrupt();2574thread.join(10L * 1000L);2575check(millisElapsedSince(start) < 10L * 1000L);2576check(!thread.isAlive());2577p.destroy();2578} catch (Throwable t) { unexpected(t); }25792580//----------------------------------------------------------------2581// Check that Process.waitFor(timeout, null) throws NPE.2582//----------------------------------------------------------------2583try {2584List<String> childArgs = new ArrayList<String>(javaChildArgs);2585childArgs.add("sleep");2586final Process p = new ProcessBuilder(childArgs).start();2587THROWS(NullPointerException.class,2588() -> p.waitFor(10L, null));2589THROWS(NullPointerException.class,2590() -> p.waitFor(0L, null));2591THROWS(NullPointerException.class,2592() -> p.waitFor(-1L, null));2593// Terminate process and recheck after it exits2594p.destroy();2595p.waitFor();2596THROWS(NullPointerException.class,2597() -> p.waitFor(10L, null));2598THROWS(NullPointerException.class,2599() -> p.waitFor(0L, null));2600THROWS(NullPointerException.class,2601() -> p.waitFor(-1L, null));2602} catch (Throwable t) { unexpected(t); }26032604//----------------------------------------------------------------2605// Check that default implementation of Process.waitFor(timeout, null) throws NPE.2606//----------------------------------------------------------------2607try {2608List<String> childArgs = new ArrayList<String>(javaChildArgs);2609childArgs.add("sleep");2610final Process proc = new ProcessBuilder(childArgs).start();2611final DelegatingProcess p = new DelegatingProcess(proc);26122613THROWS(NullPointerException.class,2614() -> p.waitFor(10L, null));2615THROWS(NullPointerException.class,2616() -> p.waitFor(0L, null));2617THROWS(NullPointerException.class,2618() -> p.waitFor(-1L, null));2619// Terminate process and recheck after it exits2620p.destroy();2621p.waitFor();2622THROWS(NullPointerException.class,2623() -> p.waitFor(10L, null));2624THROWS(NullPointerException.class,2625() -> p.waitFor(0L, null));2626THROWS(NullPointerException.class,2627() -> p.waitFor(-1L, null));2628} catch (Throwable t) { unexpected(t); }26292630//----------------------------------------------------------------2631// Check the default implementation for2632// Process.waitFor(long, TimeUnit)2633//----------------------------------------------------------------2634try {2635List<String> childArgs = new ArrayList<String>(javaChildArgs);2636childArgs.add("sleep");2637final Process proc = new ProcessBuilder(childArgs).start();2638DelegatingProcess p = new DelegatingProcess(proc);2639long start = System.nanoTime();26402641p.waitFor(1000, TimeUnit.MILLISECONDS);26422643long end = System.nanoTime();2644if ((end - start) < 500000000)2645fail("Test failed: waitFor didn't take long enough");26462647p.destroy();26482649p.waitFor(1000, TimeUnit.MILLISECONDS);2650} catch (Throwable t) { unexpected(t); }2651}26522653static void closeStreams(Process p) {2654try {2655p.getOutputStream().close();2656p.getInputStream().close();2657p.getErrorStream().close();2658} catch (Throwable t) { unexpected(t); }2659}26602661//----------------------------------------------------------------2662// A Policy class designed to make permissions fiddling very easy.2663//----------------------------------------------------------------2664private static class Policy extends java.security.Policy {2665static final java.security.Policy DEFAULT_POLICY = java.security.Policy.getPolicy();26662667private Permissions perms;26682669public void setPermissions(Permission...permissions) {2670perms = new Permissions();2671for (Permission permission : permissions)2672perms.add(permission);2673}26742675public Policy() { setPermissions(/* Nothing */); }26762677public PermissionCollection getPermissions(CodeSource cs) {2678return perms;2679}26802681public PermissionCollection getPermissions(ProtectionDomain pd) {2682return perms;2683}26842685public boolean implies(ProtectionDomain pd, Permission p) {2686return perms.implies(p) || DEFAULT_POLICY.implies(pd, p);2687}26882689public void refresh() {}2690}26912692private static class StreamAccumulator extends Thread {2693private final InputStream is;2694private final StringBuilder sb = new StringBuilder();2695private Throwable throwable = null;26962697public String result () throws Throwable {2698if (throwable != null)2699throw throwable;2700return sb.toString();2701}27022703StreamAccumulator (InputStream is) {2704this.is = is;2705}27062707public void run() {2708try {2709Reader r = new InputStreamReader(is);2710char[] buf = new char[4096];2711int n;2712while ((n = r.read(buf)) > 0) {2713sb.append(buf,0,n);2714}2715} catch (Throwable t) {2716throwable = t;2717} finally {2718try { is.close(); }2719catch (Throwable t) { throwable = t; }2720}2721}2722}27232724static ProcessResults run(ProcessBuilder pb) {2725try {2726return run(pb.start());2727} catch (Throwable t) { unexpected(t); return null; }2728}27292730private static ProcessResults run(Process p) {2731Throwable throwable = null;2732int exitValue = -1;2733String out = "";2734String err = "";27352736StreamAccumulator outAccumulator =2737new StreamAccumulator(p.getInputStream());2738StreamAccumulator errAccumulator =2739new StreamAccumulator(p.getErrorStream());27402741try {2742outAccumulator.start();2743errAccumulator.start();27442745exitValue = p.waitFor();27462747outAccumulator.join();2748errAccumulator.join();27492750out = outAccumulator.result();2751err = errAccumulator.result();2752} catch (Throwable t) {2753throwable = t;2754}27552756return new ProcessResults(out, err, exitValue, throwable);2757}27582759//----------------------------------------------------------------2760// Results of a command2761//----------------------------------------------------------------2762private static class ProcessResults {2763private final String out;2764private final String err;2765private final int exitValue;2766private final Throwable throwable;27672768public ProcessResults(String out,2769String err,2770int exitValue,2771Throwable throwable) {2772this.out = out;2773this.err = err;2774this.exitValue = exitValue;2775this.throwable = throwable;2776}27772778public String out() { return out; }2779public String err() { return err; }2780public int exitValue() { return exitValue; }2781public Throwable throwable() { return throwable; }27822783public String toString() {2784StringBuilder sb = new StringBuilder();2785sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")2786.append("<STDERR>\n" + err() + "</STDERR>\n")2787.append("exitValue = " + exitValue + "\n");2788if (throwable != null)2789sb.append(throwable.getStackTrace());2790return sb.toString();2791}2792}27932794//--------------------- Infrastructure ---------------------------2795static volatile int passed = 0, failed = 0;2796static void pass() {passed++;}2797static void fail() {failed++; Thread.dumpStack();}2798static void fail(String msg) {System.err.println(msg); fail();}2799static void unexpected(Throwable t) {failed++; t.printStackTrace();}2800static void check(boolean cond) {if (cond) pass(); else fail();}2801static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}2802static void equal(Object x, Object y) {2803if (x == null ? y == null : x.equals(y)) pass();2804else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'");}28052806public static void main(String[] args) throws Throwable {2807try {realMain(args);} catch (Throwable t) {unexpected(t);}2808System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);2809if (failed > 0) throw new AssertionError("Some tests failed");}2810interface Fun {void f() throws Throwable;}2811static void THROWS(Class<? extends Throwable> k, Fun... fs) {2812for (Fun f : fs)2813try { f.f(); fail("Expected " + k.getName() + " not thrown"); }2814catch (Throwable t) {2815if (k.isAssignableFrom(t.getClass())) pass();2816else unexpected(t);}}28172818static boolean isLocked(final Object monitor, final long millis) throws InterruptedException {2819return new Thread() {2820volatile boolean unlocked;28212822@Override2823public void run() {2824synchronized (monitor) { unlocked = true; }2825}28262827boolean isLocked() throws InterruptedException {2828start();2829join(millis);2830return !unlocked;2831}2832}.isLocked();2833}2834}283528362837