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(new String[] 572 { javaExe, "-classpath", absolutifyPath(classpath), 573 "Basic$JavaChild"}); 574 575 private static void testEncoding(String encoding, String tested) { 576 try { 577 // If round trip conversion works, should be able to set env vars 578 // correctly in child. 579 if (new String(tested.getBytes()).equals(tested)) { 580 out.println("Testing " + encoding + " environment values"); 581 ProcessBuilder pb = new ProcessBuilder(); 582 pb.environment().put("ASCIINAME",tested); 583 equal(getenvInChild(pb,"ASCIINAME"), tested); 584 } 585 } catch (Throwable t) { unexpected(t); } 586 } 587 588 static class Windows { 589 public static boolean is() { return is; } 590 private static final boolean is = 591 System.getProperty("os.name").startsWith("Windows"); 592 } 593 594 static class AIX { 595 public static boolean is() { return is; } 596 private static final boolean is = 597 System.getProperty("os.name").equals("AIX"); 598 } 599 600 static class Unix { 601 public static boolean is() { return is; } 602 private static final boolean is = 603 (! Windows.is() && 604 new File("/bin/sh").exists() && 605 new File("/bin/true").exists() && 606 new File("/bin/false").exists()); 607 } 608 609 static class UnicodeOS { 610 public static boolean is() { return is; } 611 private static final String osName = System.getProperty("os.name"); 612 private static final boolean is = 613 // MacOS X would probably also qualify 614 osName.startsWith("Windows") && 615 ! osName.startsWith("Windows 9") && 616 ! osName.equals("Windows Me"); 617 } 618 619 static class MacOSX { 620 public static boolean is() { return is; } 621 private static final String osName = System.getProperty("os.name"); 622 private static final boolean is = osName.contains("OS X"); 623 } 624 625 static class True { 626 public static int exitValue() { return 0; } 627 } 628 629 private static class False { 630 public static int exitValue() { return exitValue; } 631 private static final int exitValue = exitValue0(); 632 private static int exitValue0() { 633 // /bin/false returns an *unspecified* non-zero number. 634 try { 635 if (! Unix.is()) 636 return -1; 637 else { 638 int rc = new ProcessBuilder("/bin/false") 639 .start().waitFor(); 640 check(rc != 0); 641 return rc; 642 } 643 } catch (Throwable t) { unexpected(t); return -1; } 644 } 645 } 646 647 static class EnglishUnix { 648 private final static Boolean is = 649 (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL")); 650 651 private static boolean isEnglish(String envvar) { 652 String val = getenv(envvar); 653 return (val == null) || val.matches("en.*") || val.matches("C"); 654 } 655 656 /** Returns true if we can expect English OS error strings */ 657 static boolean is() { return is; } 658 } 659 660 static class DelegatingProcess extends Process { 661 final Process p; 662 663 DelegatingProcess(Process p) { 664 this.p = p; 665 } 666 667 @Override 668 public void destroy() { 669 p.destroy(); 670 } 671 672 @Override 673 public int exitValue() { 674 return p.exitValue(); 675 } 676 677 @Override 678 public int waitFor() throws InterruptedException { 679 return p.waitFor(); 680 } 681 682 @Override 683 public OutputStream getOutputStream() { 684 return p.getOutputStream(); 685 } 686 687 @Override 688 public InputStream getInputStream() { 689 return p.getInputStream(); 690 } 691 692 @Override 693 public InputStream getErrorStream() { 694 return p.getErrorStream(); 695 } 696 } 697 698 private static boolean matches(String str, String regex) { 699 return Pattern.compile(regex).matcher(str).find(); 700 } 701 702 private static String matchAndExtract(String str, String regex) { 703 Matcher matcher = Pattern.compile(regex).matcher(str); 704 if (matcher.find()) { 705 return matcher.group(); 706 } else { 707 return ""; 708 } 709 } 710 711 /* Only used for Mac OS X -- 712 * Mac OS X (may) add the variable __CF_USER_TEXT_ENCODING to an empty 713 * environment. The environment variable JAVA_MAIN_CLASS_<pid> may also 714 * be set in Mac OS X. 715 * Remove them both from the list of env variables 716 */ 717 private static String removeMacExpectedVars(String vars) { 718 // Check for __CF_USER_TEXT_ENCODING 719 String cleanedVars = vars.replace("__CF_USER_TEXT_ENCODING=" 720 +cfUserTextEncoding+",",""); 721 // Check for JAVA_MAIN_CLASS_<pid> 722 String javaMainClassStr 723 = matchAndExtract(cleanedVars, 724 "JAVA_MAIN_CLASS_\\d+=Basic.JavaChild,"); 725 return cleanedVars.replace(javaMainClassStr,""); 726 } 727 728 /* Only used for AIX -- 729 * AIX adds the variable AIXTHREAD_GUARDPAGES=0 to the environment. 730 * Remove it from the list of env variables 731 */ 732 private static String removeAixExpectedVars(String vars) { 733 return vars.replace("AIXTHREAD_GUARDPAGES=0,",""); 734 } 735 736 private static String sortByLinesWindowsly(String text) { 737 String[] lines = text.split("\n"); 738 Arrays.sort(lines, new WindowsComparator()); 739 StringBuilder sb = new StringBuilder(); 740 for (String line : lines) 741 sb.append(line).append("\n"); 742 return sb.toString(); 743 } 744 745 private static void checkMapSanity(Map<String,String> map) { 746 try { 747 Set<String> keySet = map.keySet(); 748 Collection<String> values = map.values(); 749 Set<Map.Entry<String,String>> entrySet = map.entrySet(); 750 751 equal(entrySet.size(), keySet.size()); 752 equal(entrySet.size(), values.size()); 753 754 StringBuilder s1 = new StringBuilder(); 755 for (Map.Entry<String,String> e : entrySet) 756 s1.append(e.getKey() + "=" + e.getValue() + "\n"); 757 758 StringBuilder s2 = new StringBuilder(); 759 for (String var : keySet) 760 s2.append(var + "=" + map.get(var) + "\n"); 761 762 equal(s1.toString(), s2.toString()); 763 764 Iterator<String> kIter = keySet.iterator(); 765 Iterator<String> vIter = values.iterator(); 766 Iterator<Map.Entry<String,String>> eIter = entrySet.iterator(); 767 768 while (eIter.hasNext()) { 769 Map.Entry<String,String> entry = eIter.next(); 770 String key = kIter.next(); 771 String value = vIter.next(); 772 check(entrySet.contains(entry)); 773 check(keySet.contains(key)); 774 check(values.contains(value)); 775 check(map.containsKey(key)); 776 check(map.containsValue(value)); 777 equal(entry.getKey(), key); 778 equal(entry.getValue(), value); 779 } 780 check(! kIter.hasNext() && 781 ! vIter.hasNext()); 782 783 } catch (Throwable t) { unexpected(t); } 784 } 785 786 private static void checkMapEquality(Map<String,String> map1, 787 Map<String,String> map2) { 788 try { 789 equal(map1.size(), map2.size()); 790 equal(map1.isEmpty(), map2.isEmpty()); 791 for (String key : map1.keySet()) { 792 equal(map1.get(key), map2.get(key)); 793 check(map2.keySet().contains(key)); 794 } 795 equal(map1, map2); 796 equal(map2, map1); 797 equal(map1.entrySet(), map2.entrySet()); 798 equal(map2.entrySet(), map1.entrySet()); 799 equal(map1.keySet(), map2.keySet()); 800 equal(map2.keySet(), map1.keySet()); 801 802 equal(map1.hashCode(), map2.hashCode()); 803 equal(map1.entrySet().hashCode(), map2.entrySet().hashCode()); 804 equal(map1.keySet().hashCode(), map2.keySet().hashCode()); 805 } catch (Throwable t) { unexpected(t); } 806 } 807 808 static void checkRedirects(ProcessBuilder pb, 809 Redirect in, Redirect out, Redirect err) { 810 equal(pb.redirectInput(), in); 811 equal(pb.redirectOutput(), out); 812 equal(pb.redirectError(), err); 813 } 814 815 static void redirectIO(ProcessBuilder pb, 816 Redirect in, Redirect out, Redirect err) { 817 pb.redirectInput(in); 818 pb.redirectOutput(out); 819 pb.redirectError(err); 820 } 821 822 static void setFileContents(File file, String contents) { 823 try { 824 Writer w = new FileWriter(file); 825 w.write(contents); 826 w.close(); 827 } catch (Throwable t) { unexpected(t); } 828 } 829 830 static String fileContents(File file) { 831 try { 832 Reader r = new FileReader(file); 833 StringBuilder sb = new StringBuilder(); 834 char[] buffer = new char[1024]; 835 int n; 836 while ((n = r.read(buffer)) != -1) 837 sb.append(buffer,0,n); 838 r.close(); 839 return new String(sb); 840 } catch (Throwable t) { unexpected(t); return ""; } 841 } 842 843 static void testIORedirection() throws Throwable { 844 final File ifile = new File("ifile"); 845 final File ofile = new File("ofile"); 846 final File efile = new File("efile"); 847 ifile.delete(); 848 ofile.delete(); 849 efile.delete(); 850 851 //---------------------------------------------------------------- 852 // Check mutual inequality of different types of Redirect 853 //---------------------------------------------------------------- 854 Redirect[] redirects = 855 { PIPE, 856 INHERIT, 857 Redirect.from(ifile), 858 Redirect.to(ifile), 859 Redirect.appendTo(ifile), 860 Redirect.from(ofile), 861 Redirect.to(ofile), 862 Redirect.appendTo(ofile), 863 }; 864 for (int i = 0; i < redirects.length; i++) 865 for (int j = 0; j < redirects.length; j++) 866 equal(redirects[i].equals(redirects[j]), (i == j)); 867 868 //---------------------------------------------------------------- 869 // Check basic properties of different types of Redirect 870 //---------------------------------------------------------------- 871 equal(PIPE.type(), Redirect.Type.PIPE); 872 equal(PIPE.toString(), "PIPE"); 873 equal(PIPE.file(), null); 874 875 equal(INHERIT.type(), Redirect.Type.INHERIT); 876 equal(INHERIT.toString(), "INHERIT"); 877 equal(INHERIT.file(), null); 878 879 equal(Redirect.from(ifile).type(), Redirect.Type.READ); 880 equal(Redirect.from(ifile).toString(), 881 "redirect to read from file \"ifile\""); 882 equal(Redirect.from(ifile).file(), ifile); 883 equal(Redirect.from(ifile), 884 Redirect.from(ifile)); 885 equal(Redirect.from(ifile).hashCode(), 886 Redirect.from(ifile).hashCode()); 887 888 equal(Redirect.to(ofile).type(), Redirect.Type.WRITE); 889 equal(Redirect.to(ofile).toString(), 890 "redirect to write to file \"ofile\""); 891 equal(Redirect.to(ofile).file(), ofile); 892 equal(Redirect.to(ofile), 893 Redirect.to(ofile)); 894 equal(Redirect.to(ofile).hashCode(), 895 Redirect.to(ofile).hashCode()); 896 897 equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND); 898 equal(Redirect.appendTo(efile).toString(), 899 "redirect to append to file \"efile\""); 900 equal(Redirect.appendTo(efile).file(), efile); 901 equal(Redirect.appendTo(efile), 902 Redirect.appendTo(efile)); 903 equal(Redirect.appendTo(efile).hashCode(), 904 Redirect.appendTo(efile).hashCode()); 905 906 //---------------------------------------------------------------- 907 // Check initial values of redirects 908 //---------------------------------------------------------------- 909 List<String> childArgs = new ArrayList<String>(javaChildArgs); 910 childArgs.add("testIO"); 911 final ProcessBuilder pb = new ProcessBuilder(childArgs); 912 checkRedirects(pb, PIPE, PIPE, PIPE); 913 914 //---------------------------------------------------------------- 915 // Check inheritIO 916 //---------------------------------------------------------------- 917 pb.inheritIO(); 918 checkRedirects(pb, INHERIT, INHERIT, INHERIT); 919 920 //---------------------------------------------------------------- 921 // Check setters and getters agree 922 //---------------------------------------------------------------- 923 pb.redirectInput(ifile); 924 equal(pb.redirectInput().file(), ifile); 925 equal(pb.redirectInput(), Redirect.from(ifile)); 926 927 pb.redirectOutput(ofile); 928 equal(pb.redirectOutput().file(), ofile); 929 equal(pb.redirectOutput(), Redirect.to(ofile)); 930 931 pb.redirectError(efile); 932 equal(pb.redirectError().file(), efile); 933 equal(pb.redirectError(), Redirect.to(efile)); 934 935 THROWS(IllegalArgumentException.class, 936 new Fun(){void f() { 937 pb.redirectInput(Redirect.to(ofile)); }}, 938 new Fun(){void f() { 939 pb.redirectInput(Redirect.appendTo(ofile)); }}, 940 new Fun(){void f() { 941 pb.redirectOutput(Redirect.from(ifile)); }}, 942 new Fun(){void f() { 943 pb.redirectError(Redirect.from(ifile)); }}); 944 945 THROWS(IOException.class, 946 // Input file does not exist 947 new Fun(){void f() throws Throwable { pb.start(); }}); 948 setFileContents(ifile, "standard input"); 949 950 //---------------------------------------------------------------- 951 // Writing to non-existent files 952 //---------------------------------------------------------------- 953 { 954 ProcessResults r = run(pb); 955 equal(r.exitValue(), 0); 956 equal(fileContents(ofile), "standard output"); 957 equal(fileContents(efile), "standard error"); 958 equal(r.out(), ""); 959 equal(r.err(), ""); 960 ofile.delete(); 961 efile.delete(); 962 } 963 964 //---------------------------------------------------------------- 965 // Both redirectErrorStream + redirectError 966 //---------------------------------------------------------------- 967 { 968 pb.redirectErrorStream(true); 969 ProcessResults r = run(pb); 970 equal(r.exitValue(), 0); 971 equal(fileContents(ofile), 972 "standard error" + "standard output"); 973 equal(fileContents(efile), ""); 974 equal(r.out(), ""); 975 equal(r.err(), ""); 976 ofile.delete(); 977 efile.delete(); 978 } 979 980 //---------------------------------------------------------------- 981 // Appending to existing files 982 //---------------------------------------------------------------- 983 { 984 setFileContents(ofile, "ofile-contents"); 985 setFileContents(efile, "efile-contents"); 986 pb.redirectOutput(Redirect.appendTo(ofile)); 987 pb.redirectError(Redirect.appendTo(efile)); 988 pb.redirectErrorStream(false); 989 ProcessResults r = run(pb); 990 equal(r.exitValue(), 0); 991 equal(fileContents(ofile), 992 "ofile-contents" + "standard output"); 993 equal(fileContents(efile), 994 "efile-contents" + "standard error"); 995 equal(r.out(), ""); 996 equal(r.err(), ""); 997 ofile.delete(); 998 efile.delete(); 999 } 1000 1001 //---------------------------------------------------------------- 1002 // Replacing existing files 1003 //---------------------------------------------------------------- 1004 { 1005 setFileContents(ofile, "ofile-contents"); 1006 setFileContents(efile, "efile-contents"); 1007 pb.redirectOutput(ofile); 1008 pb.redirectError(Redirect.to(efile)); 1009 ProcessResults r = run(pb); 1010 equal(r.exitValue(), 0); 1011 equal(fileContents(ofile), "standard output"); 1012 equal(fileContents(efile), "standard error"); 1013 equal(r.out(), ""); 1014 equal(r.err(), ""); 1015 ofile.delete(); 1016 efile.delete(); 1017 } 1018 1019 //---------------------------------------------------------------- 1020 // Appending twice to the same file? 1021 //---------------------------------------------------------------- 1022 { 1023 setFileContents(ofile, "ofile-contents"); 1024 setFileContents(efile, "efile-contents"); 1025 Redirect appender = Redirect.appendTo(ofile); 1026 pb.redirectOutput(appender); 1027 pb.redirectError(appender); 1028 ProcessResults r = run(pb); 1029 equal(r.exitValue(), 0); 1030 equal(fileContents(ofile), 1031 "ofile-contents" + 1032 "standard error" + 1033 "standard output"); 1034 equal(fileContents(efile), "efile-contents"); 1035 equal(r.out(), ""); 1036 equal(r.err(), ""); 1037 ifile.delete(); 1038 ofile.delete(); 1039 efile.delete(); 1040 } 1041 1042 //---------------------------------------------------------------- 1043 // Testing INHERIT is harder. 1044 // Note that this requires __FOUR__ nested JVMs involved in one test, 1045 // if you count the harness JVM. 1046 //---------------------------------------------------------------- 1047 for (String testName : new String[] { "testInheritIO", "testRedirectInherit" } ) { 1048 redirectIO(pb, PIPE, PIPE, PIPE); 1049 List<String> command = pb.command(); 1050 command.set(command.size() - 1, testName); 1051 Process p = pb.start(); 1052 new PrintStream(p.getOutputStream()).print("standard input"); 1053 p.getOutputStream().close(); 1054 ProcessResults r = run(p); 1055 equal(r.exitValue(), 0); 1056 equal(r.out(), "standard output"); 1057 equal(r.err(), "standard error"); 1058 } 1059 1060 //---------------------------------------------------------------- 1061 // Test security implications of I/O redirection 1062 //---------------------------------------------------------------- 1063 1064 // Read access to current directory is always granted; 1065 // So create a tmpfile for input instead. 1066 final File tmpFile = File.createTempFile("Basic", "tmp"); 1067 setFileContents(tmpFile, "standard input"); 1068 1069 final Policy policy = new Policy(); 1070 Policy.setPolicy(policy); 1071 System.setSecurityManager(new SecurityManager()); 1072 try { 1073 final Permission xPermission 1074 = new FilePermission("<<ALL FILES>>", "execute"); 1075 final Permission rxPermission 1076 = new FilePermission("<<ALL FILES>>", "read,execute"); 1077 final Permission wxPermission 1078 = new FilePermission("<<ALL FILES>>", "write,execute"); 1079 final Permission rwxPermission 1080 = new FilePermission("<<ALL FILES>>", "read,write,execute"); 1081 1082 THROWS(SecurityException.class, 1083 new Fun() { void f() throws IOException { 1084 policy.setPermissions(xPermission); 1085 redirectIO(pb, from(tmpFile), PIPE, PIPE); 1086 pb.start();}}, 1087 new Fun() { void f() throws IOException { 1088 policy.setPermissions(rxPermission); 1089 redirectIO(pb, PIPE, to(ofile), PIPE); 1090 pb.start();}}, 1091 new Fun() { void f() throws IOException { 1092 policy.setPermissions(rxPermission); 1093 redirectIO(pb, PIPE, PIPE, to(efile)); 1094 pb.start();}}); 1095 1096 { 1097 policy.setPermissions(rxPermission); 1098 redirectIO(pb, from(tmpFile), PIPE, PIPE); 1099 ProcessResults r = run(pb); 1100 equal(r.out(), "standard output"); 1101 equal(r.err(), "standard error"); 1102 } 1103 1104 { 1105 policy.setPermissions(wxPermission); 1106 redirectIO(pb, PIPE, to(ofile), to(efile)); 1107 Process p = pb.start(); 1108 new PrintStream(p.getOutputStream()).print("standard input"); 1109 p.getOutputStream().close(); 1110 ProcessResults r = run(p); 1111 policy.setPermissions(rwxPermission); 1112 equal(fileContents(ofile), "standard output"); 1113 equal(fileContents(efile), "standard error"); 1114 } 1115 1116 { 1117 policy.setPermissions(rwxPermission); 1118 redirectIO(pb, from(tmpFile), to(ofile), to(efile)); 1119 ProcessResults r = run(pb); 1120 policy.setPermissions(rwxPermission); 1121 equal(fileContents(ofile), "standard output"); 1122 equal(fileContents(efile), "standard error"); 1123 } 1124 1125 } finally { 1126 policy.setPermissions(new RuntimePermission("setSecurityManager")); 1127 System.setSecurityManager(null); 1128 tmpFile.delete(); 1129 ifile.delete(); 1130 ofile.delete(); 1131 efile.delete(); 1132 } 1133 } 1134 1135 private static void realMain(String[] args) throws Throwable { 1136 if (Windows.is()) 1137 System.out.println("This appears to be a Windows system."); 1138 if (Unix.is()) 1139 System.out.println("This appears to be a Unix system."); 1140 if (UnicodeOS.is()) 1141 System.out.println("This appears to be a Unicode-based OS."); 1142 1143 try { testIORedirection(); } 1144 catch (Throwable t) { unexpected(t); } 1145 1146 //---------------------------------------------------------------- 1147 // Basic tests for setting, replacing and deleting envvars 1148 //---------------------------------------------------------------- 1149 try { 1150 ProcessBuilder pb = new ProcessBuilder(); 1151 Map<String,String> environ = pb.environment(); 1152 1153 // New env var 1154 environ.put("QUUX", "BAR"); 1155 equal(environ.get("QUUX"), "BAR"); 1156 equal(getenvInChild(pb,"QUUX"), "BAR"); 1157 1158 // Modify env var 1159 environ.put("QUUX","bear"); 1160 equal(environ.get("QUUX"), "bear"); 1161 equal(getenvInChild(pb,"QUUX"), "bear"); 1162 checkMapSanity(environ); 1163 1164 // Remove env var 1165 environ.remove("QUUX"); 1166 equal(environ.get("QUUX"), null); 1167 equal(getenvInChild(pb,"QUUX"), "null"); 1168 checkMapSanity(environ); 1169 1170 // Remove non-existent env var 1171 environ.remove("QUUX"); 1172 equal(environ.get("QUUX"), null); 1173 equal(getenvInChild(pb,"QUUX"), "null"); 1174 checkMapSanity(environ); 1175 } catch (Throwable t) { unexpected(t); } 1176 1177 //---------------------------------------------------------------- 1178 // Pass Empty environment to child 1179 //---------------------------------------------------------------- 1180 try { 1181 ProcessBuilder pb = new ProcessBuilder(); 1182 pb.environment().clear(); 1183 String expected = Windows.is() ? "SystemRoot="+systemRoot+",": ""; 1184 expected = AIX.is() ? "LIBPATH="+libpath+",": expected; 1185 if (Windows.is()) { 1186 pb.environment().put("SystemRoot", systemRoot); 1187 } 1188 if (AIX.is()) { 1189 pb.environment().put("LIBPATH", libpath); 1190 } 1191 String result = getenvInChild(pb); 1192 if (MacOSX.is()) { 1193 result = removeMacExpectedVars(result); 1194 } 1195 if (AIX.is()) { 1196 result = removeAixExpectedVars(result); 1197 } 1198 equal(result, expected); 1199 } catch (Throwable t) { unexpected(t); } 1200 1201 //---------------------------------------------------------------- 1202 // System.getenv() is read-only. 1203 //---------------------------------------------------------------- 1204 THROWS(UnsupportedOperationException.class, 1205 new Fun(){void f(){ getenv().put("FOO","BAR");}}, 1206 new Fun(){void f(){ getenv().remove("PATH");}}, 1207 new Fun(){void f(){ getenv().keySet().remove("PATH");}}, 1208 new Fun(){void f(){ getenv().values().remove("someValue");}}); 1209 1210 try { 1211 Collection<Map.Entry<String,String>> c = getenv().entrySet(); 1212 if (! c.isEmpty()) 1213 try { 1214 c.iterator().next().setValue("foo"); 1215 fail("Expected UnsupportedOperationException not thrown"); 1216 } catch (UnsupportedOperationException e) {} // OK 1217 } catch (Throwable t) { unexpected(t); } 1218 1219 //---------------------------------------------------------------- 1220 // System.getenv() always returns the same object in our implementation. 1221 //---------------------------------------------------------------- 1222 try { 1223 check(System.getenv() == System.getenv()); 1224 } catch (Throwable t) { unexpected(t); } 1225 1226 //---------------------------------------------------------------- 1227 // You can't create an env var name containing "=", 1228 // or an env var name or value containing NUL. 1229 //---------------------------------------------------------------- 1230 { 1231 final Map<String,String> m = new ProcessBuilder().environment(); 1232 THROWS(IllegalArgumentException.class, 1233 new Fun(){void f(){ m.put("FOO=","BAR");}}, 1234 new Fun(){void f(){ m.put("FOO\u0000","BAR");}}, 1235 new Fun(){void f(){ m.put("FOO","BAR\u0000");}}); 1236 } 1237 1238 //---------------------------------------------------------------- 1239 // Commands must never be null. 1240 //---------------------------------------------------------------- 1241 THROWS(NullPointerException.class, 1242 new Fun(){void f(){ 1243 new ProcessBuilder((List<String>)null);}}, 1244 new Fun(){void f(){ 1245 new ProcessBuilder().command((List<String>)null);}}); 1246 1247 //---------------------------------------------------------------- 1248 // Put in a command; get the same one back out. 1249 //---------------------------------------------------------------- 1250 try { 1251 List<String> command = new ArrayList<String>(); 1252 ProcessBuilder pb = new ProcessBuilder(command); 1253 check(pb.command() == command); 1254 List<String> command2 = new ArrayList<String>(2); 1255 command2.add("foo"); 1256 command2.add("bar"); 1257 pb.command(command2); 1258 check(pb.command() == command2); 1259 pb.command("foo", "bar"); 1260 check(pb.command() != command2 && pb.command().equals(command2)); 1261 pb.command(command2); 1262 command2.add("baz"); 1263 equal(pb.command().get(2), "baz"); 1264 } catch (Throwable t) { unexpected(t); } 1265 1266 //---------------------------------------------------------------- 1267 // Commands must contain at least one element. 1268 //---------------------------------------------------------------- 1269 THROWS(IndexOutOfBoundsException.class, 1270 new Fun() { void f() throws IOException { 1271 new ProcessBuilder().start();}}, 1272 new Fun() { void f() throws IOException { 1273 new ProcessBuilder(new ArrayList<String>()).start();}}, 1274 new Fun() { void f() throws IOException { 1275 Runtime.getRuntime().exec(new String[]{});}}); 1276 1277 //---------------------------------------------------------------- 1278 // Commands must not contain null elements at start() time. 1279 //---------------------------------------------------------------- 1280 THROWS(NullPointerException.class, 1281 new Fun() { void f() throws IOException { 1282 new ProcessBuilder("foo",null,"bar").start();}}, 1283 new Fun() { void f() throws IOException { 1284 new ProcessBuilder((String)null).start();}}, 1285 new Fun() { void f() throws IOException { 1286 new ProcessBuilder(new String[]{null}).start();}}, 1287 new Fun() { void f() throws IOException { 1288 new ProcessBuilder(new String[]{"foo",null,"bar"}).start();}}); 1289 1290 //---------------------------------------------------------------- 1291 // Command lists are growable. 1292 //---------------------------------------------------------------- 1293 try { 1294 new ProcessBuilder().command().add("foo"); 1295 new ProcessBuilder("bar").command().add("foo"); 1296 new ProcessBuilder(new String[]{"1","2"}).command().add("3"); 1297 } catch (Throwable t) { unexpected(t); } 1298 1299 //---------------------------------------------------------------- 1300 // Nulls in environment updates generate NullPointerException 1301 //---------------------------------------------------------------- 1302 try { 1303 final Map<String,String> env = new ProcessBuilder().environment(); 1304 THROWS(NullPointerException.class, 1305 new Fun(){void f(){ env.put("foo",null);}}, 1306 new Fun(){void f(){ env.put(null,"foo");}}, 1307 new Fun(){void f(){ env.remove(null);}}, 1308 new Fun(){void f(){ 1309 for (Map.Entry<String,String> e : env.entrySet()) 1310 e.setValue(null);}}, 1311 new Fun() { void f() throws IOException { 1312 Runtime.getRuntime().exec(new String[]{"foo"}, 1313 new String[]{null});}}); 1314 } catch (Throwable t) { unexpected(t); } 1315 1316 //---------------------------------------------------------------- 1317 // Non-String types in environment updates generate ClassCastException 1318 //---------------------------------------------------------------- 1319 try { 1320 final Map<String,String> env = new ProcessBuilder().environment(); 1321 THROWS(ClassCastException.class, 1322 new Fun(){void f(){ env.remove(TRUE);}}, 1323 new Fun(){void f(){ env.keySet().remove(TRUE);}}, 1324 new Fun(){void f(){ env.values().remove(TRUE);}}, 1325 new Fun(){void f(){ env.entrySet().remove(TRUE);}}); 1326 } catch (Throwable t) { unexpected(t); } 1327 1328 //---------------------------------------------------------------- 1329 // Check query operations on environment maps 1330 //---------------------------------------------------------------- 1331 try { 1332 List<Map<String,String>> envs = 1333 new ArrayList<Map<String,String>>(2); 1334 envs.add(System.getenv()); 1335 envs.add(new ProcessBuilder().environment()); 1336 for (final Map<String,String> env : envs) { 1337 //---------------------------------------------------------------- 1338 // Nulls in environment queries are forbidden. 1339 //---------------------------------------------------------------- 1340 THROWS(NullPointerException.class, 1341 new Fun(){void f(){ getenv(null);}}, 1342 new Fun(){void f(){ env.get(null);}}, 1343 new Fun(){void f(){ env.containsKey(null);}}, 1344 new Fun(){void f(){ env.containsValue(null);}}, 1345 new Fun(){void f(){ env.keySet().contains(null);}}, 1346 new Fun(){void f(){ env.values().contains(null);}}); 1347 1348 //---------------------------------------------------------------- 1349 // Non-String types in environment queries are forbidden. 1350 //---------------------------------------------------------------- 1351 THROWS(ClassCastException.class, 1352 new Fun(){void f(){ env.get(TRUE);}}, 1353 new Fun(){void f(){ env.containsKey(TRUE);}}, 1354 new Fun(){void f(){ env.containsValue(TRUE);}}, 1355 new Fun(){void f(){ env.keySet().contains(TRUE);}}, 1356 new Fun(){void f(){ env.values().contains(TRUE);}}); 1357 1358 //---------------------------------------------------------------- 1359 // Illegal String values in environment queries are (grumble) OK 1360 //---------------------------------------------------------------- 1361 equal(env.get("\u0000"), null); 1362 check(! env.containsKey("\u0000")); 1363 check(! env.containsValue("\u0000")); 1364 check(! env.keySet().contains("\u0000")); 1365 check(! env.values().contains("\u0000")); 1366 } 1367 1368 } catch (Throwable t) { unexpected(t); } 1369 1370 try { 1371 final Set<Map.Entry<String,String>> entrySet = 1372 new ProcessBuilder().environment().entrySet(); 1373 THROWS(NullPointerException.class, 1374 new Fun(){void f(){ entrySet.contains(null);}}); 1375 THROWS(ClassCastException.class, 1376 new Fun(){void f(){ entrySet.contains(TRUE);}}, 1377 new Fun(){void f(){ 1378 entrySet.contains( 1379 new SimpleImmutableEntry<Boolean,String>(TRUE,""));}}); 1380 1381 check(! entrySet.contains 1382 (new SimpleImmutableEntry<String,String>("", ""))); 1383 } catch (Throwable t) { unexpected(t); } 1384 1385 //---------------------------------------------------------------- 1386 // Put in a directory; get the same one back out. 1387 //---------------------------------------------------------------- 1388 try { 1389 ProcessBuilder pb = new ProcessBuilder(); 1390 File foo = new File("foo"); 1391 equal(pb.directory(), null); 1392 equal(pb.directory(foo).directory(), foo); 1393 equal(pb.directory(null).directory(), null); 1394 } catch (Throwable t) { unexpected(t); } 1395 1396 //---------------------------------------------------------------- 1397 // If round-trip conversion works, check envvar pass-through to child 1398 //---------------------------------------------------------------- 1399 try { 1400 testEncoding("ASCII", "xyzzy"); 1401 testEncoding("Latin1", "\u00f1\u00e1"); 1402 testEncoding("Unicode", "\u22f1\u11e1"); 1403 } catch (Throwable t) { unexpected(t); } 1404 1405 //---------------------------------------------------------------- 1406 // A surprisingly large number of ways to delete an environment var. 1407 //---------------------------------------------------------------- 1408 testVariableDeleter(new EnvironmentFrobber() { 1409 public void doIt(Map<String,String> environ) { 1410 environ.remove("Foo");}}); 1411 1412 testVariableDeleter(new EnvironmentFrobber() { 1413 public void doIt(Map<String,String> environ) { 1414 environ.keySet().remove("Foo");}}); 1415 1416 testVariableDeleter(new EnvironmentFrobber() { 1417 public void doIt(Map<String,String> environ) { 1418 environ.values().remove("BAAR");}}); 1419 1420 testVariableDeleter(new EnvironmentFrobber() { 1421 public void doIt(Map<String,String> environ) { 1422 // Legally fabricate a ProcessEnvironment.StringEntry, 1423 // even though it's private. 1424 Map<String,String> environ2 1425 = new ProcessBuilder().environment(); 1426 environ2.clear(); 1427 environ2.put("Foo","BAAR"); 1428 // Subtlety alert. 1429 Map.Entry<String,String> e 1430 = environ2.entrySet().iterator().next(); 1431 environ.entrySet().remove(e);}}); 1432 1433 testVariableDeleter(new EnvironmentFrobber() { 1434 public void doIt(Map<String,String> environ) { 1435 Map.Entry<String,String> victim = null; 1436 for (Map.Entry<String,String> e : environ.entrySet()) 1437 if (e.getKey().equals("Foo")) 1438 victim = e; 1439 if (victim != null) 1440 environ.entrySet().remove(victim);}}); 1441 1442 testVariableDeleter(new EnvironmentFrobber() { 1443 public void doIt(Map<String,String> environ) { 1444 Iterator<String> it = environ.keySet().iterator(); 1445 while (it.hasNext()) { 1446 String val = it.next(); 1447 if (val.equals("Foo")) 1448 it.remove();}}}); 1449 1450 testVariableDeleter(new EnvironmentFrobber() { 1451 public void doIt(Map<String,String> environ) { 1452 Iterator<Map.Entry<String,String>> it 1453 = environ.entrySet().iterator(); 1454 while (it.hasNext()) { 1455 Map.Entry<String,String> e = it.next(); 1456 if (e.getKey().equals("Foo")) 1457 it.remove();}}}); 1458 1459 testVariableDeleter(new EnvironmentFrobber() { 1460 public void doIt(Map<String,String> environ) { 1461 Iterator<String> it = environ.values().iterator(); 1462 while (it.hasNext()) { 1463 String val = it.next(); 1464 if (val.equals("BAAR")) 1465 it.remove();}}}); 1466 1467 //---------------------------------------------------------------- 1468 // A surprisingly small number of ways to add an environment var. 1469 //---------------------------------------------------------------- 1470 testVariableAdder(new EnvironmentFrobber() { 1471 public void doIt(Map<String,String> environ) { 1472 environ.put("Foo","Bahrein");}}); 1473 1474 //---------------------------------------------------------------- 1475 // A few ways to modify an environment var. 1476 //---------------------------------------------------------------- 1477 testVariableModifier(new EnvironmentFrobber() { 1478 public void doIt(Map<String,String> environ) { 1479 environ.put("Foo","NewValue");}}); 1480 1481 testVariableModifier(new EnvironmentFrobber() { 1482 public void doIt(Map<String,String> environ) { 1483 for (Map.Entry<String,String> e : environ.entrySet()) 1484 if (e.getKey().equals("Foo")) 1485 e.setValue("NewValue");}}); 1486 1487 //---------------------------------------------------------------- 1488 // Fiddle with environment sizes 1489 //---------------------------------------------------------------- 1490 try { 1491 Map<String,String> environ = new ProcessBuilder().environment(); 1492 int size = environ.size(); 1493 checkSizes(environ, size); 1494 1495 environ.put("UnLiKeLYeNVIROmtNam", "someVal"); 1496 checkSizes(environ, size+1); 1497 1498 // Check for environment independence 1499 new ProcessBuilder().environment().clear(); 1500 1501 environ.put("UnLiKeLYeNVIROmtNam", "someOtherVal"); 1502 checkSizes(environ, size+1); 1503 1504 environ.remove("UnLiKeLYeNVIROmtNam"); 1505 checkSizes(environ, size); 1506 1507 environ.clear(); 1508 checkSizes(environ, 0); 1509 1510 environ.clear(); 1511 checkSizes(environ, 0); 1512 1513 environ = new ProcessBuilder().environment(); 1514 environ.keySet().clear(); 1515 checkSizes(environ, 0); 1516 1517 environ = new ProcessBuilder().environment(); 1518 environ.entrySet().clear(); 1519 checkSizes(environ, 0); 1520 1521 environ = new ProcessBuilder().environment(); 1522 environ.values().clear(); 1523 checkSizes(environ, 0); 1524 } catch (Throwable t) { unexpected(t); } 1525 1526 //---------------------------------------------------------------- 1527 // Check that various map invariants hold 1528 //---------------------------------------------------------------- 1529 checkMapSanity(new ProcessBuilder().environment()); 1530 checkMapSanity(System.getenv()); 1531 checkMapEquality(new ProcessBuilder().environment(), 1532 new ProcessBuilder().environment()); 1533 1534 1535 //---------------------------------------------------------------- 1536 // Check effects on external "env" command. 1537 //---------------------------------------------------------------- 1538 try { 1539 Set<String> env1 = new HashSet<String> 1540 (Arrays.asList(nativeEnv((String[])null).split("\n"))); 1541 1542 ProcessBuilder pb = new ProcessBuilder(); 1543 pb.environment().put("QwErTyUiOp","AsDfGhJk"); 1544 1545 Set<String> env2 = new HashSet<String> 1546 (Arrays.asList(nativeEnv(pb).split("\n"))); 1547 1548 check(env2.size() == env1.size() + 1); 1549 env1.add("QwErTyUiOp=AsDfGhJk"); 1550 check(env1.equals(env2)); 1551 } catch (Throwable t) { unexpected(t); } 1552 1553 //---------------------------------------------------------------- 1554 // Test Runtime.exec(...envp...) 1555 // Check for sort order of environment variables on Windows. 1556 //---------------------------------------------------------------- 1557 try { 1558 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot"); 1559 // '+' < 'A' < 'Z' < '_' < 'a' < 'z' < '~' 1560 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", 1561 "+=+", "_=_", "~=~", systemRoot}; 1562 String output = nativeEnv(envp); 1563 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n"; 1564 // On Windows, Java must keep the environment sorted. 1565 // Order is random on Unix, so this test does the sort. 1566 if (! Windows.is()) 1567 output = sortByLinesWindowsly(output); 1568 equal(output, expected); 1569 } catch (Throwable t) { unexpected(t); } 1570 1571 //---------------------------------------------------------------- 1572 // Test Runtime.exec(...envp...) 1573 // and check SystemRoot gets set automatically on Windows 1574 //---------------------------------------------------------------- 1575 try { 1576 if (Windows.is()) { 1577 String systemRoot = "SystemRoot=" + System.getenv("SystemRoot"); 1578 String[]envp = {"FOO=BAR","BAZ=GORP","QUUX=", 1579 "+=+", "_=_", "~=~"}; 1580 String output = nativeEnv(envp); 1581 String expected = "+=+\nBAZ=GORP\nFOO=BAR\nQUUX=\n"+systemRoot+"\n_=_\n~=~\n"; 1582 equal(output, expected); 1583 } 1584 } catch (Throwable t) { unexpected(t); } 1585 1586 //---------------------------------------------------------------- 1587 // System.getenv() must be consistent with System.getenv(String) 1588 //---------------------------------------------------------------- 1589 try { 1590 for (Map.Entry<String,String> e : getenv().entrySet()) 1591 equal(getenv(e.getKey()), e.getValue()); 1592 } catch (Throwable t) { unexpected(t); } 1593 1594 //---------------------------------------------------------------- 1595 // Fiddle with working directory in child 1596 //---------------------------------------------------------------- 1597 try { 1598 String canonicalUserDir = 1599 new File(System.getProperty("user.dir")).getCanonicalPath(); 1600 String[] sdirs = new String[] 1601 {".", "..", "/", "/bin", 1602 "C:", "c:", "C:/", "c:\\", "\\", "\\bin", 1603 "c:\\windows ", "c:\\Program Files", "c:\\Program Files\\" }; 1604 for (String sdir : sdirs) { 1605 File dir = new File(sdir); 1606 if (! (dir.isDirectory() && dir.exists())) 1607 continue; 1608 out.println("Testing directory " + dir); 1609 //dir = new File(dir.getCanonicalPath()); 1610 1611 ProcessBuilder pb = new ProcessBuilder(); 1612 equal(pb.directory(), null); 1613 equal(pwdInChild(pb), canonicalUserDir); 1614 1615 pb.directory(dir); 1616 equal(pb.directory(), dir); 1617 equal(pwdInChild(pb), dir.getCanonicalPath()); 1618 1619 pb.directory(null); 1620 equal(pb.directory(), null); 1621 equal(pwdInChild(pb), canonicalUserDir); 1622 1623 pb.directory(dir); 1624 } 1625 } catch (Throwable t) { unexpected(t); } 1626 1627 //---------------------------------------------------------------- 1628 // Working directory with Unicode in child 1629 //---------------------------------------------------------------- 1630 try { 1631 if (UnicodeOS.is()) { 1632 File dir = new File(System.getProperty("test.dir", "."), 1633 "ProcessBuilderDir\u4e00\u4e02"); 1634 try { 1635 if (!dir.exists()) 1636 dir.mkdir(); 1637 out.println("Testing Unicode directory:" + dir); 1638 ProcessBuilder pb = new ProcessBuilder(); 1639 pb.directory(dir); 1640 equal(pwdInChild(pb), dir.getCanonicalPath()); 1641 } finally { 1642 if (dir.exists()) 1643 dir.delete(); 1644 } 1645 } 1646 } catch (Throwable t) { unexpected(t); } 1647 1648 //---------------------------------------------------------------- 1649 // OOME in child allocating maximally sized array 1650 // Test for hotspot/jvmti bug 6850957 1651 //---------------------------------------------------------------- 1652 try { 1653 List<String> list = new ArrayList<String>(javaChildArgs); 1654 list.add(1, String.format("-XX:OnOutOfMemoryError=%s -version", 1655 javaExe)); 1656 list.add("ArrayOOME"); 1657 ProcessResults r = run(new ProcessBuilder(list)); 1658 check(r.out().contains("java.lang.OutOfMemoryError:")); 1659 check(r.out().contains(javaExe)); 1660 check(r.err().contains(System.getProperty("java.version"))); 1661 equal(r.exitValue(), 1); 1662 } catch (Throwable t) { unexpected(t); } 1663 1664 //---------------------------------------------------------------- 1665 // Windows has tricky semi-case-insensitive semantics 1666 //---------------------------------------------------------------- 1667 if (Windows.is()) 1668 try { 1669 out.println("Running case insensitve variable tests"); 1670 for (String[] namePair : 1671 new String[][] 1672 { new String[]{"PATH","PaTh"}, 1673 new String[]{"home","HOME"}, 1674 new String[]{"SYSTEMROOT","SystemRoot"}}) { 1675 check((getenv(namePair[0]) == null && 1676 getenv(namePair[1]) == null) 1677 || 1678 getenv(namePair[0]).equals(getenv(namePair[1])), 1679 "Windows environment variables are not case insensitive"); 1680 } 1681 } catch (Throwable t) { unexpected(t); } 1682 1683 //---------------------------------------------------------------- 1684 // Test proper Unicode child environment transfer 1685 //---------------------------------------------------------------- 1686 if (UnicodeOS.is()) 1687 try { 1688 ProcessBuilder pb = new ProcessBuilder(); 1689 pb.environment().put("\u1234","\u5678"); 1690 pb.environment().remove("PATH"); 1691 equal(getenvInChild1234(pb), "\u5678"); 1692 } catch (Throwable t) { unexpected(t); } 1693 1694 1695 //---------------------------------------------------------------- 1696 // Test Runtime.exec(...envp...) with envstrings with initial `=' 1697 //---------------------------------------------------------------- 1698 try { 1699 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1700 childArgs.add("System.getenv()"); 1701 String[] cmdp = childArgs.toArray(new String[childArgs.size()]); 1702 String[] envp; 1703 String[] envpWin = {"=C:=\\", "=ExitValue=3", "SystemRoot="+systemRoot}; 1704 String[] envpOth = {"=ExitValue=3", "=C:=\\"}; 1705 if (Windows.is()) { 1706 envp = envpWin; 1707 } else { 1708 envp = envpOth; 1709 } 1710 Process p = Runtime.getRuntime().exec(cmdp, envp); 1711 String expected = Windows.is() ? "=C:=\\,=ExitValue=3,SystemRoot="+systemRoot+"," : "=C:=\\,"; 1712 expected = AIX.is() ? expected + "LIBPATH="+libpath+",": expected; 1713 String commandOutput = commandOutput(p); 1714 if (MacOSX.is()) { 1715 commandOutput = removeMacExpectedVars(commandOutput); 1716 } 1717 if (AIX.is()) { 1718 commandOutput = removeAixExpectedVars(commandOutput); 1719 } 1720 equal(commandOutput, expected); 1721 if (Windows.is()) { 1722 ProcessBuilder pb = new ProcessBuilder(childArgs); 1723 pb.environment().clear(); 1724 pb.environment().put("SystemRoot", systemRoot); 1725 pb.environment().put("=ExitValue", "3"); 1726 pb.environment().put("=C:", "\\"); 1727 equal(commandOutput(pb), expected); 1728 } 1729 } catch (Throwable t) { unexpected(t); } 1730 1731 //---------------------------------------------------------------- 1732 // Test Runtime.exec(...envp...) with envstrings without any `=' 1733 //---------------------------------------------------------------- 1734 try { 1735 String[] cmdp = {"echo"}; 1736 String[] envp = {"Hello", "World"}; // Yuck! 1737 Process p = Runtime.getRuntime().exec(cmdp, envp); 1738 equal(commandOutput(p), "\n"); 1739 } catch (Throwable t) { unexpected(t); } 1740 1741 //---------------------------------------------------------------- 1742 // Test Runtime.exec(...envp...) with envstrings containing NULs 1743 //---------------------------------------------------------------- 1744 try { 1745 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1746 childArgs.add("System.getenv()"); 1747 String[] cmdp = childArgs.toArray(new String[childArgs.size()]); 1748 String[] envpWin = {"SystemRoot="+systemRoot, "LC_ALL=C\u0000\u0000", // Yuck! 1749 "FO\u0000=B\u0000R"}; 1750 String[] envpOth = {"LC_ALL=C\u0000\u0000", // Yuck! 1751 "FO\u0000=B\u0000R"}; 1752 String[] envp; 1753 if (Windows.is()) { 1754 envp = envpWin; 1755 } else { 1756 envp = envpOth; 1757 } 1758 System.out.println ("cmdp"); 1759 for (int i=0; i<cmdp.length; i++) { 1760 System.out.printf ("cmdp %d: %s\n", i, cmdp[i]); 1761 } 1762 System.out.println ("envp"); 1763 for (int i=0; i<envp.length; i++) { 1764 System.out.printf ("envp %d: %s\n", i, envp[i]); 1765 } 1766 Process p = Runtime.getRuntime().exec(cmdp, envp); 1767 String commandOutput = commandOutput(p); 1768 if (MacOSX.is()) { 1769 commandOutput = removeMacExpectedVars(commandOutput); 1770 } 1771 if (AIX.is()) { 1772 commandOutput = removeAixExpectedVars(commandOutput); 1773 } 1774 check(commandOutput.equals(Windows.is() 1775 ? "LC_ALL=C,SystemRoot="+systemRoot+"," 1776 : AIX.is() 1777 ? "LC_ALL=C,LIBPATH="+libpath+"," 1778 : "LC_ALL=C,"), 1779 "Incorrect handling of envstrings containing NULs"); 1780 } catch (Throwable t) { unexpected(t); } 1781 1782 //---------------------------------------------------------------- 1783 // Test the redirectErrorStream property 1784 //---------------------------------------------------------------- 1785 try { 1786 ProcessBuilder pb = new ProcessBuilder(); 1787 equal(pb.redirectErrorStream(), false); 1788 equal(pb.redirectErrorStream(true), pb); 1789 equal(pb.redirectErrorStream(), true); 1790 equal(pb.redirectErrorStream(false), pb); 1791 equal(pb.redirectErrorStream(), false); 1792 } catch (Throwable t) { unexpected(t); } 1793 1794 try { 1795 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1796 childArgs.add("OutErr"); 1797 ProcessBuilder pb = new ProcessBuilder(childArgs); 1798 { 1799 ProcessResults r = run(pb); 1800 equal(r.out(), "outout"); 1801 equal(r.err(), "errerr"); 1802 } 1803 { 1804 pb.redirectErrorStream(true); 1805 ProcessResults r = run(pb); 1806 equal(r.out(), "outerrouterr"); 1807 equal(r.err(), ""); 1808 } 1809 } catch (Throwable t) { unexpected(t); } 1810 1811 if (Unix.is()) { 1812 //---------------------------------------------------------------- 1813 // We can find true and false when PATH is null 1814 //---------------------------------------------------------------- 1815 try { 1816 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1817 childArgs.add("null PATH"); 1818 ProcessBuilder pb = new ProcessBuilder(childArgs); 1819 pb.environment().remove("PATH"); 1820 ProcessResults r = run(pb); 1821 equal(r.out(), ""); 1822 equal(r.err(), ""); 1823 equal(r.exitValue(), 0); 1824 } catch (Throwable t) { unexpected(t); } 1825 1826 //---------------------------------------------------------------- 1827 // PATH search algorithm on Unix 1828 //---------------------------------------------------------------- 1829 try { 1830 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1831 childArgs.add("PATH search algorithm"); 1832 ProcessBuilder pb = new ProcessBuilder(childArgs); 1833 pb.environment().put("PATH", "dir1:dir2:"); 1834 ProcessResults r = run(pb); 1835 equal(r.out(), ""); 1836 equal(r.err(), ""); 1837 equal(r.exitValue(), True.exitValue()); 1838 } catch (Throwable t) { unexpected(t); } 1839 1840 //---------------------------------------------------------------- 1841 // Parent's, not child's PATH is used 1842 //---------------------------------------------------------------- 1843 try { 1844 new File("suBdiR").mkdirs(); 1845 copy("/bin/true", "suBdiR/unliKely"); 1846 final ProcessBuilder pb = 1847 new ProcessBuilder(new String[]{"unliKely"}); 1848 pb.environment().put("PATH", "suBdiR"); 1849 THROWS(IOException.class, 1850 new Fun() {void f() throws Throwable {pb.start();}}); 1851 } catch (Throwable t) { unexpected(t); 1852 } finally { 1853 new File("suBdiR/unliKely").delete(); 1854 new File("suBdiR").delete(); 1855 } 1856 } 1857 1858 //---------------------------------------------------------------- 1859 // Attempt to start bogus program "" 1860 //---------------------------------------------------------------- 1861 try { 1862 new ProcessBuilder("").start(); 1863 fail("Expected IOException not thrown"); 1864 } catch (IOException e) { 1865 String m = e.getMessage(); 1866 if (EnglishUnix.is() && 1867 ! matches(m, "No such file or directory")) 1868 unexpected(e); 1869 } catch (Throwable t) { unexpected(t); } 1870 1871 //---------------------------------------------------------------- 1872 // Check that attempt to execute program name with funny 1873 // characters throws an exception containing those characters. 1874 //---------------------------------------------------------------- 1875 for (String programName : new String[] {"\u00f0", "\u01f0"}) 1876 try { 1877 new ProcessBuilder(programName).start(); 1878 fail("Expected IOException not thrown"); 1879 } catch (IOException e) { 1880 String m = e.getMessage(); 1881 Pattern p = Pattern.compile(programName); 1882 if (! matches(m, programName) 1883 || (EnglishUnix.is() 1884 && ! matches(m, "No such file or directory"))) 1885 unexpected(e); 1886 } catch (Throwable t) { unexpected(t); } 1887 1888 //---------------------------------------------------------------- 1889 // Attempt to start process in nonexistent directory fails. 1890 //---------------------------------------------------------------- 1891 try { 1892 new ProcessBuilder("echo") 1893 .directory(new File("UnLiKeLY")) 1894 .start(); 1895 fail("Expected IOException not thrown"); 1896 } catch (IOException e) { 1897 String m = e.getMessage(); 1898 if (! matches(m, "in directory") 1899 || (EnglishUnix.is() && 1900 ! matches(m, "No such file or directory"))) 1901 unexpected(e); 1902 } catch (Throwable t) { unexpected(t); } 1903 1904 //---------------------------------------------------------------- 1905 // Attempt to write 4095 bytes to the pipe buffer without a 1906 // reader to drain it would deadlock, if not for the fact that 1907 // interprocess pipe buffers are at least 4096 bytes. 1908 // 1909 // Also, check that available reports all the bytes expected 1910 // in the pipe buffer, and that I/O operations do the expected 1911 // things. 1912 //---------------------------------------------------------------- 1913 try { 1914 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1915 childArgs.add("print4095"); 1916 final int SIZE = 4095; 1917 final Process p = new ProcessBuilder(childArgs).start(); 1918 print4095(p.getOutputStream(), (byte) '!'); // Might hang! 1919 p.waitFor(); // Might hang! 1920 equal(SIZE, p.getInputStream().available()); 1921 equal(SIZE, p.getErrorStream().available()); 1922 THROWS(IOException.class, 1923 new Fun(){void f() throws IOException { 1924 p.getOutputStream().write((byte) '!'); 1925 p.getOutputStream().flush(); 1926 }}); 1927 1928 final byte[] bytes = new byte[SIZE + 1]; 1929 equal(SIZE, p.getInputStream().read(bytes)); 1930 for (int i = 0; i < SIZE; i++) 1931 equal((byte) '!', bytes[i]); 1932 equal((byte) 0, bytes[SIZE]); 1933 1934 equal(SIZE, p.getErrorStream().read(bytes)); 1935 for (int i = 0; i < SIZE; i++) 1936 equal((byte) 'E', bytes[i]); 1937 equal((byte) 0, bytes[SIZE]); 1938 1939 equal(0, p.getInputStream().available()); 1940 equal(0, p.getErrorStream().available()); 1941 equal(-1, p.getErrorStream().read()); 1942 equal(-1, p.getInputStream().read()); 1943 1944 equal(p.exitValue(), 5); 1945 1946 p.getInputStream().close(); 1947 p.getErrorStream().close(); 1948 try { p.getOutputStream().close(); } catch (IOException flushFailed) { } 1949 1950 InputStream[] streams = { p.getInputStream(), p.getErrorStream() }; 1951 for (final InputStream in : streams) { 1952 Fun[] ops = { 1953 new Fun(){void f() throws IOException { 1954 in.read(); }}, 1955 new Fun(){void f() throws IOException { 1956 in.read(bytes); }}, 1957 new Fun(){void f() throws IOException { 1958 in.available(); }} 1959 }; 1960 for (Fun op : ops) { 1961 try { 1962 op.f(); 1963 fail(); 1964 } catch (IOException expected) { 1965 check(expected.getMessage() 1966 .matches("[Ss]tream [Cc]losed")); 1967 } 1968 } 1969 } 1970 } catch (Throwable t) { unexpected(t); } 1971 1972 //---------------------------------------------------------------- 1973 // Check that reads which are pending when Process.destroy is 1974 // called, get EOF, not IOException("Stream closed"). 1975 //---------------------------------------------------------------- 1976 try { 1977 final int cases = 4; 1978 for (int i = 0; i < cases; i++) { 1979 final int action = i; 1980 List<String> childArgs = new ArrayList<String>(javaChildArgs); 1981 childArgs.add("sleep"); 1982 final byte[] bytes = new byte[10]; 1983 final Process p = new ProcessBuilder(childArgs).start(); 1984 final CountDownLatch latch = new CountDownLatch(1); 1985 final InputStream s; 1986 switch (action & 0x1) { 1987 case 0: s = p.getInputStream(); break; 1988 case 1: s = p.getErrorStream(); break; 1989 default: throw new Error(); 1990 } 1991 final Thread thread = new Thread() { 1992 public void run() { 1993 try { 1994 int r; 1995 latch.countDown(); 1996 switch (action & 0x2) { 1997 case 0: r = s.read(); break; 1998 case 2: r = s.read(bytes); break; 1999 default: throw new Error(); 2000 } 2001 equal(-1, r); 2002 } catch (Throwable t) { unexpected(t); }}}; 2003 2004 thread.start(); 2005 latch.await(); 2006 Thread.sleep(10); 2007 2008 String os = System.getProperty("os.name"); 2009 if (os.equalsIgnoreCase("Solaris") || 2010 os.equalsIgnoreCase("SunOS")) 2011 { 2012 final Object deferred; 2013 Class<?> c = s.getClass(); 2014 if (c.getName().equals( 2015 "java.lang.UNIXProcess$DeferredCloseInputStream")) 2016 { 2017 deferred = s; 2018 } else { 2019 Field deferredField = p.getClass(). 2020 getDeclaredField("stdout_inner_stream"); 2021 deferredField.setAccessible(true); 2022 deferred = deferredField.get(p); 2023 } 2024 Field useCountField = deferred.getClass(). 2025 getDeclaredField("useCount"); 2026 useCountField.setAccessible(true); 2027 2028 while (useCountField.getInt(deferred) <= 0) { 2029 Thread.yield(); 2030 } 2031 } else if (s instanceof BufferedInputStream) { 2032 Field f = Unsafe.class.getDeclaredField("theUnsafe"); 2033 f.setAccessible(true); 2034 Unsafe unsafe = (Unsafe)f.get(null); 2035 2036 while (unsafe.tryMonitorEnter(s)) { 2037 unsafe.monitorExit(s); 2038 Thread.sleep(1); 2039 } 2040 } 2041 p.destroy(); 2042 thread.join(); 2043 } 2044 } catch (Throwable t) { unexpected(t); } 2045 2046 //---------------------------------------------------------------- 2047 // Check that subprocesses which create subprocesses of their 2048 // own do not cause parent to hang waiting for file 2049 // descriptors to be closed. 2050 //---------------------------------------------------------------- 2051 try { 2052 if (Unix.is() 2053 && new File("/bin/bash").exists() 2054 && new File("/bin/sleep").exists()) { 2055 // Notice that we only destroy the process created by us (i.e. 2056 // our child) but not our grandchild (i.e. '/bin/sleep'). So 2057 // pay attention that the grandchild doesn't run too long to 2058 // avoid polluting the process space with useless processes. 2059 // Running the grandchild for 60s should be more than enough. 2060 final String[] cmd = { "/bin/bash", "-c", "(/bin/sleep 60)" }; 2061 final ProcessBuilder pb = new ProcessBuilder(cmd); 2062 final Process p = pb.start(); 2063 final InputStream stdout = p.getInputStream(); 2064 final InputStream stderr = p.getErrorStream(); 2065 final OutputStream stdin = p.getOutputStream(); 2066 final Thread reader = new Thread() { 2067 public void run() { 2068 try { stdout.read(); } 2069 catch (IOException e) { 2070 // Check that reader failed because stream was 2071 // asynchronously closed. 2072 // e.printStackTrace(); 2073 if (EnglishUnix.is() && 2074 ! (e.getMessage().matches(".*Bad file.*"))) 2075 unexpected(e); 2076 } 2077 catch (Throwable t) { unexpected(t); }}}; 2078 reader.setDaemon(true); 2079 reader.start(); 2080 Thread.sleep(100); 2081 p.destroy(); 2082 check(p.waitFor() != 0); 2083 check(p.exitValue() != 0); 2084 // Subprocess is now dead, but file descriptors remain open. 2085 // Make sure the test will fail if we don't manage to close 2086 // the open streams within 30 seconds. Notice that this time 2087 // must be shorter than the sleep time of the grandchild. 2088 Timer t = new Timer("test/java/lang/ProcessBuilder/Basic.java process reaper", true); 2089 t.schedule(new TimerTask() { 2090 public void run() { 2091 fail("Subprocesses which create subprocesses of " + 2092 "their own caused the parent to hang while " + 2093 "waiting for file descriptors to be closed."); 2094 System.exit(-1); 2095 } 2096 }, 30000); 2097 stdout.close(); 2098 stderr.close(); 2099 stdin.close(); 2100 // All streams successfully closed so we can cancel the timer. 2101 t.cancel(); 2102 //---------------------------------------------------------- 2103 // There remain unsolved issues with asynchronous close. 2104 // Here's a highly non-portable experiment to demonstrate: 2105 //---------------------------------------------------------- 2106 if (Boolean.getBoolean("wakeupJeff!")) { 2107 System.out.println("wakeupJeff!"); 2108 // Initialize signal handler for INTERRUPT_SIGNAL. 2109 new FileInputStream("/bin/sleep").getChannel().close(); 2110 // Send INTERRUPT_SIGNAL to every thread in this java. 2111 String[] wakeupJeff = { 2112 "/bin/bash", "-c", 2113 "/bin/ps --noheaders -Lfp $PPID | " + 2114 "/usr/bin/perl -nale 'print $F[3]' | " + 2115 // INTERRUPT_SIGNAL == 62 on my machine du jour. 2116 "/usr/bin/xargs kill -62" 2117 }; 2118 new ProcessBuilder(wakeupJeff).start().waitFor(); 2119 // If wakeupJeff worked, reader probably got EBADF. 2120 reader.join(); 2121 } 2122 } 2123 } catch (Throwable t) { unexpected(t); } 2124 2125 //---------------------------------------------------------------- 2126 // Attempt to start process with insufficient permissions fails. 2127 //---------------------------------------------------------------- 2128 try { 2129 new File("emptyCommand").delete(); 2130 new FileOutputStream("emptyCommand").close(); 2131 new File("emptyCommand").setExecutable(false); 2132 new ProcessBuilder("./emptyCommand").start(); 2133 fail("Expected IOException not thrown"); 2134 } catch (IOException e) { 2135 new File("./emptyCommand").delete(); 2136 String m = e.getMessage(); 2137 if (EnglishUnix.is() && 2138 ! matches(m, "Permission denied")) 2139 unexpected(e); 2140 } catch (Throwable t) { unexpected(t); } 2141 2142 new File("emptyCommand").delete(); 2143 2144 //---------------------------------------------------------------- 2145 // Check for correct security permission behavior 2146 //---------------------------------------------------------------- 2147 final Policy policy = new Policy(); 2148 Policy.setPolicy(policy); 2149 System.setSecurityManager(new SecurityManager()); 2150 2151 try { 2152 // No permissions required to CREATE a ProcessBuilder 2153 policy.setPermissions(/* Nothing */); 2154 new ProcessBuilder("env").directory(null).directory(); 2155 new ProcessBuilder("env").directory(new File("dir")).directory(); 2156 new ProcessBuilder("env").command("??").command(); 2157 } catch (Throwable t) { unexpected(t); } 2158 2159 THROWS(SecurityException.class, 2160 new Fun() { void f() throws IOException { 2161 policy.setPermissions(/* Nothing */); 2162 System.getenv("foo");}}, 2163 new Fun() { void f() throws IOException { 2164 policy.setPermissions(/* Nothing */); 2165 System.getenv();}}, 2166 new Fun() { void f() throws IOException { 2167 policy.setPermissions(/* Nothing */); 2168 new ProcessBuilder("echo").start();}}, 2169 new Fun() { void f() throws IOException { 2170 policy.setPermissions(/* Nothing */); 2171 Runtime.getRuntime().exec("echo");}}, 2172 new Fun() { void f() throws IOException { 2173 policy.setPermissions(new RuntimePermission("getenv.bar")); 2174 System.getenv("foo");}}); 2175 2176 try { 2177 policy.setPermissions(new RuntimePermission("getenv.foo")); 2178 System.getenv("foo"); 2179 2180 policy.setPermissions(new RuntimePermission("getenv.*")); 2181 System.getenv("foo"); 2182 System.getenv(); 2183 new ProcessBuilder().environment(); 2184 } catch (Throwable t) { unexpected(t); } 2185 2186 2187 final Permission execPermission 2188 = new FilePermission("<<ALL FILES>>", "execute"); 2189 2190 THROWS(SecurityException.class, 2191 new Fun() { void f() throws IOException { 2192 // environment permission by itself insufficient 2193 policy.setPermissions(new RuntimePermission("getenv.*")); 2194 ProcessBuilder pb = new ProcessBuilder("env"); 2195 pb.environment().put("foo","bar"); 2196 pb.start();}}, 2197 new Fun() { void f() throws IOException { 2198 // exec permission by itself insufficient 2199 policy.setPermissions(execPermission); 2200 ProcessBuilder pb = new ProcessBuilder("env"); 2201 pb.environment().put("foo","bar"); 2202 pb.start();}}); 2203 2204 try { 2205 // Both permissions? OK. 2206 policy.setPermissions(new RuntimePermission("getenv.*"), 2207 execPermission); 2208 ProcessBuilder pb = new ProcessBuilder("env"); 2209 pb.environment().put("foo","bar"); 2210 Process p = pb.start(); 2211 closeStreams(p); 2212 } catch (IOException e) { // OK 2213 } catch (Throwable t) { unexpected(t); } 2214 2215 try { 2216 // Don't need environment permission unless READING environment 2217 policy.setPermissions(execPermission); 2218 Runtime.getRuntime().exec("env", new String[]{}); 2219 } catch (IOException e) { // OK 2220 } catch (Throwable t) { unexpected(t); } 2221 2222 try { 2223 // Don't need environment permission unless READING environment 2224 policy.setPermissions(execPermission); 2225 new ProcessBuilder("env").start(); 2226 } catch (IOException e) { // OK 2227 } catch (Throwable t) { unexpected(t); } 2228 2229 // Restore "normal" state without a security manager 2230 policy.setPermissions(new RuntimePermission("setSecurityManager")); 2231 System.setSecurityManager(null); 2232 2233 //---------------------------------------------------------------- 2234 // Check that Process.isAlive() & 2235 // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected. 2236 //---------------------------------------------------------------- 2237 try { 2238 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2239 childArgs.add("sleep"); 2240 final Process p = new ProcessBuilder(childArgs).start(); 2241 long start = System.nanoTime(); 2242 if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) { 2243 fail("Test failed: Process exited prematurely"); 2244 } 2245 long end = System.nanoTime(); 2246 // give waitFor(timeout) a wide berth (100ms) 2247 // Old AIX machines my need a little longer. 2248 if ((end - start) > 100000000L * (AIX.is() ? 4 : 1)) 2249 fail("Test failed: waitFor took too long (" + (end - start) + "ns)"); 2250 2251 p.destroy(); 2252 p.waitFor(); 2253 2254 if (p.isAlive() || 2255 !p.waitFor(0, TimeUnit.MILLISECONDS)) 2256 { 2257 fail("Test failed: Process still alive - please terminate " + 2258 p.toString() + " manually"); 2259 } 2260 } catch (Throwable t) { unexpected(t); } 2261 2262 //---------------------------------------------------------------- 2263 // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) 2264 // works as expected. 2265 //---------------------------------------------------------------- 2266 try { 2267 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2268 childArgs.add("sleep"); 2269 final Process p = new ProcessBuilder(childArgs).start(); 2270 long start = System.nanoTime(); 2271 2272 p.waitFor(1000, TimeUnit.MILLISECONDS); 2273 2274 long end = System.nanoTime(); 2275 if ((end - start) < 500000000) 2276 fail("Test failed: waitFor didn't take long enough (" + (end - start) + "ns)"); 2277 2278 p.destroy(); 2279 2280 start = System.nanoTime(); 2281 p.waitFor(1000, TimeUnit.MILLISECONDS); 2282 end = System.nanoTime(); 2283 if ((end - start) > 900000000) 2284 fail("Test failed: waitFor took too long on a dead process. (" + (end - start) + "ns)"); 2285 } catch (Throwable t) { unexpected(t); } 2286 2287 //---------------------------------------------------------------- 2288 // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) 2289 // interrupt works as expected. 2290 //---------------------------------------------------------------- 2291 try { 2292 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2293 childArgs.add("sleep"); 2294 final Process p = new ProcessBuilder(childArgs).start(); 2295 final long start = System.nanoTime(); 2296 final CountDownLatch latch = new CountDownLatch(1); 2297 2298 final Thread thread = new Thread() { 2299 public void run() { 2300 try { 2301 try { 2302 latch.countDown(); 2303 p.waitFor(10000, TimeUnit.MILLISECONDS); 2304 } catch (InterruptedException e) { 2305 return; 2306 } 2307 fail("waitFor() wasn't interrupted"); 2308 } catch (Throwable t) { unexpected(t); }}}; 2309 2310 thread.start(); 2311 latch.await(); 2312 Thread.sleep(1000); 2313 thread.interrupt(); 2314 p.destroy(); 2315 } catch (Throwable t) { unexpected(t); } 2316 2317 //---------------------------------------------------------------- 2318 // Check the default implementation for 2319 // Process.waitFor(long, TimeUnit) 2320 //---------------------------------------------------------------- 2321 try { 2322 List<String> childArgs = new ArrayList<String>(javaChildArgs); 2323 childArgs.add("sleep"); 2324 final Process proc = new ProcessBuilder(childArgs).start(); 2325 DelegatingProcess p = new DelegatingProcess(proc); 2326 long start = System.nanoTime(); 2327 2328 p.waitFor(1000, TimeUnit.MILLISECONDS); 2329 2330 long end = System.nanoTime(); 2331 if ((end - start) < 500000000) 2332 fail("Test failed: waitFor didn't take long enough"); 2333 2334 p.destroy(); 2335 2336 p.waitFor(1000, TimeUnit.MILLISECONDS); 2337 } catch (Throwable t) { unexpected(t); } 2338 } 2339 2340 static void closeStreams(Process p) { 2341 try { 2342 p.getOutputStream().close(); 2343 p.getInputStream().close(); 2344 p.getErrorStream().close(); 2345 } catch (Throwable t) { unexpected(t); } 2346 } 2347 2348 //---------------------------------------------------------------- 2349 // A Policy class designed to make permissions fiddling very easy. 2350 //---------------------------------------------------------------- 2351 private static class Policy extends java.security.Policy { 2352 private Permissions perms; 2353 2354 public void setPermissions(Permission...permissions) { 2355 perms = new Permissions(); 2356 for (Permission permission : permissions) 2357 perms.add(permission); 2358 } 2359 2360 public Policy() { setPermissions(/* Nothing */); } 2361 2362 public PermissionCollection getPermissions(CodeSource cs) { 2363 return perms; 2364 } 2365 2366 public PermissionCollection getPermissions(ProtectionDomain pd) { 2367 return perms; 2368 } 2369 2370 public boolean implies(ProtectionDomain pd, Permission p) { 2371 return perms.implies(p); 2372 } 2373 2374 public void refresh() {} 2375 } 2376 2377 private static class StreamAccumulator extends Thread { 2378 private final InputStream is; 2379 private final StringBuilder sb = new StringBuilder(); 2380 private Throwable throwable = null; 2381 2382 public String result () throws Throwable { 2383 if (throwable != null) 2384 throw throwable; 2385 return sb.toString(); 2386 } 2387 2388 StreamAccumulator (InputStream is) { 2389 this.is = is; 2390 } 2391 2392 public void run() { 2393 try { 2394 Reader r = new InputStreamReader(is); 2395 char[] buf = new char[4096]; 2396 int n; 2397 while ((n = r.read(buf)) > 0) { 2398 sb.append(buf,0,n); 2399 } 2400 } catch (Throwable t) { 2401 throwable = t; 2402 } finally { 2403 try { is.close(); } 2404 catch (Throwable t) { throwable = t; } 2405 } 2406 } 2407 } 2408 2409 static ProcessResults run(ProcessBuilder pb) { 2410 try { 2411 return run(pb.start()); 2412 } catch (Throwable t) { unexpected(t); return null; } 2413 } 2414 2415 private static ProcessResults run(Process p) { 2416 Throwable throwable = null; 2417 int exitValue = -1; 2418 String out = ""; 2419 String err = ""; 2420 2421 StreamAccumulator outAccumulator = 2422 new StreamAccumulator(p.getInputStream()); 2423 StreamAccumulator errAccumulator = 2424 new StreamAccumulator(p.getErrorStream()); 2425 2426 try { 2427 outAccumulator.start(); 2428 errAccumulator.start(); 2429 2430 exitValue = p.waitFor(); 2431 2432 outAccumulator.join(); 2433 errAccumulator.join(); 2434 2435 out = outAccumulator.result(); 2436 err = errAccumulator.result(); 2437 } catch (Throwable t) { 2438 throwable = t; 2439 } 2440 2441 return new ProcessResults(out, err, exitValue, throwable); 2442 } 2443 2444 //---------------------------------------------------------------- 2445 // Results of a command 2446 //---------------------------------------------------------------- 2447 private static class ProcessResults { 2448 private final String out; 2449 private final String err; 2450 private final int exitValue; 2451 private final Throwable throwable; 2452 2453 public ProcessResults(String out, 2454 String err, 2455 int exitValue, 2456 Throwable throwable) { 2457 this.out = out; 2458 this.err = err; 2459 this.exitValue = exitValue; 2460 this.throwable = throwable; 2461 } 2462 2463 public String out() { return out; } 2464 public String err() { return err; } 2465 public int exitValue() { return exitValue; } 2466 public Throwable throwable() { return throwable; } 2467 2468 public String toString() { 2469 StringBuilder sb = new StringBuilder(); 2470 sb.append("<STDOUT>\n" + out() + "</STDOUT>\n") 2471 .append("<STDERR>\n" + err() + "</STDERR>\n") 2472 .append("exitValue = " + exitValue + "\n"); 2473 if (throwable != null) 2474 sb.append(throwable.getStackTrace()); 2475 return sb.toString(); 2476 } 2477 } 2478 2479 //--------------------- Infrastructure --------------------------- 2480 static volatile int passed = 0, failed = 0; 2481 static void pass() {passed++;} 2482 static void fail() {failed++; Thread.dumpStack();} 2483 static void fail(String msg) {System.out.println(msg); fail();} 2484 static void unexpected(Throwable t) {failed++; t.printStackTrace();} 2485 static void check(boolean cond) {if (cond) pass(); else fail();} 2486 static void check(boolean cond, String m) {if (cond) pass(); else fail(m);} 2487 static void equal(Object x, Object y) { 2488 if (x == null ? y == null : x.equals(y)) pass(); 2489 else fail(x + " not equal to " + y);} 2490 2491 public static void main(String[] args) throws Throwable { 2492 try {realMain(args);} catch (Throwable t) {unexpected(t);} 2493 System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); 2494 if (failed > 0) throw new AssertionError("Some tests failed");} 2495 private static abstract class Fun {abstract void f() throws Throwable;} 2496 static void THROWS(Class<? extends Throwable> k, Fun... fs) { 2497 for (Fun f : fs) 2498 try { f.f(); fail("Expected " + k.getName() + " not thrown"); } 2499 catch (Throwable t) { 2500 if (k.isAssignableFrom(t.getClass())) pass(); 2501 else unexpected(t);}} 2502 }