1 /*
  2  * Copyright (c) 2003, 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.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 java.security.PrivilegedAction;
 46 import java.security.PrivilegedActionException;
 47 import java.security.PrivilegedExceptionAction;
 48 import java.util.Properties;
 49 import java.util.concurrent.locks.Condition;
 50 import java.util.concurrent.locks.ReentrantLock;
 51 
 52 import jdk.internal.access.JavaIOFileDescriptorAccess;
 53 import jdk.internal.access.SharedSecrets;
 54 import jdk.internal.util.StaticProperty;
 55 import sun.security.action.GetPropertyAction;
 56 
 57 /**
 58  * java.lang.Process subclass in the UNIX environment.
 59  *
 60  * @author Mario Wolczko and Ross Knippel.
 61  * @author Konstantin Kladko (ported to Linux and Bsd)
 62  * @author Martin Buchholz
 63  * @author Volker Simonis (ported to AIX)
 64  * @since   1.5
 65  */
 66 final class ProcessImpl extends Process {
 67     private static final JavaIOFileDescriptorAccess fdAccess
 68         = SharedSecrets.getJavaIOFileDescriptorAccess();
 69 
 70     // Linux platforms support a normal (non-forcible) kill signal.
 71     static final boolean SUPPORTS_NORMAL_TERMINATION = true;
 72 
 73     private final int pid;
 74     private final ProcessHandleImpl processHandle;
 75     private int exitcode;
 76     private boolean hasExited;
 77 
 78     private final ReentrantLock lock = new ReentrantLock();
 79     private final Condition condition = lock.newCondition();
 80 
 81     private /* final */ OutputStream stdin;
 82     private /* final */ InputStream  stdout;
 83     private /* final */ InputStream  stderr;
 84 
 85     private static enum LaunchMechanism {
 86         // order IS important!
 87         FORK,
 88         POSIX_SPAWN,
 89         VFORK
 90     }
 91 
 92     private static enum Platform {
 93 
 94         LINUX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.VFORK, LaunchMechanism.FORK),
 95 
 96         BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
 97 
 98         AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
 99 
100         final LaunchMechanism defaultLaunchMechanism;
101         final Set<LaunchMechanism> validLaunchMechanisms;
102 
103         Platform(LaunchMechanism ... launchMechanisms) {
104             this.defaultLaunchMechanism = launchMechanisms[0];
105             this.validLaunchMechanisms =
106                 EnumSet.copyOf(Arrays.asList(launchMechanisms));
107         }
108 
109         @SuppressWarnings("removal")
110         LaunchMechanism launchMechanism() {
111             return AccessController.doPrivileged(
112                 (PrivilegedAction<LaunchMechanism>) () -> {
113                     String s = System.getProperty(
114                         "jdk.lang.Process.launchMechanism");
115                     LaunchMechanism lm;
116                     if (s == null) {
117                         lm = defaultLaunchMechanism;
118                         s = lm.name().toLowerCase(Locale.ENGLISH);
119                     } else {
120                         try {
121                             lm = LaunchMechanism.valueOf(
122                                 s.toUpperCase(Locale.ENGLISH));
123                         } catch (IllegalArgumentException e) {
124                             lm = null;
125                         }
126                     }
127                     if (lm == null || !validLaunchMechanisms.contains(lm)) {
128                         throw new Error(
129                             s + " is not a supported " +
130                             "process launch mechanism on this platform."
131                         );
132                     }
133                     return lm;
134                 }
135             );
136         }
137 
138         static Platform get() {
139             String osName = GetPropertyAction.privilegedGetProperty("os.name");
140 
141             if (osName.equals("Linux")) { return LINUX; }
142             if (osName.contains("OS X")) { return BSD; }
143             if (osName.equals("AIX")) { return AIX; }
144 
145             throw new Error(osName + " is not a supported OS platform.");
146         }
147     }
148 
149     private static final Platform platform = Platform.get();
150     private static final LaunchMechanism launchMechanism = platform.launchMechanism();
151     private static final byte[] helperpath = toCString(StaticProperty.javaHome() + "/lib/jspawnhelper");
152 
153     private static byte[] toCString(String s) {
154         if (s == null)
155             return null;
156         byte[] bytes = s.getBytes();
157         byte[] result = new byte[bytes.length + 1];
158         System.arraycopy(bytes, 0,
159                          result, 0,
160                          bytes.length);
161         result[result.length-1] = (byte)0;
162         return result;
163     }
164 
165     // Only for use by ProcessBuilder.start()
166     static Process start(String[] cmdarray,
167                          java.util.Map<String,String> environment,
168                          String dir,
169                          ProcessBuilder.Redirect[] redirects,
170                          boolean redirectErrorStream)
171             throws IOException
172     {
173         assert cmdarray != null && cmdarray.length > 0;
174 
175         // Convert arguments to a contiguous block; it's easier to do
176         // memory management in Java than in C.
177         byte[][] args = new byte[cmdarray.length-1][];
178         int size = args.length; // For added NUL bytes
179         for (int i = 0; i < args.length; i++) {
180             args[i] = cmdarray[i+1].getBytes();
181             size += args[i].length;
182         }
183         byte[] argBlock = new byte[size];
184         int i = 0;
185         for (byte[] arg : args) {
186             System.arraycopy(arg, 0, argBlock, i, arg.length);
187             i += arg.length + 1;
188             // No need to write NUL bytes explicitly
189         }
190 
191         int[] envc = new int[1];
192         byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
193 
194         int[] std_fds;
195 
196         FileInputStream  f0 = null;
197         FileOutputStream f1 = null;
198         FileOutputStream f2 = null;
199 
200         try {
201             boolean forceNullOutputStream = false;
202             if (redirects == null) {
203                 std_fds = new int[] { -1, -1, -1 };
204             } else {
205                 std_fds = new int[3];
206 
207                 if (redirects[0] == Redirect.PIPE) {
208                     std_fds[0] = -1;
209                 } else if (redirects[0] == Redirect.INHERIT) {
210                     std_fds[0] = 0;
211                 } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
212                     std_fds[0] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd());
213                 } else {
214                     f0 = new FileInputStream(redirects[0].file());
215                     std_fds[0] = fdAccess.get(f0.getFD());
216                 }
217 
218                 if (redirects[1] == Redirect.PIPE) {
219                     std_fds[1] = -1;
220                 } else if (redirects[1] == Redirect.INHERIT) {
221                     std_fds[1] = 1;
222                 } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
223                     std_fds[1] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
224                     // Force getInputStream to return a null stream,
225                     // the fd is directly assigned to the next process.
226                     forceNullOutputStream = true;
227                 } else {
228                     f1 = new FileOutputStream(redirects[1].file(),
229                             redirects[1].append());
230                     std_fds[1] = fdAccess.get(f1.getFD());
231                 }
232 
233                 if (redirects[2] == Redirect.PIPE) {
234                     std_fds[2] = -1;
235                 } else if (redirects[2] == Redirect.INHERIT) {
236                     std_fds[2] = 2;
237                 } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
238                     std_fds[2] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
239                 } else {
240                     f2 = new FileOutputStream(redirects[2].file(),
241                             redirects[2].append());
242                     std_fds[2] = fdAccess.get(f2.getFD());
243                 }
244             }
245 
246             Process p = new ProcessImpl
247                     (toCString(cmdarray[0]),
248                             argBlock, args.length,
249                             envBlock, envc[0],
250                             toCString(dir),
251                             std_fds,
252                             forceNullOutputStream,
253                             redirectErrorStream);
254             if (redirects != null) {
255                 // Copy the fd's if they are to be redirected to another process
256                 if (std_fds[0] >= 0 &&
257                         redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
258                     fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), std_fds[0]);
259                 }
260                 if (std_fds[1] >= 0 &&
261                         redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
262                     fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), std_fds[1]);
263                 }
264                 if (std_fds[2] >= 0 &&
265                         redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
266                     fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), std_fds[2]);
267                 }
268             }
269             return p;
270         } finally {
271             // In theory, close() can throw IOException
272             // (although it is rather unlikely to happen here)
273             try { if (f0 != null) f0.close(); }
274             finally {
275                 try { if (f1 != null) f1.close(); }
276                 finally { if (f2 != null) f2.close(); }
277             }
278         }
279     }
280 
281 
282     /**
283      * Creates a process. Depending on the {@code mode} flag, this is done by
284      * one of the following mechanisms:
285      * <pre>
286      *   1 - fork(2) and exec(2)
287      *   2 - posix_spawn(3P)
288      *   3 - vfork(2) and exec(2)
289      * </pre>
290      * @param fds an array of three file descriptors.
291      *        Indexes 0, 1, and 2 correspond to standard input,
292      *        standard output and standard error, respectively.  On
293      *        input, a value of -1 means to create a pipe to connect
294      *        child and parent processes.  On output, a value which
295      *        is not -1 is the parent pipe fd corresponding to the
296      *        pipe which has been created.  An element of this array
297      *        is -1 on input if and only if it is <em>not</em> -1 on
298      *        output.
299      * @return the pid of the subprocess
300      */
301     private native int forkAndExec(int mode, byte[] helperpath,
302                                    byte[] prog,
303                                    byte[] argBlock, int argc,
304                                    byte[] envBlock, int envc,
305                                    byte[] dir,
306                                    int[] fds,
307                                    boolean redirectErrorStream)
308         throws IOException;
309 
310     @SuppressWarnings("removal")
311     private ProcessImpl(final byte[] prog,
312                 final byte[] argBlock, final int argc,
313                 final byte[] envBlock, final int envc,
314                 final byte[] dir,
315                 final int[] fds,
316                 final boolean forceNullOutputStream,
317                 final boolean redirectErrorStream)
318             throws IOException {
319 
320         pid = forkAndExec(launchMechanism.ordinal() + 1,
321                           helperpath,
322                           prog,
323                           argBlock, argc,
324                           envBlock, envc,
325                           dir,
326                           fds,
327                           redirectErrorStream);
328         processHandle = ProcessHandleImpl.getInternal(pid);
329 
330         try {
331             AccessController.doPrivileged((PrivilegedExceptionAction<Void>) () -> {
332                 initStreams(fds, forceNullOutputStream);
333                 return null;
334             });
335         } catch (PrivilegedActionException ex) {
336             throw (IOException) ex.getCause();
337         }
338     }
339 
340     static FileDescriptor newFileDescriptor(int fd) {
341         FileDescriptor fileDescriptor = new FileDescriptor();
342         fdAccess.set(fileDescriptor, fd);
343         return fileDescriptor;
344     }
345 
346     /**
347      * Initialize the streams from the file descriptors.
348      * @param fds array of stdin, stdout, stderr fds
349      * @param forceNullOutputStream true if the stdout is being directed to
350      *        a subsequent process. The stdout stream should be a null output stream .
351      * @throws IOException
352      */
353     void initStreams(int[] fds, boolean forceNullOutputStream) throws IOException {
354         switch (platform) {
355             case LINUX:
356             case BSD:
357                 stdin = (fds[0] == -1) ?
358                         ProcessBuilder.NullOutputStream.INSTANCE :
359                         new ProcessPipeOutputStream(fds[0]);
360 
361                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
362                          ProcessBuilder.NullInputStream.INSTANCE :
363                          new ProcessPipeInputStream(fds[1]);
364 
365                 stderr = (fds[2] == -1) ?
366                          ProcessBuilder.NullInputStream.INSTANCE :
367                          new ProcessPipeInputStream(fds[2]);
368 
369                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
370                     lock.lock();
371                     try {
372                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
373                         this.hasExited = true;
374                         condition.signalAll();
375                     } finally {
376                         lock.unlock();
377                     }
378 
379                     if (stdout instanceof ProcessPipeInputStream)
380                         ((ProcessPipeInputStream) stdout).processExited();
381 
382                     if (stderr instanceof ProcessPipeInputStream)
383                         ((ProcessPipeInputStream) stderr).processExited();
384 
385                     if (stdin instanceof ProcessPipeOutputStream)
386                         ((ProcessPipeOutputStream) stdin).processExited();
387 
388                     return null;
389                 });
390                 break;
391 
392             case AIX:
393                 stdin = (fds[0] == -1) ?
394                         ProcessBuilder.NullOutputStream.INSTANCE :
395                         new ProcessPipeOutputStream(fds[0]);
396 
397                 stdout = (fds[1] == -1 || forceNullOutputStream) ?
398                          ProcessBuilder.NullInputStream.INSTANCE :
399                          new DeferredCloseProcessPipeInputStream(fds[1]);
400 
401                 stderr = (fds[2] == -1) ?
402                          ProcessBuilder.NullInputStream.INSTANCE :
403                          new DeferredCloseProcessPipeInputStream(fds[2]);
404 
405                 ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
406                     lock.lock();
407                     try {
408                         this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
409                         this.hasExited = true;
410                         condition.signalAll();
411                     } finally {
412                         lock.unlock();
413                     }
414 
415                     if (stdout instanceof DeferredCloseProcessPipeInputStream)
416                         ((DeferredCloseProcessPipeInputStream) stdout).processExited();
417 
418                     if (stderr instanceof DeferredCloseProcessPipeInputStream)
419                         ((DeferredCloseProcessPipeInputStream) stderr).processExited();
420 
421                     if (stdin instanceof ProcessPipeOutputStream)
422                         ((ProcessPipeOutputStream) stdin).processExited();
423 
424                     return null;
425                 });
426                 break;
427 
428             default: throw new AssertionError("Unsupported platform: " + platform);
429         }
430     }
431 
432     public OutputStream getOutputStream() {
433         return stdin;
434     }
435 
436     public InputStream getInputStream() {
437         return stdout;
438     }
439 
440     public InputStream getErrorStream() {
441         return stderr;
442     }
443 
444     public int waitFor() throws InterruptedException {
445         lock.lock();
446         try {
447             while (!hasExited) {
448                 condition.await();
449             }
450             return exitcode;
451         } finally {
452             lock.unlock();
453         }
454     }
455 
456     public boolean waitFor(long timeout, TimeUnit unit)
457         throws InterruptedException
458     {
459         lock.lock();
460         try {
461             long remainingNanos = unit.toNanos(timeout);    // throw NPE before other conditions
462             while (remainingNanos > 0 && !hasExited) {
463                 remainingNanos = condition.awaitNanos(remainingNanos);
464             }
465             return hasExited;
466         } finally {
467             lock.unlock();
468         }
469     }
470 
471     public int exitValue() {
472         lock.lock();
473         try {
474             if (!hasExited) {
475                 throw new IllegalThreadStateException("process hasn't exited");
476             }
477             return exitcode;
478         } finally {
479             lock.unlock();
480         }
481     }
482 
483     private void destroy(boolean force) {
484         switch (platform) {
485             case LINUX:
486             case BSD:
487             case AIX:
488                 // There is a risk that pid will be recycled, causing us to
489                 // kill the wrong process!  So we only terminate processes
490                 // that appear to still be running.  Even with this check,
491                 // there is an unavoidable race condition here, but the window
492                 // is very small, and OSes try hard to not recycle pids too
493                 // soon, so this is quite safe.
494                 lock.lock();
495                 try {
496                     if (!hasExited)
497                         processHandle.destroyProcess(force);
498                 } finally {
499                     lock.unlock();
500                 }
501                 try { stdin.close();  } catch (IOException ignored) {}
502                 try { stdout.close(); } catch (IOException ignored) {}
503                 try { stderr.close(); } catch (IOException ignored) {}
504                 break;
505 
506             default: throw new AssertionError("Unsupported platform: " + platform);
507         }
508     }
509 
510     @Override
511     public CompletableFuture<Process> onExit() {
512         return ProcessHandleImpl.completion(pid, false)
513                 .handleAsync((unusedExitStatus, unusedThrowable) -> {
514                     boolean interrupted = false;
515                     while (true) {
516                         // Ensure that the concurrent task setting the exit status has completed
517                         try {
518                             waitFor();
519                             break;
520                         } catch (InterruptedException ie) {
521                             interrupted = true;
522                         }
523                     }
524                     if (interrupted) {
525                         Thread.currentThread().interrupt();
526                     }
527                     return this;
528                 });
529     }
530 
531     @Override
532     public ProcessHandle toHandle() {
533         @SuppressWarnings("removal")
534         SecurityManager sm = System.getSecurityManager();
535         if (sm != null) {
536             sm.checkPermission(new RuntimePermission("manageProcess"));
537         }
538         return processHandle;
539     }
540 
541     @Override
542     public boolean supportsNormalTermination() {
543         return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
544     }
545 
546     @Override
547     public void destroy() {
548         destroy(false);
549     }
550 
551     @Override
552     public Process destroyForcibly() {
553         destroy(true);
554         return this;
555     }
556 
557     @Override
558     public long pid() {
559         return pid;
560     }
561 
562     @Override
563     public boolean isAlive() {
564         lock.lock();
565         try {
566             return !hasExited;
567         } finally {
568             lock.unlock();
569         }
570     }
571 
572     /**
573      * The {@code toString} method returns a string consisting of
574      * the native process ID of the process and the exit value of the process.
575      *
576      * @return a string representation of the object.
577      */
578     @Override
579     public String toString() {
580         return new StringBuilder("Process[pid=").append(pid)
581                 .append(", exitValue=").append(hasExited ? exitcode : "\"not exited\"")
582                 .append("]").toString();
583     }
584 
585     private static native void init();
586 
587     static {
588         init();
589     }
590 
591     /**
592      * A buffered input stream for a subprocess pipe file descriptor
593      * that allows the underlying file descriptor to be reclaimed when
594      * the process exits, via the processExited hook.
595      *
596      * This is tricky because we do not want the user-level InputStream to be
597      * closed until the user invokes close(), and we need to continue to be
598      * able to read any buffered data lingering in the OS pipe buffer.
599      */
600     private static class ProcessPipeInputStream extends BufferedInputStream {
601         private final Object closeLock = new Object();
602 
603         ProcessPipeInputStream(int fd) {
604             super(new PipeInputStream(newFileDescriptor(fd)));
605         }
606         private static byte[] drainInputStream(InputStream in)
607                 throws IOException {
608             int n = 0;
609             int j;
610             byte[] a = null;
611             while ((j = in.available()) > 0) {
612                 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
613                 n += in.read(a, n, j);
614             }
615             return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
616         }
617 
618         /** Called by the process reaper thread when the process exits. */
619         synchronized void processExited() {
620             synchronized (closeLock) {
621                 try {
622                     InputStream in = this.in;
623                     // this stream is closed if and only if: in == null
624                     if (in != null) {
625                         byte[] stragglers = drainInputStream(in);
626                         in.close();
627                         this.in = (stragglers == null) ?
628                             ProcessBuilder.NullInputStream.INSTANCE :
629                             new ByteArrayInputStream(stragglers);
630                     }
631                 } catch (IOException ignored) {}
632             }
633         }
634 
635         @Override
636         public void close() throws IOException {
637             // BufferedInputStream#close() is not synchronized unlike most other
638             // methods. Synchronizing helps avoid race with processExited().
639             synchronized (closeLock) {
640                 super.close();
641             }
642         }
643     }
644 
645     /**
646      * A buffered output stream for a subprocess pipe file descriptor
647      * that allows the underlying file descriptor to be reclaimed when
648      * the process exits, via the processExited hook.
649      */
650     private static class ProcessPipeOutputStream extends BufferedOutputStream {
651         ProcessPipeOutputStream(int fd) {
652             super(new FileOutputStream(newFileDescriptor(fd)));
653         }
654 
655         /** Called by the process reaper thread when the process exits. */
656         synchronized void processExited() {
657             OutputStream out = this.out;
658             if (out != null) {
659                 try {
660                     out.close();
661                 } catch (IOException ignored) {
662                     // We know of no reason to get an IOException, but if
663                     // we do, there's nothing else to do but carry on.
664                 }
665                 this.out = ProcessBuilder.NullOutputStream.INSTANCE;
666             }
667         }
668     }
669 
670     // A FileInputStream that supports the deferment of the actual close
671     // operation until the last pending I/O operation on the stream has
672     // finished.  This is required on Solaris because we must close the stdin
673     // and stdout streams in the destroy method in order to reclaim the
674     // underlying file descriptors.  Doing so, however, causes any thread
675     // currently blocked in a read on one of those streams to receive an
676     // IOException("Bad file number"), which is incompatible with historical
677     // behavior.  By deferring the close we allow any pending reads to see -1
678     // (EOF) as they did before.
679     //
680     private static class DeferredCloseInputStream extends PipeInputStream {
681         DeferredCloseInputStream(FileDescriptor fd) {
682             super(fd);
683         }
684 
685         private Object lock = new Object();     // For the following fields
686         private boolean closePending = false;
687         private int useCount = 0;
688         private InputStream streamToClose;
689 
690         private void raise() {
691             synchronized (lock) {
692                 useCount++;
693             }
694         }
695 
696         private void lower() throws IOException {
697             synchronized (lock) {
698                 useCount--;
699                 if (useCount == 0 && closePending) {
700                     streamToClose.close();
701                 }
702             }
703         }
704 
705         // stc is the actual stream to be closed; it might be this object, or
706         // it might be an upstream object for which this object is downstream.
707         //
708         private void closeDeferred(InputStream stc) throws IOException {
709             synchronized (lock) {
710                 if (useCount == 0) {
711                     stc.close();
712                 } else {
713                     closePending = true;
714                     streamToClose = stc;
715                 }
716             }
717         }
718 
719         public void close() throws IOException {
720             synchronized (lock) {
721                 useCount = 0;
722                 closePending = false;
723             }
724             super.close();
725         }
726 
727         public int read() throws IOException {
728             raise();
729             try {
730                 return super.read();
731             } finally {
732                 lower();
733             }
734         }
735 
736         public int read(byte[] b) throws IOException {
737             raise();
738             try {
739                 return super.read(b);
740             } finally {
741                 lower();
742             }
743         }
744 
745         public int read(byte[] b, int off, int len) throws IOException {
746             raise();
747             try {
748                 return super.read(b, off, len);
749             } finally {
750                 lower();
751             }
752         }
753 
754         public long skip(long n) throws IOException {
755             raise();
756             try {
757                 return super.skip(n);
758             } finally {
759                 lower();
760             }
761         }
762 
763         public int available() throws IOException {
764             raise();
765             try {
766                 return super.available();
767             } finally {
768                 lower();
769             }
770         }
771     }
772 
773     /**
774      * A buffered input stream for a subprocess pipe file descriptor
775      * that allows the underlying file descriptor to be reclaimed when
776      * the process exits, via the processExited hook.
777      *
778      * This is tricky because we do not want the user-level InputStream to be
779      * closed until the user invokes close(), and we need to continue to be
780      * able to read any buffered data lingering in the OS pipe buffer.
781      *
782      * On AIX this is especially tricky, because the 'close()' system call
783      * will block if another thread is at the same time blocked in a file
784      * operation (e.g. 'read()') on the same file descriptor. We therefore
785      * combine 'ProcessPipeInputStream' approach used on Linux and Bsd
786      * with the DeferredCloseInputStream approach used on Solaris. This means
787      * that every potentially blocking operation on the file descriptor
788      * increments a counter before it is executed and decrements it once it
789      * finishes. The 'close()' operation will only be executed if there are
790      * no pending operations. Otherwise it is deferred after the last pending
791      * operation has finished.
792      *
793      */
794     private static class DeferredCloseProcessPipeInputStream
795         extends BufferedInputStream {
796 
797         private final Object closeLock = new Object();
798         private int useCount = 0;
799         private boolean closePending = false;
800 
801         DeferredCloseProcessPipeInputStream(int fd) {
802             super(new PipeInputStream(newFileDescriptor(fd)));
803         }
804 
805         private InputStream drainInputStream(InputStream in)
806                 throws IOException {
807             int n = 0;
808             int j;
809             byte[] a = null;
810             synchronized (closeLock) {
811                 if (buf == null) // asynchronous close()?
812                     return null; // discard
813                 j = in.available();
814             }
815             while (j > 0) {
816                 a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
817                 synchronized (closeLock) {
818                     if (buf == null) // asynchronous close()?
819                         return null; // discard
820                     n += in.read(a, n, j);
821                     j = in.available();
822                 }
823             }
824             return (a == null) ?
825                     ProcessBuilder.NullInputStream.INSTANCE :
826                     new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));
827         }
828 
829         /** Called by the process reaper thread when the process exits. */
830         synchronized void processExited() {
831             try {
832                 InputStream in = this.in;
833                 if (in != null) {
834                     InputStream stragglers = drainInputStream(in);
835                     in.close();
836                     this.in = stragglers;
837                 }
838             } catch (IOException ignored) { }
839         }
840 
841         private void raise() {
842             synchronized (closeLock) {
843                 useCount++;
844             }
845         }
846 
847         private void lower() throws IOException {
848             synchronized (closeLock) {
849                 useCount--;
850                 if (useCount == 0 && closePending) {
851                     closePending = false;
852                     super.close();
853                 }
854             }
855         }
856 
857         @Override
858         public int read() throws IOException {
859             raise();
860             try {
861                 return super.read();
862             } finally {
863                 lower();
864             }
865         }
866 
867         @Override
868         public int read(byte[] b) throws IOException {
869             raise();
870             try {
871                 return super.read(b);
872             } finally {
873                 lower();
874             }
875         }
876 
877         @Override
878         public int read(byte[] b, int off, int len) throws IOException {
879             raise();
880             try {
881                 return super.read(b, off, len);
882             } finally {
883                 lower();
884             }
885         }
886 
887         @Override
888         public long skip(long n) throws IOException {
889             raise();
890             try {
891                 return super.skip(n);
892             } finally {
893                 lower();
894             }
895         }
896 
897         @Override
898         public int available() throws IOException {
899             raise();
900             try {
901                 return super.available();
902             } finally {
903                 lower();
904             }
905         }
906 
907         @Override
908         public void close() throws IOException {
909             // BufferedInputStream#close() is not synchronized unlike most other
910             // methods. Synchronizing helps avoid racing with drainInputStream().
911             synchronized (closeLock) {
912                 if (useCount == 0) {
913                     super.close();
914                 }
915                 else {
916                     closePending = true;
917                 }
918             }
919         }
920     }
921 }