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