1 /*
2  * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test
26  * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
27  *      5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
28  *      6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958
29  *      4947220 7018606 7034570 4244896 5049299
30  *      8067796
31  * @summary Basic tests for Process and Environment Variable code
32  * @run main/othervm/timeout=300 Basic
33  * @run main/othervm/timeout=300 -Djdk.lang.Process.launchMechanism=fork Basic
34  * @author Martin Buchholz
35  */
36 
37 import java.lang.ProcessBuilder.Redirect;
38 import static java.lang.ProcessBuilder.Redirect.*;
39 
40 import java.io.*;
41 import java.lang.reflect.Field;
42 import java.util.*;
43 import java.util.concurrent.CountDownLatch;
44 import java.util.concurrent.TimeUnit;
45 import java.security.*;
46 import sun.misc.Unsafe;
47 import java.util.regex.Pattern;
48 import java.util.regex.Matcher;
49 import static java.lang.System.getenv;
50 import static java.lang.System.out;
51 import static java.lang.Boolean.TRUE;
52 import static java.util.AbstractMap.SimpleImmutableEntry;
53 
54 public class Basic {
55 
56     /* used for Windows only */
57     static final String systemRoot = System.getenv("SystemRoot");
58 
59     /* used for Mac OS X only */
60     static final String cfUserTextEncoding = System.getenv("__CF_USER_TEXT_ENCODING");
61 
62     /* used for AIX only */
63     static final String libpath = System.getenv("LIBPATH");
64 
65     /**
66      * Returns the number of milliseconds since time given by
67      * startNanoTime, which must have been previously returned from a
68      * call to {@link System.nanoTime()}.
69      */
millisElapsedSince(long startNanoTime)70     private static long millisElapsedSince(long startNanoTime) {
71         return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanoTime);
72     }
73 
commandOutput(Reader r)74     private static String commandOutput(Reader r) throws Throwable {
75         StringBuilder sb = new StringBuilder();
76         int c;
77         while ((c = r.read()) > 0)
78             if (c != '\r')
79                 sb.append((char) c);
80         return sb.toString();
81     }
82 
commandOutput(Process p)83     private static String commandOutput(Process p) throws Throwable {
84         check(p.getInputStream()  == p.getInputStream());
85         check(p.getOutputStream() == p.getOutputStream());
86         check(p.getErrorStream()  == p.getErrorStream());
87         Reader r = new InputStreamReader(p.getInputStream(),"UTF-8");
88         String output = commandOutput(r);
89         equal(p.waitFor(), 0);
90         equal(p.exitValue(), 0);
91         // The debug/fastdebug versions of the VM may write some warnings to stdout
92         // (i.e. "Warning:  Cannot open log file: hotspot.log" if the VM is started
93         // in a directory without write permissions). These warnings will confuse tests
94         // which match the entire output of the child process so better filter them out.
95         return output.replaceAll("Warning:.*\\n", "");
96     }
97 
commandOutput(ProcessBuilder pb)98     private static String commandOutput(ProcessBuilder pb) {
99         try {
100             return commandOutput(pb.start());
101         } catch (Throwable t) {
102             String commandline = "";
103             for (String arg : pb.command())
104                 commandline += " " + arg;
105             System.out.println("Exception trying to run process: " + commandline);
106             unexpected(t);
107             return "";
108         }
109     }
110 
commandOutput(String...command)111     private static String commandOutput(String...command) {
112         try {
113             return commandOutput(Runtime.getRuntime().exec(command));
114         } catch (Throwable t) {
115             String commandline = "";
116             for (String arg : command)
117                 commandline += " " + arg;
118             System.out.println("Exception trying to run process: " + commandline);
119             unexpected(t);
120             return "";
121         }
122     }
123 
checkCommandOutput(ProcessBuilder pb, String expected, String failureMsg)124     private static void checkCommandOutput(ProcessBuilder pb,
125                                            String expected,
126                                            String failureMsg) {
127         String got = commandOutput(pb);
128         check(got.equals(expected),
129               failureMsg + "\n" +
130               "Expected: \"" + expected + "\"\n" +
131               "Got: \"" + got + "\"");
132     }
133 
absolutifyPath(String path)134     private static String absolutifyPath(String path) {
135         StringBuilder sb = new StringBuilder();
136         for (String file : path.split(File.pathSeparator)) {
137             if (sb.length() != 0)
138                 sb.append(File.pathSeparator);
139             sb.append(new File(file).getAbsolutePath());
140         }
141         return sb.toString();
142     }
143 
144     // compare windows-style, by canonicalizing to upper case,
145     // not lower case as String.compareToIgnoreCase does
146     private static class WindowsComparator
147         implements Comparator<String> {
compare(String x, String y)148         public int compare(String x, String y) {
149             return x.toUpperCase(Locale.US)
150                 .compareTo(y.toUpperCase(Locale.US));
151         }
152     }
153 
sortedLines(String lines)154     private static String sortedLines(String lines) {
155         String[] arr = lines.split("\n");
156         List<String> ls = new ArrayList<String>();
157         for (String s : arr)
158             ls.add(s);
159         Collections.sort(ls, new WindowsComparator());
160         StringBuilder sb = new StringBuilder();
161         for (String s : ls)
162             sb.append(s + "\n");
163         return sb.toString();
164     }
165 
compareLinesIgnoreCase(String lines1, String lines2)166     private static void compareLinesIgnoreCase(String lines1, String lines2) {
167         if (! (sortedLines(lines1).equalsIgnoreCase(sortedLines(lines2)))) {
168             String dashes =
169                 "-----------------------------------------------------";
170             out.println(dashes);
171             out.print(sortedLines(lines1));
172             out.println(dashes);
173             out.print(sortedLines(lines2));
174             out.println(dashes);
175             out.println("sizes: " + sortedLines(lines1).length() +
176                         " " + sortedLines(lines2).length());
177 
178             fail("Sorted string contents differ");
179         }
180     }
181 
182     private static final Runtime runtime = Runtime.getRuntime();
183 
184     private static final String[] winEnvCommand = {"cmd.exe", "/c", "set"};
185 
winEnvFilter(String env)186     private static String winEnvFilter(String env) {
187         return env.replaceAll("\r", "")
188             .replaceAll("(?m)^(?:COMSPEC|PROMPT|PATHEXT)=.*\n","");
189     }
190 
unixEnvProg()191     private static String unixEnvProg() {
192         return new File("/usr/bin/env").canExecute() ? "/usr/bin/env"
193             : "/bin/env";
194     }
195 
nativeEnv(String[] env)196     private static String nativeEnv(String[] env) {
197         try {
198             if (Windows.is()) {
199                 return winEnvFilter
200                     (commandOutput(runtime.exec(winEnvCommand, env)));
201             } else {
202                 return commandOutput(runtime.exec(unixEnvProg(), env));
203             }
204         } catch (Throwable t) { throw new Error(t); }
205     }
206 
nativeEnv(ProcessBuilder pb)207     private static String nativeEnv(ProcessBuilder pb) {
208         try {
209             if (Windows.is()) {
210                 pb.command(winEnvCommand);
211                 return winEnvFilter(commandOutput(pb));
212             } else {
213                 pb.command(new String[]{unixEnvProg()});
214                 return commandOutput(pb);
215             }
216         } catch (Throwable t) { throw new Error(t); }
217     }
218 
checkSizes(Map<String,String> environ, int size)219     private static void checkSizes(Map<String,String> environ, int size) {
220         try {
221             equal(size, environ.size());
222             equal(size, environ.entrySet().size());
223             equal(size, environ.keySet().size());
224             equal(size, environ.values().size());
225 
226             boolean isEmpty = (size == 0);
227             equal(isEmpty, environ.isEmpty());
228             equal(isEmpty, environ.entrySet().isEmpty());
229             equal(isEmpty, environ.keySet().isEmpty());
230             equal(isEmpty, environ.values().isEmpty());
231         } catch (Throwable t) { unexpected(t); }
232     }
233 
234     private interface EnvironmentFrobber {
doIt(Map<String,String> environ)235         void doIt(Map<String,String> environ);
236     }
237 
testVariableDeleter(EnvironmentFrobber fooDeleter)238     private static void testVariableDeleter(EnvironmentFrobber fooDeleter) {
239         try {
240             Map<String,String> environ = new ProcessBuilder().environment();
241             environ.put("Foo", "BAAR");
242             fooDeleter.doIt(environ);
243             equal(environ.get("Foo"), null);
244             equal(environ.remove("Foo"), null);
245         } catch (Throwable t) { unexpected(t); }
246     }
247 
testVariableAdder(EnvironmentFrobber fooAdder)248     private static void testVariableAdder(EnvironmentFrobber fooAdder) {
249         try {
250             Map<String,String> environ = new ProcessBuilder().environment();
251             environ.remove("Foo");
252             fooAdder.doIt(environ);
253             equal(environ.get("Foo"), "Bahrein");
254         } catch (Throwable t) { unexpected(t); }
255     }
256 
testVariableModifier(EnvironmentFrobber fooModifier)257     private static void testVariableModifier(EnvironmentFrobber fooModifier) {
258         try {
259             Map<String,String> environ = new ProcessBuilder().environment();
260             environ.put("Foo","OldValue");
261             fooModifier.doIt(environ);
262             equal(environ.get("Foo"), "NewValue");
263         } catch (Throwable t) { unexpected(t); }
264     }
265 
printUTF8(String s)266     private static void printUTF8(String s) throws IOException {
267         out.write(s.getBytes("UTF-8"));
268     }
269 
getenvAsString(Map<String,String> environment)270     private static String getenvAsString(Map<String,String> environment) {
271         StringBuilder sb = new StringBuilder();
272         environment = new TreeMap<>(environment);
273         for (Map.Entry<String,String> e : environment.entrySet())
274             // Ignore magic environment variables added by the launcher
275             if (! e.getKey().equals("NLSPATH") &&
276                 ! e.getKey().equals("XFILESEARCHPATH") &&
277                 ! e.getKey().equals("LD_LIBRARY_PATH"))
278                 sb.append(e.getKey())
279                     .append('=')
280                     .append(e.getValue())
281                     .append(',');
282         return sb.toString();
283     }
284 
print4095(OutputStream s, byte b)285     static void print4095(OutputStream s, byte b) throws Throwable {
286         byte[] bytes = new byte[4095];
287         Arrays.fill(bytes, b);
288         s.write(bytes);         // Might hang!
289     }
290 
checkPermissionDenied(ProcessBuilder pb)291     static void checkPermissionDenied(ProcessBuilder pb) {
292         try {
293             pb.start();
294             fail("Expected IOException not thrown");
295         } catch (IOException e) {
296             String m = e.getMessage();
297             if (EnglishUnix.is() &&
298                 ! matches(m, "Permission denied"))
299                 unexpected(e);
300         } catch (Throwable t) { unexpected(t); }
301     }
302 
303     public static class JavaChild {
main(String args[])304         public static void main(String args[]) throws Throwable {
305             String action = args[0];
306             if (action.equals("sleep")) {
307                 Thread.sleep(10 * 60 * 1000L);
308             } else if (action.equals("testIO")) {
309                 String expected = "standard input";
310                 char[] buf = new char[expected.length()+1];
311                 int n = new InputStreamReader(System.in).read(buf,0,buf.length);
312                 if (n != expected.length())
313                     System.exit(5);
314                 if (! new String(buf,0,n).equals(expected))
315                     System.exit(5);
316                 System.err.print("standard error");
317                 System.out.print("standard output");
318             } else if (action.equals("testInheritIO")
319                     || action.equals("testRedirectInherit")) {
320                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
321                 childArgs.add("testIO");
322                 ProcessBuilder pb = new ProcessBuilder(childArgs);
323                 if (action.equals("testInheritIO"))
324                     pb.inheritIO();
325                 else
326                     redirectIO(pb, INHERIT, INHERIT, INHERIT);
327                 ProcessResults r = run(pb);
328                 if (! r.out().equals(""))
329                     System.exit(7);
330                 if (! r.err().equals(""))
331                     System.exit(8);
332                 if (r.exitValue() != 0)
333                     System.exit(9);
334             } else if (action.equals("System.getenv(String)")) {
335                 String val = System.getenv(args[1]);
336                 printUTF8(val == null ? "null" : val);
337             } else if (action.equals("System.getenv(\\u1234)")) {
338                 String val = System.getenv("\u1234");
339                 printUTF8(val == null ? "null" : val);
340             } else if (action.equals("System.getenv()")) {
341                 printUTF8(getenvAsString(System.getenv()));
342             } else if (action.equals("ArrayOOME")) {
343                 Object dummy;
344                 switch(new Random().nextInt(3)) {
345                 case 0: dummy = new Integer[Integer.MAX_VALUE]; break;
346                 case 1: dummy = new double[Integer.MAX_VALUE];  break;
347                 case 2: dummy = new byte[Integer.MAX_VALUE][];  break;
348                 default: throw new InternalError();
349                 }
350             } else if (action.equals("pwd")) {
351                 printUTF8(new File(System.getProperty("user.dir"))
352                           .getCanonicalPath());
353             } else if (action.equals("print4095")) {
354                 print4095(System.out, (byte) '!');
355                 print4095(System.err, (byte) 'E');
356                 System.exit(5);
357             } else if (action.equals("OutErr")) {
358                 // You might think the system streams would be
359                 // buffered, and in fact they are implemented using
360                 // BufferedOutputStream, but each and every print
361                 // causes immediate operating system I/O.
362                 System.out.print("out");
363                 System.err.print("err");
364                 System.out.print("out");
365                 System.err.print("err");
366             } else if (action.equals("null PATH")) {
367                 equal(System.getenv("PATH"), null);
368                 check(new File("/bin/true").exists());
369                 check(new File("/bin/false").exists());
370                 ProcessBuilder pb1 = new ProcessBuilder();
371                 ProcessBuilder pb2 = new ProcessBuilder();
372                 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
373                 ProcessResults r;
374 
375                 for (final ProcessBuilder pb :
376                          new ProcessBuilder[] {pb1, pb2}) {
377                     pb.command("true");
378                     equal(run(pb).exitValue(), True.exitValue());
379 
380                     pb.command("false");
381                     equal(run(pb).exitValue(), False.exitValue());
382                 }
383 
384                 if (failed != 0) throw new Error("null PATH");
385             } else if (action.equals("PATH search algorithm")) {
386                 equal(System.getenv("PATH"), "dir1:dir2:");
387                 check(new File("/bin/true").exists());
388                 check(new File("/bin/false").exists());
389                 String[] cmd = {"prog"};
390                 ProcessBuilder pb1 = new ProcessBuilder(cmd);
391                 ProcessBuilder pb2 = new ProcessBuilder(cmd);
392                 ProcessBuilder pb3 = new ProcessBuilder(cmd);
393                 pb2.environment().put("PATH", "anyOldPathIgnoredAnyways");
394                 pb3.environment().remove("PATH");
395 
396                 for (final ProcessBuilder pb :
397                          new ProcessBuilder[] {pb1, pb2, pb3}) {
398                     try {
399                         // Not on PATH at all; directories don't exist
400                         try {
401                             pb.start();
402                             fail("Expected IOException not thrown");
403                         } catch (IOException e) {
404                             String m = e.getMessage();
405                             if (EnglishUnix.is() &&
406                                 ! matches(m, "No such file"))
407                                 unexpected(e);
408                         } catch (Throwable t) { unexpected(t); }
409 
410                         // Not on PATH at all; directories exist
411                         new File("dir1").mkdirs();
412                         new File("dir2").mkdirs();
413                         try {
414                             pb.start();
415                             fail("Expected IOException not thrown");
416                         } catch (IOException e) {
417                             String m = e.getMessage();
418                             if (EnglishUnix.is() &&
419                                 ! matches(m, "No such file"))
420                                 unexpected(e);
421                         } catch (Throwable t) { unexpected(t); }
422 
423                         // Can't execute a directory -- permission denied
424                         // Report EACCES errno
425                         new File("dir1/prog").mkdirs();
426                         checkPermissionDenied(pb);
427 
428                         // continue searching if EACCES
429                         copy("/bin/true", "dir2/prog");
430                         equal(run(pb).exitValue(), True.exitValue());
431                         new File("dir1/prog").delete();
432                         new File("dir2/prog").delete();
433 
434                         new File("dir2/prog").mkdirs();
435                         copy("/bin/true", "dir1/prog");
436                         equal(run(pb).exitValue(), True.exitValue());
437 
438                         // Check empty PATH component means current directory.
439                         //
440                         // While we're here, let's test different kinds of
441                         // Unix executables, and PATH vs explicit searching.
442                         new File("dir1/prog").delete();
443                         new File("dir2/prog").delete();
444                         for (String[] command :
445                                  new String[][] {
446                                      new String[] {"./prog"},
447                                      cmd}) {
448                             pb.command(command);
449                             File prog = new File("./prog");
450                             // "Normal" binaries
451                             copy("/bin/true", "./prog");
452                             equal(run(pb).exitValue(),
453                                   True.exitValue());
454                             copy("/bin/false", "./prog");
455                             equal(run(pb).exitValue(),
456                                   False.exitValue());
457                             prog.delete();
458                             // Interpreter scripts with #!
459                             setFileContents(prog, "#!/bin/true\n");
460                             prog.setExecutable(true);
461                             equal(run(pb).exitValue(),
462                                   True.exitValue());
463                             prog.delete();
464                             setFileContents(prog, "#!/bin/false\n");
465                             prog.setExecutable(true);
466                             equal(run(pb).exitValue(),
467                                   False.exitValue());
468                             // Traditional shell scripts without #!
469                             setFileContents(prog, "exec /bin/true\n");
470                             prog.setExecutable(true);
471                             equal(run(pb).exitValue(),
472                                   True.exitValue());
473                             prog.delete();
474                             setFileContents(prog, "exec /bin/false\n");
475                             prog.setExecutable(true);
476                             equal(run(pb).exitValue(),
477                                   False.exitValue());
478                             prog.delete();
479                         }
480 
481                         // Test Unix interpreter scripts
482                         File dir1Prog = new File("dir1/prog");
483                         dir1Prog.delete();
484                         pb.command(new String[] {"prog", "world"});
485                         setFileContents(dir1Prog, "#!/bin/echo hello\n");
486                         checkPermissionDenied(pb);
487                         dir1Prog.setExecutable(true);
488                         equal(run(pb).out(), "hello dir1/prog world\n");
489                         equal(run(pb).exitValue(), True.exitValue());
490                         dir1Prog.delete();
491                         pb.command(cmd);
492 
493                         // Test traditional shell scripts without #!
494                         setFileContents(dir1Prog, "/bin/echo \"$@\"\n");
495                         pb.command(new String[] {"prog", "hello", "world"});
496                         checkPermissionDenied(pb);
497                         dir1Prog.setExecutable(true);
498                         equal(run(pb).out(), "hello world\n");
499                         equal(run(pb).exitValue(), True.exitValue());
500                         dir1Prog.delete();
501                         pb.command(cmd);
502 
503                         // If prog found on both parent and child's PATH,
504                         // parent's is used.
505                         new File("dir1/prog").delete();
506                         new File("dir2/prog").delete();
507                         new File("prog").delete();
508                         new File("dir3").mkdirs();
509                         copy("/bin/true", "dir1/prog");
510                         copy("/bin/false", "dir3/prog");
511                         pb.environment().put("PATH","dir3");
512                         equal(run(pb).exitValue(), True.exitValue());
513                         copy("/bin/true", "dir3/prog");
514                         copy("/bin/false", "dir1/prog");
515                         equal(run(pb).exitValue(), False.exitValue());
516 
517                     } finally {
518                         // cleanup
519                         new File("dir1/prog").delete();
520                         new File("dir2/prog").delete();
521                         new File("dir3/prog").delete();
522                         new File("dir1").delete();
523                         new File("dir2").delete();
524                         new File("dir3").delete();
525                         new File("prog").delete();
526                     }
527                 }
528 
529                 if (failed != 0) throw new Error("PATH search algorithm");
530             }
531             else throw new Error("JavaChild invocation error");
532         }
533     }
534 
copy(String src, String dst)535     private static void copy(String src, String dst) {
536         system("/bin/cp", "-fp", src, dst);
537     }
538 
system(String... command)539     private static void system(String... command) {
540         try {
541             ProcessBuilder pb = new ProcessBuilder(command);
542             ProcessResults r = run(pb.start());
543             equal(r.exitValue(), 0);
544             equal(r.out(), "");
545             equal(r.err(), "");
546         } catch (Throwable t) { unexpected(t); }
547     }
548 
javaChildOutput(ProcessBuilder pb, String...args)549     private static String javaChildOutput(ProcessBuilder pb, String...args) {
550         List<String> list = new ArrayList<String>(javaChildArgs);
551         for (String arg : args)
552             list.add(arg);
553         pb.command(list);
554         return commandOutput(pb);
555     }
556 
getenvInChild(ProcessBuilder pb)557     private static String getenvInChild(ProcessBuilder pb) {
558         return javaChildOutput(pb, "System.getenv()");
559     }
560 
getenvInChild1234(ProcessBuilder pb)561     private static String getenvInChild1234(ProcessBuilder pb) {
562         return javaChildOutput(pb, "System.getenv(\\u1234)");
563     }
564 
getenvInChild(ProcessBuilder pb, String name)565     private static String getenvInChild(ProcessBuilder pb, String name) {
566         return javaChildOutput(pb, "System.getenv(String)", name);
567     }
568 
pwdInChild(ProcessBuilder pb)569     private static String pwdInChild(ProcessBuilder pb) {
570         return javaChildOutput(pb, "pwd");
571     }
572 
573     private static final String javaExe =
574         System.getProperty("java.home") +
575         File.separator + "bin" + File.separator + "java";
576 
577     private static final String classpath =
578         System.getProperty("java.class.path");
579 
580     private static final List<String> javaChildArgs =
581         Arrays.asList(javaExe,
582                       "-XX:+DisplayVMOutputToStderr",
583                       "-classpath", absolutifyPath(classpath),
584                       "Basic$JavaChild");
585 
testEncoding(String encoding, String tested)586     private static void testEncoding(String encoding, String tested) {
587         try {
588             // If round trip conversion works, should be able to set env vars
589             // correctly in child.
590             if (new String(tested.getBytes()).equals(tested)) {
591                 out.println("Testing " + encoding + " environment values");
592                 ProcessBuilder pb = new ProcessBuilder();
593                 pb.environment().put("ASCIINAME",tested);
594                 equal(getenvInChild(pb,"ASCIINAME"), tested);
595             }
596         } catch (Throwable t) { unexpected(t); }
597     }
598 
599     static class Windows {
is()600         public static boolean is() { return is; }
601         private static final boolean is =
602             System.getProperty("os.name").startsWith("Windows");
603     }
604 
605     static class AIX {
is()606         public static boolean is() { return is; }
607         private static final boolean is =
608             System.getProperty("os.name").equals("AIX");
609     }
610 
611     static class Unix {
is()612         public static boolean is() { return is; }
613         private static final boolean is =
614             (! Windows.is() &&
615              new File("/bin/sh").exists() &&
616              new File("/bin/true").exists() &&
617              new File("/bin/false").exists());
618     }
619 
620     static class UnicodeOS {
is()621         public static boolean is() { return is; }
622         private static final String osName = System.getProperty("os.name");
623         private static final boolean is =
624             // MacOS X would probably also qualify
625             osName.startsWith("Windows")   &&
626             ! osName.startsWith("Windows 9") &&
627             ! osName.equals("Windows Me");
628     }
629 
630     static class MacOSX {
is()631         public static boolean is() { return is; }
632         private static final String osName = System.getProperty("os.name");
633         private static final boolean is = osName.contains("OS X");
634     }
635 
636     static class True {
exitValue()637         public static int exitValue() { return 0; }
638     }
639 
640     private static class False {
exitValue()641         public static int exitValue() { return exitValue; }
642         private static final int exitValue = exitValue0();
exitValue0()643         private static int exitValue0() {
644             // /bin/false returns an *unspecified* non-zero number.
645             try {
646                 if (! Unix.is())
647                     return -1;
648                 else {
649                     int rc = new ProcessBuilder("/bin/false")
650                         .start().waitFor();
651                     check(rc != 0);
652                     return rc;
653                 }
654             } catch (Throwable t) { unexpected(t); return -1; }
655         }
656     }
657 
658     static class EnglishUnix {
659         private final static Boolean is =
660             (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL"));
661 
isEnglish(String envvar)662         private static boolean isEnglish(String envvar) {
663             String val = getenv(envvar);
664             return (val == null) || val.matches("en.*") || val.matches("C");
665         }
666 
667         /** Returns true if we can expect English OS error strings */
is()668         static boolean is() { return is; }
669     }
670 
671     static class DelegatingProcess extends Process {
672         final Process p;
673 
DelegatingProcess(Process p)674         DelegatingProcess(Process p) {
675             this.p = p;
676         }
677 
678         @Override
destroy()679         public void destroy() {
680             p.destroy();
681         }
682 
683         @Override
exitValue()684         public int exitValue() {
685             return p.exitValue();
686         }
687 
688         @Override
waitFor()689         public int waitFor() throws InterruptedException {
690             return p.waitFor();
691         }
692 
693         @Override
getOutputStream()694         public OutputStream getOutputStream() {
695             return p.getOutputStream();
696         }
697 
698         @Override
getInputStream()699         public InputStream getInputStream() {
700             return p.getInputStream();
701         }
702 
703         @Override
getErrorStream()704         public InputStream getErrorStream() {
705             return p.getErrorStream();
706         }
707     }
708 
matches(String str, String regex)709     private static boolean matches(String str, String regex) {
710         return Pattern.compile(regex).matcher(str).find();
711     }
712 
matchAndExtract(String str, String regex)713     private static String matchAndExtract(String str, String regex) {
714         Matcher matcher = Pattern.compile(regex).matcher(str);
715         if (matcher.find()) {
716             return matcher.group();
717         } else {
718             return "";
719         }
720     }
721 
722     /* Only used for Mac OS X --
723      * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty
724      * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also
725      * be set in Mac OS X.
726      * Remove them both from the list of env variables
727      */
removeMacExpectedVars(String vars)728     private static String removeMacExpectedVars(String vars) {
729         // Check for __CF_USER_TEXT_ENCODING
730         String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING="
731                                             +cfUserTextEncoding+",","");
732         // Check for JAVA_MAIN_CLASS_<pid>
733         String javaMainClassStr
734                 = matchAndExtract(cleanedVars,
735                                     "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,");
736         return cleanedVars.replace(javaMainClassStr,"");
737     }
738 
739     /* Only used for AIX --
740      * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment.
741      * Remove it from the list of env variables
742      */
removeAixExpectedVars(String vars)743     private static String removeAixExpectedVars(String vars) {
744         return vars.replace("AIXTHREAD_GUARDPAGES=0,","");
745     }
746 
sortByLinesWindowsly(String text)747     private static String sortByLinesWindowsly(String text) {
748         String[] lines = text.split("\n");
749         Arrays.sort(lines, new WindowsComparator());
750         StringBuilder sb = new StringBuilder();
751         for (String line : lines)
752             sb.append(line).append("\n");
753         return sb.toString();
754     }
755 
checkMapSanity(Map<String,String> map)756     private static void checkMapSanity(Map<String,String> map) {
757         try {
758             Set<String> keySet = map.keySet();
759             Collection<String> values = map.values();
760             Set<Map.Entry<String,String>> entrySet = map.entrySet();
761 
762             equal(entrySet.size(), keySet.size());
763             equal(entrySet.size(), values.size());
764 
765             StringBuilder s1 = new StringBuilder();
766             for (Map.Entry<String,String> e : entrySet)
767                 s1.append(e.getKey() + "=" + e.getValue() + "\n");
768 
769             StringBuilder s2 = new StringBuilder();
770             for (String var : keySet)
771                 s2.append(var + "=" + map.get(var) + "\n");
772 
773             equal(s1.toString(), s2.toString());
774 
775             Iterator<String> kIter = keySet.iterator();
776             Iterator<String> vIter = values.iterator();
777             Iterator<Map.Entry<String,String>> eIter = entrySet.iterator();
778 
779             while (eIter.hasNext()) {
780                 Map.Entry<String,String> entry = eIter.next();
781                 String key   = kIter.next();
782                 String value = vIter.next();
783                 check(entrySet.contains(entry));
784                 check(keySet.contains(key));
785                 check(values.contains(value));
786                 check(map.containsKey(key));
787                 check(map.containsValue(value));
788                 equal(entry.getKey(), key);
789                 equal(entry.getValue(), value);
790             }
791             check(! kIter.hasNext() &&
792                   ! vIter.hasNext());
793 
794         } catch (Throwable t) { unexpected(t); }
795     }
796 
checkMapEquality(Map<String,String> map1, Map<String,String> map2)797     private static void checkMapEquality(Map<String,String> map1,
798                                          Map<String,String> map2) {
799         try {
800             equal(map1.size(), map2.size());
801             equal(map1.isEmpty(), map2.isEmpty());
802             for (String key : map1.keySet()) {
803                 equal(map1.get(key), map2.get(key));
804                 check(map2.keySet().contains(key));
805             }
806             equal(map1, map2);
807             equal(map2, map1);
808             equal(map1.entrySet(), map2.entrySet());
809             equal(map2.entrySet(), map1.entrySet());
810             equal(map1.keySet(), map2.keySet());
811             equal(map2.keySet(), map1.keySet());
812 
813             equal(map1.hashCode(), map2.hashCode());
814             equal(map1.entrySet().hashCode(), map2.entrySet().hashCode());
815             equal(map1.keySet().hashCode(), map2.keySet().hashCode());
816         } catch (Throwable t) { unexpected(t); }
817     }
818 
checkRedirects(ProcessBuilder pb, Redirect in, Redirect out, Redirect err)819     static void checkRedirects(ProcessBuilder pb,
820                                Redirect in, Redirect out, Redirect err) {
821         equal(pb.redirectInput(),  in);
822         equal(pb.redirectOutput(), out);
823         equal(pb.redirectError(),  err);
824     }
825 
redirectIO(ProcessBuilder pb, Redirect in, Redirect out, Redirect err)826     static void redirectIO(ProcessBuilder pb,
827                            Redirect in, Redirect out, Redirect err) {
828         pb.redirectInput(in);
829         pb.redirectOutput(out);
830         pb.redirectError(err);
831     }
832 
setFileContents(File file, String contents)833     static void setFileContents(File file, String contents) {
834         try {
835             Writer w = new FileWriter(file);
836             w.write(contents);
837             w.close();
838         } catch (Throwable t) { unexpected(t); }
839     }
840 
fileContents(File file)841     static String fileContents(File file) {
842         try {
843             Reader r = new FileReader(file);
844             StringBuilder sb = new StringBuilder();
845             char[] buffer = new char[1024];
846             int n;
847             while ((n = r.read(buffer)) != -1)
848                 sb.append(buffer,0,n);
849             r.close();
850             return new String(sb);
851         } catch (Throwable t) { unexpected(t); return ""; }
852     }
853 
testIORedirection()854     static void testIORedirection() throws Throwable {
855         final File ifile = new File("ifile");
856         final File ofile = new File("ofile");
857         final File efile = new File("efile");
858         ifile.delete();
859         ofile.delete();
860         efile.delete();
861 
862         //----------------------------------------------------------------
863         // Check mutual inequality of different types of Redirect
864         //----------------------------------------------------------------
865         Redirect[] redirects =
866             { PIPE,
867               INHERIT,
868               Redirect.from(ifile),
869               Redirect.to(ifile),
870               Redirect.appendTo(ifile),
871               Redirect.from(ofile),
872               Redirect.to(ofile),
873               Redirect.appendTo(ofile),
874             };
875         for (int i = 0; i < redirects.length; i++)
876             for (int j = 0; j < redirects.length; j++)
877                 equal(redirects[i].equals(redirects[j]), (i == j));
878 
879         //----------------------------------------------------------------
880         // Check basic properties of different types of Redirect
881         //----------------------------------------------------------------
882         equal(PIPE.type(), Redirect.Type.PIPE);
883         equal(PIPE.toString(), "PIPE");
884         equal(PIPE.file(), null);
885 
886         equal(INHERIT.type(), Redirect.Type.INHERIT);
887         equal(INHERIT.toString(), "INHERIT");
888         equal(INHERIT.file(), null);
889 
890         equal(Redirect.from(ifile).type(), Redirect.Type.READ);
891         equal(Redirect.from(ifile).toString(),
892               "redirect to read from file \"ifile\"");
893         equal(Redirect.from(ifile).file(), ifile);
894         equal(Redirect.from(ifile),
895               Redirect.from(ifile));
896         equal(Redirect.from(ifile).hashCode(),
897               Redirect.from(ifile).hashCode());
898 
899         equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
900         equal(Redirect.to(ofile).toString(),
901               "redirect to write to file \"ofile\"");
902         equal(Redirect.to(ofile).file(), ofile);
903         equal(Redirect.to(ofile),
904               Redirect.to(ofile));
905         equal(Redirect.to(ofile).hashCode(),
906               Redirect.to(ofile).hashCode());
907 
908         equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
909         equal(Redirect.appendTo(efile).toString(),
910               "redirect to append to file \"efile\"");
911         equal(Redirect.appendTo(efile).file(), efile);
912         equal(Redirect.appendTo(efile),
913               Redirect.appendTo(efile));
914         equal(Redirect.appendTo(efile).hashCode(),
915               Redirect.appendTo(efile).hashCode());
916 
917         //----------------------------------------------------------------
918         // Check initial values of redirects
919         //----------------------------------------------------------------
920         List<String> childArgs = new ArrayList<String>(javaChildArgs);
921         childArgs.add("testIO");
922         final ProcessBuilder pb = new ProcessBuilder(childArgs);
923         checkRedirects(pb, PIPE, PIPE, PIPE);
924 
925         //----------------------------------------------------------------
926         // Check inheritIO
927         //----------------------------------------------------------------
928         pb.inheritIO();
929         checkRedirects(pb, INHERIT, INHERIT, INHERIT);
930 
931         //----------------------------------------------------------------
932         // Check setters and getters agree
933         //----------------------------------------------------------------
934         pb.redirectInput(ifile);
935         equal(pb.redirectInput().file(), ifile);
936         equal(pb.redirectInput(), Redirect.from(ifile));
937 
938         pb.redirectOutput(ofile);
939         equal(pb.redirectOutput().file(), ofile);
940         equal(pb.redirectOutput(), Redirect.to(ofile));
941 
942         pb.redirectError(efile);
943         equal(pb.redirectError().file(), efile);
944         equal(pb.redirectError(), Redirect.to(efile));
945 
946         THROWS(IllegalArgumentException.class,
947                () -> pb.redirectInput(Redirect.to(ofile)),
948                () -> pb.redirectOutput(Redirect.from(ifile)),
949                () -> pb.redirectError(Redirect.from(ifile)));
950 
951         THROWS(IOException.class,
952                // Input file does not exist
953                () -> pb.start());
954         setFileContents(ifile, "standard input");
955 
956         //----------------------------------------------------------------
957         // Writing to non-existent files
958         //----------------------------------------------------------------
959         {
960             ProcessResults r = run(pb);
961             equal(r.exitValue(), 0);
962             equal(fileContents(ofile), "standard output");
963             equal(fileContents(efile), "standard error");
964             equal(r.out(), "");
965             equal(r.err(), "");
966             ofile.delete();
967             efile.delete();
968         }
969 
970         //----------------------------------------------------------------
971         // Both redirectErrorStream + redirectError
972         //----------------------------------------------------------------
973         {
974             pb.redirectErrorStream(true);
975             ProcessResults r = run(pb);
976             equal(r.exitValue(), 0);
977             equal(fileContents(ofile),
978                   "standard error" + "standard output");
979             equal(fileContents(efile), "");
980             equal(r.out(), "");
981             equal(r.err(), "");
982             ofile.delete();
983             efile.delete();
984         }
985 
986         //----------------------------------------------------------------
987         // Appending to existing files
988         //----------------------------------------------------------------
989         {
990             setFileContents(ofile, "ofile-contents");
991             setFileContents(efile, "efile-contents");
992             pb.redirectOutput(Redirect.appendTo(ofile));
993             pb.redirectError(Redirect.appendTo(efile));
994             pb.redirectErrorStream(false);
995             ProcessResults r = run(pb);
996             equal(r.exitValue(), 0);
997             equal(fileContents(ofile),
998                   "ofile-contents" + "standard output");
999             equal(fileContents(efile),
1000                   "efile-contents" + "standard error");
1001             equal(r.out(), "");
1002             equal(r.err(), "");
1003             ofile.delete();
1004             efile.delete();
1005         }
1006 
1007         //----------------------------------------------------------------
1008         // Replacing existing files
1009         //----------------------------------------------------------------
1010         {
1011             setFileContents(ofile, "ofile-contents");
1012             setFileContents(efile, "efile-contents");
1013             pb.redirectOutput(ofile);
1014             pb.redirectError(Redirect.to(efile));
1015             ProcessResults r = run(pb);
1016             equal(r.exitValue(), 0);
1017             equal(fileContents(ofile), "standard output");
1018             equal(fileContents(efile), "standard error");
1019             equal(r.out(), "");
1020             equal(r.err(), "");
1021             ofile.delete();
1022             efile.delete();
1023         }
1024 
1025         //----------------------------------------------------------------
1026         // Appending twice to the same file?
1027         //----------------------------------------------------------------
1028         {
1029             setFileContents(ofile, "ofile-contents");
1030             setFileContents(efile, "efile-contents");
1031             Redirect appender = Redirect.appendTo(ofile);
1032             pb.redirectOutput(appender);
1033             pb.redirectError(appender);
1034             ProcessResults r = run(pb);
1035             equal(r.exitValue(), 0);
1036             equal(fileContents(ofile),
1037                   "ofile-contents" +
1038                   "standard error" +
1039                   "standard output");
1040             equal(fileContents(efile), "efile-contents");
1041             equal(r.out(), "");
1042             equal(r.err(), "");
1043             ifile.delete();
1044             ofile.delete();
1045             efile.delete();
1046         }
1047 
1048         //----------------------------------------------------------------
1049         // Testing INHERIT is harder.
1050         // Note that this requires __FOUR__ nested JVMs involved in one test,
1051         // if you count the harness JVM.
1052         //----------------------------------------------------------------
1053         for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) {
1054             redirectIO(pb, PIPE, PIPE, PIPE);
1055             List<String> command = pb.command();
1056             command.set(command.size() - 1, testName);
1057             Process p = pb.start();
1058             new PrintStream(p.getOutputStream()).print("standard input");
1059             p.getOutputStream().close();
1060             ProcessResults r = run(p);
1061             equal(r.exitValue(), 0);
1062             equal(r.out(), "standard output");
1063             equal(r.err(), "standard error");
1064         }
1065 
1066         //----------------------------------------------------------------
1067         // Test security implications of I/O redirection
1068         //----------------------------------------------------------------
1069 
1070         // Read access to current directory is always granted;
1071         // So create a tmpfile for input instead.
1072         final File tmpFile = File.createTempFile("Basic", "tmp");
1073         setFileContents(tmpFile, "standard input");
1074 
1075         final Policy policy = new Policy();
1076         Policy.setPolicy(policy);
1077         System.setSecurityManager(new SecurityManager());
1078         try {
1079             final Permission xPermission
1080                 = new FilePermission("<<ALL FILES>>", "execute");
1081             final Permission rxPermission
1082                 = new FilePermission("<<ALL FILES>>", "read,execute");
1083             final Permission wxPermission
1084                 = new FilePermission("<<ALL FILES>>", "write,execute");
1085             final Permission rwxPermission
1086                 = new FilePermission("<<ALL FILES>>", "read,write,execute");
1087 
1088             THROWS(SecurityException.class,
1089                    () -> { policy.setPermissions(xPermission);
1090                            redirectIO(pb, from(tmpFile), PIPE, PIPE);
1091                            pb.start();},
1092                    () -> { policy.setPermissions(rxPermission);
1093                            redirectIO(pb, PIPE, to(ofile), PIPE);
1094                            pb.start();},
1095                    () -> { policy.setPermissions(rxPermission);
1096                            redirectIO(pb, PIPE, PIPE, to(efile));
1097                            pb.start();});
1098 
1099             {
1100                 policy.setPermissions(rxPermission);
1101                 redirectIO(pb, from(tmpFile), PIPE, PIPE);
1102                 ProcessResults r = run(pb);
1103                 equal(r.out(), "standard output");
1104                 equal(r.err(), "standard error");
1105             }
1106 
1107             {
1108                 policy.setPermissions(wxPermission);
1109                 redirectIO(pb, PIPE, to(ofile), to(efile));
1110                 Process p = pb.start();
1111                 new PrintStream(p.getOutputStream()).print("standard input");
1112                 p.getOutputStream().close();
1113                 ProcessResults r = run(p);
1114                 policy.setPermissions(rwxPermission);
1115                 equal(fileContents(ofile), "standard output");
1116                 equal(fileContents(efile), "standard error");
1117             }
1118 
1119             {
1120                 policy.setPermissions(rwxPermission);
1121                 redirectIO(pb, from(tmpFile), to(ofile), to(efile));
1122                 ProcessResults r = run(pb);
1123                 policy.setPermissions(rwxPermission);
1124                 equal(fileContents(ofile), "standard output");
1125                 equal(fileContents(efile), "standard error");
1126             }
1127 
1128         } finally {
1129             policy.setPermissions(new RuntimePermission("setSecurityManager"));
1130             System.setSecurityManager(null);
1131             tmpFile.delete();
1132             ifile.delete();
1133             ofile.delete();
1134             efile.delete();
1135         }
1136     }
1137 
realMain(String[] args)1138     private static void realMain(String[] args) throws Throwable {
1139         if (Windows.is())
1140             System.out.println("This appears to be a Windows system.");
1141         if (Unix.is())
1142             System.out.println("This appears to be a Unix system.");
1143         if (UnicodeOS.is())
1144             System.out.println("This appears to be a Unicode-based OS.");
1145 
1146         try { testIORedirection(); }
1147         catch (Throwable t) { unexpected(t); }
1148 
1149         //----------------------------------------------------------------
1150         // Basic tests for setting, replacing and deleting envvars
1151         //----------------------------------------------------------------
1152         try {
1153             ProcessBuilder pb = new ProcessBuilder();
1154             Map<String,String> environ = pb.environment();
1155 
1156             // New env var
1157             environ.put("QUUX", "BAR");
1158             equal(environ.get("QUUX"), "BAR");
1159             equal(getenvInChild(pb,"QUUX"), "BAR");
1160 
1161             // Modify env var
1162             environ.put("QUUX","bear");
1163             equal(environ.get("QUUX"), "bear");
1164             equal(getenvInChild(pb,"QUUX"), "bear");
1165             checkMapSanity(environ);
1166 
1167             // Remove env var
1168             environ.remove("QUUX");
1169             equal(environ.get("QUUX"), null);
1170             equal(getenvInChild(pb,"QUUX"), "null");
1171             checkMapSanity(environ);
1172 
1173             // Remove non-existent env var
1174             environ.remove("QUUX");
1175             equal(environ.get("QUUX"), null);
1176             equal(getenvInChild(pb,"QUUX"), "null");
1177             checkMapSanity(environ);
1178         } catch (Throwable t) { unexpected(t); }
1179 
1180         //----------------------------------------------------------------
1181         // Pass Empty environment to child
1182         //----------------------------------------------------------------
1183         try {
1184             ProcessBuilder pb = new ProcessBuilder();
1185             pb.environment().clear();
1186             String expected = Windows.is() ? "SystemRoot="+systemRoot+",": "";
1187             expected = AIX.is() ? "LIBPATH="+libpath+",": expected;
1188             if (Windows.is()) {
1189                 pb.environment().put("SystemRoot", systemRoot);
1190             }
1191             if (AIX.is()) {
1192                 pb.environment().put("LIBPATH", libpath);
1193             }
1194             String result = getenvInChild(pb);
1195             if (MacOSX.is()) {
1196                 result = removeMacExpectedVars(result);
1197             }
1198             if (AIX.is()) {
1199                 result = removeAixExpectedVars(result);
1200             }
1201             equal(result, expected);
1202         } catch (Throwable t) { unexpected(t); }
1203 
1204         //----------------------------------------------------------------
1205         // System.getenv() is read-only.
1206         //----------------------------------------------------------------
1207         THROWS(UnsupportedOperationException.class,
1208                () -> getenv().put("FOO","BAR"),
1209                () -> getenv().remove("PATH"),
1210                () -> getenv().keySet().remove("PATH"),
1211                () -> getenv().values().remove("someValue"));
1212 
1213         try {
1214             Collection<Map.Entry<String,String>> c = getenv().entrySet();
1215             if (! c.isEmpty())
1216                 try {
1217                     c.iterator().next().setValue("foo");
1218                     fail("Expected UnsupportedOperationException not thrown");
1219                 } catch (UnsupportedOperationException e) {} // OK
1220         } catch (Throwable t) { unexpected(t); }
1221 
1222         //----------------------------------------------------------------
1223         // System.getenv() always returns the same object in our implementation.
1224         //----------------------------------------------------------------
1225         try {
1226             check(System.getenv() == System.getenv());
1227         } catch (Throwable t) { unexpected(t); }
1228 
1229         //----------------------------------------------------------------
1230         // You can't create an env var name containing "=",
1231         // or an env var name or value containing NUL.
1232         //----------------------------------------------------------------
1233         {
1234             final Map<String,String> m = new ProcessBuilder().environment();
1235             THROWS(IllegalArgumentException.class,
1236                    () -> m.put("FOO=","BAR"),
1237                    () -> m.put("FOO\u0000","BAR"),
1238                    () -> m.put("FOO","BAR\u0000"));
1239         }
1240 
1241         //----------------------------------------------------------------
1242         // Commands must never be null.
1243         //----------------------------------------------------------------
1244         THROWS(NullPointerException.class,
1245                () -> new ProcessBuilder((List<String>)null),
1246                () -> new ProcessBuilder().command((List<String>)null));
1247 
1248         //----------------------------------------------------------------
1249         // Put in a command; get the same one back out.
1250         //----------------------------------------------------------------
1251         try {
1252             List<String> command = new ArrayList<String>();
1253             ProcessBuilder pb = new ProcessBuilder(command);
1254             check(pb.command() == command);
1255             List<String> command2 = new ArrayList<String>(2);
1256             command2.add("foo");
1257             command2.add("bar");
1258             pb.command(command2);
1259             check(pb.command() == command2);
1260             pb.command("foo", "bar");
1261             check(pb.command() != command2 && pb.command().equals(command2));
1262             pb.command(command2);
1263             command2.add("baz");
1264             equal(pb.command().get(2), "baz");
1265         } catch (Throwable t) { unexpected(t); }
1266 
1267         //----------------------------------------------------------------
1268         // Commands must contain at least one element.
1269         //----------------------------------------------------------------
1270         THROWS(IndexOutOfBoundsException.class,
1271                () -> new ProcessBuilder().start(),
1272                () -> new ProcessBuilder(new ArrayList<String>()).start(),
1273                () -> Runtime.getRuntime().exec(new String[]{}));
1274 
1275         //----------------------------------------------------------------
1276         // Commands must not contain null elements at start() time.
1277         //----------------------------------------------------------------
1278         THROWS(NullPointerException.class,
1279                () -> new ProcessBuilder("foo",null,"bar").start(),
1280                () -> new ProcessBuilder((String)null).start(),
1281                () -> new ProcessBuilder(new String[]{null}).start(),
1282                () -> new ProcessBuilder(new String[]{"foo",null,"bar"}).start());
1283 
1284         //----------------------------------------------------------------
1285         // Command lists are growable.
1286         //----------------------------------------------------------------
1287         try {
1288             new ProcessBuilder().command().add("foo");
1289             new ProcessBuilder("bar").command().add("foo");
1290             new ProcessBuilder(new String[]{"1","2"}).command().add("3");
1291         } catch (Throwable t) { unexpected(t); }
1292 
1293         //----------------------------------------------------------------
1294         // Nulls in environment updates generate NullPointerException
1295         //----------------------------------------------------------------
1296         try {
1297             final Map<String,String> env = new ProcessBuilder().environment();
1298             THROWS(NullPointerException.class,
1299                    () -> env.put("foo",null),
1300                    () -> env.put(null,"foo"),
1301                    () -> env.remove(null),
1302                    () -> { for (Map.Entry<String,String> e : env.entrySet())
1303                                e.setValue(null);},
1304                    () -> Runtime.getRuntime().exec(new String[]{"foo"},
1305                                                    new String[]{null}));
1306         } catch (Throwable t) { unexpected(t); }
1307 
1308         //----------------------------------------------------------------
1309         // Non-String types in environment updates generate ClassCastException
1310         //----------------------------------------------------------------
1311         try {
1312             final Map<String,String> env = new ProcessBuilder().environment();
1313             THROWS(ClassCastException.class,
1314                    () -> env.remove(TRUE),
1315                    () -> env.keySet().remove(TRUE),
1316                    () -> env.values().remove(TRUE),
1317                    () -> env.entrySet().remove(TRUE));
1318         } catch (Throwable t) { unexpected(t); }
1319 
1320         //----------------------------------------------------------------
1321         // Check query operations on environment maps
1322         //----------------------------------------------------------------
1323         try {
1324             List<Map<String,String>> envs =
1325                 new ArrayList<Map<String,String>>(2);
1326             envs.add(System.getenv());
1327             envs.add(new ProcessBuilder().environment());
1328             for (final Map<String,String> env : envs) {
1329                 //----------------------------------------------------------------
1330                 // Nulls in environment queries are forbidden.
1331                 //----------------------------------------------------------------
1332                 THROWS(NullPointerException.class,
1333                        () -> getenv(null),
1334                        () -> env.get(null),
1335                        () -> env.containsKey(null),
1336                        () -> env.containsValue(null),
1337                        () -> env.keySet().contains(null),
1338                        () -> env.values().contains(null));
1339 
1340                 //----------------------------------------------------------------
1341                 // Non-String types in environment queries are forbidden.
1342                 //----------------------------------------------------------------
1343                 THROWS(ClassCastException.class,
1344                        () -> env.get(TRUE),
1345                        () -> env.containsKey(TRUE),
1346                        () -> env.containsValue(TRUE),
1347                        () -> env.keySet().contains(TRUE),
1348                        () -> env.values().contains(TRUE));
1349 
1350                 //----------------------------------------------------------------
1351                 // Illegal String values in environment queries are (grumble) OK
1352                 //----------------------------------------------------------------
1353                 equal(env.get("\u0000"), null);
1354                 check(! env.containsKey("\u0000"));
1355                 check(! env.containsValue("\u0000"));
1356                 check(! env.keySet().contains("\u0000"));
1357                 check(! env.values().contains("\u0000"));
1358             }
1359 
1360         } catch (Throwable t) { unexpected(t); }
1361 
1362         try {
1363             final Set<Map.Entry<String,String>> entrySet =
1364                 new ProcessBuilder().environment().entrySet();
1365             THROWS(NullPointerException.class,
1366                    () -> entrySet.contains(null));
1367             THROWS(ClassCastException.class,
1368                    () -> entrySet.contains(TRUE),
1369                    () -> entrySet.contains(
1370                              new SimpleImmutableEntry<Boolean,String>(TRUE,"")));
1371 
1372             check(! entrySet.contains
1373                   (new SimpleImmutableEntry<String,String>("", "")));
1374         } catch (Throwable t) { unexpected(t); }
1375 
1376         //----------------------------------------------------------------
1377         // Put in a directory; get the same one back out.
1378         //----------------------------------------------------------------
1379         try {
1380             ProcessBuilder pb = new ProcessBuilder();
1381             File foo = new File("foo");
1382             equal(pb.directory(), null);
1383             equal(pb.directory(foo).directory(), foo);
1384             equal(pb.directory(null).directory(), null);
1385         } catch (Throwable t) { unexpected(t); }
1386 
1387         //----------------------------------------------------------------
1388         // If round-trip conversion works, check envvar pass-through to child
1389         //----------------------------------------------------------------
1390         try {
1391             testEncoding("ASCII",   "xyzzy");
1392             testEncoding("Latin1",  "\u00f1\u00e1");
1393             testEncoding("Unicode", "\u22f1\u11e1");
1394         } catch (Throwable t) { unexpected(t); }
1395 
1396         //----------------------------------------------------------------
1397         // A surprisingly large number of ways to delete an environment var.
1398         //----------------------------------------------------------------
1399         testVariableDeleter(new EnvironmentFrobber() {
1400                 public void doIt(Map<String,String> environ) {
1401                     environ.remove("Foo");}});
1402 
1403         testVariableDeleter(new EnvironmentFrobber() {
1404                 public void doIt(Map<String,String> environ) {
1405                     environ.keySet().remove("Foo");}});
1406 
1407         testVariableDeleter(new EnvironmentFrobber() {
1408                 public void doIt(Map<String,String> environ) {
1409                     environ.values().remove("BAAR");}});
1410 
1411         testVariableDeleter(new EnvironmentFrobber() {
1412                 public void doIt(Map<String,String> environ) {
1413                     // Legally fabricate a ProcessEnvironment.StringEntry,
1414                     // even though it's private.
1415                     Map<String,String> environ2
1416                         = new ProcessBuilder().environment();
1417                     environ2.clear();
1418                     environ2.put("Foo","BAAR");
1419                     // Subtlety alert.
1420                     Map.Entry<String,String> e
1421                         = environ2.entrySet().iterator().next();
1422                     environ.entrySet().remove(e);}});
1423 
1424         testVariableDeleter(new EnvironmentFrobber() {
1425                 public void doIt(Map<String,String> environ) {
1426                     Map.Entry<String,String> victim = null;
1427                     for (Map.Entry<String,String> e : environ.entrySet())
1428                         if (e.getKey().equals("Foo"))
1429                             victim = e;
1430                     if (victim != null)
1431                         environ.entrySet().remove(victim);}});
1432 
1433         testVariableDeleter(new EnvironmentFrobber() {
1434                 public void doIt(Map<String,String> environ) {
1435                     Iterator<String> it = environ.keySet().iterator();
1436                     while (it.hasNext()) {
1437                         String val = it.next();
1438                         if (val.equals("Foo"))
1439                             it.remove();}}});
1440 
1441         testVariableDeleter(new EnvironmentFrobber() {
1442                 public void doIt(Map<String,String> environ) {
1443                     Iterator<Map.Entry<String,String>> it
1444                         = environ.entrySet().iterator();
1445                     while (it.hasNext()) {
1446                         Map.Entry<String,String> e = it.next();
1447                         if (e.getKey().equals("Foo"))
1448                             it.remove();}}});
1449 
1450         testVariableDeleter(new EnvironmentFrobber() {
1451                 public void doIt(Map<String,String> environ) {
1452                     Iterator<String> it = environ.values().iterator();
1453                     while (it.hasNext()) {
1454                         String val = it.next();
1455                         if (val.equals("BAAR"))
1456                             it.remove();}}});
1457 
1458         //----------------------------------------------------------------
1459         // A surprisingly small number of ways to add an environment var.
1460         //----------------------------------------------------------------
1461         testVariableAdder(new EnvironmentFrobber() {
1462                 public void doIt(Map<String,String> environ) {
1463                     environ.put("Foo","Bahrein");}});
1464 
1465         //----------------------------------------------------------------
1466         // A few ways to modify an environment var.
1467         //----------------------------------------------------------------
1468         testVariableModifier(new EnvironmentFrobber() {
1469                 public void doIt(Map<String,String> environ) {
1470                     environ.put("Foo","NewValue");}});
1471 
1472         testVariableModifier(new EnvironmentFrobber() {
1473                 public void doIt(Map<String,String> environ) {
1474                     for (Map.Entry<String,String> e : environ.entrySet())
1475                         if (e.getKey().equals("Foo"))
1476                             e.setValue("NewValue");}});
1477 
1478         //----------------------------------------------------------------
1479         // Fiddle with environment sizes
1480         //----------------------------------------------------------------
1481         try {
1482             Map<String,String> environ = new ProcessBuilder().environment();
1483             int size = environ.size();
1484             checkSizes(environ, size);
1485 
1486             environ.put("UnLiKeLYeNVIROmtNam", "someVal");
1487             checkSizes(environ, size+1);
1488 
1489             // Check for environment independence
1490             new ProcessBuilder().environment().clear();
1491 
1492             environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal");
1493             checkSizes(environ, size+1);
1494 
1495             environ.remove("UnLiKeLYeNVIROmtNam");
1496             checkSizes(environ, size);
1497 
1498             environ.clear();
1499             checkSizes(environ, 0);
1500 
1501             environ.clear();
1502             checkSizes(environ, 0);
1503 
1504             environ = new ProcessBuilder().environment();
1505             environ.keySet().clear();
1506             checkSizes(environ, 0);
1507 
1508             environ = new ProcessBuilder().environment();
1509             environ.entrySet().clear();
1510             checkSizes(environ, 0);
1511 
1512             environ = new ProcessBuilder().environment();
1513             environ.values().clear();
1514             checkSizes(environ, 0);
1515         } catch (Throwable t) { unexpected(t); }
1516 
1517         //----------------------------------------------------------------
1518         // Check that various map invariants hold
1519         //----------------------------------------------------------------
1520         checkMapSanity(new ProcessBuilder().environment());
1521         checkMapSanity(System.getenv());
1522         checkMapEquality(new ProcessBuilder().environment(),
1523                          new ProcessBuilder().environment());
1524 
1525 
1526         //----------------------------------------------------------------
1527         // Check effects on external "env" command.
1528         //----------------------------------------------------------------
1529         try {
1530             Set<String> env1 = new HashSet<String>
1531                 (Arrays.asList(nativeEnv((String[])null).split("\n")));
1532 
1533             ProcessBuilder pb = new ProcessBuilder();
1534             pb.environment().put("QwErTyUiOp","AsDfGhJk");
1535 
1536             Set<String> env2 = new HashSet<String>
1537                 (Arrays.asList(nativeEnv(pb).split("\n")));
1538 
1539             check(env2.size() == env1.size() + 1);
1540             env1.add("QwErTyUiOp=AsDfGhJk");
1541             check(env1.equals(env2));
1542         } catch (Throwable t) { unexpected(t); }
1543 
1544         //----------------------------------------------------------------
1545         // Test Runtime.exec(...envp...)
1546         // Check for sort order of environment variables on Windows.
1547         //----------------------------------------------------------------
1548         try {
1549             String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1550             // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~'
1551             String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1552                             "+=+", "_=_", "~=~", systemRoot};
1553             String output = nativeEnv(envp);
1554             String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1555             // On Windows, Java must keep the environment sorted.
1556             // Order is random on Unix, so this test does the sort.
1557             if (! Windows.is())
1558                 output = sortByLinesWindowsly(output);
1559             equal(output, expected);
1560         } catch (Throwable t) { unexpected(t); }
1561 
1562         //----------------------------------------------------------------
1563         // Test Runtime.exec(...envp...)
1564         // and check SystemRoot gets set automatically on Windows
1565         //----------------------------------------------------------------
1566         try {
1567             if (Windows.is()) {
1568                 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot");
1569                 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=",
1570                                 "+=+", "_=_", "~=~"};
1571                 String output = nativeEnv(envp);
1572                 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n";
1573                 equal(output, expected);
1574             }
1575         } catch (Throwable t) { unexpected(t); }
1576 
1577         //----------------------------------------------------------------
1578         // System.getenv() must be consistent with System.getenv(String)
1579         //----------------------------------------------------------------
1580         try {
1581             for (Map.Entry<String,String> e : getenv().entrySet())
1582                 equal(getenv(e.getKey()), e.getValue());
1583         } catch (Throwable t) { unexpected(t); }
1584 
1585         //----------------------------------------------------------------
1586         // Fiddle with working directory in child
1587         //----------------------------------------------------------------
1588         try {
1589             String canonicalUserDir =
1590                 new File(System.getProperty("user.dir")).getCanonicalPath();
1591             String[] sdirs = new String[]
1592                 {".", "..", "/", "/bin",
1593                  "C:", "c:", "C:/", "c:\\", "\\", "\\bin",
1594                  "c:\\windows  ", "c:\\Program Files", "c:\\Program Files\\" };
1595             for (String sdir : sdirs) {
1596                 File dir = new File(sdir);
1597                 if (! (dir.isDirectory() && dir.exists()))
1598                     continue;
1599                 out.println("Testing directory " + dir);
1600                 //dir = new File(dir.getCanonicalPath());
1601 
1602                 ProcessBuilder pb = new ProcessBuilder();
1603                 equal(pb.directory(), null);
1604                 equal(pwdInChild(pb), canonicalUserDir);
1605 
1606                 pb.directory(dir);
1607                 equal(pb.directory(), dir);
1608                 equal(pwdInChild(pb), dir.getCanonicalPath());
1609 
1610                 pb.directory(null);
1611                 equal(pb.directory(), null);
1612                 equal(pwdInChild(pb), canonicalUserDir);
1613 
1614                 pb.directory(dir);
1615             }
1616         } catch (Throwable t) { unexpected(t); }
1617 
1618         //----------------------------------------------------------------
1619         // Working directory with Unicode in child
1620         //----------------------------------------------------------------
1621         try {
1622             if (UnicodeOS.is()) {
1623                 File dir = new File(System.getProperty("test.dir", "."),
1624                                     "ProcessBuilderDir\u4e00\u4e02");
1625                 try {
1626                     if (!dir.exists())
1627                         dir.mkdir();
1628                     out.println("Testing Unicode directory:" + dir);
1629                     ProcessBuilder pb = new ProcessBuilder();
1630                     pb.directory(dir);
1631                     equal(pwdInChild(pb), dir.getCanonicalPath());
1632                 } finally {
1633                     if (dir.exists())
1634                         dir.delete();
1635                 }
1636             }
1637         } catch (Throwable t) { unexpected(t); }
1638 
1639         //----------------------------------------------------------------
1640         // OOME in child allocating maximally sized array
1641         // Test for hotspot/jvmti bug 6850957
1642         //----------------------------------------------------------------
1643         try {
1644             List<String> list = new ArrayList<String>(javaChildArgs);
1645             list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version",
1646                                       javaExe));
1647             list.add("ArrayOOME");
1648             ProcessResults r = run(new ProcessBuilder(list));
1649             check(r.err().contains("java.lang.OutOfMemoryError:"));
1650             check(r.err().contains(javaExe));
1651             check(r.err().contains(System.getProperty("java.version")));
1652             equal(r.exitValue(), 1);
1653         } catch (Throwable t) { unexpected(t); }
1654 
1655         //----------------------------------------------------------------
1656         // Windows has tricky semi-case-insensitive semantics
1657         //----------------------------------------------------------------
1658         if (Windows.is())
1659             try {
1660                 out.println("Running case insensitve variable tests");
1661                 for (String[] namePair :
1662                          new String[][]
1663                     { new String[]{"PATH","PaTh"},
1664                       new String[]{"home","HOME"},
1665                       new String[]{"SYSTEMROOT","SystemRoot"}}) {
1666                     check((getenv(namePair[0]) == null &&
1667                            getenv(namePair[1]) == null)
1668                           ||
1669                           getenv(namePair[0]).equals(getenv(namePair[1])),
1670                           "Windows environment variables are not case insensitive");
1671                 }
1672             } catch (Throwable t) { unexpected(t); }
1673 
1674         //----------------------------------------------------------------
1675         // Test proper Unicode child environment transfer
1676         //----------------------------------------------------------------
1677         if (UnicodeOS.is())
1678             try {
1679                 ProcessBuilder pb = new ProcessBuilder();
1680                 pb.environment().put("\u1234","\u5678");
1681                 pb.environment().remove("PATH");
1682                 equal(getenvInChild1234(pb), "\u5678");
1683             } catch (Throwable t) { unexpected(t); }
1684 
1685 
1686         //----------------------------------------------------------------
1687         // Test Runtime.exec(...envp...) with envstrings with initial `='
1688         //----------------------------------------------------------------
1689         try {
1690             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1691             childArgs.add("System.getenv()");
1692             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1693             String[] envp;
1694             String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot};
1695             String[] envpOth = {"=ExitValue=3", "=C:=\\"};
1696             if (Windows.is()) {
1697                 envp = envpWin;
1698             } else {
1699                 envp = envpOth;
1700             }
1701             Process p = Runtime.getRuntime().exec(cmdp, envp);
1702             String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,";
1703             expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected;
1704             String commandOutput = commandOutput(p);
1705             if (MacOSX.is()) {
1706                 commandOutput = removeMacExpectedVars(commandOutput);
1707             }
1708             if (AIX.is()) {
1709                 commandOutput = removeAixExpectedVars(commandOutput);
1710             }
1711             equal(commandOutput, expected);
1712             if (Windows.is()) {
1713                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1714                 pb.environment().clear();
1715                 pb.environment().put("SystemRoot", systemRoot);
1716                 pb.environment().put("=ExitValue", "3");
1717                 pb.environment().put("=C:", "\\");
1718                 equal(commandOutput(pb), expected);
1719             }
1720         } catch (Throwable t) { unexpected(t); }
1721 
1722         //----------------------------------------------------------------
1723         // Test Runtime.exec(...envp...) with envstrings without any `='
1724         //----------------------------------------------------------------
1725         try {
1726             String[] cmdp = {"echo"};
1727             String[] envp = {"Hello", "World"}; // Yuck!
1728             Process p = Runtime.getRuntime().exec(cmdp, envp);
1729             equal(commandOutput(p), "\n");
1730         } catch (Throwable t) { unexpected(t); }
1731 
1732         //----------------------------------------------------------------
1733         // Test Runtime.exec(...envp...) with envstrings containing NULs
1734         //----------------------------------------------------------------
1735         try {
1736             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1737             childArgs.add("System.getenv()");
1738             String[] cmdp = childArgs.toArray(new String[childArgs.size()]);
1739             String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck!
1740                              "FO\u0000=B\u0000R"};
1741             String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck!
1742                              "FO\u0000=B\u0000R"};
1743             String[] envp;
1744             if (Windows.is()) {
1745                 envp = envpWin;
1746             } else {
1747                 envp = envpOth;
1748             }
1749             System.out.println ("cmdp");
1750             for (int i=0; i<cmdp.length; i++) {
1751                 System.out.printf ("cmdp %d: %s\n", i, cmdp[i]);
1752             }
1753             System.out.println ("envp");
1754             for (int i=0; i<envp.length; i++) {
1755                 System.out.printf ("envp %d: %s\n", i, envp[i]);
1756             }
1757             Process p = Runtime.getRuntime().exec(cmdp, envp);
1758             String commandOutput = commandOutput(p);
1759             if (MacOSX.is()) {
1760                 commandOutput = removeMacExpectedVars(commandOutput);
1761             }
1762             if (AIX.is()) {
1763                 commandOutput = removeAixExpectedVars(commandOutput);
1764             }
1765             check(commandOutput.equals(Windows.is()
1766                     ? "LC_ALL=C,SystemRoot="+systemRoot+","
1767                     : AIX.is()
1768                             ? "LC_ALL=C,LIBPATH="+libpath+","
1769                             : "LC_ALL=C,"),
1770                   "Incorrect handling of envstrings containing NULs");
1771         } catch (Throwable t) { unexpected(t); }
1772 
1773         //----------------------------------------------------------------
1774         // Test the redirectErrorStream property
1775         //----------------------------------------------------------------
1776         try {
1777             ProcessBuilder pb = new ProcessBuilder();
1778             equal(pb.redirectErrorStream(), false);
1779             equal(pb.redirectErrorStream(true), pb);
1780             equal(pb.redirectErrorStream(), true);
1781             equal(pb.redirectErrorStream(false), pb);
1782             equal(pb.redirectErrorStream(), false);
1783         } catch (Throwable t) { unexpected(t); }
1784 
1785         try {
1786             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1787             childArgs.add("OutErr");
1788             ProcessBuilder pb = new ProcessBuilder(childArgs);
1789             {
1790                 ProcessResults r = run(pb);
1791                 equal(r.out(), "outout");
1792                 equal(r.err(), "errerr");
1793             }
1794             {
1795                 pb.redirectErrorStream(true);
1796                 ProcessResults r = run(pb);
1797                 equal(r.out(), "outerrouterr");
1798                 equal(r.err(), "");
1799             }
1800         } catch (Throwable t) { unexpected(t); }
1801 
1802         if (Unix.is()) {
1803             //----------------------------------------------------------------
1804             // We can find true and false when PATH is null
1805             //----------------------------------------------------------------
1806             try {
1807                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1808                 childArgs.add("null PATH");
1809                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1810                 pb.environment().remove("PATH");
1811                 ProcessResults r = run(pb);
1812                 equal(r.out(), "");
1813                 equal(r.err(), "");
1814                 equal(r.exitValue(), 0);
1815             } catch (Throwable t) { unexpected(t); }
1816 
1817             //----------------------------------------------------------------
1818             // PATH search algorithm on Unix
1819             //----------------------------------------------------------------
1820             try {
1821                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1822                 childArgs.add("PATH search algorithm");
1823                 ProcessBuilder pb = new ProcessBuilder(childArgs);
1824                 pb.environment().put("PATH", "dir1:dir2:");
1825                 ProcessResults r = run(pb);
1826                 equal(r.out(), "");
1827                 equal(r.err(), "");
1828                 equal(r.exitValue(), True.exitValue());
1829             } catch (Throwable t) { unexpected(t); }
1830 
1831             //----------------------------------------------------------------
1832             // Parent's, not child's PATH is used
1833             //----------------------------------------------------------------
1834             try {
1835                 new File("suBdiR").mkdirs();
1836                 copy("/bin/true", "suBdiR/unliKely");
1837                 final ProcessBuilder pb =
1838                     new ProcessBuilder(new String[]{"unliKely"});
1839                 pb.environment().put("PATH", "suBdiR");
1840                 THROWS(IOException.class, () -> pb.start());
1841             } catch (Throwable t) { unexpected(t);
1842             } finally {
1843                 new File("suBdiR/unliKely").delete();
1844                 new File("suBdiR").delete();
1845             }
1846         }
1847 
1848         //----------------------------------------------------------------
1849         // Attempt to start bogus program ""
1850         //----------------------------------------------------------------
1851         try {
1852             new ProcessBuilder("").start();
1853             fail("Expected IOException not thrown");
1854         } catch (IOException e) {
1855             String m = e.getMessage();
1856             if (EnglishUnix.is() &&
1857                 ! matches(m, "No such file or directory"))
1858                 unexpected(e);
1859         } catch (Throwable t) { unexpected(t); }
1860 
1861         //----------------------------------------------------------------
1862         // Check that attempt to execute program name with funny
1863         // characters throws an exception containing those characters.
1864         //----------------------------------------------------------------
1865         for (String programName : new String[] {"\u00f0", "\u01f0"})
1866             try {
1867                 new ProcessBuilder(programName).start();
1868                 fail("Expected IOException not thrown");
1869             } catch (IOException e) {
1870                 String m = e.getMessage();
1871                 Pattern p = Pattern.compile(programName);
1872                 if (! matches(m, programName)
1873                     || (EnglishUnix.is()
1874                         && ! matches(m, "No such file or directory")))
1875                     unexpected(e);
1876             } catch (Throwable t) { unexpected(t); }
1877 
1878         //----------------------------------------------------------------
1879         // Attempt to start process in nonexistent directory fails.
1880         //----------------------------------------------------------------
1881         try {
1882             new ProcessBuilder("echo")
1883                 .directory(new File("UnLiKeLY"))
1884                 .start();
1885             fail("Expected IOException not thrown");
1886         } catch (IOException e) {
1887             String m = e.getMessage();
1888             if (! matches(m, "in directory")
1889                 || (EnglishUnix.is() &&
1890                     ! matches(m, "No such file or directory")))
1891                 unexpected(e);
1892         } catch (Throwable t) { unexpected(t); }
1893 
1894         //----------------------------------------------------------------
1895         // Attempt to write 4095 bytes to the pipe buffer without a
1896         // reader to drain it would deadlock, if not for the fact that
1897         // interprocess pipe buffers are at least 4096 bytes.
1898         //
1899         // Also, check that available reports all the bytes expected
1900         // in the pipe buffer, and that I/O operations do the expected
1901         // things.
1902         //----------------------------------------------------------------
1903         try {
1904             List<String> childArgs = new ArrayList<String>(javaChildArgs);
1905             childArgs.add("print4095");
1906             final int SIZE = 4095;
1907             final Process p = new ProcessBuilder(childArgs).start();
1908             print4095(p.getOutputStream(), (byte) '!'); // Might hang!
1909             p.waitFor();                                // Might hang!
1910             equal(SIZE, p.getInputStream().available());
1911             equal(SIZE, p.getErrorStream().available());
1912             THROWS(IOException.class,
1913                    () -> { p.getOutputStream().write((byte) '!');
1914                            p.getOutputStream().flush();});
1915 
1916             final byte[] bytes = new byte[SIZE + 1];
1917             equal(SIZE, p.getInputStream().read(bytes));
1918             for (int i = 0; i < SIZE; i++)
1919                 equal((byte) '!', bytes[i]);
1920             equal((byte) 0, bytes[SIZE]);
1921 
1922             equal(SIZE, p.getErrorStream().read(bytes));
1923             for (int i = 0; i < SIZE; i++)
1924                 equal((byte) 'E', bytes[i]);
1925             equal((byte) 0, bytes[SIZE]);
1926 
1927             equal(0, p.getInputStream().available());
1928             equal(0, p.getErrorStream().available());
1929             equal(-1, p.getErrorStream().read());
1930             equal(-1, p.getInputStream().read());
1931 
1932             equal(p.exitValue(), 5);
1933 
1934             p.getInputStream().close();
1935             p.getErrorStream().close();
1936             try { p.getOutputStream().close(); } catch (IOException flushFailed) { }
1937 
1938             InputStream[] streams = { p.getInputStream(), p.getErrorStream() };
1939             for (final InputStream in : streams) {
1940                 Fun[] ops = {
1941                     () -> in.read(),
1942                     () -> in.read(bytes),
1943                     () -> in.available()
1944                 };
1945                 for (Fun op : ops) {
1946                     try {
1947                         op.f();
1948                         fail();
1949                     } catch (IOException expected) {
1950                         check(expected.getMessage()
1951                               .matches("[Ss]tream [Cc]losed"));
1952                     }
1953                 }
1954             }
1955         } catch (Throwable t) { unexpected(t); }
1956 
1957         //----------------------------------------------------------------
1958         // Check that reads which are pending when Process.destroy is
1959         // called, get EOF, not IOException("Stream closed").
1960         //----------------------------------------------------------------
1961         try {
1962             final int cases = 4;
1963             for (int i = 0; i < cases; i++) {
1964                 final int action = i;
1965                 List<String> childArgs = new ArrayList<String>(javaChildArgs);
1966                 childArgs.add("sleep");
1967                 final byte[] bytes = new byte[10];
1968                 final Process p = new ProcessBuilder(childArgs).start();
1969                 final CountDownLatch latch = new CountDownLatch(1);
1970                 final InputStream s;
1971                 switch (action & 0x1) {
1972                     case 0: s = p.getInputStream(); break;
1973                     case 1: s = p.getErrorStream(); break;
1974                     default: throw new Error();
1975                 }
1976                 final Thread thread = new Thread() {
1977                     public void run() {
1978                         try {
1979                             int r;
1980                             latch.countDown();
1981                             switch (action & 0x2) {
1982                                 case 0: r = s.read(); break;
1983                                 case 2: r = s.read(bytes); break;
1984                                 default: throw new Error();
1985                             }
1986                             equal(-1, r);
1987                         } catch (Throwable t) { unexpected(t); }}};
1988 
1989                 thread.start();
1990                 latch.await();
1991                 Thread.sleep(10);
1992 
1993                 String os = System.getProperty("os.name");
1994                 if (os.equalsIgnoreCase("Solaris") ||
1995                     os.equalsIgnoreCase("SunOS"))
1996                 {
1997                     final Object deferred;
1998                     Class<?> c = s.getClass();
1999                     if (c.getName().equals(
2000                         "java.lang.UNIXProcess$DeferredCloseInputStream"))
2001                     {
2002                         deferred = s;
2003                     } else {
2004                         Field deferredField = p.getClass().
2005                             getDeclaredField("stdout_inner_stream");
2006                         deferredField.setAccessible(true);
2007                         deferred = deferredField.get(p);
2008                     }
2009                     Field useCountField = deferred.getClass().
2010                         getDeclaredField("useCount");
2011                     useCountField.setAccessible(true);
2012 
2013                     while (useCountField.getInt(deferred) <= 0) {
2014                         Thread.yield();
2015                     }
2016                 } else if (s instanceof BufferedInputStream) {
2017                     Field f = Unsafe.class.getDeclaredField("theUnsafe");
2018                     f.setAccessible(true);
2019                     Unsafe unsafe = (Unsafe)f.get(null);
2020 
2021                     while (unsafe.tryMonitorEnter(s)) {
2022                         unsafe.monitorExit(s);
2023                         Thread.sleep(1);
2024                     }
2025                 }
2026                 p.destroy();
2027                 thread.join();
2028             }
2029         } catch (Throwable t) { unexpected(t); }
2030 
2031         //----------------------------------------------------------------
2032         // Check that subprocesses which create subprocesses of their
2033         // own do not cause parent to hang waiting for file
2034         // descriptors to be closed.
2035         //----------------------------------------------------------------
2036         try {
2037             if (Unix.is()
2038                 && new File("/bin/bash").exists()
2039                 && new File("/bin/sleep").exists()) {
2040                 // Notice that we only destroy the process created by us (i.e.
2041                 // our child) but not our grandchild (i.e. '/bin/sleep'). So
2042                 // pay attention that the grandchild doesn't run too long to
2043                 // avoid polluting the process space with useless processes.
2044                 // Running the grandchild for 60s should be more than enough.
2045                 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" };
2046                 final String[] cmdkill = { "/bin/bash", "-c", "(/usr/bin/pkill -f \"sleep 60\")" };
2047                 final ProcessBuilder pb = new ProcessBuilder(cmd);
2048                 final Process p = pb.start();
2049                 final InputStream stdout = p.getInputStream();
2050                 final InputStream stderr = p.getErrorStream();
2051                 final OutputStream stdin = p.getOutputStream();
2052                 final Thread reader = new Thread() {
2053                     public void run() {
2054                         try { stdout.read(); }
2055                         catch (IOException e) {
2056                             // Check that reader failed because stream was
2057                             // asynchronously closed.
2058                             // e.printStackTrace();
2059                             if (EnglishUnix.is() &&
2060                                 ! (e.getMessage().matches(".*Bad file.*")))
2061                                 unexpected(e);
2062                         }
2063                         catch (Throwable t) { unexpected(t); }}};
2064                 reader.setDaemon(true);
2065                 reader.start();
2066                 Thread.sleep(100);
2067                 p.destroy();
2068                 check(p.waitFor() != 0);
2069                 check(p.exitValue() != 0);
2070                 // Subprocess is now dead, but file descriptors remain open.
2071                 // Make sure the test will fail if we don't manage to close
2072                 // the open streams within 30 seconds. Notice that this time
2073                 // must be shorter than the sleep time of the grandchild.
2074                 Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true);
2075                 t.schedule(new TimerTask() {
2076                       public void run() {
2077                           fail("Subprocesses which create subprocesses of " +
2078                                "their own caused the parent to hang while " +
2079                                "waiting for file descriptors to be closed.");
2080                           System.exit(-1);
2081                       }
2082                   }, 30000);
2083                 stdout.close();
2084                 stderr.close();
2085                 stdin.close();
2086                 new ProcessBuilder(cmdkill).start();
2087                 // All streams successfully closed so we can cancel the timer.
2088                 t.cancel();
2089                 //----------------------------------------------------------
2090                 // There remain unsolved issues with asynchronous close.
2091                 // Here's a highly non-portable experiment to demonstrate:
2092                 //----------------------------------------------------------
2093                 if (Boolean.getBoolean("wakeupJeff!")) {
2094                     System.out.println("wakeupJeff!");
2095                     // Initialize signal handler for INTERRUPT_SIGNAL.
2096                     new FileInputStream("/bin/sleep").getChannel().close();
2097                     // Send INTERRUPT_SIGNAL to every thread in this java.
2098                     String[] wakeupJeff = {
2099                         "/bin/bash", "-c",
2100                         "/bin/ps --noheaders -Lfp $PPID | " +
2101                         "/usr/bin/perl -nale 'print $F[3]' | " +
2102                         // INTERRUPT_SIGNAL == 62 on my machine du jour.
2103                         "/usr/bin/xargs kill -62"
2104                     };
2105                     new ProcessBuilder(wakeupJeff).start().waitFor();
2106                     // If wakeupJeff worked, reader probably got EBADF.
2107                     reader.join();
2108                 }
2109             }
2110         } catch (Throwable t) { unexpected(t); }
2111 
2112         //----------------------------------------------------------------
2113         // Attempt to start process with insufficient permissions fails.
2114         //----------------------------------------------------------------
2115         try {
2116             new File("emptyCommand").delete();
2117             new FileOutputStream("emptyCommand").close();
2118             new File("emptyCommand").setExecutable(false);
2119             new ProcessBuilder("./emptyCommand").start();
2120             fail("Expected IOException not thrown");
2121         } catch (IOException e) {
2122             new File("./emptyCommand").delete();
2123             String m = e.getMessage();
2124             if (EnglishUnix.is() &&
2125                 ! matches(m, "Permission denied"))
2126                 unexpected(e);
2127         } catch (Throwable t) { unexpected(t); }
2128 
2129         new File("emptyCommand").delete();
2130 
2131         //----------------------------------------------------------------
2132         // Check for correct security permission behavior
2133         //----------------------------------------------------------------
2134         final Policy policy = new Policy();
2135         Policy.setPolicy(policy);
2136         System.setSecurityManager(new SecurityManager());
2137 
2138         try {
2139             // No permissions required to CREATE a ProcessBuilder
2140             policy.setPermissions(/* Nothing */);
2141             new ProcessBuilder("env").directory(null).directory();
2142             new ProcessBuilder("env").directory(new File("dir")).directory();
2143             new ProcessBuilder("env").command("??").command();
2144         } catch (Throwable t) { unexpected(t); }
2145 
2146         THROWS(SecurityException.class,
2147                () -> { policy.setPermissions(/* Nothing */);
2148                        System.getenv("foo");},
2149                () -> { policy.setPermissions(/* Nothing */);
2150                        System.getenv();},
2151                () -> { policy.setPermissions(/* Nothing */);
2152                        new ProcessBuilder("echo").start();},
2153                () -> { policy.setPermissions(/* Nothing */);
2154                        Runtime.getRuntime().exec("echo");},
2155                () -> { policy.setPermissions(
2156                                new RuntimePermission("getenv.bar"));
2157                        System.getenv("foo");});
2158 
2159         try {
2160             policy.setPermissions(new RuntimePermission("getenv.foo"));
2161             System.getenv("foo");
2162 
2163             policy.setPermissions(new RuntimePermission("getenv.*"));
2164             System.getenv("foo");
2165             System.getenv();
2166             new ProcessBuilder().environment();
2167         } catch (Throwable t) { unexpected(t); }
2168 
2169 
2170         final Permission execPermission
2171             = new FilePermission("<<ALL FILES>>", "execute");
2172 
2173         THROWS(SecurityException.class,
2174                () -> { // environment permission by itself insufficient
2175                        policy.setPermissions(new RuntimePermission("getenv.*"));
2176                        ProcessBuilder pb = new ProcessBuilder("env");
2177                        pb.environment().put("foo","bar");
2178                        pb.start();},
2179                () -> { // exec permission by itself insufficient
2180                        policy.setPermissions(execPermission);
2181                        ProcessBuilder pb = new ProcessBuilder("env");
2182                        pb.environment().put("foo","bar");
2183                        pb.start();});
2184 
2185         try {
2186             // Both permissions? OK.
2187             policy.setPermissions(new RuntimePermission("getenv.*"),
2188                                   execPermission);
2189             ProcessBuilder pb = new ProcessBuilder("env");
2190             pb.environment().put("foo","bar");
2191             Process p = pb.start();
2192             closeStreams(p);
2193         } catch (IOException e) { // OK
2194         } catch (Throwable t) { unexpected(t); }
2195 
2196         try {
2197             // Don't need environment permission unless READING environment
2198             policy.setPermissions(execPermission);
2199             Runtime.getRuntime().exec("env", new String[]{});
2200         } catch (IOException e) { // OK
2201         } catch (Throwable t) { unexpected(t); }
2202 
2203         try {
2204             // Don't need environment permission unless READING environment
2205             policy.setPermissions(execPermission);
2206             new ProcessBuilder("env").start();
2207         } catch (IOException e) { // OK
2208         } catch (Throwable t) { unexpected(t); }
2209 
2210         // Restore "normal" state without a security manager
2211         policy.setPermissions(new RuntimePermission("setSecurityManager"));
2212         System.setSecurityManager(null);
2213 
2214         //----------------------------------------------------------------
2215         // Check that Process.isAlive() &
2216         // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected.
2217         //----------------------------------------------------------------
2218         try {
2219             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2220             childArgs.add("sleep");
2221             final Process p = new ProcessBuilder(childArgs).start();
2222             long start = System.nanoTime();
2223             if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) {
2224                 fail("Test failed: Process exited prematurely");
2225             }
2226             long end = System.nanoTime();
2227             // give waitFor(timeout) a wide berth (2s)
2228             System.out.printf(" waitFor process: delta: %d%n",(end - start) );
2229 
2230             if ((end - start) > TimeUnit.SECONDS.toNanos(2))
2231                 fail("Test failed: waitFor took too long (" + (end - start) + "ns)");
2232 
2233             p.destroy();
2234             p.waitFor();
2235 
2236             if (p.isAlive() ||
2237                 !p.waitFor(0, TimeUnit.MILLISECONDS))
2238             {
2239                 fail("Test failed: Process still alive - please terminate " +
2240                     p.toString() + " manually");
2241             }
2242         } catch (Throwable t) { unexpected(t); }
2243 
2244         //----------------------------------------------------------------
2245         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2246         // works as expected.
2247         //----------------------------------------------------------------
2248         try {
2249             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2250             childArgs.add("sleep");
2251             final Process p = new ProcessBuilder(childArgs).start();
2252             long start = System.nanoTime();
2253 
2254             p.waitFor(10, TimeUnit.MILLISECONDS);
2255 
2256             long end = System.nanoTime();
2257             if ((end - start) < TimeUnit.MILLISECONDS.toNanos(10))
2258                 fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)");
2259 
2260             p.destroy();
2261         } catch (Throwable t) { unexpected(t); }
2262 
2263         //----------------------------------------------------------------
2264         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2265         // interrupt works as expected, if interrupted while waiting.
2266         //----------------------------------------------------------------
2267         try {
2268             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2269             childArgs.add("sleep");
2270             final Process p = new ProcessBuilder(childArgs).start();
2271             final long start = System.nanoTime();
2272             final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
2273 
2274             final Thread thread = new Thread() {
2275                 public void run() {
2276                     try {
2277                         aboutToWaitFor.countDown();
2278                         Thread.currentThread().interrupt();
2279                         boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);
2280                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2281                     } catch (InterruptedException success) {
2282                     } catch (Throwable t) { unexpected(t); }
2283                 }
2284             };
2285 
2286             thread.start();
2287             aboutToWaitFor.await();
2288             thread.interrupt();
2289             thread.join(10L * 1000L);
2290             check(millisElapsedSince(start) < 10L * 1000L);
2291             check(!thread.isAlive());
2292             p.destroy();
2293         } catch (Throwable t) { unexpected(t); }
2294 
2295         //----------------------------------------------------------------
2296         // Check that Process.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS)
2297         // interrupt works as expected, if interrupted while waiting.
2298         //----------------------------------------------------------------
2299         try {
2300             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2301             childArgs.add("sleep");
2302             final Process p = new ProcessBuilder(childArgs).start();
2303             final long start = System.nanoTime();
2304             final CountDownLatch aboutToWaitFor = new CountDownLatch(1);
2305 
2306             final Thread thread = new Thread() {
2307                 public void run() {
2308                     try {
2309                         aboutToWaitFor.countDown();
2310                         Thread.currentThread().interrupt();
2311                         boolean result = p.waitFor(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
2312                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2313                     } catch (InterruptedException success) {
2314                     } catch (Throwable t) { unexpected(t); }
2315                 }
2316             };
2317 
2318             thread.start();
2319             aboutToWaitFor.await();
2320             thread.interrupt();
2321             thread.join(10L * 1000L);
2322             check(millisElapsedSince(start) < 10L * 1000L);
2323             check(!thread.isAlive());
2324             p.destroy();
2325         } catch (Throwable t) { unexpected(t); }
2326 
2327         //----------------------------------------------------------------
2328         // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS)
2329         // interrupt works as expected, if interrupted before waiting.
2330         //----------------------------------------------------------------
2331         try {
2332             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2333             childArgs.add("sleep");
2334             final Process p = new ProcessBuilder(childArgs).start();
2335             final long start = System.nanoTime();
2336             final CountDownLatch threadStarted = new CountDownLatch(1);
2337 
2338             final Thread thread = new Thread() {
2339                 public void run() {
2340                     try {
2341                         threadStarted.countDown();
2342                         do { Thread.yield(); }
2343                         while (!Thread.currentThread().isInterrupted());
2344                         boolean result = p.waitFor(30L * 1000L, TimeUnit.MILLISECONDS);
2345                         fail("waitFor() wasn't interrupted, its return value was: " + result);
2346                     } catch (InterruptedException success) {
2347                     } catch (Throwable t) { unexpected(t); }
2348                 }
2349             };
2350 
2351             thread.start();
2352             threadStarted.await();
2353             thread.interrupt();
2354             thread.join(10L * 1000L);
2355             check(millisElapsedSince(start) < 10L * 1000L);
2356             check(!thread.isAlive());
2357             p.destroy();
2358         } catch (Throwable t) { unexpected(t); }
2359 
2360         //----------------------------------------------------------------
2361         // Check that Process.waitFor(timeout, null) throws NPE.
2362         //----------------------------------------------------------------
2363         try {
2364             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2365             childArgs.add("sleep");
2366             final Process p = new ProcessBuilder(childArgs).start();
2367             THROWS(NullPointerException.class,
2368                     () ->  p.waitFor(10L, null));
2369             THROWS(NullPointerException.class,
2370                     () ->  p.waitFor(0L, null));
2371             THROWS(NullPointerException.class,
2372                     () -> p.waitFor(-1L, null));
2373             // Terminate process and recheck after it exits
2374             p.destroy();
2375             p.waitFor();
2376             THROWS(NullPointerException.class,
2377                     () -> p.waitFor(10L, null));
2378             THROWS(NullPointerException.class,
2379                     () -> p.waitFor(0L, null));
2380             THROWS(NullPointerException.class,
2381                     () -> p.waitFor(-1L, null));
2382         } catch (Throwable t) { unexpected(t); }
2383 
2384         //----------------------------------------------------------------
2385         // Check that default implementation of Process.waitFor(timeout, null) throws NPE.
2386         //----------------------------------------------------------------
2387         try {
2388             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2389             childArgs.add("sleep");
2390             final Process proc = new ProcessBuilder(childArgs).start();
2391             final DelegatingProcess p = new DelegatingProcess(proc);
2392 
2393             THROWS(NullPointerException.class,
2394                     () ->  p.waitFor(10L, null));
2395             THROWS(NullPointerException.class,
2396                     () ->  p.waitFor(0L, null));
2397             THROWS(NullPointerException.class,
2398                     () ->  p.waitFor(-1L, null));
2399             // Terminate process and recheck after it exits
2400             p.destroy();
2401             p.waitFor();
2402             THROWS(NullPointerException.class,
2403                     () -> p.waitFor(10L, null));
2404             THROWS(NullPointerException.class,
2405                     () -> p.waitFor(0L, null));
2406             THROWS(NullPointerException.class,
2407                     () -> p.waitFor(-1L, null));
2408         } catch (Throwable t) { unexpected(t); }
2409 
2410         //----------------------------------------------------------------
2411         // Check the default implementation for
2412         // Process.waitFor(long, TimeUnit)
2413         //----------------------------------------------------------------
2414         try {
2415             List<String> childArgs = new ArrayList<String>(javaChildArgs);
2416             childArgs.add("sleep");
2417             final Process proc = new ProcessBuilder(childArgs).start();
2418             DelegatingProcess p = new DelegatingProcess(proc);
2419             long start = System.nanoTime();
2420 
2421             p.waitFor(1000, TimeUnit.MILLISECONDS);
2422 
2423             long end = System.nanoTime();
2424             if ((end - start) < 500000000)
2425                 fail("Test failed: waitFor didn't take long enough");
2426 
2427             p.destroy();
2428 
2429             p.waitFor(1000, TimeUnit.MILLISECONDS);
2430         } catch (Throwable t) { unexpected(t); }
2431     }
2432 
closeStreams(Process p)2433     static void closeStreams(Process p) {
2434         try {
2435             p.getOutputStream().close();
2436             p.getInputStream().close();
2437             p.getErrorStream().close();
2438         } catch (Throwable t) { unexpected(t); }
2439     }
2440 
2441     //----------------------------------------------------------------
2442     // A Policy class designed to make permissions fiddling very easy.
2443     //----------------------------------------------------------------
2444     private static class Policy extends java.security.Policy {
2445         private Permissions perms;
2446 
setPermissions(Permission...permissions)2447         public void setPermissions(Permission...permissions) {
2448             perms = new Permissions();
2449             for (Permission permission : permissions)
2450                 perms.add(permission);
2451         }
2452 
Policy()2453         public Policy() { setPermissions(/* Nothing */); }
2454 
getPermissions(CodeSource cs)2455         public PermissionCollection getPermissions(CodeSource cs) {
2456             return perms;
2457         }
2458 
getPermissions(ProtectionDomain pd)2459         public PermissionCollection getPermissions(ProtectionDomain pd) {
2460             return perms;
2461         }
2462 
implies(ProtectionDomain pd, Permission p)2463         public boolean implies(ProtectionDomain pd, Permission p) {
2464             return perms.implies(p);
2465         }
2466 
refresh()2467         public void refresh() {}
2468     }
2469 
2470     private static class StreamAccumulator extends Thread {
2471         private final InputStream is;
2472         private final StringBuilder sb = new StringBuilder();
2473         private Throwable throwable = null;
2474 
result()2475         public String result () throws Throwable {
2476             if (throwable != null)
2477                 throw throwable;
2478             return sb.toString();
2479         }
2480 
StreamAccumulator(InputStream is)2481         StreamAccumulator (InputStream is) {
2482             this.is = is;
2483         }
2484 
run()2485         public void run() {
2486             try {
2487                 Reader r = new InputStreamReader(is);
2488                 char[] buf = new char[4096];
2489                 int n;
2490                 while ((n = r.read(buf)) > 0) {
2491                     sb.append(buf,0,n);
2492                 }
2493             } catch (Throwable t) {
2494                 throwable = t;
2495             } finally {
2496                 try { is.close(); }
2497                 catch (Throwable t) { throwable = t; }
2498             }
2499         }
2500     }
2501 
run(ProcessBuilder pb)2502     static ProcessResults run(ProcessBuilder pb) {
2503         try {
2504             return run(pb.start());
2505         } catch (Throwable t) { unexpected(t); return null; }
2506     }
2507 
run(Process p)2508     private static ProcessResults run(Process p) {
2509         Throwable throwable = null;
2510         int exitValue = -1;
2511         String out = "";
2512         String err = "";
2513 
2514         StreamAccumulator outAccumulator =
2515             new StreamAccumulator(p.getInputStream());
2516         StreamAccumulator errAccumulator =
2517             new StreamAccumulator(p.getErrorStream());
2518 
2519         try {
2520             outAccumulator.start();
2521             errAccumulator.start();
2522 
2523             exitValue = p.waitFor();
2524 
2525             outAccumulator.join();
2526             errAccumulator.join();
2527 
2528             out = outAccumulator.result();
2529             err = errAccumulator.result();
2530         } catch (Throwable t) {
2531             throwable = t;
2532         }
2533 
2534         return new ProcessResults(out, err, exitValue, throwable);
2535     }
2536 
2537     //----------------------------------------------------------------
2538     // Results of a command
2539     //----------------------------------------------------------------
2540     private static class ProcessResults {
2541         private final String out;
2542         private final String err;
2543         private final int exitValue;
2544         private final Throwable throwable;
2545 
ProcessResults(String out, String err, int exitValue, Throwable throwable)2546         public ProcessResults(String out,
2547                               String err,
2548                               int exitValue,
2549                               Throwable throwable) {
2550             this.out = out;
2551             this.err = err;
2552             this.exitValue = exitValue;
2553             this.throwable = throwable;
2554         }
2555 
out()2556         public String out()          { return out; }
err()2557         public String err()          { return err; }
exitValue()2558         public int exitValue()       { return exitValue; }
throwable()2559         public Throwable throwable() { return throwable; }
2560 
toString()2561         public String toString() {
2562             StringBuilder sb = new StringBuilder();
2563             sb.append("<STDOUT>\n" + out() + "</STDOUT>\n")
2564                 .append("<STDERR>\n" + err() + "</STDERR>\n")
2565                 .append("exitValue = " + exitValue + "\n");
2566             if (throwable != null)
2567                 sb.append(throwable.getStackTrace());
2568             return sb.toString();
2569         }
2570     }
2571 
2572     //--------------------- Infrastructure ---------------------------
2573     static volatile int passed = 0, failed = 0;
pass()2574     static void pass() {passed++;}
fail()2575     static void fail() {failed++; Thread.dumpStack();}
fail(String msg)2576     static void fail(String msg) {System.out.println(msg); fail();}
unexpected(Throwable t)2577     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
check(boolean cond)2578     static void check(boolean cond) {if (cond) pass(); else fail();}
check(boolean cond, String m)2579     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
equal(Object x, Object y)2580     static void equal(Object x, Object y) {
2581         if (x == null ? y == null : x.equals(y)) pass();
2582         else fail(x + " not equal to " + y);}
2583 
main(String[] args)2584     public static void main(String[] args) throws Throwable {
2585         try {realMain(args);} catch (Throwable t) {unexpected(t);}
2586         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
2587         if (failed > 0) throw new AssertionError("Some tests failed");}
f()2588     interface Fun {void f() throws Throwable;}
THROWS(Class<? extends Throwable> k, Fun... fs)2589     static void THROWS(Class<? extends Throwable> k, Fun... fs) {
2590         for (Fun f : fs)
2591             try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
2592             catch (Throwable t) {
2593                 if (k.isAssignableFrom(t.getClass())) pass();
2594                 else unexpected(t);}}
2595 }
2596