1 /*
  2  * Copyright (c) 1995, 2023, 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 jdk.internal.misc.Blocker;
 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                 if (verificationType == VERIFICATION_WIN32_SAFE ||
273                     verificationType == VERIFICATION_LEGACY) {
274                     int count = countLeadingBackslash(verificationType, s, s.length());
275                     while (count-- > 0) {
276                         cmdbuf.append(BACKSLASH);   // double the number of backslashes
277                     }
278                 }
279                 cmdbuf.append('"');
280             } else if (verificationType == VERIFICATION_WIN32_SAFE &&
281                  (s.startsWith("\"") && s.endsWith("\"") && s.length() > 2)) {
282                 // Check that quoted argument does not escape the final quote
283                 cmdbuf.append(s);
284                 int count = countLeadingBackslash(verificationType, s, s.length() - 1);
285                 while (count-- > 0) {
286                     cmdbuf.insert(cmdbuf.length() - 1, BACKSLASH);    // double the number of backslashes
287                 }
288             } else {
289                 cmdbuf.append(s);
290             }
291         }
292         return cmdbuf.toString();
293     }
294 
295     /**
296      * Return the argument without quotes (first and last) if quoted, otherwise the arg.
297      * @param str a string
298      * @return the string without quotes
299      */
300     private static String unQuote(String str) {
301         if (!str.startsWith("\"") || !str.endsWith("\"") || str.length() < 2)
302             return str;    // no beginning or ending quote, or too short not quoted
303 
304         // Strip leading and trailing quotes
305         return str.substring(1, str.length() - 1);
306     }
307 
308     private static boolean needsEscaping(int verificationType, String arg) {
309         if (arg.isEmpty())
310             return true;            // Empty string is to be quoted
311 
312         // Switch off MS heuristic for internal ["].
313         // Please, use the explicit [cmd.exe] call
314         // if you need the internal ["].
315         //    Example: "cmd.exe", "/C", "Extended_MS_Syntax"
316 
317         // For [.exe] or [.com] file the unpaired/internal ["]
318         // in the argument is not a problem.
319         String unquotedArg = unQuote(arg);
320         boolean argIsQuoted = !arg.equals(unquotedArg);
321         boolean embeddedQuote = unquotedArg.indexOf(DOUBLEQUOTE) >= 0;
322 
323         switch (verificationType) {
324             case VERIFICATION_CMD_BAT:
325                 if (embeddedQuote) {
326                     throw new IllegalArgumentException("Argument has embedded quote, " +
327                             "use the explicit CMD.EXE call.");
328                 }
329                 break;  // break determine whether to quote
330             case VERIFICATION_WIN32_SAFE:
331                 if (argIsQuoted && embeddedQuote)  {
332                     throw new IllegalArgumentException("Malformed argument has embedded quote: "
333                             + unquotedArg);
334                 }
335                 break;
336             default:
337                 break;
338         }
339 
340         if (!argIsQuoted) {
341             char testEscape[] = ESCAPE_VERIFICATION[verificationType];
342             for (int i = 0; i < testEscape.length; ++i) {
343                 if (arg.indexOf(testEscape[i]) >= 0) {
344                     return true;
345                 }
346             }
347         }
348         return false;
349     }
350 
351     private static String getExecutablePath(String path)
352         throws IOException
353     {
354         String name = unQuote(path);
355         if (name.indexOf(DOUBLEQUOTE) >= 0) {
356             throw new IllegalArgumentException("Executable name has embedded quote, " +
357                     "split the arguments: " + name);
358         }
359         // Win32 CreateProcess requires path to be normalized
360         File fileToRun = new File(name);
361 
362         // From the [CreateProcess] function documentation:
363         //
364         // "If the file name does not contain an extension, .exe is appended.
365         // Therefore, if the file name extension is .com, this parameter
366         // must include the .com extension. If the file name ends in
367         // a period (.) with no extension, or if the file name contains a path,
368         // .exe is not appended."
369         //
370         // "If the file name !does not contain a directory path!,
371         // the system searches for the executable file in the following
372         // sequence:..."
373         //
374         // In practice ANY non-existent path is extended by [.exe] extension
375         // in the [CreateProcess] function with the only exception:
376         // the path ends by (.)
377 
378         return fileToRun.getPath();
379     }
380 
381     /**
382      * An executable is any program that is an EXE or does not have an extension
383      * and the Windows createProcess will be looking for .exe.
384      * The comparison is case insensitive based on the name.
385      * @param executablePath the executable file
386      * @return true if the path ends in .exe or does not have an extension.
387      */
388     private boolean isExe(String executablePath) {
389         File file = new File(executablePath);
390         String upName = file.getName().toUpperCase(Locale.ROOT);
391         return (upName.endsWith(".EXE") || upName.indexOf('.') < 0);
392     }
393 
394     // Old version that can be bypassed
395     private boolean isShellFile(String executablePath) {
396         String upPath = executablePath.toUpperCase(Locale.ROOT);
397         return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT"));
398     }
399 
400     private String quoteString(String arg) {
401         StringBuilder argbuf = new StringBuilder(arg.length() + 2);
402         return argbuf.append('"').append(arg).append('"').toString();
403     }
404 
405     // Count backslashes before start index of string.
406     // .bat files don't include backslashes as part of the quote
407     private static int countLeadingBackslash(int verificationType,
408                                              CharSequence input, int start) {
409         if (verificationType == VERIFICATION_CMD_BAT)
410             return 0;
411         int j;
412         for (j = start - 1; j >= 0 && input.charAt(j) == BACKSLASH; j--) {
413             // just scanning backwards
414         }
415         return (start - 1) - j;  // number of BACKSLASHES
416     }
417 
418     private static final char DOUBLEQUOTE = '\"';
419     private static final char BACKSLASH = '\\';
420 
421     private final long handle;
422     private final ProcessHandle processHandle;
423     private OutputStream stdin_stream;
424     private InputStream stdout_stream;
425     private InputStream stderr_stream;
426 
427     @SuppressWarnings("removal")
428     private ProcessImpl(String cmd[],
429                         final String envblock,
430                         final String path,
431                         final long[] stdHandles,
432                         boolean forceNullOutputStream,
433                         final boolean redirectErrorStream)
434         throws IOException
435     {
436         String cmdstr;
437         final SecurityManager security = System.getSecurityManager();
438         final String value = GetPropertyAction.
439                 privilegedGetProperty("jdk.lang.Process.allowAmbiguousCommands",
440                         (security == null ? "true" : "false"));
441         final boolean allowAmbiguousCommands = !"false".equalsIgnoreCase(value);
442 
443         if (allowAmbiguousCommands && security == null) {
444             // Legacy mode.
445 
446             // Normalize path if possible.
447             String executablePath = new File(cmd[0]).getPath();
448 
449             // No worry about internal, unpaired ["], and redirection/piping.
450             if (needsEscaping(VERIFICATION_LEGACY, executablePath) )
451                 executablePath = quoteString(executablePath);
452 
453             cmdstr = createCommandLine(
454                 //legacy mode doesn't worry about extended verification
455                 VERIFICATION_LEGACY,
456                 executablePath,
457                 cmd);
458         } else {
459             String executablePath;
460             try {
461                 executablePath = getExecutablePath(cmd[0]);
462             } catch (IllegalArgumentException e) {
463                 // Workaround for the calls like
464                 // Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar")
465 
466                 // No chance to avoid CMD/BAT injection, except to do the work
467                 // right from the beginning. Otherwise we have too many corner
468                 // cases from
469                 //    Runtime.getRuntime().exec(String[] cmd [, ...])
470                 // calls with internal ["] and escape sequences.
471 
472                 // Restore original command line.
473                 StringBuilder join = new StringBuilder();
474                 // terminal space in command line is ok
475                 for (String s : cmd)
476                     join.append(s).append(' ');
477 
478                 // Parse the command line again.
479                 cmd = getTokensFromCommand(join.toString());
480                 executablePath = getExecutablePath(cmd[0]);
481 
482                 // Check new executable name once more
483                 if (security != null)
484                     security.checkExec(executablePath);
485             }
486 
487             // Quotation protects from interpretation of the [path] argument as
488             // start of longer path with spaces. Quotation has no influence to
489             // [.exe] extension heuristic.
490             boolean isShell = allowAmbiguousCommands ? isShellFile(executablePath)
491                     : !isExe(executablePath);
492             cmdstr = createCommandLine(
493                     // We need the extended verification procedures
494                     isShell ? VERIFICATION_CMD_BAT
495                             : (allowAmbiguousCommands ? VERIFICATION_WIN32 : VERIFICATION_WIN32_SAFE),
496                     quoteString(executablePath),
497                     cmd);
498         }
499 
500         handle = create(cmdstr, envblock, path,
501                         stdHandles, redirectErrorStream);
502         // Register a cleaning function to close the handle
503         final long local_handle = handle;    // local to prevent capture of this
504         CleanerFactory.cleaner().register(this, () -> closeHandle(local_handle));
505 
506         processHandle = ProcessHandleImpl.getInternal(getProcessId0(handle));
507 
508         java.security.AccessController.doPrivileged(
509         new java.security.PrivilegedAction<Void>() {
510         public Void run() {
511             if (stdHandles[0] == -1L)
512                 stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE;
513             else {
514                 FileDescriptor stdin_fd = new FileDescriptor();
515                 fdAccess.setHandle(stdin_fd, stdHandles[0]);
516                 fdAccess.registerCleanup(stdin_fd);
517                 stdin_stream = new BufferedOutputStream(
518                     new PipeOutputStream(stdin_fd));
519             }
520 
521             if (stdHandles[1] == -1L || forceNullOutputStream)
522                 stdout_stream = ProcessBuilder.NullInputStream.INSTANCE;
523             else {
524                 FileDescriptor stdout_fd = new FileDescriptor();
525                 fdAccess.setHandle(stdout_fd, stdHandles[1]);
526                 fdAccess.registerCleanup(stdout_fd);
527                 stdout_stream = new BufferedInputStream(
528                     new PipeInputStream(stdout_fd));
529             }
530 
531             if (stdHandles[2] == -1L)
532                 stderr_stream = ProcessBuilder.NullInputStream.INSTANCE;
533             else {
534                 FileDescriptor stderr_fd = new FileDescriptor();
535                 fdAccess.setHandle(stderr_fd, stdHandles[2]);
536                 fdAccess.registerCleanup(stderr_fd);
537                 stderr_stream = new PipeInputStream(stderr_fd);
538             }
539 
540             return null; }});
541     }
542 
543     public OutputStream getOutputStream() {
544         return stdin_stream;
545     }
546 
547     public InputStream getInputStream() {
548         return stdout_stream;
549     }
550 
551     public InputStream getErrorStream() {
552         return stderr_stream;
553     }
554 
555     private static final int STILL_ACTIVE = getStillActive();
556     private static native int getStillActive();
557 
558     public int exitValue() {
559         int exitCode = getExitCodeProcess(handle);
560         if (exitCode == STILL_ACTIVE)
561             throw new IllegalThreadStateException("process has not exited");
562         return exitCode;
563     }
564     private static native int getExitCodeProcess(long handle);
565 
566     public int waitFor() throws InterruptedException {
567         boolean attempted = Blocker.begin();
568         try {
569             waitForInterruptibly(handle);
570         } finally {
571             Blocker.end(attempted);
572         }
573         if (Thread.interrupted())
574             throw new InterruptedException();
575         return getExitCodeProcess(handle);
576     }
577 
578     private static native void waitForInterruptibly(long handle);
579 
580     @Override
581     public boolean waitFor(long timeout, TimeUnit unit)
582         throws InterruptedException
583     {
584         long remainingNanos = unit.toNanos(timeout);    // throw NPE before other conditions
585         if (getExitCodeProcess(handle) != STILL_ACTIVE) return true;
586         if (timeout <= 0) return false;
587 
588         long deadline = System.nanoTime() + remainingNanos;
589         do {
590             // Round up to next millisecond
591             long msTimeout = TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L);
592             if (msTimeout < 0) {
593                 // if wraps around then wait a long while
594                 msTimeout = Integer.MAX_VALUE;
595             }
596             boolean attempted = Blocker.begin();
597             try {
598                 waitForTimeoutInterruptibly(handle, msTimeout);
599             } finally {
600                 Blocker.end(attempted);
601             }
602             if (Thread.interrupted())
603                 throw new InterruptedException();
604             if (getExitCodeProcess(handle) != STILL_ACTIVE) {
605                 return true;
606             }
607             remainingNanos = deadline - System.nanoTime();
608         } while (remainingNanos > 0);
609 
610         return (getExitCodeProcess(handle) != STILL_ACTIVE);
611     }
612 
613     private static native void waitForTimeoutInterruptibly(
614         long handle, long timeoutMillis);
615 
616     @Override
617     public void destroy() {
618         terminateProcess(handle);
619     }
620 
621     @Override
622     public CompletableFuture<Process> onExit() {
623         return ProcessHandleImpl.completion(pid(), false)
624                 .handleAsync((exitStatus, unusedThrowable) -> this);
625     }
626 
627     @Override
628     public ProcessHandle toHandle() {
629         @SuppressWarnings("removal")
630         SecurityManager sm = System.getSecurityManager();
631         if (sm != null) {
632             sm.checkPermission(new RuntimePermission("manageProcess"));
633         }
634         return processHandle;
635     }
636 
637     @Override
638     public boolean supportsNormalTermination() {
639         return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
640     }
641 
642     @Override
643     public Process destroyForcibly() {
644         destroy();
645         return this;
646     }
647 
648     private static native void terminateProcess(long handle);
649 
650     @Override
651     public long pid() {
652         return processHandle.pid();
653     }
654 
655     private static native int getProcessId0(long handle);
656 
657     @Override
658     public boolean isAlive() {
659         return isProcessAlive(handle);
660     }
661 
662     private static native boolean isProcessAlive(long handle);
663 
664     /**
665      * The {@code toString} method returns a string consisting of
666      * the native process ID of the process and the exit value of the process.
667      *
668      * @return a string representation of the object.
669      */
670     @Override
671     public String toString() {
672         int exitCode = getExitCodeProcess(handle);
673         return new StringBuilder("Process[pid=").append(pid())
674                 .append(", exitValue=").append(exitCode == STILL_ACTIVE ? "\"not exited\"" : exitCode)
675                 .append("]").toString();
676     }
677 
678     /**
679      * Create a process using the win32 function CreateProcess.
680      * The method is synchronized due to MS kb315939 problem.
681      * All native handles should restore the inherit flag at the end of call.
682      *
683      * @param cmdstr the Windows command line
684      * @param envblock NUL-separated, double-NUL-terminated list of
685      *        environment strings in VAR=VALUE form
686      * @param dir the working directory of the process, or null if
687      *        inheriting the current directory from the parent process
688      * @param stdHandles array of windows HANDLEs.  Indexes 0, 1, and
689      *        2 correspond to standard input, standard output and
690      *        standard error, respectively.  On input, a value of -1
691      *        means to create a pipe to connect child and parent
692      *        processes.  On output, a value which is not -1 is the
693      *        parent pipe handle corresponding to the pipe which has
694      *        been created.  An element of this array is -1 on input
695      *        if and only if it is <em>not</em> -1 on output.
696      * @param redirectErrorStream redirectErrorStream attribute
697      * @return the native subprocess HANDLE returned by CreateProcess
698      */
699     private static synchronized native long create(String cmdstr,
700                                       String envblock,
701                                       String dir,
702                                       long[] stdHandles,
703                                       boolean redirectErrorStream)
704         throws IOException;
705 
706     /**
707      * Opens a file for atomic append. The file is created if it doesn't
708      * already exist.
709      *
710      * @param path the file to open or create
711      * @return the native HANDLE
712      */
713     private static native long openForAtomicAppend(String path)
714         throws IOException;
715 
716     private static native boolean closeHandle(long handle);
717 }