Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/windows/classes/java/lang/ProcessImpl.java
41133 views
1
/*
2
* Copyright (c) 1995, 2021, 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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package java.lang;
27
28
import java.io.BufferedInputStream;
29
import java.io.BufferedOutputStream;
30
import java.io.File;
31
import java.io.FileDescriptor;
32
import java.io.FileInputStream;
33
import java.io.FileOutputStream;
34
import java.io.IOException;
35
import java.io.InputStream;
36
import java.io.OutputStream;
37
import java.lang.ProcessBuilder.Redirect;
38
import java.security.AccessController;
39
import java.security.PrivilegedAction;
40
import java.util.ArrayList;
41
import java.util.Locale;
42
import java.util.concurrent.CompletableFuture;
43
import java.util.concurrent.TimeUnit;
44
import java.util.regex.Matcher;
45
import java.util.regex.Pattern;
46
47
import jdk.internal.access.JavaIOFileDescriptorAccess;
48
import jdk.internal.access.SharedSecrets;
49
import jdk.internal.ref.CleanerFactory;
50
import sun.security.action.GetBooleanAction;
51
import sun.security.action.GetPropertyAction;
52
53
/* This class is for the exclusive use of ProcessBuilder.start() to
54
* create new processes.
55
*
56
* @author Martin Buchholz
57
* @since 1.5
58
*/
59
60
final class ProcessImpl extends Process {
61
private static final JavaIOFileDescriptorAccess fdAccess
62
= SharedSecrets.getJavaIOFileDescriptorAccess();
63
64
// Windows platforms support a forcible kill signal.
65
static final boolean SUPPORTS_NORMAL_TERMINATION = false;
66
67
/**
68
* Open a file for writing. If {@code append} is {@code true} then the file
69
* is opened for atomic append directly and a FileOutputStream constructed
70
* with the resulting handle. This is because a FileOutputStream created
71
* to append to a file does not open the file in a manner that guarantees
72
* that writes by the child process will be atomic.
73
*/
74
@SuppressWarnings("removal")
75
private static FileOutputStream newFileOutputStream(File f, boolean append)
76
throws IOException
77
{
78
if (append) {
79
String path = f.getPath();
80
SecurityManager sm = System.getSecurityManager();
81
if (sm != null)
82
sm.checkWrite(path);
83
long handle = openForAtomicAppend(path);
84
final FileDescriptor fd = new FileDescriptor();
85
fdAccess.setHandle(fd, handle);
86
return AccessController.doPrivileged(
87
new PrivilegedAction<FileOutputStream>() {
88
public FileOutputStream run() {
89
return new FileOutputStream(fd);
90
}
91
}
92
);
93
} else {
94
return new FileOutputStream(f);
95
}
96
}
97
98
// System-dependent portion of ProcessBuilder.start()
99
static Process start(String cmdarray[],
100
java.util.Map<String,String> environment,
101
String dir,
102
ProcessBuilder.Redirect[] redirects,
103
boolean redirectErrorStream)
104
throws IOException
105
{
106
String envblock = ProcessEnvironment.toEnvironmentBlock(environment);
107
108
FileInputStream f0 = null;
109
FileOutputStream f1 = null;
110
FileOutputStream f2 = null;
111
112
try {
113
boolean forceNullOutputStream = false;
114
long[] stdHandles;
115
if (redirects == null) {
116
stdHandles = new long[] { -1L, -1L, -1L };
117
} else {
118
stdHandles = new long[3];
119
120
if (redirects[0] == Redirect.PIPE) {
121
stdHandles[0] = -1L;
122
} else if (redirects[0] == Redirect.INHERIT) {
123
stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
124
} else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
125
stdHandles[0] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd());
126
} else {
127
f0 = new FileInputStream(redirects[0].file());
128
stdHandles[0] = fdAccess.getHandle(f0.getFD());
129
}
130
131
if (redirects[1] == Redirect.PIPE) {
132
stdHandles[1] = -1L;
133
} else if (redirects[1] == Redirect.INHERIT) {
134
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
135
} else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
136
stdHandles[1] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
137
// Force getInputStream to return a null stream,
138
// the handle is directly assigned to the next process.
139
forceNullOutputStream = true;
140
} else {
141
f1 = newFileOutputStream(redirects[1].file(),
142
redirects[1].append());
143
stdHandles[1] = fdAccess.getHandle(f1.getFD());
144
}
145
146
if (redirects[2] == Redirect.PIPE) {
147
stdHandles[2] = -1L;
148
} else if (redirects[2] == Redirect.INHERIT) {
149
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
150
} else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
151
stdHandles[2] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
152
} else {
153
f2 = newFileOutputStream(redirects[2].file(),
154
redirects[2].append());
155
stdHandles[2] = fdAccess.getHandle(f2.getFD());
156
}
157
}
158
159
Process p = new ProcessImpl(cmdarray, envblock, dir,
160
stdHandles, forceNullOutputStream, redirectErrorStream);
161
if (redirects != null) {
162
// Copy the handles's if they are to be redirected to another process
163
if (stdHandles[0] >= 0
164
&& redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
165
fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(),
166
stdHandles[0]);
167
}
168
if (stdHandles[1] >= 0
169
&& redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
170
fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(),
171
stdHandles[1]);
172
}
173
if (stdHandles[2] >= 0
174
&& redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
175
fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(),
176
stdHandles[2]);
177
}
178
}
179
return p;
180
} finally {
181
// In theory, close() can throw IOException
182
// (although it is rather unlikely to happen here)
183
try { if (f0 != null) f0.close(); }
184
finally {
185
try { if (f1 != null) f1.close(); }
186
finally { if (f2 != null) f2.close(); }
187
}
188
}
189
190
}
191
192
private static class LazyPattern {
193
// Escape-support version:
194
// "(\")((?:\\\\\\1|.)+?)\\1|([^\\s\"]+)";
195
private static final Pattern PATTERN =
196
Pattern.compile("[^\\s\"]+|\"[^\"]*\"");
197
};
198
199
/* Parses the command string parameter into the executable name and
200
* program arguments.
201
*
202
* The command string is broken into tokens. The token separator is a space
203
* or quota character. The space inside quotation is not a token separator.
204
* There are no escape sequences.
205
*/
206
private static String[] getTokensFromCommand(String command) {
207
ArrayList<String> matchList = new ArrayList<>(8);
208
Matcher regexMatcher = LazyPattern.PATTERN.matcher(command);
209
while (regexMatcher.find())
210
matchList.add(regexMatcher.group());
211
return matchList.toArray(new String[matchList.size()]);
212
}
213
214
private static final int VERIFICATION_CMD_BAT = 0;
215
private static final int VERIFICATION_WIN32 = 1;
216
private static final int VERIFICATION_WIN32_SAFE = 2; // inside quotes not allowed
217
private static final int VERIFICATION_LEGACY = 3;
218
// See Command shell overview for documentation of special characters.
219
// https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb490954(v=technet.10)
220
private static final char ESCAPE_VERIFICATION[][] = {
221
// We guarantee the only command file execution for implicit [cmd.exe] run.
222
// http://technet.microsoft.com/en-us/library/bb490954.aspx
223
{' ', '\t', '\"', '<', '>', '&', '|', '^'},
224
{' ', '\t', '\"', '<', '>'},
225
{' ', '\t', '\"', '<', '>'},
226
{' ', '\t'}
227
};
228
229
private static String createCommandLine(int verificationType,
230
final String executablePath,
231
final String cmd[])
232
{
233
StringBuilder cmdbuf = new StringBuilder(80);
234
235
cmdbuf.append(executablePath);
236
237
for (int i = 1; i < cmd.length; ++i) {
238
cmdbuf.append(' ');
239
String s = cmd[i];
240
if (needsEscaping(verificationType, s)) {
241
cmdbuf.append('"');
242
243
if (verificationType == VERIFICATION_WIN32_SAFE) {
244
// Insert the argument, adding '\' to quote any interior quotes
245
int length = s.length();
246
for (int j = 0; j < length; j++) {
247
char c = s.charAt(j);
248
if (c == DOUBLEQUOTE) {
249
int count = countLeadingBackslash(verificationType, s, j);
250
while (count-- > 0) {
251
cmdbuf.append(BACKSLASH); // double the number of backslashes
252
}
253
cmdbuf.append(BACKSLASH); // backslash to quote the quote
254
}
255
cmdbuf.append(c);
256
}
257
} else {
258
cmdbuf.append(s);
259
}
260
// The code protects the [java.exe] and console command line
261
// parser, that interprets the [\"] combination as an escape
262
// sequence for the ["] char.
263
// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
264
//
265
// If the argument is an FS path, doubling of the tail [\]
266
// char is not a problem for non-console applications.
267
//
268
// The [\"] sequence is not an escape sequence for the [cmd.exe]
269
// command line parser. The case of the [""] tail escape
270
// sequence could not be realized due to the argument validation
271
// procedure.
272
int count = countLeadingBackslash(verificationType, s, s.length());
273
while (count-- > 0) {
274
cmdbuf.append(BACKSLASH); // double the number of backslashes
275
}
276
cmdbuf.append('"');
277
} else {
278
cmdbuf.append(s);
279
}
280
}
281
return cmdbuf.toString();
282
}
283
284
/**
285
* Return the argument without quotes (1st and last) if properly quoted, else the arg.
286
* A properly quoted string has first and last characters as quote and
287
* the last quote is not escaped.
288
* @param str a string
289
* @return the string without quotes
290
*/
291
private static String unQuote(String str) {
292
if (!str.startsWith("\"") || !str.endsWith("\"") || str.length() < 2)
293
return str; // no beginning or ending quote, or too short not quoted
294
295
if (str.endsWith("\\\"")) {
296
return str; // not properly quoted, treat as unquoted
297
}
298
// Strip leading and trailing quotes
299
return str.substring(1, str.length() - 1);
300
}
301
302
private static boolean needsEscaping(int verificationType, String arg) {
303
if (arg.isEmpty())
304
return true; // Empty string is to be quoted
305
306
// Switch off MS heuristic for internal ["].
307
// Please, use the explicit [cmd.exe] call
308
// if you need the internal ["].
309
// Example: "cmd.exe", "/C", "Extended_MS_Syntax"
310
311
// For [.exe] or [.com] file the unpaired/internal ["]
312
// in the argument is not a problem.
313
String unquotedArg = unQuote(arg);
314
boolean argIsQuoted = !arg.equals(unquotedArg);
315
boolean embeddedQuote = unquotedArg.indexOf(DOUBLEQUOTE) >= 0;
316
317
switch (verificationType) {
318
case VERIFICATION_CMD_BAT:
319
if (embeddedQuote) {
320
throw new IllegalArgumentException("Argument has embedded quote, " +
321
"use the explicit CMD.EXE call.");
322
}
323
break; // break determine whether to quote
324
case VERIFICATION_WIN32_SAFE:
325
if (argIsQuoted && embeddedQuote) {
326
throw new IllegalArgumentException("Malformed argument has embedded quote: "
327
+ unquotedArg);
328
}
329
break;
330
default:
331
break;
332
}
333
334
if (!argIsQuoted) {
335
char testEscape[] = ESCAPE_VERIFICATION[verificationType];
336
for (int i = 0; i < testEscape.length; ++i) {
337
if (arg.indexOf(testEscape[i]) >= 0) {
338
return true;
339
}
340
}
341
}
342
return false;
343
}
344
345
private static String getExecutablePath(String path)
346
throws IOException
347
{
348
String name = unQuote(path);
349
if (name.indexOf(DOUBLEQUOTE) >= 0) {
350
throw new IllegalArgumentException("Executable name has embedded quote, " +
351
"split the arguments: " + name);
352
}
353
// Win32 CreateProcess requires path to be normalized
354
File fileToRun = new File(name);
355
356
// From the [CreateProcess] function documentation:
357
//
358
// "If the file name does not contain an extension, .exe is appended.
359
// Therefore, if the file name extension is .com, this parameter
360
// must include the .com extension. If the file name ends in
361
// a period (.) with no extension, or if the file name contains a path,
362
// .exe is not appended."
363
//
364
// "If the file name !does not contain a directory path!,
365
// the system searches for the executable file in the following
366
// sequence:..."
367
//
368
// In practice ANY non-existent path is extended by [.exe] extension
369
// in the [CreateProcess] function with the only exception:
370
// the path ends by (.)
371
372
return fileToRun.getPath();
373
}
374
375
/**
376
* An executable is any program that is an EXE or does not have an extension
377
* and the Windows createProcess will be looking for .exe.
378
* The comparison is case insensitive based on the name.
379
* @param executablePath the executable file
380
* @return true if the path ends in .exe or does not have an extension.
381
*/
382
private boolean isExe(String executablePath) {
383
File file = new File(executablePath);
384
String upName = file.getName().toUpperCase(Locale.ROOT);
385
return (upName.endsWith(".EXE") || upName.indexOf('.') < 0);
386
}
387
388
// Old version that can be bypassed
389
private boolean isShellFile(String executablePath) {
390
String upPath = executablePath.toUpperCase();
391
return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT"));
392
}
393
394
private String quoteString(String arg) {
395
StringBuilder argbuf = new StringBuilder(arg.length() + 2);
396
return argbuf.append('"').append(arg).append('"').toString();
397
}
398
399
// Count backslashes before start index of string.
400
// .bat files don't include backslashes as part of the quote
401
private static int countLeadingBackslash(int verificationType,
402
CharSequence input, int start) {
403
if (verificationType == VERIFICATION_CMD_BAT)
404
return 0;
405
int j;
406
for (j = start - 1; j >= 0 && input.charAt(j) == BACKSLASH; j--) {
407
// just scanning backwards
408
}
409
return (start - 1) - j; // number of BACKSLASHES
410
}
411
412
private static final char DOUBLEQUOTE = '\"';
413
private static final char BACKSLASH = '\\';
414
415
private final long handle;
416
private final ProcessHandle processHandle;
417
private OutputStream stdin_stream;
418
private InputStream stdout_stream;
419
private InputStream stderr_stream;
420
421
@SuppressWarnings("removal")
422
private ProcessImpl(String cmd[],
423
final String envblock,
424
final String path,
425
final long[] stdHandles,
426
boolean forceNullOutputStream,
427
final boolean redirectErrorStream)
428
throws IOException
429
{
430
String cmdstr;
431
final SecurityManager security = System.getSecurityManager();
432
final String value = GetPropertyAction.
433
privilegedGetProperty("jdk.lang.Process.allowAmbiguousCommands",
434
(security == null ? "true" : "false"));
435
final boolean allowAmbiguousCommands = !"false".equalsIgnoreCase(value);
436
437
if (allowAmbiguousCommands && security == null) {
438
// Legacy mode.
439
440
// Normalize path if possible.
441
String executablePath = new File(cmd[0]).getPath();
442
443
// No worry about internal, unpaired ["], and redirection/piping.
444
if (needsEscaping(VERIFICATION_LEGACY, executablePath) )
445
executablePath = quoteString(executablePath);
446
447
cmdstr = createCommandLine(
448
//legacy mode doesn't worry about extended verification
449
VERIFICATION_LEGACY,
450
executablePath,
451
cmd);
452
} else {
453
String executablePath;
454
try {
455
executablePath = getExecutablePath(cmd[0]);
456
} catch (IllegalArgumentException e) {
457
// Workaround for the calls like
458
// Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar")
459
460
// No chance to avoid CMD/BAT injection, except to do the work
461
// right from the beginning. Otherwise we have too many corner
462
// cases from
463
// Runtime.getRuntime().exec(String[] cmd [, ...])
464
// calls with internal ["] and escape sequences.
465
466
// Restore original command line.
467
StringBuilder join = new StringBuilder();
468
// terminal space in command line is ok
469
for (String s : cmd)
470
join.append(s).append(' ');
471
472
// Parse the command line again.
473
cmd = getTokensFromCommand(join.toString());
474
executablePath = getExecutablePath(cmd[0]);
475
476
// Check new executable name once more
477
if (security != null)
478
security.checkExec(executablePath);
479
}
480
481
// Quotation protects from interpretation of the [path] argument as
482
// start of longer path with spaces. Quotation has no influence to
483
// [.exe] extension heuristic.
484
boolean isShell = allowAmbiguousCommands ? isShellFile(executablePath)
485
: !isExe(executablePath);
486
cmdstr = createCommandLine(
487
// We need the extended verification procedures
488
isShell ? VERIFICATION_CMD_BAT
489
: (allowAmbiguousCommands ? VERIFICATION_WIN32 : VERIFICATION_WIN32_SAFE),
490
quoteString(executablePath),
491
cmd);
492
}
493
494
handle = create(cmdstr, envblock, path,
495
stdHandles, redirectErrorStream);
496
// Register a cleaning function to close the handle
497
final long local_handle = handle; // local to prevent capture of this
498
CleanerFactory.cleaner().register(this, () -> closeHandle(local_handle));
499
500
processHandle = ProcessHandleImpl.getInternal(getProcessId0(handle));
501
502
java.security.AccessController.doPrivileged(
503
new java.security.PrivilegedAction<Void>() {
504
public Void run() {
505
if (stdHandles[0] == -1L)
506
stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE;
507
else {
508
FileDescriptor stdin_fd = new FileDescriptor();
509
fdAccess.setHandle(stdin_fd, stdHandles[0]);
510
fdAccess.registerCleanup(stdin_fd);
511
stdin_stream = new BufferedOutputStream(
512
new FileOutputStream(stdin_fd));
513
}
514
515
if (stdHandles[1] == -1L || forceNullOutputStream)
516
stdout_stream = ProcessBuilder.NullInputStream.INSTANCE;
517
else {
518
FileDescriptor stdout_fd = new FileDescriptor();
519
fdAccess.setHandle(stdout_fd, stdHandles[1]);
520
fdAccess.registerCleanup(stdout_fd);
521
stdout_stream = new BufferedInputStream(
522
new PipeInputStream(stdout_fd));
523
}
524
525
if (stdHandles[2] == -1L)
526
stderr_stream = ProcessBuilder.NullInputStream.INSTANCE;
527
else {
528
FileDescriptor stderr_fd = new FileDescriptor();
529
fdAccess.setHandle(stderr_fd, stdHandles[2]);
530
fdAccess.registerCleanup(stderr_fd);
531
stderr_stream = new PipeInputStream(stderr_fd);
532
}
533
534
return null; }});
535
}
536
537
public OutputStream getOutputStream() {
538
return stdin_stream;
539
}
540
541
public InputStream getInputStream() {
542
return stdout_stream;
543
}
544
545
public InputStream getErrorStream() {
546
return stderr_stream;
547
}
548
549
private static final int STILL_ACTIVE = getStillActive();
550
private static native int getStillActive();
551
552
public int exitValue() {
553
int exitCode = getExitCodeProcess(handle);
554
if (exitCode == STILL_ACTIVE)
555
throw new IllegalThreadStateException("process has not exited");
556
return exitCode;
557
}
558
private static native int getExitCodeProcess(long handle);
559
560
public int waitFor() throws InterruptedException {
561
waitForInterruptibly(handle);
562
if (Thread.interrupted())
563
throw new InterruptedException();
564
return exitValue();
565
}
566
567
private static native void waitForInterruptibly(long handle);
568
569
@Override
570
public boolean waitFor(long timeout, TimeUnit unit)
571
throws InterruptedException
572
{
573
long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions
574
if (getExitCodeProcess(handle) != STILL_ACTIVE) return true;
575
if (timeout <= 0) return false;
576
577
long deadline = System.nanoTime() + remainingNanos;
578
do {
579
// Round up to next millisecond
580
long msTimeout = TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L);
581
if (msTimeout < 0) {
582
// if wraps around then wait a long while
583
msTimeout = Integer.MAX_VALUE;
584
}
585
waitForTimeoutInterruptibly(handle, msTimeout);
586
if (Thread.interrupted())
587
throw new InterruptedException();
588
if (getExitCodeProcess(handle) != STILL_ACTIVE) {
589
return true;
590
}
591
remainingNanos = deadline - System.nanoTime();
592
} while (remainingNanos > 0);
593
594
return (getExitCodeProcess(handle) != STILL_ACTIVE);
595
}
596
597
private static native void waitForTimeoutInterruptibly(
598
long handle, long timeoutMillis);
599
600
@Override
601
public void destroy() {
602
terminateProcess(handle);
603
}
604
605
@Override
606
public CompletableFuture<Process> onExit() {
607
return ProcessHandleImpl.completion(pid(), false)
608
.handleAsync((exitStatus, unusedThrowable) -> this);
609
}
610
611
@Override
612
public ProcessHandle toHandle() {
613
@SuppressWarnings("removal")
614
SecurityManager sm = System.getSecurityManager();
615
if (sm != null) {
616
sm.checkPermission(new RuntimePermission("manageProcess"));
617
}
618
return processHandle;
619
}
620
621
@Override
622
public boolean supportsNormalTermination() {
623
return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
624
}
625
626
@Override
627
public Process destroyForcibly() {
628
destroy();
629
return this;
630
}
631
632
private static native void terminateProcess(long handle);
633
634
@Override
635
public long pid() {
636
return processHandle.pid();
637
}
638
639
private static native int getProcessId0(long handle);
640
641
@Override
642
public boolean isAlive() {
643
return isProcessAlive(handle);
644
}
645
646
private static native boolean isProcessAlive(long handle);
647
648
/**
649
* The {@code toString} method returns a string consisting of
650
* the native process ID of the process and the exit value of the process.
651
*
652
* @return a string representation of the object.
653
*/
654
@Override
655
public String toString() {
656
int exitCode = getExitCodeProcess(handle);
657
return new StringBuilder("Process[pid=").append(pid())
658
.append(", exitValue=").append(exitCode == STILL_ACTIVE ? "\"not exited\"" : exitCode)
659
.append("]").toString();
660
}
661
662
/**
663
* Create a process using the win32 function CreateProcess.
664
* The method is synchronized due to MS kb315939 problem.
665
* All native handles should restore the inherit flag at the end of call.
666
*
667
* @param cmdstr the Windows command line
668
* @param envblock NUL-separated, double-NUL-terminated list of
669
* environment strings in VAR=VALUE form
670
* @param dir the working directory of the process, or null if
671
* inheriting the current directory from the parent process
672
* @param stdHandles array of windows HANDLEs. Indexes 0, 1, and
673
* 2 correspond to standard input, standard output and
674
* standard error, respectively. On input, a value of -1
675
* means to create a pipe to connect child and parent
676
* processes. On output, a value which is not -1 is the
677
* parent pipe handle corresponding to the pipe which has
678
* been created. An element of this array is -1 on input
679
* if and only if it is <em>not</em> -1 on output.
680
* @param redirectErrorStream redirectErrorStream attribute
681
* @return the native subprocess HANDLE returned by CreateProcess
682
*/
683
private static synchronized native long create(String cmdstr,
684
String envblock,
685
String dir,
686
long[] stdHandles,
687
boolean redirectErrorStream)
688
throws IOException;
689
690
/**
691
* Opens a file for atomic append. The file is created if it doesn't
692
* already exist.
693
*
694
* @param path the file to open or create
695
* @return the native HANDLE
696
*/
697
private static native long openForAtomicAppend(String path)
698
throws IOException;
699
700
private static native boolean closeHandle(long handle);
701
}
702
703