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