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