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