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