Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/java/lang/UNIXProcess.java
32287 views
/*1* Copyright (c) 1995, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.lang;2627import java.io.BufferedInputStream;28import java.io.BufferedOutputStream;29import java.io.ByteArrayInputStream;30import java.io.FileDescriptor;31import java.io.FileInputStream;32import java.io.FileOutputStream;33import java.io.IOException;34import java.io.InputStream;35import java.io.OutputStream;36import java.util.Arrays;37import java.util.EnumSet;38import java.util.Locale;39import java.util.Set;40import java.util.concurrent.Executors;41import java.util.concurrent.Executor;42import java.util.concurrent.ThreadFactory;43import java.util.concurrent.TimeUnit;44import java.security.AccessController;45import static java.security.AccessController.doPrivileged;46import java.security.PrivilegedAction;47import java.security.PrivilegedActionException;48import java.security.PrivilegedExceptionAction;4950/**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 Buchholz56* @author Volker Simonis (ported to AIX)57*/58final class UNIXProcess extends Process {59private static final sun.misc.JavaIOFileDescriptorAccess fdAccess60= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();6162private final int pid;63private int exitcode;64private boolean hasExited;6566private /* final */ OutputStream stdin;67private /* final */ InputStream stdout;68private /* final */ InputStream stderr;6970// only used on Solaris71private /* final */ DeferredCloseInputStream stdout_inner_stream;7273private static enum LaunchMechanism {74// order IS important!75FORK,76POSIX_SPAWN,77VFORK78}7980private static enum Platform {8182LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK),8384BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),8586SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),8788AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);8990final LaunchMechanism defaultLaunchMechanism;91final Set<LaunchMechanism> validLaunchMechanisms;9293Platform(LaunchMechanism ... launchMechanisms) {94this.defaultLaunchMechanism = launchMechanisms[0];95this.validLaunchMechanisms =96EnumSet.copyOf(Arrays.asList(launchMechanisms));97}9899private String helperPath(String javahome, String osArch) {100switch (this) {101case SOLARIS:102if (osArch.equals("x86")) { osArch = "i386"; }103else if (osArch.equals("x86_64")) { osArch = "amd64"; }104// fall through...105case LINUX:106case AIX:107return javahome + "/lib/" + osArch + "/jspawnhelper";108109case BSD:110return javahome + "/lib/jspawnhelper";111112default:113throw new AssertionError("Unsupported platform: " + this);114}115}116117String helperPath() {118return AccessController.doPrivileged(119(PrivilegedAction<String>) () ->120helperPath(System.getProperty("java.home"),121System.getProperty("os.arch"))122);123}124125LaunchMechanism launchMechanism() {126return AccessController.doPrivileged(127(PrivilegedAction<LaunchMechanism>) () -> {128String s = System.getProperty(129"jdk.lang.Process.launchMechanism");130LaunchMechanism lm;131if (s == null) {132lm = defaultLaunchMechanism;133s = lm.name().toLowerCase(Locale.ENGLISH);134} else {135try {136lm = LaunchMechanism.valueOf(137s.toUpperCase(Locale.ENGLISH));138} catch (IllegalArgumentException e) {139lm = null;140}141}142if (lm == null || !validLaunchMechanisms.contains(lm)) {143throw new Error(144s + " is not a supported " +145"process launch mechanism on this platform."146);147}148return lm;149}150);151}152153static Platform get() {154String osName = AccessController.doPrivileged(155(PrivilegedAction<String>) () -> System.getProperty("os.name")156);157158if (osName.equals("Linux")) { return LINUX; }159if (osName.contains("OS X")) { return BSD; }160if (osName.equals("SunOS")) { return SOLARIS; }161if (osName.equals("AIX")) { return AIX; }162163throw new Error(osName + " is not a supported OS platform.");164}165}166167private static final Platform platform = Platform.get();168private static final LaunchMechanism launchMechanism = platform.launchMechanism();169private static final byte[] helperpath = toCString(platform.helperPath());170171private static byte[] toCString(String s) {172if (s == null)173return null;174byte[] bytes = s.getBytes();175byte[] result = new byte[bytes.length + 1];176System.arraycopy(bytes, 0,177result, 0,178bytes.length);179result[result.length-1] = (byte)0;180return result;181}182183/* this is for the reaping thread */184private native int waitForProcessExit(int pid);185186/**187* Creates a process. Depending on the {@code mode} flag, this is done by188* one of the following mechanisms:189* <pre>190* 1 - fork(2) and exec(2)191* 2 - posix_spawn(3P)192* 3 - vfork(2) and exec(2)193*194* (4 - clone(2) and exec(2) - obsolete and currently disabled in native code)195* </pre>196* @param fds an array of three file descriptors.197* Indexes 0, 1, and 2 correspond to standard input,198* standard output and standard error, respectively. On199* input, a value of -1 means to create a pipe to connect200* child and parent processes. On output, a value which201* is not -1 is the parent pipe fd corresponding to the202* pipe which has been created. An element of this array203* is -1 on input if and only if it is <em>not</em> -1 on204* output.205* @return the pid of the subprocess206*/207private native int forkAndExec(int mode, byte[] helperpath,208byte[] prog,209byte[] argBlock, int argc,210byte[] envBlock, int envc,211byte[] dir,212int[] fds,213boolean redirectErrorStream)214throws IOException;215216/**217* The thread pool of "process reaper" daemon threads.218*/219private static final Executor processReaperExecutor =220doPrivileged((PrivilegedAction<Executor>) () -> {221222ThreadGroup tg = Thread.currentThread().getThreadGroup();223while (tg.getParent() != null) tg = tg.getParent();224ThreadGroup systemThreadGroup = tg;225226ThreadFactory threadFactory = grimReaper -> {227long stackSize = Boolean.getBoolean("jdk.lang.processReaperUseDefaultStackSize") ? 0 : 32768;228Thread t = new Thread(systemThreadGroup, grimReaper,"process reaper", stackSize);229t.setDaemon(true);230// A small attempt (probably futile) to avoid priority inversion231t.setPriority(Thread.MAX_PRIORITY);232return t;233};234235return Executors.newCachedThreadPool(threadFactory);236});237238UNIXProcess(final byte[] prog,239final byte[] argBlock, final int argc,240final byte[] envBlock, final int envc,241final byte[] dir,242final int[] fds,243final boolean redirectErrorStream)244throws IOException {245246pid = forkAndExec(launchMechanism.ordinal() + 1,247helperpath,248prog,249argBlock, argc,250envBlock, envc,251dir,252fds,253redirectErrorStream);254255try {256doPrivileged((PrivilegedExceptionAction<Void>) () -> {257initStreams(fds);258return null;259});260} catch (PrivilegedActionException ex) {261throw (IOException) ex.getException();262}263}264265static FileDescriptor newFileDescriptor(int fd) {266FileDescriptor fileDescriptor = new FileDescriptor();267fdAccess.set(fileDescriptor, fd);268return fileDescriptor;269}270271void initStreams(int[] fds) throws IOException {272switch (platform) {273case LINUX:274case BSD:275stdin = (fds[0] == -1) ?276ProcessBuilder.NullOutputStream.INSTANCE :277new ProcessPipeOutputStream(fds[0]);278279stdout = (fds[1] == -1) ?280ProcessBuilder.NullInputStream.INSTANCE :281new ProcessPipeInputStream(fds[1]);282283stderr = (fds[2] == -1) ?284ProcessBuilder.NullInputStream.INSTANCE :285new ProcessPipeInputStream(fds[2]);286287processReaperExecutor.execute(() -> {288int exitcode = waitForProcessExit(pid);289290synchronized (this) {291this.exitcode = exitcode;292this.hasExited = true;293this.notifyAll();294}295296if (stdout instanceof ProcessPipeInputStream)297((ProcessPipeInputStream) stdout).processExited();298299if (stderr instanceof ProcessPipeInputStream)300((ProcessPipeInputStream) stderr).processExited();301302if (stdin instanceof ProcessPipeOutputStream)303((ProcessPipeOutputStream) stdin).processExited();304});305break;306307case SOLARIS:308stdin = (fds[0] == -1) ?309ProcessBuilder.NullOutputStream.INSTANCE :310new BufferedOutputStream(311new FileOutputStream(newFileDescriptor(fds[0])));312313stdout = (fds[1] == -1) ?314ProcessBuilder.NullInputStream.INSTANCE :315new BufferedInputStream(316stdout_inner_stream =317new DeferredCloseInputStream(318newFileDescriptor(fds[1])));319320stderr = (fds[2] == -1) ?321ProcessBuilder.NullInputStream.INSTANCE :322new DeferredCloseInputStream(newFileDescriptor(fds[2]));323324/*325* For each subprocess forked a corresponding reaper task326* is submitted. That task is the only thread which waits327* for the subprocess to terminate and it doesn't hold any328* locks while doing so. This design allows waitFor() and329* exitStatus() to be safely executed in parallel (and they330* need no native code).331*/332processReaperExecutor.execute(() -> {333int exitcode = waitForProcessExit(pid);334335synchronized (this) {336this.exitcode = exitcode;337this.hasExited = true;338this.notifyAll();339}340});341break;342343case AIX:344stdin = (fds[0] == -1) ?345ProcessBuilder.NullOutputStream.INSTANCE :346new ProcessPipeOutputStream(fds[0]);347348stdout = (fds[1] == -1) ?349ProcessBuilder.NullInputStream.INSTANCE :350new DeferredCloseProcessPipeInputStream(fds[1]);351352stderr = (fds[2] == -1) ?353ProcessBuilder.NullInputStream.INSTANCE :354new DeferredCloseProcessPipeInputStream(fds[2]);355356processReaperExecutor.execute(() -> {357int exitcode = waitForProcessExit(pid);358359synchronized (this) {360this.exitcode = exitcode;361this.hasExited = true;362this.notifyAll();363}364365if (stdout instanceof DeferredCloseProcessPipeInputStream)366((DeferredCloseProcessPipeInputStream) stdout).processExited();367368if (stderr instanceof DeferredCloseProcessPipeInputStream)369((DeferredCloseProcessPipeInputStream) stderr).processExited();370371if (stdin instanceof ProcessPipeOutputStream)372((ProcessPipeOutputStream) stdin).processExited();373});374break;375376default: throw new AssertionError("Unsupported platform: " + platform);377}378}379380public OutputStream getOutputStream() {381return stdin;382}383384public InputStream getInputStream() {385return stdout;386}387388public InputStream getErrorStream() {389return stderr;390}391392public synchronized int waitFor() throws InterruptedException {393while (!hasExited) {394wait();395}396return exitcode;397}398399@Override400public synchronized boolean waitFor(long timeout, TimeUnit unit)401throws InterruptedException402{403long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions404if (hasExited) return true;405if (timeout <= 0) return false;406407long deadline = System.nanoTime() + remainingNanos;408do {409TimeUnit.NANOSECONDS.timedWait(this, remainingNanos);410if (hasExited) {411return true;412}413remainingNanos = deadline - System.nanoTime();414} while (remainingNanos > 0);415return hasExited;416}417418public synchronized int exitValue() {419if (!hasExited) {420throw new IllegalThreadStateException("process hasn't exited");421}422return exitcode;423}424425private static native void destroyProcess(int pid, boolean force);426427private void destroy(boolean force) {428switch (platform) {429case LINUX:430case BSD:431case AIX:432// There is a risk that pid will be recycled, causing us to433// kill the wrong process! So we only terminate processes434// that appear to still be running. Even with this check,435// there is an unavoidable race condition here, but the window436// is very small, and OSes try hard to not recycle pids too437// soon, so this is quite safe.438synchronized (this) {439if (!hasExited)440destroyProcess(pid, force);441}442try { stdin.close(); } catch (IOException ignored) {}443try { stdout.close(); } catch (IOException ignored) {}444try { stderr.close(); } catch (IOException ignored) {}445break;446447case SOLARIS:448// There is a risk that pid will be recycled, causing us to449// kill the wrong process! So we only terminate processes450// that appear to still be running. Even with this check,451// there is an unavoidable race condition here, but the window452// is very small, and OSes try hard to not recycle pids too453// soon, so this is quite safe.454synchronized (this) {455if (!hasExited)456destroyProcess(pid, force);457try {458stdin.close();459if (stdout_inner_stream != null)460stdout_inner_stream.closeDeferred(stdout);461if (stderr instanceof DeferredCloseInputStream)462((DeferredCloseInputStream) stderr)463.closeDeferred(stderr);464} catch (IOException e) {465// ignore466}467}468break;469470default: throw new AssertionError("Unsupported platform: " + platform);471}472}473474public void destroy() {475destroy(false);476}477478@Override479public Process destroyForcibly() {480destroy(true);481return this;482}483484@Override485public synchronized boolean isAlive() {486return !hasExited;487}488489private static native void init();490491static {492init();493}494495/**496* A buffered input stream for a subprocess pipe file descriptor497* that allows the underlying file descriptor to be reclaimed when498* the process exits, via the processExited hook.499*500* This is tricky because we do not want the user-level InputStream to be501* closed until the user invokes close(), and we need to continue to be502* able to read any buffered data lingering in the OS pipe buffer.503*/504private static class ProcessPipeInputStream extends BufferedInputStream {505private final Object closeLock = new Object();506507ProcessPipeInputStream(int fd) {508super(new FileInputStream(newFileDescriptor(fd)));509}510private static byte[] drainInputStream(InputStream in)511throws IOException {512int n = 0;513int j;514byte[] a = null;515while ((j = in.available()) > 0) {516a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);517n += in.read(a, n, j);518}519return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);520}521522/** Called by the process reaper thread when the process exits. */523synchronized void processExited() {524synchronized (closeLock) {525try {526InputStream in = this.in;527// this stream is closed if and only if: in == null528if (in != null) {529byte[] stragglers = drainInputStream(in);530in.close();531this.in = (stragglers == null) ?532ProcessBuilder.NullInputStream.INSTANCE :533new ByteArrayInputStream(stragglers);534}535} catch (IOException ignored) {}536}537}538539@Override540public void close() throws IOException {541// BufferedInputStream#close() is not synchronized unlike most other542// methods. Synchronizing helps avoid race with processExited().543synchronized (closeLock) {544super.close();545}546}547}548549/**550* A buffered output stream for a subprocess pipe file descriptor551* that allows the underlying file descriptor to be reclaimed when552* the process exits, via the processExited hook.553*/554private static class ProcessPipeOutputStream extends BufferedOutputStream {555ProcessPipeOutputStream(int fd) {556super(new FileOutputStream(newFileDescriptor(fd)));557}558559/** Called by the process reaper thread when the process exits. */560synchronized void processExited() {561OutputStream out = this.out;562if (out != null) {563try {564out.close();565} catch (IOException ignored) {566// We know of no reason to get an IOException, but if567// we do, there's nothing else to do but carry on.568}569this.out = ProcessBuilder.NullOutputStream.INSTANCE;570}571}572}573574// A FileInputStream that supports the deferment of the actual close575// operation until the last pending I/O operation on the stream has576// finished. This is required on Solaris because we must close the stdin577// and stdout streams in the destroy method in order to reclaim the578// underlying file descriptors. Doing so, however, causes any thread579// currently blocked in a read on one of those streams to receive an580// IOException("Bad file number"), which is incompatible with historical581// behavior. By deferring the close we allow any pending reads to see -1582// (EOF) as they did before.583//584private static class DeferredCloseInputStream extends FileInputStream585{586DeferredCloseInputStream(FileDescriptor fd) {587super(fd);588}589590private Object lock = new Object(); // For the following fields591private boolean closePending = false;592private int useCount = 0;593private InputStream streamToClose;594595private void raise() {596synchronized (lock) {597useCount++;598}599}600601private void lower() throws IOException {602synchronized (lock) {603useCount--;604if (useCount == 0 && closePending) {605streamToClose.close();606}607}608}609610// stc is the actual stream to be closed; it might be this object, or611// it might be an upstream object for which this object is downstream.612//613private void closeDeferred(InputStream stc) throws IOException {614synchronized (lock) {615if (useCount == 0) {616stc.close();617} else {618closePending = true;619streamToClose = stc;620}621}622}623624public void close() throws IOException {625synchronized (lock) {626useCount = 0;627closePending = false;628}629super.close();630}631632public int read() throws IOException {633raise();634try {635return super.read();636} finally {637lower();638}639}640641public int read(byte[] b) throws IOException {642raise();643try {644return super.read(b);645} finally {646lower();647}648}649650public int read(byte[] b, int off, int len) throws IOException {651raise();652try {653return super.read(b, off, len);654} finally {655lower();656}657}658659public long skip(long n) throws IOException {660raise();661try {662return super.skip(n);663} finally {664lower();665}666}667668public int available() throws IOException {669raise();670try {671return super.available();672} finally {673lower();674}675}676}677678/**679* A buffered input stream for a subprocess pipe file descriptor680* that allows the underlying file descriptor to be reclaimed when681* the process exits, via the processExited hook.682*683* This is tricky because we do not want the user-level InputStream to be684* closed until the user invokes close(), and we need to continue to be685* able to read any buffered data lingering in the OS pipe buffer.686*687* On AIX this is especially tricky, because the 'close()' system call688* will block if another thread is at the same time blocked in a file689* operation (e.g. 'read()') on the same file descriptor. We therefore690* combine 'ProcessPipeInputStream' approach used on Linux and Bsd691* with the DeferredCloseInputStream approach used on Solaris. This means692* that every potentially blocking operation on the file descriptor693* increments a counter before it is executed and decrements it once it694* finishes. The 'close()' operation will only be executed if there are695* no pending operations. Otherwise it is deferred after the last pending696* operation has finished.697*698*/699private static class DeferredCloseProcessPipeInputStream700extends BufferedInputStream {701702private final Object closeLock = new Object();703private int useCount = 0;704private boolean closePending = false;705706DeferredCloseProcessPipeInputStream(int fd) {707super(new FileInputStream(newFileDescriptor(fd)));708}709710private InputStream drainInputStream(InputStream in)711throws IOException {712int n = 0;713int j;714byte[] a = null;715synchronized (closeLock) {716if (buf == null) // asynchronous close()?717return null; // discard718j = in.available();719}720while (j > 0) {721a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);722synchronized (closeLock) {723if (buf == null) // asynchronous close()?724return null; // discard725n += in.read(a, n, j);726j = in.available();727}728}729return (a == null) ?730ProcessBuilder.NullInputStream.INSTANCE :731new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));732}733734/** Called by the process reaper thread when the process exits. */735synchronized void processExited() {736try {737InputStream in = this.in;738if (in != null) {739InputStream stragglers = drainInputStream(in);740in.close();741this.in = stragglers;742}743} catch (IOException ignored) { }744}745746private void raise() {747synchronized (closeLock) {748useCount++;749}750}751752private void lower() throws IOException {753synchronized (closeLock) {754useCount--;755if (useCount == 0 && closePending) {756closePending = false;757super.close();758}759}760}761762@Override763public int read() throws IOException {764raise();765try {766return super.read();767} finally {768lower();769}770}771772@Override773public int read(byte[] b) throws IOException {774raise();775try {776return super.read(b);777} finally {778lower();779}780}781782@Override783public int read(byte[] b, int off, int len) throws IOException {784raise();785try {786return super.read(b, off, len);787} finally {788lower();789}790}791792@Override793public long skip(long n) throws IOException {794raise();795try {796return super.skip(n);797} finally {798lower();799}800}801802@Override803public int available() throws IOException {804raise();805try {806return super.available();807} finally {808lower();809}810}811812@Override813public void close() throws IOException {814// BufferedInputStream#close() is not synchronized unlike most other815// methods. Synchronizing helps avoid racing with drainInputStream().816synchronized (closeLock) {817if (useCount == 0) {818super.close();819}820else {821closePending = true;822}823}824}825}826}827828829