Path: blob/master/test/jdk/java/lang/ProcessBuilder/Basic.java
66644 views
/*1* Copyright (c) 2003, 2022, 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 8265173 8272600 8231297 828221930* @key intermittent31* @summary Basic tests for Process and Environment Variable code32* @modules java.base/java.lang:open33* @requires !vm.musl34* @library /test/lib35* @run main/othervm/native/timeout=300 -Djava.security.manager=allow Basic36* @run main/othervm/native/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=fork Basic37* @author Martin Buchholz38*/3940/*41* @test42* @modules java.base/java.lang:open43* @requires (os.family == "linux" & !vm.musl)44* @library /test/lib45* @run main/othervm/timeout=300 -Djava.security.manager=allow -Djdk.lang.Process.launchMechanism=posix_spawn Basic46*/4748import java.lang.ProcessBuilder.Redirect;49import java.lang.ProcessHandle;50import static java.lang.ProcessBuilder.Redirect.*;5152import java.io.*;53import java.nio.file.Files;54import java.nio.file.Path;55import java.nio.file.Paths;56import java.nio.file.StandardCopyOption;57import java.util.*;58import java.util.concurrent.CountDownLatch;59import java.util.concurrent.TimeUnit;60import java.security.*;61import java.util.regex.Pattern;62import java.util.regex.Matcher;63import static java.lang.System.getenv;64import static java.lang.System.out;65import static java.lang.Boolean.TRUE;66import static java.util.AbstractMap.SimpleImmutableEntry;6768import jdk.test.lib.Platform;6970public class Basic {7172/* used for Windows only */73static final String systemRoot = System.getenv("SystemRoot");7475/* used for Mac OS X only */76static final String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING");7778/* used for AIX only */79static final String libpath = System.getenv("LIBPATH");8081/* Used for regex String matching for long error messages */82static final String PERMISSION_DENIED_ERROR_MSG = "(Permission denied|error=13)";83static final String NO_SUCH_FILE_ERROR_MSG = "(No such file|error=2)";8485/**86* Returns the number of milliseconds since time given by87* startNanoTime, which must have been previously returned from a88* call to {@link System#nanoTime()}.89*/90private static long millisElapsedSince(long startNanoTime) {91return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);92}9394private static String commandOutput(Reader r) throws Throwable {95StringBuilder sb = new StringBuilder();96int c;97while ((c = r.read()) > 0)98if (c != '\r')99sb.append((char) c);100return sb.toString();101}102103private static String commandOutput(Process p) throws Throwable {104check(p.getInputStream() == p.getInputStream());105check(p.getOutputStream() == p.getOutputStream());106check(p.getErrorStream() == p.getErrorStream());107Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");108String output = commandOutput(r);109equal(p.waitFor(), 0);110equal(p.exitValue(), 0);111// The debug/fastdebug versions of the VM may write some warnings to stdout112// (i.e. "Warning: Cannot open log file: hotspot.log" if the VM is started113// in a directory without write permissions). These warnings will confuse tests114// which match the entire output of the child process so better filter them out.115return output.replaceAll("Warning:.*\\n", "");116}117118private static String commandOutput(ProcessBuilder pb) {119try {120return commandOutput(pb.start());121} catch (Throwable t) {122String commandline = "";123for (String arg : pb.command())124commandline += " " + arg;125System.out.println("Exception trying to run process: " + commandline);126unexpected(t);127return "";128}129}130131private static String commandOutput(String...command) {132try {133return commandOutput(Runtime.getRuntime().exec(command));134} catch (Throwable t) {135String commandline = "";136for (String arg : command)137commandline += " " + arg;138System.out.println("Exception trying to run process: " + commandline);139unexpected(t);140return "";141}142}143144private static void checkCommandOutput(ProcessBuilder pb,145String expected,146String failureMsg) {147String got = commandOutput(pb);148check(got.equals(expected),149failureMsg + "\n" +150"Expected: \"" + expected + "\"\n" +151"Got: \"" + got + "\"");152}153154private static String absolutifyPath(String path) {155StringBuilder sb = new StringBuilder();156for (String file : path.split(File.pathSeparator)) {157if (sb.length() != 0)158sb.append(File.pathSeparator);159sb.append(new File(file).getAbsolutePath());160}161return sb.toString();162}163164// compare windows-style, by canonicalizing to upper case,165// not lower case as String.compareToIgnoreCase does166private static class WindowsComparator167implements Comparator<String> {168public int compare(String x, String y) {169return x.toUpperCase(Locale.US)170.compareTo(y.toUpperCase(Locale.US));171}172}173174private static String sortedLines(String lines) {175String[] arr = lines.split("\n");176List<String> ls = new ArrayList<String>();177for (String s : arr)178ls.add(s);179Collections.sort(ls, new WindowsComparator());180StringBuilder sb = new StringBuilder();181for (String s : ls)182sb.append(s + "\n");183return sb.toString();184}185186private static void compareLinesIgnoreCase(String lines1, String lines2) {187if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) {188String dashes =189"-----------------------------------------------------";190out.println(dashes);191out.print(sortedLines(lines1));192out.println(dashes);193out.print(sortedLines(lines2));194out.println(dashes);195out.println("sizes: " + sortedLines(lines1).length() +196" " + sortedLines(lines2).length());197198fail("Sorted string contents differ");199}200}201202private static final Runtime runtime = Runtime.getRuntime();203204private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"};205206private static String winEnvFilter(String env) {207return env.replaceAll("\r", "")208.replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n","");209}210211private static String unixEnvProg() {212return new File("/usr/bin/env").canExecute() ? "/usr/bin/env"213: "/bin/env";214}215216private static String nativeEnv(String[] env) {217try {218if (Windows.is()) {219return winEnvFilter220(commandOutput(runtime.exec(winEnvCommand, env)));221} else {222return commandOutput(runtime.exec(unixEnvProg(), env));223}224} catch (Throwable t) { throw new Error(t); }225}226227private static String nativeEnv(ProcessBuilder pb) {228try {229if (Windows.is()) {230pb.command(winEnvCommand);231return winEnvFilter(commandOutput(pb));232} else {233pb.command(new String[]{unixEnvProg()});234return commandOutput(pb);235}236} catch (Throwable t) { throw new Error(t); }237}238239private static void checkSizes(Map<String,String> environ, int size) {240try {241equal(size, environ.size());242equal(size, environ.entrySet().size());243equal(size, environ.keySet().size());244equal(size, environ.values().size());245246boolean isEmpty = (size == 0);247equal(isEmpty, environ.isEmpty());248equal(isEmpty, environ.entrySet().isEmpty());249equal(isEmpty, environ.keySet().isEmpty());250equal(isEmpty, environ.values().isEmpty());251} catch (Throwable t) { unexpected(t); }252}253254private interface EnvironmentFrobber {255void doIt(Map<String,String> environ);256}257258private static void testVariableDeleter(EnvironmentFrobber fooDeleter) {259try {260Map<String,String> environ = new ProcessBuilder().environment();261environ.put("Foo", "BAAR");262fooDeleter.doIt(environ);263equal(environ.get("Foo"), null);264equal(environ.remove("Foo"), null);265} catch (Throwable t) { unexpected(t); }266}267268private static void testVariableAdder(EnvironmentFrobber fooAdder) {269try {270Map<String,String> environ = new ProcessBuilder().environment();271environ.remove("Foo");272fooAdder.doIt(environ);273equal(environ.get("Foo"), "Bahrein");274} catch (Throwable t) { unexpected(t); }275}276277private static void testVariableModifier(EnvironmentFrobber fooModifier) {278try {279Map<String,String> environ = new ProcessBuilder().environment();280environ.put("Foo","OldValue");281fooModifier.doIt(environ);282equal(environ.get("Foo"), "NewValue");283} catch (Throwable t) { unexpected(t); }284}285286private static void printUTF8(String s) throws IOException {287out.write(s.getBytes("UTF-8"));288}289290private static String getenvAsString(Map<String,String> environment) {291StringBuilder sb = new StringBuilder();292environment = new TreeMap<>(environment);293for (Map.Entry<String,String> e : environment.entrySet())294// Ignore magic environment variables added by the launcher295if (! e.getKey().equals("LD_LIBRARY_PATH"))296sb.append(e.getKey())297.append('=')298.append(e.getValue())299.append(',');300return sb.toString();301}302303static void print4095(OutputStream s, byte b) throws Throwable {304byte[] bytes = new byte[4095];305Arrays.fill(bytes, b);306s.write(bytes); // Might hang!307}308309static void checkPermissionDenied(ProcessBuilder pb) {310try {311pb.start();312fail("Expected IOException not thrown");313} catch (IOException e) {314String m = e.getMessage();315if (EnglishUnix.is() &&316! matches(m, PERMISSION_DENIED_ERROR_MSG))317unexpected(e);318} catch (Throwable t) { unexpected(t); }319}320321public static class JavaChild {322public static void main(String args[]) throws Throwable {323String action = args[0];324if (action.equals("sleep")) {325Thread.sleep(10 * 60 * 1000L);326} else if (action.equals("pid")) {327System.out.println(ProcessHandle.current().pid());328} else if (action.equals("testIO")) {329String expected = "standard input";330char[] buf = new char[expected.length()+1];331int n = new InputStreamReader(System.in).read(buf,0,buf.length);332if (n != expected.length())333System.exit(5);334if (! new String(buf,0,n).equals(expected))335System.exit(5);336System.err.print("standard error");337System.out.print("standard output");338} else if (action.equals("testInheritIO")339|| action.equals("testRedirectInherit")) {340List<String> childArgs = new ArrayList<String>(javaChildArgs);341childArgs.add("testIO");342ProcessBuilder pb = new ProcessBuilder(childArgs);343if (action.equals("testInheritIO"))344pb.inheritIO();345else346redirectIO(pb, INHERIT, INHERIT, INHERIT);347ProcessResults r = run(pb);348if (! r.out().equals(""))349System.exit(7);350if (! r.err().equals(""))351System.exit(8);352if (r.exitValue() != 0)353System.exit(9);354} else if (action.equals("System.getenv(String)")) {355String val = System.getenv(args[1]);356printUTF8(val == null ? "null" : val);357} else if (action.equals("System.getenv(\\u1234)")) {358String val = System.getenv("\u1234");359printUTF8(val == null ? "null" : val);360} else if (action.equals("System.getenv()")) {361printUTF8(getenvAsString(System.getenv()));362} else if (action.equals("ArrayOOME")) {363Object dummy;364switch(new Random().nextInt(3)) {365case 0: dummy = new Integer[Integer.MAX_VALUE]; break;366case 1: dummy = new double[Integer.MAX_VALUE]; break;367case 2: dummy = new byte[Integer.MAX_VALUE][]; break;368default: throw new InternalError();369}370} else if (action.equals("pwd")) {371printUTF8(new File(System.getProperty("user.dir"))372.getCanonicalPath());373} else if (action.equals("print4095")) {374print4095(System.out, (byte) '!');375print4095(System.err, (byte) 'E');376System.exit(5);377} else if (action.equals("OutErr")) {378// You might think the system streams would be379// buffered, and in fact they are implemented using380// BufferedOutputStream, but each and every print381// causes immediate operating system I/O.382System.out.print("out");383System.err.print("err");384System.out.print("out");385System.err.print("err");386} else if (action.equals("null PATH")) {387equal(System.getenv("PATH"), null);388check(new File("/bin/true").exists());389check(new File("/bin/false").exists());390ProcessBuilder pb1 = new ProcessBuilder();391ProcessBuilder pb2 = new ProcessBuilder();392pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");393ProcessResults r;394395for (final ProcessBuilder pb :396new ProcessBuilder[] {pb1, pb2}) {397pb.command("true");398equal(run(pb).exitValue(), True.exitValue());399400pb.command("false");401equal(run(pb).exitValue(), False.exitValue());402}403404if (failed != 0) throw new Error("null PATH");405} else if (action.equals("PATH search algorithm")) {406equal(System.getenv("PATH"), "dir1:dir2:");407check(new File(TrueExe.path()).exists());408check(new File(FalseExe.path()).exists());409String[] cmd = {"prog"};410ProcessBuilder pb1 = new ProcessBuilder(cmd);411ProcessBuilder pb2 = new ProcessBuilder(cmd);412ProcessBuilder pb3 = new ProcessBuilder(cmd);413pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");414pb3.environment().remove("PATH");415416for (final ProcessBuilder pb :417new ProcessBuilder[] {pb1, pb2, pb3}) {418try {419// Not on PATH at all; directories don't exist420try {421pb.start();422fail("Expected IOException not thrown");423} catch (IOException e) {424String m = e.getMessage();425if (EnglishUnix.is() &&426! matches(m, NO_SUCH_FILE_ERROR_MSG))427unexpected(e);428} catch (Throwable t) { unexpected(t); }429430// Not on PATH at all; directories exist431new File("dir1").mkdirs();432new File("dir2").mkdirs();433try {434pb.start();435fail("Expected IOException not thrown");436} catch (IOException e) {437String m = e.getMessage();438if (EnglishUnix.is() &&439! matches(m, NO_SUCH_FILE_ERROR_MSG))440unexpected(e);441} catch (Throwable t) { unexpected(t); }442443// Can't execute a directory -- permission denied444// Report EACCES errno445new File("dir1/prog").mkdirs();446checkPermissionDenied(pb);447448// continue searching if EACCES449copy(TrueExe.path(), "dir2/prog");450equal(run(pb).exitValue(), True.exitValue());451new File("dir1/prog").delete();452new File("dir2/prog").delete();453454new File("dir2/prog").mkdirs();455copy(TrueExe.path(), "dir1/prog");456equal(run(pb).exitValue(), True.exitValue());457458// Check empty PATH component means current directory.459//460// While we're here, let's test different kinds of461// Unix executables, and PATH vs explicit searching.462new File("dir1/prog").delete();463new File("dir2/prog").delete();464for (String[] command :465new String[][] {466new String[] {"./prog"},467cmd}) {468pb.command(command);469File prog = new File("./prog");470// "Normal" binaries471copy(TrueExe.path(), "./prog");472equal(run(pb).exitValue(),473True.exitValue());474copy(FalseExe.path(), "./prog");475equal(run(pb).exitValue(),476False.exitValue());477prog.delete();478// Interpreter scripts with #!479setFileContents(prog, "#!/bin/true\n");480prog.setExecutable(true);481equal(run(pb).exitValue(),482True.exitValue());483prog.delete();484setFileContents(prog, "#!/bin/false\n");485prog.setExecutable(true);486equal(run(pb).exitValue(),487False.exitValue());488// Traditional shell scripts without #!489setFileContents(prog, "exec /bin/true\n");490prog.setExecutable(true);491equal(run(pb).exitValue(),492True.exitValue());493prog.delete();494setFileContents(prog, "exec /bin/false\n");495prog.setExecutable(true);496equal(run(pb).exitValue(),497False.exitValue());498prog.delete();499}500501// Test Unix interpreter scripts502File dir1Prog = new File("dir1/prog");503dir1Prog.delete();504pb.command(new String[] {"prog", "world"});505setFileContents(dir1Prog, "#!/bin/echo hello\n");506checkPermissionDenied(pb);507dir1Prog.setExecutable(true);508equal(run(pb).out(), "hello dir1/prog world\n");509equal(run(pb).exitValue(), True.exitValue());510dir1Prog.delete();511pb.command(cmd);512513// Test traditional shell scripts without #!514setFileContents(dir1Prog, "/bin/echo \"$@\"\n");515pb.command(new String[] {"prog", "hello", "world"});516checkPermissionDenied(pb);517dir1Prog.setExecutable(true);518equal(run(pb).out(), "hello world\n");519equal(run(pb).exitValue(), True.exitValue());520dir1Prog.delete();521pb.command(cmd);522523// If prog found on both parent and child's PATH,524// parent's is used.525new File("dir1/prog").delete();526new File("dir2/prog").delete();527new File("prog").delete();528new File("dir3").mkdirs();529copy(TrueExe.path(), "dir1/prog");530copy(FalseExe.path(), "dir3/prog");531pb.environment().put("PATH","dir3");532equal(run(pb).exitValue(), True.exitValue());533copy(TrueExe.path(), "dir3/prog");534copy(FalseExe.path(), "dir1/prog");535equal(run(pb).exitValue(), False.exitValue());536537} finally {538// cleanup539new File("dir1/prog").delete();540new File("dir2/prog").delete();541new File("dir3/prog").delete();542new File("dir1").delete();543new File("dir2").delete();544new File("dir3").delete();545new File("prog").delete();546}547}548549if (failed != 0) throw new Error("PATH search algorithm");550}551else throw new Error("JavaChild invocation error");552}553}554555private static void copy(String src, String dst) throws IOException {556Files.copy(Paths.get(src), Paths.get(dst),557StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);558}559560private static String javaChildOutput(ProcessBuilder pb, String...args) {561List<String> list = new ArrayList<String>(javaChildArgs);562for (String arg : args)563list.add(arg);564pb.command(list);565return commandOutput(pb);566}567568private static String getenvInChild(ProcessBuilder pb) {569return javaChildOutput(pb, "System.getenv()");570}571572private static String getenvInChild1234(ProcessBuilder pb) {573return javaChildOutput(pb, "System.getenv(\\u1234)");574}575576private static String getenvInChild(ProcessBuilder pb, String name) {577return javaChildOutput(pb, "System.getenv(String)", name);578}579580private static String pwdInChild(ProcessBuilder pb) {581return javaChildOutput(pb, "pwd");582}583584private static final String javaExe =585System.getProperty("java.home") +586File.separator + "bin" + File.separator + "java";587588private static final String classpath =589System.getProperty("java.class.path");590591private static final List<String> javaChildArgs =592Arrays.asList(javaExe,593"-XX:+DisplayVMOutputToStderr",594"-classpath", absolutifyPath(classpath),595"Basic$JavaChild");596597private static void testEncoding(String encoding, String tested) {598try {599// If round trip conversion works, should be able to set env vars600// correctly in child.601if (new String(tested.getBytes()).equals(tested)) {602out.println("Testing " + encoding + " environment values");603ProcessBuilder pb = new ProcessBuilder();604pb.environment().put("ASCIINAME",tested);605equal(getenvInChild(pb,"ASCIINAME"), tested);606}607} catch (Throwable t) { unexpected(t); }608}609610static class Windows {611public static boolean is() { return is; }612private static final boolean is =613System.getProperty("os.name").startsWith("Windows");614}615616static class AIX {617public static boolean is() { return is; }618private static final boolean is =619System.getProperty("os.name").equals("AIX");620}621622static class Unix {623public static boolean is() { return is; }624private static final boolean is =625(! Windows.is() &&626new File("/bin/sh").exists() &&627new File("/bin/true").exists() &&628new File("/bin/false").exists());629}630631static class UnicodeOS {632public static boolean is() { return is; }633private static final String osName = System.getProperty("os.name");634private static final boolean is =635// MacOS X would probably also qualify636osName.startsWith("Windows") &&637! osName.startsWith("Windows 9") &&638! osName.equals("Windows Me");639}640641static class MacOSX {642public static boolean is() { return is; }643private static final String osName = System.getProperty("os.name");644private static final boolean is = osName.contains("OS X");645}646647static class True {648public static int exitValue() { return 0; }649}650651private static class False {652public static int exitValue() { return exitValue; }653private static final int exitValue = exitValue0();654private static int exitValue0() {655// /bin/false returns an *unspecified* non-zero number.656try {657if (! Unix.is())658return -1;659else {660int rc = new ProcessBuilder("/bin/false")661.start().waitFor();662check(rc != 0);663return rc;664}665} catch (Throwable t) { unexpected(t); return -1; }666}667}668669// On Alpine Linux, /bin/true and /bin/false are just links to /bin/busybox.670// Some tests copy /bin/true and /bin/false to files with a different filename.671// However, copying the busbox executable into a file with a different name672// won't result in the expected return codes. As workaround, we create673// executable files that can be copied and produce the expected return674// values.675676private static class TrueExe {677public static String path() { return path; }678private static final String path = path0();679private static String path0(){680if (!Platform.isBusybox("/bin/true")) {681return "/bin/true";682} else {683File trueExe = new File("true");684setFileContents(trueExe, "#!/bin/true\n");685trueExe.setExecutable(true);686return trueExe.getAbsolutePath();687}688}689}690691private static class FalseExe {692public static String path() { return path; }693private static final String path = path0();694private static String path0(){695if (!Platform.isBusybox("/bin/false")) {696return "/bin/false";697} else {698File falseExe = new File("false");699setFileContents(falseExe, "#!/bin/false\n");700falseExe.setExecutable(true);701return falseExe.getAbsolutePath();702}703}704}705706static class EnglishUnix {707private static final Boolean is =708(! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));709710private static boolean isEnglish(String envvar) {711String val = getenv(envvar);712return (val == null) || val.matches("en.*") || val.matches("C");713}714715/** Returns true if we can expect English OS error strings */716static boolean is() { return is; }717}718719static class DelegatingProcess extends Process {720final Process p;721722DelegatingProcess(Process p) {723this.p = p;724}725726@Override727public void destroy() {728p.destroy();729}730731@Override732public int exitValue() {733return p.exitValue();734}735736@Override737public int waitFor() throws InterruptedException {738return p.waitFor();739}740741@Override742public OutputStream getOutputStream() {743return p.getOutputStream();744}745746@Override747public InputStream getInputStream() {748return p.getInputStream();749}750751@Override752public InputStream getErrorStream() {753return p.getErrorStream();754}755}756757private static boolean matches(String str, String regex) {758return Pattern.compile(regex).matcher(str).find();759}760761private static String matchAndExtract(String str, String regex) {762Matcher matcher = Pattern.compile(regex).matcher(str);763if (matcher.find()) {764return matcher.group();765} else {766return "";767}768}769770/* Only used for Mac OS X --771* Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty772* environment. The environment variable JAVA_MAIN_CLASS_<pid> may also773* be set in Mac OS X.774* Remove them both from the list of env variables775*/776private static String removeMacExpectedVars(String vars) {777// Check for __CF_USER_TEXT_ENCODING778String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="779+cfUserTextEncoding+",","");780// Check for JAVA_MAIN_CLASS_<pid>781String javaMainClassStr782= matchAndExtract(cleanedVars,783"JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");784return cleanedVars.replace(javaMainClassStr,"");785}786787/* Only used for AIX --788* AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.789* Remove it from the list of env variables790*/791private static String removeAixExpectedVars(String vars) {792return vars.replace("AIXTHREAD_GUARDPAGES=0,", "");793}794795private static String sortByLinesWindowsly(String text) {796String[] lines = text.split("\n");797Arrays.sort(lines, new WindowsComparator());798StringBuilder sb = new StringBuilder();799for (String line : lines)800sb.append(line).append("\n");801return sb.toString();802}803804private static void checkMapSanity(Map<String,String> map) {805try {806Set<String> keySet = map.keySet();807Collection<String> values = map.values();808Set<Map.Entry<String,String>> entrySet = map.entrySet();809810equal(entrySet.size(), keySet.size());811equal(entrySet.size(), values.size());812813StringBuilder s1 = new StringBuilder();814for (Map.Entry<String,String> e : entrySet)815s1.append(e.getKey() + "=" + e.getValue() + "\n");816817StringBuilder s2 = new StringBuilder();818for (String var : keySet)819s2.append(var + "=" + map.get(var) + "\n");820821equal(s1.toString(), s2.toString());822823Iterator<String> kIter = keySet.iterator();824Iterator<String> vIter = values.iterator();825Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();826827while (eIter.hasNext()) {828Map.Entry<String,String> entry = eIter.next();829String key = kIter.next();830String value = vIter.next();831check(entrySet.contains(entry));832check(keySet.contains(key));833check(values.contains(value));834check(map.containsKey(key));835check(map.containsValue(value));836equal(entry.getKey(), key);837equal(entry.getValue(), value);838}839check(!kIter.hasNext() &&840!vIter.hasNext());841842} catch (Throwable t) { unexpected(t); }843}844845private static void checkMapEquality(Map<String,String> map1,846Map<String,String> map2) {847try {848equal(map1.size(), map2.size());849equal(map1.isEmpty(), map2.isEmpty());850for (String key : map1.keySet()) {851equal(map1.get(key), map2.get(key));852check(map2.keySet().contains(key));853}854equal(map1, map2);855equal(map2, map1);856equal(map1.entrySet(), map2.entrySet());857equal(map2.entrySet(), map1.entrySet());858equal(map1.keySet(), map2.keySet());859equal(map2.keySet(), map1.keySet());860861equal(map1.hashCode(), map2.hashCode());862equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());863equal(map1.keySet().hashCode(), map2.keySet().hashCode());864} catch (Throwable t) { unexpected(t); }865}866867static void checkRedirects(ProcessBuilder pb,868Redirect in, Redirect out, Redirect err) {869equal(pb.redirectInput(), in);870equal(pb.redirectOutput(), out);871equal(pb.redirectError(), err);872}873874static void redirectIO(ProcessBuilder pb,875Redirect in, Redirect out, Redirect err) {876pb.redirectInput(in);877pb.redirectOutput(out);878pb.redirectError(err);879}880881static void setFileContents(File file, String contents) {882try {883Writer w = new FileWriter(file);884w.write(contents);885w.close();886} catch (Throwable t) { unexpected(t); }887}888889static String fileContents(File file) {890try {891Reader r = new FileReader(file);892StringBuilder sb = new StringBuilder();893char[] buffer = new char[1024];894int n;895while ((n = r.read(buffer)) != -1)896sb.append(buffer,0,n);897r.close();898return new String(sb);899} catch (Throwable t) { unexpected(t); return ""; }900}901902@SuppressWarnings("removal")903static void testIORedirection() throws Throwable {904final File ifile = new File("ifile");905final File ofile = new File("ofile");906final File efile = new File("efile");907ifile.delete();908ofile.delete();909efile.delete();910911//----------------------------------------------------------------912// Check mutual inequality of different types of Redirect913//----------------------------------------------------------------914Redirect[] redirects =915{ PIPE,916INHERIT,917DISCARD,918Redirect.from(ifile),919Redirect.to(ifile),920Redirect.appendTo(ifile),921Redirect.from(ofile),922Redirect.to(ofile),923Redirect.appendTo(ofile),924};925for (int i = 0; i < redirects.length; i++)926for (int j = 0; j < redirects.length; j++)927equal(redirects[i].equals(redirects[j]), (i == j));928929//----------------------------------------------------------------930// Check basic properties of different types of Redirect931//----------------------------------------------------------------932equal(PIPE.type(), Redirect.Type.PIPE);933equal(PIPE.toString(), "PIPE");934equal(PIPE.file(), null);935936equal(INHERIT.type(), Redirect.Type.INHERIT);937equal(INHERIT.toString(), "INHERIT");938equal(INHERIT.file(), null);939940equal(DISCARD.type(), Redirect.Type.WRITE);941equal(DISCARD.toString(), "WRITE");942equal(DISCARD.file(), new File((Windows.is() ? "NUL" : "/dev/null")));943944equal(Redirect.from(ifile).type(), Redirect.Type.READ);945equal(Redirect.from(ifile).toString(),946"redirect to read from file \"ifile\"");947equal(Redirect.from(ifile).file(), ifile);948equal(Redirect.from(ifile),949Redirect.from(ifile));950equal(Redirect.from(ifile).hashCode(),951Redirect.from(ifile).hashCode());952953equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);954equal(Redirect.to(ofile).toString(),955"redirect to write to file \"ofile\"");956equal(Redirect.to(ofile).file(), ofile);957equal(Redirect.to(ofile),958Redirect.to(ofile));959equal(Redirect.to(ofile).hashCode(),960Redirect.to(ofile).hashCode());961962equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);963equal(Redirect.appendTo(efile).toString(),964"redirect to append to file \"efile\"");965equal(Redirect.appendTo(efile).file(), efile);966equal(Redirect.appendTo(efile),967Redirect.appendTo(efile));968equal(Redirect.appendTo(efile).hashCode(),969Redirect.appendTo(efile).hashCode());970971//----------------------------------------------------------------972// Check initial values of redirects973//----------------------------------------------------------------974List<String> childArgs = new ArrayList<String>(javaChildArgs);975childArgs.add("testIO");976final ProcessBuilder pb = new ProcessBuilder(childArgs);977checkRedirects(pb, PIPE, PIPE, PIPE);978979//----------------------------------------------------------------980// Check inheritIO981//----------------------------------------------------------------982pb.inheritIO();983checkRedirects(pb, INHERIT, INHERIT, INHERIT);984985//----------------------------------------------------------------986// Check DISCARD for stdout,stderr987//----------------------------------------------------------------988redirectIO(pb, INHERIT, DISCARD, DISCARD);989checkRedirects(pb, INHERIT, DISCARD, DISCARD);990991//----------------------------------------------------------------992// Check setters and getters agree993//----------------------------------------------------------------994pb.redirectInput(ifile);995equal(pb.redirectInput().file(), ifile);996equal(pb.redirectInput(), Redirect.from(ifile));997998pb.redirectOutput(ofile);999equal(pb.redirectOutput().file(), ofile);1000equal(pb.redirectOutput(), Redirect.to(ofile));10011002pb.redirectError(efile);1003equal(pb.redirectError().file(), efile);1004equal(pb.redirectError(), Redirect.to(efile));10051006THROWS(IllegalArgumentException.class,1007() -> pb.redirectInput(Redirect.to(ofile)),1008() -> pb.redirectOutput(Redirect.from(ifile)),1009() -> pb.redirectError(Redirect.from(ifile)),1010() -> pb.redirectInput(DISCARD));10111012THROWS(NullPointerException.class,1013() -> pb.redirectInput((File)null),1014() -> pb.redirectOutput((File)null),1015() -> pb.redirectError((File)null),1016() -> pb.redirectInput((Redirect)null),1017() -> pb.redirectOutput((Redirect)null),1018() -> pb.redirectError((Redirect)null));10191020THROWS(IOException.class,1021// Input file does not exist1022() -> pb.start());1023setFileContents(ifile, "standard input");10241025//----------------------------------------------------------------1026// Writing to non-existent files1027//----------------------------------------------------------------1028{1029ProcessResults r = run(pb);1030equal(r.exitValue(), 0);1031equal(fileContents(ofile), "standard output");1032equal(fileContents(efile), "standard error");1033equal(r.out(), "");1034equal(r.err(), "");1035ofile.delete();1036efile.delete();1037}10381039//----------------------------------------------------------------1040// Both redirectErrorStream + redirectError1041//----------------------------------------------------------------1042{1043pb.redirectErrorStream(true);1044ProcessResults r = run(pb);1045equal(r.exitValue(), 0);1046equal(fileContents(ofile),1047"standard error" + "standard output");1048equal(fileContents(efile), "");1049equal(r.out(), "");1050equal(r.err(), "");1051ofile.delete();1052efile.delete();1053}10541055//----------------------------------------------------------------1056// Appending to existing files1057//----------------------------------------------------------------1058{1059setFileContents(ofile, "ofile-contents");1060setFileContents(efile, "efile-contents");1061pb.redirectOutput(Redirect.appendTo(ofile));1062pb.redirectError(Redirect.appendTo(efile));1063pb.redirectErrorStream(false);1064ProcessResults r = run(pb);1065equal(r.exitValue(), 0);1066equal(fileContents(ofile),1067"ofile-contents" + "standard output");1068equal(fileContents(efile),1069"efile-contents" + "standard error");1070equal(r.out(), "");1071equal(r.err(), "");1072ofile.delete();1073efile.delete();1074}10751076//----------------------------------------------------------------1077// Replacing existing files1078//----------------------------------------------------------------1079{1080setFileContents(ofile, "ofile-contents");1081setFileContents(efile, "efile-contents");1082pb.redirectOutput(ofile);1083pb.redirectError(Redirect.to(efile));1084ProcessResults r = run(pb);1085equal(r.exitValue(), 0);1086equal(fileContents(ofile), "standard output");1087equal(fileContents(efile), "standard error");1088equal(r.out(), "");1089equal(r.err(), "");1090ofile.delete();1091efile.delete();1092}10931094//----------------------------------------------------------------1095// Appending twice to the same file?1096//----------------------------------------------------------------1097{1098setFileContents(ofile, "ofile-contents");1099setFileContents(efile, "efile-contents");1100Redirect appender = Redirect.appendTo(ofile);1101pb.redirectOutput(appender);1102pb.redirectError(appender);1103ProcessResults r = run(pb);1104equal(r.exitValue(), 0);1105equal(fileContents(ofile),1106"ofile-contents" +1107"standard error" +1108"standard output");1109equal(fileContents(efile), "efile-contents");1110equal(r.out(), "");1111equal(r.err(), "");1112ifile.delete();1113ofile.delete();1114efile.delete();1115}11161117//----------------------------------------------------------------1118// DISCARDing output1119//----------------------------------------------------------------1120{1121setFileContents(ifile, "standard input");1122pb.redirectOutput(DISCARD);1123pb.redirectError(DISCARD);1124ProcessResults r = run(pb);1125equal(r.exitValue(), 0);1126equal(r.out(), "");1127equal(r.err(), "");1128}11291130//----------------------------------------------------------------1131// DISCARDing output and redirecting error1132//----------------------------------------------------------------1133{1134setFileContents(ifile, "standard input");1135setFileContents(ofile, "ofile-contents");1136setFileContents(efile, "efile-contents");1137pb.redirectOutput(DISCARD);1138pb.redirectError(efile);1139ProcessResults r = run(pb);1140equal(r.exitValue(), 0);1141equal(fileContents(ofile), "ofile-contents");1142equal(fileContents(efile), "standard error");1143equal(r.out(), "");1144equal(r.err(), "");1145ofile.delete();1146efile.delete();1147}11481149//----------------------------------------------------------------1150// DISCARDing error and redirecting output1151//----------------------------------------------------------------1152{1153setFileContents(ifile, "standard input");1154setFileContents(ofile, "ofile-contents");1155setFileContents(efile, "efile-contents");1156pb.redirectOutput(ofile);1157pb.redirectError(DISCARD);1158ProcessResults r = run(pb);1159equal(r.exitValue(), 0);1160equal(fileContents(ofile), "standard output");1161equal(fileContents(efile), "efile-contents");1162equal(r.out(), "");1163equal(r.err(), "");1164ofile.delete();1165efile.delete();1166}11671168//----------------------------------------------------------------1169// DISCARDing output and merging error into output1170//----------------------------------------------------------------1171{1172setFileContents(ifile, "standard input");1173setFileContents(ofile, "ofile-contents");1174setFileContents(efile, "efile-contents");1175pb.redirectOutput(DISCARD);1176pb.redirectErrorStream(true);1177pb.redirectError(efile);1178ProcessResults r = run(pb);1179equal(r.exitValue(), 0);1180equal(fileContents(ofile), "ofile-contents"); // untouched1181equal(fileContents(efile), ""); // empty1182equal(r.out(), "");1183equal(r.err(), "");1184ifile.delete();1185ofile.delete();1186efile.delete();1187pb.redirectErrorStream(false); // reset for next test1188}11891190//----------------------------------------------------------------1191// Testing INHERIT is harder.1192// Note that this requires __FOUR__ nested JVMs involved in one test,1193// if you count the harness JVM.1194//----------------------------------------------------------------1195for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {1196redirectIO(pb, PIPE, PIPE, PIPE);1197List<String> command = pb.command();1198command.set(command.size() - 1, testName);1199Process p = pb.start();1200new PrintStream(p.getOutputStream()).print("standard input");1201p.getOutputStream().close();1202ProcessResults r = run(p);1203equal(r.exitValue(), 0);1204equal(r.out(), "standard output");1205equal(r.err(), "standard error");1206}12071208//----------------------------------------------------------------1209// Test security implications of I/O redirection1210//----------------------------------------------------------------12111212// Read access to current directory is always granted;1213// So create a tmpfile for input instead.1214final File tmpFile = File.createTempFile("Basic", "tmp");1215setFileContents(tmpFile, "standard input");12161217final Policy policy = new Policy();1218Policy.setPolicy(policy);1219System.setSecurityManager(new SecurityManager());1220try {1221final Permission xPermission1222= new FilePermission("<<ALL FILES>>", "execute");1223final Permission rxPermission1224= new FilePermission("<<ALL FILES>>", "read,execute");1225final Permission wxPermission1226= new FilePermission("<<ALL FILES>>", "write,execute");1227final Permission rwxPermission1228= new FilePermission("<<ALL FILES>>", "read,write,execute");12291230THROWS(SecurityException.class,1231() -> { policy.setPermissions(xPermission);1232redirectIO(pb, from(tmpFile), PIPE, PIPE);1233pb.start();},1234() -> { policy.setPermissions(rxPermission);1235redirectIO(pb, PIPE, to(ofile), PIPE);1236pb.start();},1237() -> { policy.setPermissions(rxPermission);1238redirectIO(pb, PIPE, PIPE, to(efile));1239pb.start();});12401241{1242policy.setPermissions(rxPermission);1243redirectIO(pb, from(tmpFile), PIPE, PIPE);1244ProcessResults r = run(pb);1245equal(r.out(), "standard output");1246equal(r.err(), "standard error");1247}12481249{1250policy.setPermissions(wxPermission);1251redirectIO(pb, PIPE, to(ofile), to(efile));1252Process p = pb.start();1253new PrintStream(p.getOutputStream()).print("standard input");1254p.getOutputStream().close();1255ProcessResults r = run(p);1256policy.setPermissions(rwxPermission);1257equal(fileContents(ofile), "standard output");1258equal(fileContents(efile), "standard error");1259}12601261{1262policy.setPermissions(rwxPermission);1263redirectIO(pb, from(tmpFile), to(ofile), to(efile));1264ProcessResults r = run(pb);1265policy.setPermissions(rwxPermission);1266equal(fileContents(ofile), "standard output");1267equal(fileContents(efile), "standard error");1268}12691270} finally {1271policy.setPermissions(new RuntimePermission("setSecurityManager"));1272System.setSecurityManager(null);1273tmpFile.delete();1274ifile.delete();1275ofile.delete();1276efile.delete();1277}1278}12791280static void checkProcessPid() {1281ProcessBuilder pb = new ProcessBuilder();1282List<String> list = new ArrayList<String>(javaChildArgs);1283list.add("pid");1284pb.command(list);1285try {1286Process p = pb.start();1287String s = commandOutput(p);1288long actualPid = Long.valueOf(s.trim());1289long expectedPid = p.pid();1290equal(actualPid, expectedPid);1291} catch (Throwable t) {1292unexpected(t);1293}129412951296// Test the default implementation of Process.getPid1297DelegatingProcess p = new DelegatingProcess(null);1298THROWS(UnsupportedOperationException.class,1299() -> p.pid(),1300() -> p.toHandle(),1301() -> p.supportsNormalTermination(),1302() -> p.children(),1303() -> p.descendants());13041305}13061307@SuppressWarnings("removal")1308private static void realMain(String[] args) throws Throwable {1309if (Windows.is())1310System.out.println("This appears to be a Windows system.");1311if (Unix.is())1312System.out.println("This appears to be a Unix system.");1313if (UnicodeOS.is())1314System.out.println("This appears to be a Unicode-based OS.");13151316try { testIORedirection(); }1317catch (Throwable t) { unexpected(t); }13181319//----------------------------------------------------------------1320// Basic tests for getPid()1321//----------------------------------------------------------------1322checkProcessPid();13231324//----------------------------------------------------------------1325// Basic tests for setting, replacing and deleting envvars1326//----------------------------------------------------------------1327try {1328ProcessBuilder pb = new ProcessBuilder();1329Map<String,String> environ = pb.environment();13301331// New env var1332environ.put("QUUX", "BAR");1333equal(environ.get("QUUX"), "BAR");1334equal(getenvInChild(pb,"QUUX"), "BAR");13351336// Modify env var1337environ.put("QUUX","bear");1338equal(environ.get("QUUX"), "bear");1339equal(getenvInChild(pb,"QUUX"), "bear");1340checkMapSanity(environ);13411342// Remove env var1343environ.remove("QUUX");1344equal(environ.get("QUUX"), null);1345equal(getenvInChild(pb,"QUUX"), "null");1346checkMapSanity(environ);13471348// Remove non-existent env var1349environ.remove("QUUX");1350equal(environ.get("QUUX"), null);1351equal(getenvInChild(pb,"QUUX"), "null");1352checkMapSanity(environ);1353} catch (Throwable t) { unexpected(t); }13541355//----------------------------------------------------------------1356// Pass Empty environment to child1357//----------------------------------------------------------------1358try {1359ProcessBuilder pb = new ProcessBuilder();1360pb.environment().clear();1361String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";1362expected = AIX.is() ? "LIBPATH="+libpath+",": expected;1363if (Windows.is()) {1364pb.environment().put("SystemRoot", systemRoot);1365}1366if (AIX.is()) {1367pb.environment().put("LIBPATH", libpath);1368}1369String result = getenvInChild(pb);1370if (MacOSX.is()) {1371result = removeMacExpectedVars(result);1372}1373if (AIX.is()) {1374result = removeAixExpectedVars(result);1375}1376equal(result, expected);1377} catch (Throwable t) { unexpected(t); }13781379//----------------------------------------------------------------1380// System.getenv() is read-only.1381//----------------------------------------------------------------1382THROWS(UnsupportedOperationException.class,1383() -> getenv().put("FOO","BAR"),1384() -> getenv().remove("PATH"),1385() -> getenv().keySet().remove("PATH"),1386() -> getenv().values().remove("someValue"));13871388try {1389Collection<Map.Entry<String,String>> c = getenv().entrySet();1390if (! c.isEmpty())1391try {1392c.iterator().next().setValue("foo");1393fail("Expected UnsupportedOperationException not thrown");1394} catch (UnsupportedOperationException e) {} // OK1395} catch (Throwable t) { unexpected(t); }13961397//----------------------------------------------------------------1398// System.getenv() always returns the same object in our implementation.1399//----------------------------------------------------------------1400try {1401check(System.getenv() == System.getenv());1402} catch (Throwable t) { unexpected(t); }14031404//----------------------------------------------------------------1405// You can't create an env var name containing "=",1406// or an env var name or value containing NUL.1407//----------------------------------------------------------------1408{1409final Map<String,String> m = new ProcessBuilder().environment();1410THROWS(IllegalArgumentException.class,1411() -> m.put("FOO=","BAR"),1412() -> m.put("FOO\u0000","BAR"),1413() -> m.put("FOO","BAR\u0000"));1414}14151416//----------------------------------------------------------------1417// Commands must never be null.1418//----------------------------------------------------------------1419THROWS(NullPointerException.class,1420() -> new ProcessBuilder((List<String>)null),1421() -> new ProcessBuilder().command((List<String>)null));14221423//----------------------------------------------------------------1424// Put in a command; get the same one back out.1425//----------------------------------------------------------------1426try {1427List<String> command = new ArrayList<String>();1428ProcessBuilder pb = new ProcessBuilder(command);1429check(pb.command() == command);1430List<String> command2 = new ArrayList<String>(2);1431command2.add("foo");1432command2.add("bar");1433pb.command(command2);1434check(pb.command() == command2);1435pb.command("foo", "bar");1436check(pb.command() != command2 && pb.command().equals(command2));1437pb.command(command2);1438command2.add("baz");1439equal(pb.command().get(2), "baz");1440} catch (Throwable t) { unexpected(t); }14411442//----------------------------------------------------------------1443// Commands must contain at least one element.1444//----------------------------------------------------------------1445THROWS(IndexOutOfBoundsException.class,1446() -> new ProcessBuilder().start(),1447() -> new ProcessBuilder(new ArrayList<String>()).start(),1448() -> Runtime.getRuntime().exec(new String[]{}));14491450//----------------------------------------------------------------1451// Commands must not contain null elements at start() time.1452//----------------------------------------------------------------1453THROWS(NullPointerException.class,1454() -> new ProcessBuilder("foo",null,"bar").start(),1455() -> new ProcessBuilder((String)null).start(),1456() -> new ProcessBuilder(new String[]{null}).start(),1457() -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());14581459//----------------------------------------------------------------1460// Command lists are growable.1461//----------------------------------------------------------------1462try {1463new ProcessBuilder().command().add("foo");1464new ProcessBuilder("bar").command().add("foo");1465new ProcessBuilder(new String[]{"1","2"}).command().add("3");1466} catch (Throwable t) { unexpected(t); }14671468//----------------------------------------------------------------1469// Nulls in environment updates generate NullPointerException1470//----------------------------------------------------------------1471try {1472final Map<String,String> env = new ProcessBuilder().environment();1473THROWS(NullPointerException.class,1474() -> env.put("foo",null),1475() -> env.put(null,"foo"),1476() -> env.remove(null),1477() -> { for (Map.Entry<String,String> e : env.entrySet())1478e.setValue(null);},1479() -> Runtime.getRuntime().exec(new String[]{"foo"},1480new String[]{null}));1481} catch (Throwable t) { unexpected(t); }14821483//----------------------------------------------------------------1484// Non-String types in environment updates generate ClassCastException1485//----------------------------------------------------------------1486try {1487final Map<String,String> env = new ProcessBuilder().environment();1488THROWS(ClassCastException.class,1489() -> env.remove(TRUE),1490() -> env.keySet().remove(TRUE),1491() -> env.values().remove(TRUE),1492() -> env.entrySet().remove(TRUE));1493} catch (Throwable t) { unexpected(t); }14941495//----------------------------------------------------------------1496// Check query operations on environment maps1497//----------------------------------------------------------------1498try {1499List<Map<String,String>> envs =1500new ArrayList<Map<String,String>>(2);1501envs.add(System.getenv());1502envs.add(new ProcessBuilder().environment());1503for (final Map<String,String> env : envs) {1504//----------------------------------------------------------------1505// Nulls in environment queries are forbidden.1506//----------------------------------------------------------------1507THROWS(NullPointerException.class,1508() -> getenv(null),1509() -> env.get(null),1510() -> env.containsKey(null),1511() -> env.containsValue(null),1512() -> env.keySet().contains(null),1513() -> env.values().contains(null));15141515//----------------------------------------------------------------1516// Non-String types in environment queries are forbidden.1517//----------------------------------------------------------------1518THROWS(ClassCastException.class,1519() -> env.get(TRUE),1520() -> env.containsKey(TRUE),1521() -> env.containsValue(TRUE),1522() -> env.keySet().contains(TRUE),1523() -> env.values().contains(TRUE));15241525//----------------------------------------------------------------1526// Illegal String values in environment queries are (grumble) OK1527//----------------------------------------------------------------1528equal(env.get("\u0000"), null);1529check(! env.containsKey("\u0000"));1530check(! env.containsValue("\u0000"));1531check(! env.keySet().contains("\u0000"));1532check(! env.values().contains("\u0000"));1533}15341535} catch (Throwable t) { unexpected(t); }15361537try {1538final Set<Map.Entry<String,String>> entrySet =1539new ProcessBuilder().environment().entrySet();1540THROWS(NullPointerException.class,1541() -> entrySet.contains(null));1542THROWS(ClassCastException.class,1543() -> entrySet.contains(TRUE),1544() -> entrySet.contains(1545new SimpleImmutableEntry<Boolean,String>(TRUE,"")));15461547check(! entrySet.contains1548(new SimpleImmutableEntry<String,String>("", "")));1549} catch (Throwable t) { unexpected(t); }15501551//----------------------------------------------------------------1552// Put in a directory; get the same one back out.1553//----------------------------------------------------------------1554try {1555ProcessBuilder pb = new ProcessBuilder();1556File foo = new File("foo");1557equal(pb.directory(), null);1558equal(pb.directory(foo).directory(), foo);1559equal(pb.directory(null).directory(), null);1560} catch (Throwable t) { unexpected(t); }15611562//----------------------------------------------------------------1563// If round-trip conversion works, check envvar pass-through to child1564//----------------------------------------------------------------1565try {1566testEncoding("ASCII", "xyzzy");1567testEncoding("Latin1", "\u00f1\u00e1");1568testEncoding("Unicode", "\u22f1\u11e1");1569} catch (Throwable t) { unexpected(t); }15701571//----------------------------------------------------------------1572// A surprisingly large number of ways to delete an environment var.1573//----------------------------------------------------------------1574testVariableDeleter(new EnvironmentFrobber() {1575public void doIt(Map<String,String> environ) {1576environ.remove("Foo");}});15771578testVariableDeleter(new EnvironmentFrobber() {1579public void doIt(Map<String,String> environ) {1580environ.keySet().remove("Foo");}});15811582testVariableDeleter(new EnvironmentFrobber() {1583public void doIt(Map<String,String> environ) {1584environ.values().remove("BAAR");}});15851586testVariableDeleter(new EnvironmentFrobber() {1587public void doIt(Map<String,String> environ) {1588// Legally fabricate a ProcessEnvironment.StringEntry,1589// even though it's private.1590Map<String,String> environ21591= new ProcessBuilder().environment();1592environ2.clear();1593environ2.put("Foo","BAAR");1594// Subtlety alert.1595Map.Entry<String,String> e1596= environ2.entrySet().iterator().next();1597environ.entrySet().remove(e);}});15981599testVariableDeleter(new EnvironmentFrobber() {1600public void doIt(Map<String,String> environ) {1601Map.Entry<String,String> victim = null;1602for (Map.Entry<String,String> e : environ.entrySet())1603if (e.getKey().equals("Foo"))1604victim = e;1605if (victim != null)1606environ.entrySet().remove(victim);}});16071608testVariableDeleter(new EnvironmentFrobber() {1609public void doIt(Map<String,String> environ) {1610Iterator<String> it = environ.keySet().iterator();1611while (it.hasNext()) {1612String val = it.next();1613if (val.equals("Foo"))1614it.remove();}}});16151616testVariableDeleter(new EnvironmentFrobber() {1617public void doIt(Map<String,String> environ) {1618Iterator<Map.Entry<String,String>> it1619= environ.entrySet().iterator();1620while (it.hasNext()) {1621Map.Entry<String,String> e = it.next();1622if (e.getKey().equals("Foo"))1623it.remove();}}});16241625testVariableDeleter(new EnvironmentFrobber() {1626public void doIt(Map<String,String> environ) {1627Iterator<String> it = environ.values().iterator();1628while (it.hasNext()) {1629String val = it.next();1630if (val.equals("BAAR"))1631it.remove();}}});16321633//----------------------------------------------------------------1634// A surprisingly small number of ways to add an environment var.1635//----------------------------------------------------------------1636testVariableAdder(new EnvironmentFrobber() {1637public void doIt(Map<String,String> environ) {1638environ.put("Foo","Bahrein");}});16391640//----------------------------------------------------------------1641// A few ways to modify an environment var.1642//----------------------------------------------------------------1643testVariableModifier(new EnvironmentFrobber() {1644public void doIt(Map<String,String> environ) {1645environ.put("Foo","NewValue");}});16461647testVariableModifier(new EnvironmentFrobber() {1648public void doIt(Map<String,String> environ) {1649for (Map.Entry<String,String> e : environ.entrySet())1650if (e.getKey().equals("Foo"))1651e.setValue("NewValue");}});16521653//----------------------------------------------------------------1654// Fiddle with environment sizes1655//----------------------------------------------------------------1656try {1657Map<String,String> environ = new ProcessBuilder().environment();1658int size = environ.size();1659checkSizes(environ, size);16601661environ.put("UnLiKeLYeNVIROmtNam", "someVal");1662checkSizes(environ, size+1);16631664// Check for environment independence1665new ProcessBuilder().environment().clear();16661667environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");1668checkSizes(environ, size+1);16691670environ.remove("UnLiKeLYeNVIROmtNam");1671checkSizes(environ, size);16721673environ.clear();1674checkSizes(environ, 0);16751676environ.clear();1677checkSizes(environ, 0);16781679environ = new ProcessBuilder().environment();1680environ.keySet().clear();1681checkSizes(environ, 0);16821683environ = new ProcessBuilder().environment();1684environ.entrySet().clear();1685checkSizes(environ, 0);16861687environ = new ProcessBuilder().environment();1688environ.values().clear();1689checkSizes(environ, 0);1690} catch (Throwable t) { unexpected(t); }16911692//----------------------------------------------------------------1693// Check that various map invariants hold1694//----------------------------------------------------------------1695checkMapSanity(new ProcessBuilder().environment());1696checkMapSanity(System.getenv());1697checkMapEquality(new ProcessBuilder().environment(),1698new ProcessBuilder().environment());169917001701//----------------------------------------------------------------1702// Check effects on external "env" command.1703//----------------------------------------------------------------1704try {1705Set<String> env1 = new HashSet<String>1706(Arrays.asList(nativeEnv((String[])null).split("\n")));17071708ProcessBuilder pb = new ProcessBuilder();1709pb.environment().put("QwErTyUiOp","AsDfGhJk");17101711Set<String> env2 = new HashSet<String>1712(Arrays.asList(nativeEnv(pb).split("\n")));17131714check(env2.size() == env1.size() + 1);1715env1.add("QwErTyUiOp=AsDfGhJk");1716check(env1.equals(env2));1717} catch (Throwable t) { unexpected(t); }17181719//----------------------------------------------------------------1720// Test Runtime.exec(...envp...)1721// Check for sort order of environment variables on Windows.1722//----------------------------------------------------------------1723try {1724String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");1725// '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'1726String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",1727"+=+", "_=_", "~=~", systemRoot};1728String output = nativeEnv(envp);1729String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";1730// On Windows, Java must keep the environment sorted.1731// Order is random on Unix, so this test does the sort.1732if (! Windows.is())1733output = sortByLinesWindowsly(output);1734equal(output, expected);1735} catch (Throwable t) { unexpected(t); }17361737//----------------------------------------------------------------1738// Test Runtime.exec(...envp...)1739// and check SystemRoot gets set automatically on Windows1740//----------------------------------------------------------------1741try {1742if (Windows.is()) {1743String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");1744String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",1745"+=+", "_=_", "~=~"};1746String output = nativeEnv(envp);1747String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";1748equal(output, expected);1749}1750} catch (Throwable t) { unexpected(t); }17511752//----------------------------------------------------------------1753// System.getenv() must be consistent with System.getenv(String)1754//----------------------------------------------------------------1755try {1756for (Map.Entry<String,String> e : getenv().entrySet())1757equal(getenv(e.getKey()), e.getValue());1758} catch (Throwable t) { unexpected(t); }17591760//----------------------------------------------------------------1761// Fiddle with working directory in child1762//----------------------------------------------------------------1763try {1764String canonicalUserDir =1765new File(System.getProperty("user.dir")).getCanonicalPath();1766String[] sdirs = new String[]1767{".", "..", "/", "/bin",1768"C:", "c:", "C:/", "c:\\", "\\", "\\bin",1769"c:\\windows ", "c:\\Program Files", "c:\\Program Files\\" };1770for (String sdir : sdirs) {1771File dir = new File(sdir);1772if (! (dir.isDirectory() && dir.exists()))1773continue;1774out.println("Testing directory " + dir);1775//dir = new File(dir.getCanonicalPath());17761777ProcessBuilder pb = new ProcessBuilder();1778equal(pb.directory(), null);1779equal(pwdInChild(pb), canonicalUserDir);17801781pb.directory(dir);1782equal(pb.directory(), dir);1783equal(pwdInChild(pb), dir.getCanonicalPath());17841785pb.directory(null);1786equal(pb.directory(), null);1787equal(pwdInChild(pb), canonicalUserDir);17881789pb.directory(dir);1790}1791} catch (Throwable t) { unexpected(t); }17921793//----------------------------------------------------------------1794// Working directory with Unicode in child1795//----------------------------------------------------------------1796try {1797if (UnicodeOS.is()) {1798File dir = new File(System.getProperty("test.dir", "."),1799"ProcessBuilderDir\u4e00\u4e02");1800try {1801if (!dir.exists())1802dir.mkdir();1803out.println("Testing Unicode directory:" + dir);1804ProcessBuilder pb = new ProcessBuilder();1805pb.directory(dir);1806equal(pwdInChild(pb), dir.getCanonicalPath());1807} finally {1808if (dir.exists())1809dir.delete();1810}1811}1812} catch (Throwable t) { unexpected(t); }18131814//----------------------------------------------------------------1815// OOME in child allocating maximally sized array1816// Test for hotspot/jvmti bug 68509571817//----------------------------------------------------------------1818try {1819List<String> list = new ArrayList<String>(javaChildArgs);1820list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",1821javaExe));1822list.add("ArrayOOME");1823ProcessResults r = run(new ProcessBuilder(list));1824check(r.err().contains("java.lang.OutOfMemoryError:"));1825check(r.err().contains(javaExe));1826check(r.err().contains(System.getProperty("java.version")));1827equal(r.exitValue(), 1);1828} catch (Throwable t) { unexpected(t); }18291830//----------------------------------------------------------------1831// Windows has tricky semi-case-insensitive semantics1832//----------------------------------------------------------------1833if (Windows.is())1834try {1835out.println("Running case insensitve variable tests");1836for (String[] namePair :1837new String[][]1838{ new String[]{"PATH","PaTh"},1839new String[]{"home","HOME"},1840new String[]{"SYSTEMROOT","SystemRoot"}}) {1841check((getenv(namePair[0]) == null &&1842getenv(namePair[1]) == null)1843||1844getenv(namePair[0]).equals(getenv(namePair[1])),1845"Windows environment variables are not case insensitive");1846}1847} catch (Throwable t) { unexpected(t); }18481849//----------------------------------------------------------------1850// Test proper Unicode child environment transfer1851//----------------------------------------------------------------1852if (UnicodeOS.is())1853try {1854ProcessBuilder pb = new ProcessBuilder();1855pb.environment().put("\u1234","\u5678");1856pb.environment().remove("PATH");1857equal(getenvInChild1234(pb), "\u5678");1858} catch (Throwable t) { unexpected(t); }185918601861//----------------------------------------------------------------1862// Test Runtime.exec(...envp...) with envstrings with initial `='1863//----------------------------------------------------------------1864try {1865List<String> childArgs = new ArrayList<String>(javaChildArgs);1866childArgs.add("System.getenv()");1867String[] cmdp = childArgs.toArray(new String[childArgs.size()]);1868String[] envp;1869String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};1870String[] envpOth = {"=ExitValue=3", "=C:=\\"};1871if (Windows.is()) {1872envp = envpWin;1873} else if (AIX.is()) {1874envp = new String[] {"=ExitValue=3", "=C:=\\", "LIBPATH=" + libpath};1875} else {1876envp = envpOth;1877}1878Process p = Runtime.getRuntime().exec(cmdp, envp);1879String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";1880expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected;1881String commandOutput = commandOutput(p);1882if (MacOSX.is()) {1883commandOutput = removeMacExpectedVars(commandOutput);1884}1885if (AIX.is()) {1886commandOutput = removeAixExpectedVars(commandOutput);1887}1888equal(commandOutput, expected);1889if (Windows.is()) {1890ProcessBuilder pb = new ProcessBuilder(childArgs);1891pb.environment().clear();1892pb.environment().put("SystemRoot", systemRoot);1893pb.environment().put("=ExitValue", "3");1894pb.environment().put("=C:", "\\");1895equal(commandOutput(pb), expected);1896}1897} catch (Throwable t) { unexpected(t); }18981899//----------------------------------------------------------------1900// Test Runtime.exec(...envp...) with envstrings without any `='1901//----------------------------------------------------------------1902try {1903String[] cmdp = {"echo"};1904String[] envp = {"Hello", "World"}; // Yuck!1905Process p = Runtime.getRuntime().exec(cmdp, envp);1906equal(commandOutput(p), "\n");1907} catch (Throwable t) { unexpected(t); }19081909//----------------------------------------------------------------1910// Test Runtime.exec(...envp...) with envstrings containing NULs1911//----------------------------------------------------------------1912try {1913List<String> childArgs = new ArrayList<String>(javaChildArgs);1914childArgs.add("System.getenv()");1915String[] cmdp = childArgs.toArray(new String[childArgs.size()]);1916String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!1917"FO\u0000=B\u0000R"};1918String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!1919"FO\u0000=B\u0000R"};1920String[] envp;1921if (Windows.is()) {1922envp = envpWin;1923} else if (AIX.is()) {1924envp = new String[] {"LC_ALL=C\u0000\u0000", // Yuck!1925"FO\u0000=B\u0000R", "LIBPATH=" + libpath};1926} else {1927envp = envpOth;1928}1929System.out.println ("cmdp");1930for (int i=0; i<cmdp.length; i++) {1931System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);1932}1933System.out.println ("envp");1934for (int i=0; i<envp.length; i++) {1935System.out.printf ("envp %d: %s\n", i, envp[i]);1936}1937Process p = Runtime.getRuntime().exec(cmdp, envp);1938String commandOutput = commandOutput(p);1939if (MacOSX.is()) {1940commandOutput = removeMacExpectedVars(commandOutput);1941}1942if (AIX.is()) {1943commandOutput = removeAixExpectedVars(commandOutput);1944}1945check(commandOutput.equals(Windows.is()1946? "LC_ALL=C,SystemRoot="+systemRoot+","1947: AIX.is()1948? "LC_ALL=C,LIBPATH="+libpath+","1949: "LC_ALL=C,"),1950"Incorrect handling of envstrings containing NULs");1951} catch (Throwable t) { unexpected(t); }19521953//----------------------------------------------------------------1954// Test the redirectErrorStream property1955//----------------------------------------------------------------1956try {1957ProcessBuilder pb = new ProcessBuilder();1958equal(pb.redirectErrorStream(), false);1959equal(pb.redirectErrorStream(true), pb);1960equal(pb.redirectErrorStream(), true);1961equal(pb.redirectErrorStream(false), pb);1962equal(pb.redirectErrorStream(), false);1963} catch (Throwable t) { unexpected(t); }19641965try {1966List<String> childArgs = new ArrayList<String>(javaChildArgs);1967childArgs.add("OutErr");1968ProcessBuilder pb = new ProcessBuilder(childArgs);1969{1970ProcessResults r = run(pb);1971equal(r.out(), "outout");1972equal(r.err(), "errerr");1973}1974{1975pb.redirectErrorStream(true);1976ProcessResults r = run(pb);1977equal(r.out(), "outerrouterr");1978equal(r.err(), "");1979}1980} catch (Throwable t) { unexpected(t); }19811982if (Unix.is()) {1983//----------------------------------------------------------------1984// We can find true and false when PATH is null1985//----------------------------------------------------------------1986try {1987List<String> childArgs = new ArrayList<String>(javaChildArgs);1988childArgs.add("null PATH");1989ProcessBuilder pb = new ProcessBuilder(childArgs);1990pb.environment().remove("PATH");1991ProcessResults r = run(pb);1992equal(r.out(), "");1993equal(r.err(), "");1994equal(r.exitValue(), 0);1995} catch (Throwable t) { unexpected(t); }19961997//----------------------------------------------------------------1998// PATH search algorithm on Unix1999//----------------------------------------------------------------2000try {2001List<String> childArgs = new ArrayList<String>(javaChildArgs);2002childArgs.add("PATH search algorithm");2003ProcessBuilder pb = new ProcessBuilder(childArgs);2004pb.environment().put("PATH", "dir1:dir2:");2005ProcessResults r = run(pb);2006equal(r.out(), "");2007equal(r.err(), "");2008equal(r.exitValue(), True.exitValue());2009} catch (Throwable t) { unexpected(t); }20102011//----------------------------------------------------------------2012// Parent's, not child's PATH is used2013//----------------------------------------------------------------2014try {2015new File("suBdiR").mkdirs();2016copy(TrueExe.path(), "suBdiR/unliKely");2017final ProcessBuilder pb =2018new ProcessBuilder(new String[]{"unliKely"});2019pb.environment().put("PATH", "suBdiR");2020THROWS(IOException.class, () -> pb.start());2021} catch (Throwable t) { unexpected(t);2022} finally {2023new File("suBdiR/unliKely").delete();2024new File("suBdiR").delete();2025}2026}20272028//----------------------------------------------------------------2029// Attempt to start bogus program ""2030//----------------------------------------------------------------2031try {2032new ProcessBuilder("").start();2033fail("Expected IOException not thrown");2034} catch (IOException e) {2035String m = e.getMessage();2036if (EnglishUnix.is() &&2037! matches(m, NO_SUCH_FILE_ERROR_MSG))2038unexpected(e);2039} catch (Throwable t) { unexpected(t); }20402041//----------------------------------------------------------------2042// Check that attempt to execute program name with funny2043// characters throws an exception containing those characters.2044//----------------------------------------------------------------2045for (String programName : new String[] {"\u00f0", "\u01f0"})2046try {2047new ProcessBuilder(programName).start();2048fail("Expected IOException not thrown");2049} catch (IOException e) {2050String m = e.getMessage();2051Pattern p = Pattern.compile(programName);2052if (! matches(m, programName)2053|| (EnglishUnix.is() &&2054! matches(m, NO_SUCH_FILE_ERROR_MSG)))2055unexpected(e);2056} catch (Throwable t) { unexpected(t); }20572058//----------------------------------------------------------------2059// Attempt to start process in nonexistent directory fails.2060//----------------------------------------------------------------2061try {2062new ProcessBuilder("echo")2063.directory(new File("UnLiKeLY"))2064.start();2065fail("Expected IOException not thrown");2066} catch (IOException e) {2067String m = e.getMessage();2068if (! matches(m, "in directory")2069|| (EnglishUnix.is() &&2070! matches(m, NO_SUCH_FILE_ERROR_MSG)))2071unexpected(e);2072} catch (Throwable t) { unexpected(t); }20732074//----------------------------------------------------------------2075// Attempt to write 4095 bytes to the pipe buffer without a2076// reader to drain it would deadlock, if not for the fact that2077// interprocess pipe buffers are at least 4096 bytes.2078//2079// Also, check that available reports all the bytes expected2080// in the pipe buffer, and that I/O operations do the expected2081// things.2082//----------------------------------------------------------------2083try {2084List<String> childArgs = new ArrayList<String>(javaChildArgs);2085childArgs.add("print4095");2086final int SIZE = 4095;2087final Process p = new ProcessBuilder(childArgs).start();2088print4095(p.getOutputStream(), (byte) '!'); // Might hang!2089p.waitFor(); // Might hang!2090equal(SIZE, p.getInputStream().available());2091equal(SIZE, p.getErrorStream().available());2092THROWS(IOException.class,2093() -> { p.getOutputStream().write((byte) '!');2094p.getOutputStream().flush();});20952096final byte[] bytes = new byte[SIZE + 1];2097equal(SIZE, p.getInputStream().read(bytes));2098for (int i = 0; i < SIZE; i++)2099equal((byte) '!', bytes[i]);2100equal((byte) 0, bytes[SIZE]);21012102equal(SIZE, p.getErrorStream().read(bytes));2103for (int i = 0; i < SIZE; i++)2104equal((byte) 'E', bytes[i]);2105equal((byte) 0, bytes[SIZE]);21062107equal(0, p.getInputStream().available());2108equal(0, p.getErrorStream().available());2109equal(-1, p.getErrorStream().read());2110equal(-1, p.getInputStream().read());21112112equal(p.exitValue(), 5);21132114p.getInputStream().close();2115p.getErrorStream().close();2116try { p.getOutputStream().close(); } catch (IOException flushFailed) { }21172118InputStream[] streams = { p.getInputStream(), p.getErrorStream() };2119for (final InputStream in : streams) {2120Fun[] ops = {2121() -> in.read(),2122() -> in.read(bytes),2123() -> in.available()2124};2125for (Fun op : ops) {2126try {2127op.f();2128fail();2129} catch (IOException expected) {2130check(expected.getMessage()2131.matches("[Ss]tream [Cc]losed"));2132}2133}2134}2135} catch (Throwable t) { unexpected(t); }21362137//----------------------------------------------------------------2138// Check that reads which are pending when Process.destroy is2139// called, get EOF, or IOException("Stream closed").2140//----------------------------------------------------------------2141try {2142final int cases = 4;2143for (int i = 0; i < cases; i++) {2144final int action = i;2145List<String> childArgs = getSleepArgs();2146final ProcessBuilder pb = new ProcessBuilder(childArgs);2147final byte[] bytes = new byte[10];2148final Process p = pb.start();2149final CountDownLatch latch = new CountDownLatch(1);2150final InputStream s;2151switch (action & 0x1) {2152case 0: s = p.getInputStream(); break;2153case 1: s = p.getErrorStream(); break;2154default: throw new Error();2155}2156final Thread thread = new Thread() {2157public void run() {2158try {2159int r;2160latch.countDown();2161switch (action & 0x2) {2162case 0: r = s.read(); break;2163case 2: r = s.read(bytes); break;2164default: throw new Error();2165}2166if (r >= 0) {2167// The child sent unexpected output; print it to diagnose2168System.out.println("Unexpected child output, to: " +2169((action & 0x1) == 0 ? "getInputStream" : "getErrorStream"));2170System.out.println("Child args: " + childArgs);2171if ((action & 0x2) == 0) {2172System.out.write(r); // Single character21732174} else {2175System.out.write(bytes, 0, r);2176}2177for (int c = s.read(); c >= 0; c = s.read())2178System.out.write(c);2179System.out.println("\nEND Child output.");2180}2181equal(-1, r);2182} catch (IOException ioe) {2183if (!ioe.getMessage().equals("Stream closed")) {2184// BufferedInputStream may throw IOE("Stream closed").2185unexpected(ioe);2186}2187} catch (Throwable t) { unexpected(t); }}};21882189thread.start();2190latch.await();2191Thread.sleep(30);21922193if (s instanceof BufferedInputStream) {2194// Wait until after the s.read occurs in "thread" by2195// checking when the input stream monitor is acquired2196// (BufferedInputStream.read is synchronized)2197while (!isLocked(s, 10)) {2198Thread.sleep(100);2199}2200}2201p.destroy();2202thread.join();2203}2204} catch (Throwable t) { unexpected(t); }22052206//----------------------------------------------------------------2207// Check that subprocesses which create subprocesses of their2208// own do not cause parent to hang waiting for file2209// descriptors to be closed.2210//----------------------------------------------------------------2211try {2212if (Unix.is()2213&& new File("/bin/bash").exists()2214&& new File("/bin/sleep").exists()) {2215// Notice that we only destroy the process created by us (i.e.2216// our child) but not our grandchild (i.e. '/bin/sleep'). So2217// pay attention that the grandchild doesn't run too long to2218// avoid polluting the process space with useless processes.2219// Running the grandchild for 59s should be more than enough.2220// A unique (59s) time is needed to avoid killing other sleep processes.2221final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 59)" };2222final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 59\")" };2223final ProcessBuilder pb = new ProcessBuilder(cmd);2224final Process p = pb.start();2225final InputStream stdout = p.getInputStream();2226final InputStream stderr = p.getErrorStream();2227final OutputStream stdin = p.getOutputStream();2228final Thread reader = new Thread() {2229public void run() {2230try { stdout.read(); }2231catch (IOException e) {2232// Check that reader failed because stream was2233// asynchronously closed.2234// e.printStackTrace();2235String msg = e.getMessage();2236if (EnglishUnix.is() &&2237! (msg.matches(".*Bad file.*") ||2238msg.matches(".*Stream closed.*")))2239unexpected(e);2240}2241catch (Throwable t) { unexpected(t); }}};2242reader.setDaemon(true);2243reader.start();2244Thread.sleep(100);2245p.destroy();2246check(p.waitFor() != 0);2247check(p.exitValue() != 0);2248// Subprocess is now dead, but file descriptors remain open.2249// Make sure the test will fail if we don't manage to close2250// the open streams within 30 seconds. Notice that this time2251// must be shorter than the sleep time of the grandchild.2252Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);2253t.schedule(new TimerTask() {2254public void run() {2255fail("Subprocesses which create subprocesses of " +2256"their own caused the parent to hang while " +2257"waiting for file descriptors to be closed.");2258System.exit(-1);2259}2260}, 30000);2261stdout.close();2262stderr.close();2263stdin.close();2264new ProcessBuilder(cmdkill).start();2265// All streams successfully closed so we can cancel the timer.2266t.cancel();2267//----------------------------------------------------------2268// There remain unsolved issues with asynchronous close.2269// Here's a highly non-portable experiment to demonstrate:2270//----------------------------------------------------------2271if (Boolean.getBoolean("wakeupJeff!")) {2272System.out.println("wakeupJeff!");2273// Initialize signal handler for INTERRUPT_SIGNAL.2274new FileInputStream("/bin/sleep").getChannel().close();2275// Send INTERRUPT_SIGNAL to every thread in this java.2276String[] wakeupJeff = {2277"/bin/bash", "-c",2278"/bin/ps --noheaders -Lfp $PPID | " +2279"/usr/bin/perl -nale 'print $F[3]' | " +2280// INTERRUPT_SIGNAL == 62 on my machine du jour.2281"/usr/bin/xargs kill -62"2282};2283new ProcessBuilder(wakeupJeff).start().waitFor();2284// If wakeupJeff worked, reader probably got EBADF.2285reader.join();2286}2287}22882289//----------------------------------------------------------------2290// Check the Process toString() method2291//----------------------------------------------------------------2292{2293List<String> childArgs = new ArrayList<String>(javaChildArgs);2294childArgs.add("testIO");2295ProcessBuilder pb = new ProcessBuilder(childArgs);2296pb.redirectInput(Redirect.PIPE);2297pb.redirectOutput(DISCARD);2298pb.redirectError(DISCARD);2299final Process p = pb.start();2300// Child process waits until it gets input2301String s = p.toString();2302check(s.contains("not exited"));2303check(s.contains("pid=" + p.pid() + ","));23042305new PrintStream(p.getOutputStream()).print("standard input");2306p.getOutputStream().close();23072308// Check the toString after it exits2309int exitValue = p.waitFor();2310s = p.toString();2311check(s.contains("pid=" + p.pid() + ","));2312check(s.contains("exitValue=" + exitValue) &&2313!s.contains("not exited"));2314}2315} catch (Throwable t) { unexpected(t); }23162317//----------------------------------------------------------------2318// Attempt to start process with insufficient permissions fails.2319//----------------------------------------------------------------2320try {2321new File("emptyCommand").delete();2322new FileOutputStream("emptyCommand").close();2323new File("emptyCommand").setExecutable(false);2324new ProcessBuilder("./emptyCommand").start();2325fail("Expected IOException not thrown");2326} catch (IOException e) {2327new File("./emptyCommand").delete();2328String m = e.getMessage();2329if (EnglishUnix.is() &&2330! matches(m, PERMISSION_DENIED_ERROR_MSG))2331unexpected(e);2332} catch (Throwable t) { unexpected(t); }23332334new File("emptyCommand").delete();23352336//----------------------------------------------------------------2337// Check for correct security permission behavior2338//----------------------------------------------------------------2339final Policy policy = new Policy();2340Policy.setPolicy(policy);2341System.setSecurityManager(new SecurityManager());23422343try {2344// No permissions required to CREATE a ProcessBuilder2345policy.setPermissions(/* Nothing */);2346new ProcessBuilder("env").directory(null).directory();2347new ProcessBuilder("env").directory(new File("dir")).directory();2348new ProcessBuilder("env").command("??").command();2349} catch (Throwable t) { unexpected(t); }23502351THROWS(SecurityException.class,2352() -> { policy.setPermissions(/* Nothing */);2353System.getenv("foo");},2354() -> { policy.setPermissions(/* Nothing */);2355System.getenv();},2356() -> { policy.setPermissions(/* Nothing */);2357new ProcessBuilder("echo").start();},2358() -> { policy.setPermissions(/* Nothing */);2359Runtime.getRuntime().exec("echo");},2360() -> { policy.setPermissions(2361new RuntimePermission("getenv.bar"));2362System.getenv("foo");});23632364try {2365policy.setPermissions(new RuntimePermission("getenv.foo"));2366System.getenv("foo");23672368policy.setPermissions(new RuntimePermission("getenv.*"));2369System.getenv("foo");2370System.getenv();2371new ProcessBuilder().environment();2372} catch (Throwable t) { unexpected(t); }237323742375final Permission execPermission2376= new FilePermission("<<ALL FILES>>", "execute");23772378THROWS(SecurityException.class,2379() -> { // environment permission by itself insufficient2380policy.setPermissions(new RuntimePermission("getenv.*"));2381ProcessBuilder pb = new ProcessBuilder("env");2382pb.environment().put("foo","bar");2383pb.start();},2384() -> { // exec permission by itself insufficient2385policy.setPermissions(execPermission);2386ProcessBuilder pb = new ProcessBuilder("env");2387pb.environment().put("foo","bar");2388pb.start();});23892390try {2391// Both permissions? OK.2392policy.setPermissions(new RuntimePermission("getenv.*"),2393execPermission);2394ProcessBuilder pb = new ProcessBuilder("env");2395pb.environment().put("foo","bar");2396Process p = pb.start();2397closeStreams(p);2398} catch (IOException e) { // OK2399} catch (Throwable t) { unexpected(t); }24002401try {2402// Don't need environment permission unless READING environment2403policy.setPermissions(execPermission);2404Runtime.getRuntime().exec("env", new String[]{});2405} catch (IOException e) { // OK2406} catch (Throwable t) { unexpected(t); }24072408try {2409// Don't need environment permission unless READING environment2410policy.setPermissions(execPermission);2411new ProcessBuilder("env").start();2412} catch (IOException e) { // OK2413} catch (Throwable t) { unexpected(t); }24142415// Restore "normal" state without a security manager2416policy.setPermissions(new RuntimePermission("setSecurityManager"));2417System.setSecurityManager(null);24182419//----------------------------------------------------------------2420// Check that Process.isAlive() &2421// Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.2422//----------------------------------------------------------------2423try {2424List<String> childArgs = getSleepArgs();2425final Process p = new ProcessBuilder(childArgs).start();2426long start = System.nanoTime();2427if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {2428fail("Test failed: Process exited prematurely");2429}2430long end = System.nanoTime();2431// give waitFor(timeout) a wide berth (2s)2432System.out.printf(" waitFor process: delta: %d%n",(end - start) );24332434if ((end - start) > TimeUnit.SECONDS.toNanos(2))2435fail("Test failed: waitFor took too long (" + (end - start) + "ns)");24362437p.destroy();2438p.waitFor();24392440if (p.isAlive() ||2441!p.waitFor(0, TimeUnit.MILLISECONDS))2442{2443fail("Test failed: Process still alive - please terminate " +2444p.toString() + " manually");2445}2446} catch (Throwable t) { unexpected(t); }24472448//----------------------------------------------------------------2449// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2450// works as expected.2451//----------------------------------------------------------------2452try {2453List<String> childArgs = getSleepArgs();2454final Process p = new ProcessBuilder(childArgs).start();2455long start = System.nanoTime();24562457if (p.waitFor(10, TimeUnit.MILLISECONDS)) {2458var msg = "External sleep process terminated early: exitValue: %d, (%dns)%n"2459.formatted(p.exitValue(), (System.nanoTime() - start));2460fail(msg);2461} else {2462long end = System.nanoTime();2463if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))2464fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");2465}2466p.destroy();2467} catch (Throwable t) { unexpected(t); }24682469//----------------------------------------------------------------2470// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2471// interrupt works as expected, if interrupted while waiting.2472//----------------------------------------------------------------2473try {2474List<String> childArgs = getSleepArgs();2475final Process p = new ProcessBuilder(childArgs).start();2476final long start = System.nanoTime();2477final CountDownLatch aboutToWaitFor = new CountDownLatch(1);24782479final Thread thread = new Thread() {2480public void run() {2481try {2482aboutToWaitFor.countDown();2483Thread.currentThread().interrupt();2484boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);2485fail("waitFor() wasn't interrupted, its return value was: " + result);2486} catch (InterruptedException success) {2487} catch (Throwable t) { unexpected(t); }2488}2489};24902491thread.start();2492aboutToWaitFor.await();2493thread.interrupt();2494thread.join(10L * 1000L);2495check(millisElapsedSince(start) < 10L * 1000L);2496check(!thread.isAlive());2497p.destroy();2498} catch (Throwable t) { unexpected(t); }24992500//----------------------------------------------------------------2501// Check that Process.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS)2502// interrupt works as expected, if interrupted while waiting.2503//----------------------------------------------------------------2504try {2505List<String> childArgs = getSleepArgs();2506final Process p = new ProcessBuilder(childArgs).start();2507final long start = System.nanoTime();2508final CountDownLatch aboutToWaitFor = new CountDownLatch(1);25092510final Thread thread = new Thread() {2511public void run() {2512try {2513aboutToWaitFor.countDown();2514Thread.currentThread().interrupt();2515boolean result = p.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS);2516fail("waitFor() wasn't interrupted, its return value was: " + result);2517} catch (InterruptedException success) {2518} catch (Throwable t) { unexpected(t); }2519}2520};25212522thread.start();2523aboutToWaitFor.await();2524thread.interrupt();2525thread.join(10L * 1000L);2526check(millisElapsedSince(start) < 10L * 1000L);2527check(!thread.isAlive());2528p.destroy();2529} catch (Throwable t) { unexpected(t); }25302531//----------------------------------------------------------------2532// Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)2533// interrupt works as expected, if interrupted before waiting.2534//----------------------------------------------------------------2535try {2536List<String> childArgs = getSleepArgs();2537final Process p = new ProcessBuilder(childArgs).start();2538final long start = System.nanoTime();2539final CountDownLatch threadStarted = new CountDownLatch(1);25402541final Thread thread = new Thread() {2542public void run() {2543try {2544threadStarted.countDown();2545do { Thread.yield(); }2546while (!Thread.currentThread().isInterrupted());2547boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);2548fail("waitFor() wasn't interrupted, its return value was: " + result);2549} catch (InterruptedException success) {2550} catch (Throwable t) { unexpected(t); }2551}2552};25532554thread.start();2555threadStarted.await();2556thread.interrupt();2557thread.join(10L * 1000L);2558check(millisElapsedSince(start) < 10L * 1000L);2559check(!thread.isAlive());2560p.destroy();2561} catch (Throwable t) { unexpected(t); }25622563//----------------------------------------------------------------2564// Check that Process.waitFor(timeout, null) throws NPE.2565//----------------------------------------------------------------2566try {2567List<String> childArgs = getSleepArgs();2568final Process p = new ProcessBuilder(childArgs).start();2569THROWS(NullPointerException.class,2570() -> p.waitFor(10L, null));2571THROWS(NullPointerException.class,2572() -> p.waitFor(0L, null));2573THROWS(NullPointerException.class,2574() -> p.waitFor(-1L, null));2575// Terminate process and recheck after it exits2576p.destroy();2577p.waitFor();2578THROWS(NullPointerException.class,2579() -> p.waitFor(10L, null));2580THROWS(NullPointerException.class,2581() -> p.waitFor(0L, null));2582THROWS(NullPointerException.class,2583() -> p.waitFor(-1L, null));2584} catch (Throwable t) { unexpected(t); }25852586//----------------------------------------------------------------2587// Check that default implementation of Process.waitFor(timeout, null) throws NPE.2588//----------------------------------------------------------------2589try {2590List<String> childArgs = getSleepArgs();2591final Process proc = new ProcessBuilder(childArgs).start();2592final DelegatingProcess p = new DelegatingProcess(proc);25932594THROWS(NullPointerException.class,2595() -> p.waitFor(10L, null));2596THROWS(NullPointerException.class,2597() -> p.waitFor(0L, null));2598THROWS(NullPointerException.class,2599() -> p.waitFor(-1L, null));2600// Terminate process and recheck after it exits2601p.destroy();2602p.waitFor();2603THROWS(NullPointerException.class,2604() -> p.waitFor(10L, null));2605THROWS(NullPointerException.class,2606() -> p.waitFor(0L, null));2607THROWS(NullPointerException.class,2608() -> p.waitFor(-1L, null));2609} catch (Throwable t) { unexpected(t); }26102611//----------------------------------------------------------------2612// Check the default implementation for2613// Process.waitFor(long, TimeUnit)2614//----------------------------------------------------------------2615try {2616List<String> childArgs = getSleepArgs();2617final Process proc = new ProcessBuilder(childArgs).start();2618DelegatingProcess p = new DelegatingProcess(proc);2619long start = System.nanoTime();26202621if (p.waitFor(1000, TimeUnit.MILLISECONDS)) {2622var msg = "External sleep process terminated early: exitValue: %02x, (%dns)"2623.formatted(p.exitValue(), (System.nanoTime() - start));2624fail(msg);2625} else {2626long end = System.nanoTime();2627if ((end - start) < 500000000)2628fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");2629}2630p.destroy();26312632p.waitFor(1000, TimeUnit.MILLISECONDS);2633} catch (Throwable t) { unexpected(t); }2634}26352636// Path to native executables, if any2637private static final String TEST_NATIVEPATH = System.getProperty("test.nativepath");26382639// Path where "sleep" program may be found" or null2640private static final Path SLEEP_PATH = initSleepPath();26412642/**2643* Compute the Path to a sleep executable.2644* @return a Path to sleep or BasicSleep(.exe) or null if none2645*/2646private static Path initSleepPath() {2647if (Windows.is() && TEST_NATIVEPATH != null) {2648// exeBasicSleep is equivalent to sleep on Unix2649Path exePath = Path.of(TEST_NATIVEPATH).resolve("BasicSleep.exe");2650if (Files.isExecutable(exePath)) {2651return exePath;2652}2653}26542655List<String> binPaths = List.of("/bin", "/usr/bin");2656for (String dir : binPaths) {2657Path exePath = Path.of(dir).resolve("sleep");2658if (Files.isExecutable(exePath)) {2659return exePath;2660}2661}2662return null;2663}26642665/**2666* Return the list of process arguments for a child to sleep 10 minutes (600 seconds).2667*2668* @return A list of process arguments to sleep 10 minutes.2669*/2670private static List<String> getSleepArgs() {2671List<String> childArgs = null;2672if (SLEEP_PATH != null) {2673childArgs = List.of(SLEEP_PATH.toString(), "600");2674} else {2675// Fallback to the JavaChild ; its 'sleep' command is for 10 minutes.2676// The fallback the Java$Child is used if the test is run without building2677// the BasicSleep native executable (for Windows).2678childArgs = new ArrayList<>(javaChildArgs);2679childArgs.add("sleep");2680System.out.println("Sleep not found, fallback to JavaChild: " + childArgs);2681}2682return childArgs;2683}26842685static void closeStreams(Process p) {2686try {2687p.getOutputStream().close();2688p.getInputStream().close();2689p.getErrorStream().close();2690} catch (Throwable t) { unexpected(t); }2691}26922693//----------------------------------------------------------------2694// A Policy class designed to make permissions fiddling very easy.2695//----------------------------------------------------------------2696@SuppressWarnings("removal")2697private static class Policy extends java.security.Policy {2698static final java.security.Policy DEFAULT_POLICY = java.security.Policy.getPolicy();26992700private Permissions perms;27012702public void setPermissions(Permission...permissions) {2703perms = new Permissions();2704for (Permission permission : permissions)2705perms.add(permission);2706}27072708public Policy() { setPermissions(/* Nothing */); }27092710public PermissionCollection getPermissions(CodeSource cs) {2711return perms;2712}27132714public PermissionCollection getPermissions(ProtectionDomain pd) {2715return perms;2716}27172718public boolean implies(ProtectionDomain pd, Permission p) {2719return perms.implies(p) || DEFAULT_POLICY.implies(pd, p);2720}27212722public void refresh() {}2723}27242725private static class StreamAccumulator extends Thread {2726private final InputStream is;2727private final StringBuilder sb = new StringBuilder();2728private Throwable throwable = null;27292730public String result () throws Throwable {2731if (throwable != null)2732throw throwable;2733return sb.toString();2734}27352736StreamAccumulator (InputStream is) {2737this.is = is;2738}27392740public void run() {2741try {2742Reader r = new InputStreamReader(is);2743char[] buf = new char[4096];2744int n;2745while ((n = r.read(buf)) > 0) {2746sb.append(buf,0,n);2747}2748} catch (Throwable t) {2749throwable = t;2750} finally {2751try { is.close(); }2752catch (Throwable t) { throwable = t; }2753}2754}2755}27562757static ProcessResults run(ProcessBuilder pb) {2758try {2759return run(pb.start());2760} catch (Throwable t) { unexpected(t); return null; }2761}27622763private static ProcessResults run(Process p) {2764Throwable throwable = null;2765int exitValue = -1;2766String out = "";2767String err = "";27682769StreamAccumulator outAccumulator =2770new StreamAccumulator(p.getInputStream());2771StreamAccumulator errAccumulator =2772new StreamAccumulator(p.getErrorStream());27732774try {2775outAccumulator.start();2776errAccumulator.start();27772778exitValue = p.waitFor();27792780outAccumulator.join();2781errAccumulator.join();27822783out = outAccumulator.result();2784err = errAccumulator.result();2785} catch (Throwable t) {2786throwable = t;2787}27882789return new ProcessResults(out, err, exitValue, throwable);2790}27912792//----------------------------------------------------------------2793// Results of a command2794//----------------------------------------------------------------2795private static class ProcessResults {2796private final String out;2797private final String err;2798private final int exitValue;2799private final Throwable throwable;28002801public ProcessResults(String out,2802String err,2803int exitValue,2804Throwable throwable) {2805this.out = out;2806this.err = err;2807this.exitValue = exitValue;2808this.throwable = throwable;2809}28102811public String out() { return out; }2812public String err() { return err; }2813public int exitValue() { return exitValue; }2814public Throwable throwable() { return throwable; }28152816public String toString() {2817StringBuilder sb = new StringBuilder();2818sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")2819.append("<STDERR>\n" + err() + "</STDERR>\n")2820.append("exitValue = " + exitValue + "\n");2821if (throwable != null)2822sb.append(throwable.getStackTrace());2823return sb.toString();2824}2825}28262827//--------------------- Infrastructure ---------------------------2828static volatile int passed = 0, failed = 0;2829static void pass() {passed++;}2830static void fail() {failed++; Thread.dumpStack();}2831static void fail(String msg) {System.err.println(msg); fail();}2832static void unexpected(Throwable t) {failed++; t.printStackTrace();}2833static void check(boolean cond) {if (cond) pass(); else fail();}2834static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}2835static void equal(Object x, Object y) {2836if (x == null ? y == null : x.equals(y)) pass();2837else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'");}28382839public static void main(String[] args) throws Throwable {2840try {realMain(args);} catch (Throwable t) {unexpected(t);}2841System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);2842if (failed > 0) throw new AssertionError("Some tests failed");}2843interface Fun {void f() throws Throwable;}2844static void THROWS(Class<? extends Throwable> k, Fun... fs) {2845for (Fun f : fs)2846try { f.f(); fail("Expected " + k.getName() + " not thrown"); }2847catch (Throwable t) {2848if (k.isAssignableFrom(t.getClass())) pass();2849else unexpected(t);}}28502851static boolean isLocked(final Object monitor, final long millis) throws InterruptedException {2852return new Thread() {2853volatile boolean unlocked;28542855@Override2856public void run() {2857synchronized (monitor) { unlocked = true; }2858}28592860boolean isLocked() throws InterruptedException {2861start();2862join(millis);2863return !unlocked;2864}2865}.isLocked();2866}2867}286828692870