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