< prev index next >

src/java.base/unix/classes/java/lang/ProcessImpl.java

Print this page




   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.lang.ProcessBuilder.Redirect;

  29 import java.io.BufferedInputStream;
  30 import java.io.BufferedOutputStream;
  31 import java.io.ByteArrayInputStream;

  32 import java.io.FileDescriptor;
  33 import java.io.FileInputStream;
  34 import java.io.FileOutputStream;
  35 import java.io.IOException;
  36 import java.io.InputStream;
  37 import java.io.OutputStream;






  38 import java.util.Arrays;
  39 import java.util.EnumSet;
  40 import java.util.Locale;
  41 import java.util.Set;
  42 import java.util.concurrent.CompletableFuture;
  43 import java.util.concurrent.TimeUnit;
  44 import java.security.AccessController;
  45 import static java.security.AccessController.doPrivileged;
  46 import java.security.PrivilegedAction;
  47 import java.security.PrivilegedActionException;
  48 import java.security.PrivilegedExceptionAction;
  49 
  50 /**
  51  * java.lang.Process subclass in the UNIX environment.
  52  *
  53  * @author Mario Wolczko and Ross Knippel.
  54  * @author Konstantin Kladko (ported to Linux and Bsd)
  55  * @author Martin Buchholz
  56  * @author Volker Simonis (ported to AIX)
  57  * @since   1.5
  58  */
  59 final class ProcessImpl extends Process {
  60     private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
  61         = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
  62 
  63     // Linux platforms support a normal (non-forcible) kill signal.
  64     static final boolean SUPPORTS_DESTROY_FORCIBLY = false;
  65 
  66     private final int pid;
  67     private final ProcessHandle processHandle;
  68     private int exitcode;
  69     private boolean hasExited;
  70 
  71     private /* final */ OutputStream stdin;
  72     private /* final */ InputStream  stdout;
  73     private /* final */ InputStream  stderr;
  74 
  75     // only used on Solaris
  76     private /* final */ DeferredCloseInputStream stdout_inner_stream;
  77 




  78     private static enum LaunchMechanism {
  79         // order IS important!
  80         FORK,
  81         POSIX_SPAWN,
  82         VFORK
  83     }
  84 
  85     private static enum Platform {
  86 
  87         LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK),
  88 
  89         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  90 
  91         SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  92 
  93         AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
  94 
  95         final LaunchMechanism defaultLaunchMechanism;
  96         final Set<LaunchMechanism> validLaunchMechanisms;
  97 


 204             args[i] = cmdarray[i+1].getBytes();
 205             size += args[i].length;
 206         }
 207         byte[] argBlock = new byte[size];
 208         int i = 0;
 209         for (byte[] arg : args) {
 210             System.arraycopy(arg, 0, argBlock, i, arg.length);
 211             i += arg.length + 1;
 212             // No need to write NUL bytes explicitly
 213         }
 214 
 215         int[] envc = new int[1];
 216         byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
 217 
 218         int[] std_fds;
 219 
 220         FileInputStream  f0 = null;
 221         FileOutputStream f1 = null;
 222         FileOutputStream f2 = null;
 223 




 224         try {
 225             if (redirects == null) {
 226                 std_fds = new int[] { -1, -1, -1 };
 227             } else {
 228                 std_fds = new int[3];
 229 
 230                 if (redirects[0] == Redirect.PIPE)
 231                     std_fds[0] = -1;
 232                 else if (redirects[0] == Redirect.INHERIT)






 233                     std_fds[0] = 0;
 234                 else {
 235                     f0 = new FileInputStream(redirects[0].file());
 236                     std_fds[0] = fdAccess.get(f0.getFD());
 237                 }
 238 
 239                 if (redirects[1] == Redirect.PIPE)
 240                     std_fds[1] = -1;
 241                 else if (redirects[1] == Redirect.INHERIT)






 242                     std_fds[1] = 1;
 243                 else {
 244                     f1 = new FileOutputStream(redirects[1].file(),
 245                             redirects[1].append());
 246                     std_fds[1] = fdAccess.get(f1.getFD());
 247                 }
 248 
 249                 if (redirects[2] == Redirect.PIPE)
 250                     std_fds[2] = -1;
 251                 else if (redirects[2] == Redirect.INHERIT)










 252                     std_fds[2] = 2;
 253                 else {
 254                     f2 = new FileOutputStream(redirects[2].file(),
 255                             redirects[2].append());
 256                     std_fds[2] = fdAccess.get(f2.getFD());
 257                 }
 258             }
 259 
 260             return new ProcessImpl
 261                     (toCString(cmdarray[0]),
 262                             argBlock, args.length,
 263                             envBlock, envc[0],
 264                             toCString(dir),
 265                             std_fds,

 266                             redirectErrorStream);







 267         } finally {
 268             // In theory, close() can throw IOException
 269             // (although it is rather unlikely to happen here)
 270             try { if (f0 != null) f0.close(); }
 271             finally {
 272                 try { if (f1 != null) f1.close(); }
 273                 finally { if (f2 != null) f2.close(); }


 274             }
 275         }










 276     }
 277 
 278 
 279     /**
 280      * Creates a process. Depending on the {@code mode} flag, this is done by
 281      * one of the following mechanisms:
 282      * <pre>
 283      *   1 - fork(2) and exec(2)
 284      *   2 - posix_spawn(3P)
 285      *   3 - vfork(2) and exec(2)
 286      * </pre>
 287      * @param fds an array of three file descriptors.
 288      *        Indexes 0, 1, and 2 correspond to standard input,
 289      *        standard output and standard error, respectively.  On
 290      *        input, a value of -1 means to create a pipe to connect
 291      *        child and parent processes.  On output, a value which
 292      *        is not -1 is the parent pipe fd corresponding to the
 293      *        pipe which has been created.  An element of this array
 294      *        is -1 on input if and only if it is <em>not</em> -1 on
 295      *        output.
 296      * @return the pid of the subprocess
 297      */
 298     private native int forkAndExec(int mode, byte[] helperpath,
 299                                    byte[] prog,
 300                                    byte[] argBlock, int argc,
 301                                    byte[] envBlock, int envc,
 302                                    byte[] dir,
 303                                    int[] fds,
 304                                    boolean redirectErrorStream)
 305         throws IOException;
 306 
 307     private ProcessImpl(final byte[] prog,
 308                 final byte[] argBlock, final int argc,
 309                 final byte[] envBlock, final int envc,
 310                 final byte[] dir,
 311                 final int[] fds,

 312                 final boolean redirectErrorStream)
 313             throws IOException {
 314 
 315         pid = forkAndExec(launchMechanism.ordinal() + 1,
 316                           helperpath,
 317                           prog,
 318                           argBlock, argc,
 319                           envBlock, envc,
 320                           dir,
 321                           fds,
 322                           redirectErrorStream);
 323         processHandle = ProcessHandleImpl.getUnchecked(pid);
 324 
 325         try {
 326             doPrivileged((PrivilegedExceptionAction<Void>) () -> {
 327                 initStreams(fds);
 328                 return null;
 329             });
 330         } catch (PrivilegedActionException ex) {
 331             throw (IOException) ex.getException();
 332         }




 333     }
 334 
 335     static FileDescriptor newFileDescriptor(int fd) {
 336         FileDescriptor fileDescriptor = new FileDescriptor();
 337         fdAccess.set(fileDescriptor, fd);
 338         return fileDescriptor;
 339     }
 340 
 341     void initStreams(int[] fds) throws IOException {
 342         switch (platform) {
 343             case LINUX:
 344             case BSD:
 345                 stdin = (fds[0] == -1) ?
 346                         ProcessBuilder.NullOutputStream.INSTANCE :
 347                         new ProcessPipeOutputStream(fds[0]);
 348 
 349                 stdout = (fds[1] == -1) ?
 350                          ProcessBuilder.NullInputStream.INSTANCE :
 351                          new ProcessPipeInputStream(fds[1]);
 352 


 439                         ((ProcessPipeOutputStream) stdin).processExited();
 440 
 441                     return null;
 442                 });
 443                 break;
 444 
 445             default: throw new AssertionError("Unsupported platform: " + platform);
 446         }
 447     }
 448 
 449     public OutputStream getOutputStream() {
 450         return stdin;
 451     }
 452 
 453     public InputStream getInputStream() {
 454         return stdout;
 455     }
 456 
 457     public InputStream getErrorStream() {
 458         return stderr;












 459     }
 460 
 461     public synchronized int waitFor() throws InterruptedException {
 462         while (!hasExited) {
 463             wait();
 464         }
 465         return exitcode;
 466     }
 467 
 468     @Override
 469     public synchronized boolean waitFor(long timeout, TimeUnit unit)
 470         throws InterruptedException
 471     {
 472         long remainingNanos = unit.toNanos(timeout);    // throw NPE before other conditions
 473         if (hasExited) return true;
 474         if (timeout <= 0) return false;
 475 
 476         long deadline = System.nanoTime() + remainingNanos;
 477         do {
 478             // Round up to next millisecond




   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 sun.nio.ch.SelChImpl;
  29 
  30 import java.io.BufferedInputStream;
  31 import java.io.BufferedOutputStream;
  32 import java.io.ByteArrayInputStream;
  33 import java.io.Closeable;
  34 import java.io.FileDescriptor;
  35 import java.io.FileInputStream;
  36 import java.io.FileOutputStream;
  37 import java.io.IOException;
  38 import java.io.InputStream;
  39 import java.io.OutputStream;
  40 import java.lang.ProcessBuilder.Redirect;
  41 import java.nio.channels.Pipe;
  42 import java.security.AccessController;
  43 import java.security.PrivilegedAction;
  44 import java.security.PrivilegedActionException;
  45 import java.security.PrivilegedExceptionAction;
  46 import java.util.Arrays;
  47 import java.util.EnumSet;
  48 import java.util.Locale;
  49 import java.util.Set;
  50 import java.util.concurrent.CompletableFuture;
  51 import java.util.concurrent.TimeUnit;
  52 
  53 import static java.security.AccessController.doPrivileged;



  54 
  55 /**
  56  * java.lang.Process subclass in the UNIX environment.
  57  *
  58  * @author Mario Wolczko and Ross Knippel.
  59  * @author Konstantin Kladko (ported to Linux and Bsd)
  60  * @author Martin Buchholz
  61  * @author Volker Simonis (ported to AIX)
  62  * @since   1.5
  63  */
  64 final class ProcessImpl extends Process {
  65     private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
  66         = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
  67 
  68     // Linux platforms support a normal (non-forcible) kill signal.
  69     static final boolean SUPPORTS_DESTROY_FORCIBLY = false;
  70 
  71     private final int pid;
  72     private final ProcessHandle processHandle;
  73     private int exitcode;
  74     private boolean hasExited;
  75 
  76     private /* final */ OutputStream stdin;
  77     private /* final */ InputStream  stdout;
  78     private /* final */ InputStream  stderr;
  79 
  80     // only used on Solaris
  81     private /* final */ DeferredCloseInputStream stdout_inner_stream;
  82 
  83     private final Pipe.SinkChannel stdinChannel;
  84     private final Pipe.SourceChannel stdoutChannel;
  85     private final Pipe.SourceChannel stderrChannel;
  86 
  87     private static enum LaunchMechanism {
  88         // order IS important!
  89         FORK,
  90         POSIX_SPAWN,
  91         VFORK
  92     }
  93 
  94     private static enum Platform {
  95 
  96         LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK),
  97 
  98         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
  99 
 100         SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
 101 
 102         AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
 103 
 104         final LaunchMechanism defaultLaunchMechanism;
 105         final Set<LaunchMechanism> validLaunchMechanisms;
 106 


 213             args[i] = cmdarray[i+1].getBytes();
 214             size += args[i].length;
 215         }
 216         byte[] argBlock = new byte[size];
 217         int i = 0;
 218         for (byte[] arg : args) {
 219             System.arraycopy(arg, 0, argBlock, i, arg.length);
 220             i += arg.length + 1;
 221             // No need to write NUL bytes explicitly
 222         }
 223 
 224         int[] envc = new int[1];
 225         byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
 226 
 227         int[] std_fds;
 228 
 229         FileInputStream  f0 = null;
 230         FileOutputStream f1 = null;
 231         FileOutputStream f2 = null;
 232 
 233         Pipe p0 = null;
 234         Pipe p1 = null;
 235         Pipe p2 = null;
 236 
 237         try {
 238             if (redirects == null) {
 239                 std_fds = new int[] { -1, -1, -1 };
 240             } else {
 241                 std_fds = new int[3];
 242 
 243                 if (redirects[0] == Redirect.PIPE) {
 244                     std_fds[0] = -1;
 245                 } else if (redirects[0] == Redirect.PIPE_CHANNEL) {
 246                     p0 = Pipe.open();
 247                     if (!(p0.source() instanceof SelChImpl)) {
 248                         throw new UnsupportedOperationException("Unsupported SelectorProvider");
 249                     }
 250                     std_fds[0] = ((SelChImpl) p0.source()).getFDVal();
 251                 } else if (redirects[0] == Redirect.INHERIT) {
 252                     std_fds[0] = 0;
 253                 } else {
 254                     f0 = new FileInputStream(redirects[0].file());
 255                     std_fds[0] = fdAccess.get(f0.getFD());
 256                 }
 257 
 258                 if (redirects[1] == Redirect.PIPE) {
 259                     std_fds[1] = -1;
 260                 } else if (redirects[1] == Redirect.PIPE_CHANNEL) {
 261                     p1 = Pipe.open();
 262                     if (!(p1.sink() instanceof SelChImpl)) {
 263                         throw new UnsupportedOperationException("Unsupported SelectorProvider");
 264                     }
 265                     std_fds[1] = ((SelChImpl) p1.sink()).getFDVal();
 266                 } else if (redirects[1] == Redirect.INHERIT) {
 267                     std_fds[1] = 1;
 268                 } else {
 269                     f1 = new FileOutputStream(redirects[1].file(),
 270                             redirects[1].append());
 271                     std_fds[1] = fdAccess.get(f1.getFD());
 272                 }
 273 
 274                 if (redirects[2] == Redirect.PIPE) {
 275                     std_fds[2] = -1;
 276                 } else if (redirects[2] == Redirect.PIPE_CHANNEL) {
 277                     if (redirectErrorStream) {
 278                         std_fds[2] = -1;
 279                     } else {
 280                         p2 = Pipe.open();
 281                         if (!(p2.sink() instanceof SelChImpl)) {
 282                             throw new UnsupportedOperationException("Unsupported SelectorProvider");
 283                         }
 284                         std_fds[2] = ((SelChImpl) p2.sink()).getFDVal();
 285                     }
 286                 } else if (redirects[2] == Redirect.INHERIT) {
 287                     std_fds[2] = 2;
 288                 } else {
 289                     f2 = new FileOutputStream(redirects[2].file(),
 290                             redirects[2].append());
 291                     std_fds[2] = fdAccess.get(f2.getFD());
 292                 }
 293             }
 294 
 295             return new ProcessImpl
 296                     (toCString(cmdarray[0]),
 297                             argBlock, args.length,
 298                             envBlock, envc[0],
 299                             toCString(dir),
 300                             std_fds,
 301                             new Pipe[] { p0, p1, p2 },
 302                             redirectErrorStream);
 303         } catch (Exception e) {
 304             // close unused pipe ends
 305             if (p0 != null) close(p0.sink());
 306             if (p1 != null) close(p1.source());
 307             if (p2 != null) close(p2.source());
 308             // re-throw
 309             throw e;
 310         } finally {
 311             // close dup-ed file handles
 312             close(f0);
 313             close(f1);
 314             close(f2);
 315             // close dup-ed pipe ends
 316             if (p0 != null) close(p0.source());
 317             if (p1 != null) close(p1.sink());
 318             if (p2 != null) close(p2.sink());
 319         }
 320     }
 321 
 322     private static IOException close(Closeable closeable) {
 323         if (closeable != null) {
 324             try {
 325                 closeable.close();
 326             } catch (IOException e) {
 327                 return e;
 328             }
 329         }
 330         return null;
 331     }
 332 
 333 
 334     /**
 335      * Creates a process. Depending on the {@code mode} flag, this is done by
 336      * one of the following mechanisms:
 337      * <pre>
 338      *   1 - fork(2) and exec(2)
 339      *   2 - posix_spawn(3P)
 340      *   3 - vfork(2) and exec(2)
 341      * </pre>
 342      * @param fds an array of three file descriptors.
 343      *        Indexes 0, 1, and 2 correspond to standard input,
 344      *        standard output and standard error, respectively.  On
 345      *        input, a value of -1 means to create a pipe to connect
 346      *        child and parent processes.  On output, a value which
 347      *        is not -1 is the parent pipe fd corresponding to the
 348      *        pipe which has been created.  An element of this array
 349      *        is -1 on input if and only if it is <em>not</em> -1 on
 350      *        output.
 351      * @return the pid of the subprocess
 352      */
 353     private native int forkAndExec(int mode, byte[] helperpath,
 354                                    byte[] prog,
 355                                    byte[] argBlock, int argc,
 356                                    byte[] envBlock, int envc,
 357                                    byte[] dir,
 358                                    int[] fds,
 359                                    boolean redirectErrorStream)
 360         throws IOException;
 361 
 362     private ProcessImpl(final byte[] prog,
 363                 final byte[] argBlock, final int argc,
 364                 final byte[] envBlock, final int envc,
 365                 final byte[] dir,
 366                 final int[] fds,
 367                 final Pipe[] pipes,
 368                 final boolean redirectErrorStream)
 369             throws IOException {
 370 
 371         pid = forkAndExec(launchMechanism.ordinal() + 1,
 372                           helperpath,
 373                           prog,
 374                           argBlock, argc,
 375                           envBlock, envc,
 376                           dir,
 377                           fds,
 378                           redirectErrorStream);
 379         processHandle = ProcessHandleImpl.getUnchecked(pid);
 380 
 381         try {
 382             doPrivileged((PrivilegedExceptionAction<Void>) () -> {
 383                 initStreams(fds);
 384                 return null;
 385             });
 386         } catch (PrivilegedActionException ex) {
 387             throw (IOException) ex.getException();
 388         }
 389 
 390         stdinChannel = (pipes[0] == null) ? null : pipes[0].sink();
 391         stdoutChannel = (pipes[1] == null) ? null : pipes[1].source();
 392         stderrChannel = (pipes[2] == null) ? null : pipes[2].source();
 393     }
 394 
 395     static FileDescriptor newFileDescriptor(int fd) {
 396         FileDescriptor fileDescriptor = new FileDescriptor();
 397         fdAccess.set(fileDescriptor, fd);
 398         return fileDescriptor;
 399     }
 400 
 401     void initStreams(int[] fds) throws IOException {
 402         switch (platform) {
 403             case LINUX:
 404             case BSD:
 405                 stdin = (fds[0] == -1) ?
 406                         ProcessBuilder.NullOutputStream.INSTANCE :
 407                         new ProcessPipeOutputStream(fds[0]);
 408 
 409                 stdout = (fds[1] == -1) ?
 410                          ProcessBuilder.NullInputStream.INSTANCE :
 411                          new ProcessPipeInputStream(fds[1]);
 412 


 499                         ((ProcessPipeOutputStream) stdin).processExited();
 500 
 501                     return null;
 502                 });
 503                 break;
 504 
 505             default: throw new AssertionError("Unsupported platform: " + platform);
 506         }
 507     }
 508 
 509     public OutputStream getOutputStream() {
 510         return stdin;
 511     }
 512 
 513     public InputStream getInputStream() {
 514         return stdout;
 515     }
 516 
 517     public InputStream getErrorStream() {
 518         return stderr;
 519     }
 520 
 521     public Pipe.SinkChannel getOutputChannel() {
 522         return stdinChannel;
 523     }
 524 
 525     public Pipe.SourceChannel getInputChannel() {
 526         return stdoutChannel;
 527     }
 528 
 529     public Pipe.SourceChannel getErrorChannel() {
 530         return stderrChannel;
 531     }
 532 
 533     public synchronized int waitFor() throws InterruptedException {
 534         while (!hasExited) {
 535             wait();
 536         }
 537         return exitcode;
 538     }
 539 
 540     @Override
 541     public synchronized boolean waitFor(long timeout, TimeUnit unit)
 542         throws InterruptedException
 543     {
 544         long remainingNanos = unit.toNanos(timeout);    // throw NPE before other conditions
 545         if (hasExited) return true;
 546         if (timeout <= 0) return false;
 547 
 548         long deadline = System.nanoTime() + remainingNanos;
 549         do {
 550             // Round up to next millisecond


< prev index next >