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 }