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