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