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