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