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