1 /*
  2  * Copyright (c) 2014, 2024, 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 package java.lang;
 26 
 27 import jdk.internal.misc.InnocuousThread;
 28 
 29 import java.lang.annotation.Native;
 30 import java.security.AccessController;
 31 import java.security.PrivilegedAction;
 32 import java.time.Duration;
 33 import java.time.Instant;
 34 import java.util.Arrays;
 35 import java.util.Optional;
 36 import java.util.concurrent.CompletableFuture;
 37 import java.util.concurrent.ConcurrentHashMap;
 38 import java.util.concurrent.ConcurrentMap;
 39 import java.util.concurrent.Executor;
 40 import java.util.concurrent.Executors;
 41 import java.util.concurrent.ThreadFactory;
 42 import java.util.concurrent.ThreadLocalRandom;
 43 import java.util.stream.IntStream;
 44 import java.util.stream.Stream;
 45 
 46 /**
 47  * ProcessHandleImpl is the implementation of ProcessHandle.
 48  *
 49  * @see Process
 50  * @since 9
 51  */
 52 @jdk.internal.ValueBased
 53 final class ProcessHandleImpl implements ProcessHandle {
 54     /**
 55      * Default size of stack for reaper processes.
 56      */
 57     private static final long REAPER_DEFAULT_STACKSIZE = 128 * 1024;
 58 
 59     /**
 60      * Return value from waitForProcessExit0 indicating the process is not a child.
 61      */
 62     @Native
 63     private static final int NOT_A_CHILD = -2;
 64 
 65     /**
 66      * Cache the ProcessHandle of this process.
 67      */
 68     private static final ProcessHandleImpl current;
 69 
 70     /**
 71      * Map of pids to ExitCompletions.
 72      */
 73     private static final ConcurrentMap<Long, ExitCompletion>
 74             completions = new ConcurrentHashMap<>();
 75 
 76     static {
 77         initNative();
 78         long pid = getCurrentPid0();
 79         current = new ProcessHandleImpl(pid, isAlive0(pid));
 80     }
 81 
 82     private static native void initNative();
 83 
 84     /**
 85      * The thread pool of "process reaper" daemon threads.
 86      */
 87     private static final Executor processReaperExecutor = initReaper();
 88 
 89     private static Executor initReaper() {
 90         // Initialize ThreadLocalRandom and ValueObjectMethods now to avoid using the smaller stack
 91         // of the processReaper threads.
 92         ThreadLocalRandom.current();
 93         try {
 94             Class.forName("java.lang.runtime.ValueObjectMethods$MethodHandleBuilder", true, null);
 95         } catch (ClassNotFoundException cnfe) {
 96             throw new InternalError("unable to initialize ValueObjectMethodds", cnfe);
 97         }
 98         // For a debug build, the stack shadow zone is larger;
 99         // Increase the total stack size to avoid potential stack overflow.
100         int debugDelta = "release".equals(System.getProperty("jdk.debug")) ? 0 : (4*4096);
101         final long stackSize = Boolean.getBoolean("jdk.lang.processReaperUseDefaultStackSize")
102                 ? 0 : REAPER_DEFAULT_STACKSIZE + debugDelta;
103 
104         ThreadFactory threadFactory = grimReaper -> {
105             Thread t = InnocuousThread.newSystemThread("process reaper", grimReaper,
106                     stackSize, Thread.MAX_PRIORITY);
107             t.setDaemon(true);
108             return t;
109         };
110 
111         return Executors.newCachedThreadPool(threadFactory);
112     }
113 
114     private static class ExitCompletion extends CompletableFuture<Integer> {
115         final boolean isReaping;
116 
117         ExitCompletion(boolean isReaping) {
118             this.isReaping = isReaping;
119         }
120     }
121 
122     /**
123      * Returns a CompletableFuture that completes with process exit status when
124      * the process completes.
125      *
126      * @param shouldReap true if the exit value should be reaped
127      */
128     static CompletableFuture<Integer> completion(long pid, boolean shouldReap) {
129         // check canonicalizing cache 1st
130         ExitCompletion completion = completions.get(pid);
131         // re-try until we get a completion that shouldReap => isReaping
132         while (completion == null || (shouldReap && !completion.isReaping)) {
133             ExitCompletion newCompletion = new ExitCompletion(shouldReap);
134             if (completion == null) {
135                 completion = completions.putIfAbsent(pid, newCompletion);
136             } else {
137                 completion = completions.replace(pid, completion, newCompletion)
138                     ? null : completions.get(pid);
139             }
140             if (completion == null) {
141                 // newCompletion has just been installed successfully
142                 completion = newCompletion;
143                 // spawn a thread to wait for and deliver the exit value
144                 processReaperExecutor.execute(new Runnable() {
145                     // Use inner class to avoid lambda stack overhead
146                     public void run() {
147                         Thread t = Thread.currentThread();
148                         String threadName = t.getName();
149                         t.setName("process reaper (pid " + pid + ")");
150                         try {
151                             int exitValue = waitForProcessExit0(pid, shouldReap);
152                             if (exitValue == NOT_A_CHILD) {
153                                 // pid not alive or not a child of this process
154                                 // If it is alive wait for it to terminate
155                                 long sleep = 300;     // initial milliseconds to sleep
156                                 int incr = 30;        // increment to the sleep time
157 
158                                 long startTime = isAlive0(pid);
159                                 long origStart = startTime;
160                                 while (startTime >= 0) {
161                                     try {
162                                         Thread.sleep(Math.min(sleep, 5000L)); // no more than 5 sec
163                                         sleep += incr;
164                                     } catch (InterruptedException ie) {
165                                         // ignore and retry
166                                     }
167                                     startTime = isAlive0(pid);  // recheck if it is alive
168                                     if (startTime > 0 && origStart > 0 && startTime != origStart) {
169                                         // start time changed (and is not zero), pid is not the same process
170                                         break;
171                                     }
172                                 }
173                                 exitValue = 0;
174                             }
175                             newCompletion.complete(exitValue);
176                             // remove from cache afterwards
177                             completions.remove(pid, newCompletion);
178                         } finally {
179                             // Restore thread name
180                             t.setName(threadName);
181                         }
182                     }
183                 });
184             }
185         }
186         return completion;
187     }
188 
189     @Override
190     public CompletableFuture<ProcessHandle> onExit() {
191         if (this.equals(current)) {
192             throw new IllegalStateException("onExit for current process not allowed");
193         }
194 
195         return ProcessHandleImpl.completion(pid(), false)
196                 .handleAsync((exitStatus, unusedThrowable) -> this);
197     }
198 
199     /**
200      * Wait for the process to exit, return the value.
201      * Conditionally reap the value if requested
202      * @param pid the processId
203      * @param reapvalue if true, the value is retrieved,
204      *                   else return the value and leave the process waitable
205      *
206      * @return the value or -1 if an error occurs
207      */
208     private static native int waitForProcessExit0(long pid, boolean reapvalue);
209 
210     /**
211      * The pid of this ProcessHandle.
212      */
213     private final long pid;
214 
215     /**
216      * The start time of this process.
217      * If STARTTIME_ANY, the start time of the process is not available from the os.
218      * If greater than zero, the start time of the process.
219      */
220     private final long startTime;
221 
222     /* The start time should match any value.
223      * Typically, this is because the OS can not supply it.
224      * The process is known to exist but not the exact start time.
225      */
226     private static final long STARTTIME_ANY = 0L;
227 
228     /* The start time of a Process that does not exist. */
229     private static final long STARTTIME_PROCESS_UNKNOWN = -1;
230 
231     /**
232      * Private constructor.  Instances are created by the {@code get(long)} factory.
233      * @param pid the pid for this instance
234      */
235     private ProcessHandleImpl(long pid, long startTime) {
236         this.pid = pid;
237         this.startTime = startTime;
238     }
239 
240     /**
241      * Returns a ProcessHandle for an existing native process.
242      *
243      * @param  pid the native process identifier
244      * @return The ProcessHandle for the pid if the process is alive;
245      *         or {@code null} if the process ID does not exist in the native system.
246      */
247     static Optional<ProcessHandle> get(long pid) {
248         long start = isAlive0(pid);
249         return (start >= 0)
250                 ? Optional.of(new ProcessHandleImpl(pid, start))
251                 : Optional.empty();
252     }
253 
254     /**
255      * Returns a ProcessHandle for an existing native process known to be alive.
256      * The startTime of the process is retrieved and stored in the ProcessHandle.
257      * It does not perform a security check since it is called from ProcessImpl.
258      * @param pid of the known to exist process
259      * @return a ProcessHandle corresponding to an existing Process instance
260      */
261     static ProcessHandleImpl getInternal(long pid) {
262         return new ProcessHandleImpl(pid, isAlive0(pid));
263     }
264 
265     /**
266      * Returns the native process ID.
267      * A {@code long} is used to be able to fit the system specific binary values
268      * for the process.
269      *
270      * @return the native process ID
271      */
272     @Override
273     public long pid() {
274         return pid;
275     }
276 
277     /**
278      * Returns the ProcessHandle for the current native process.
279      *
280      * @return The ProcessHandle for the OS process.
281      */
282     public static ProcessHandleImpl current() {
283         return current;
284     }
285 
286     /**
287      * Return the pid of the current process.
288      *
289      * @return the pid of the  current process
290      */
291     private static native long getCurrentPid0();
292 
293     /**
294      * Returns a ProcessHandle for the parent process.
295      *
296      * @return a ProcessHandle of the parent process; {@code null} is returned
297      *         if the child process does not have a parent
298      */
299     public Optional<ProcessHandle> parent() {
300         long ppid = parent0(pid, startTime);
301         if (ppid <= 0) {
302             return Optional.empty();
303         }
304         return get(ppid);
305     }
306 
307     /**
308      * Returns the parent of the native pid argument.
309      *
310      * @param pid the process id
311      * @param startTime the startTime of the process
312      * @return the parent of the native pid; if any, otherwise -1
313      */
314     private static native long parent0(long pid, long startTime);
315 
316     /**
317      * Returns the number of pids filled in to the array.
318      * @param pid if {@code pid} equals zero, then all known processes are returned;
319      *      otherwise only direct child process pids are returned
320      * @param pids an allocated long array to receive the pids
321      * @param ppids an allocated long array to receive the parent pids; may be null
322      * @param starttimes an allocated long array to receive the child start times; may be null
323      * @return if greater than or equal to zero is the number of pids in the array;
324      *      if greater than the length of the arrays, the arrays are too small
325      */
326     private static native int getProcessPids0(long pid, long[] pids,
327                                               long[] ppids, long[] starttimes);
328 
329     /**
330      * Destroy the process for this ProcessHandle.
331      * The native code checks the start time before sending the termination request.
332      *
333      * @param force {@code true} if the process should be terminated forcibly;
334      *     else {@code false} for a normal termination
335      */
336     boolean destroyProcess(boolean force) {
337         if (this.equals(current)) {
338             throw new IllegalStateException("destroy of current process not allowed");
339         }
340         return destroy0(pid, startTime, force);
341     }
342 
343     /**
344      * Signal the process to terminate.
345      * The process is signaled only if its start time matches the known start time.
346      *
347      * @param pid  process id to kill
348      * @param startTime the start time of the process
349      * @param forcibly true to forcibly terminate (SIGKILL vs SIGTERM)
350      * @return true if the process was signaled without error; false otherwise
351      */
352     private static native boolean destroy0(long pid, long startTime, boolean forcibly);
353 
354     @Override
355     public boolean destroy() {
356         return destroyProcess(false);
357     }
358 
359     @Override
360     public boolean destroyForcibly() {
361         return destroyProcess(true);
362     }
363 
364 
365     @Override
366     public boolean supportsNormalTermination() {
367         return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
368     }
369 
370     /**
371      * Tests whether the process represented by this {@code ProcessHandle} is alive.
372      *
373      * @return {@code true} if the process represented by this
374      * {@code ProcessHandle} object has not yet terminated.
375      * @since 9
376      */
377     @Override
378     public boolean isAlive() {
379         long start = isAlive0(pid);
380         return (start >= 0 && (start == startTime || start == 0 || startTime == 0));
381     }
382 
383     /**
384      * Returns the process start time depending on whether the pid is alive.
385      * This must not reap the exitValue.
386      *
387      * @param pid the pid to check
388      * @return the start time in milliseconds since 1970,
389      *         0 if the start time cannot be determined,
390      *         -1 if the pid does not exist.
391      */
392     private static native long isAlive0(long pid);
393 
394     @Override
395     public Stream<ProcessHandle> children() {
396         // The native OS code selects based on matching the requested parent pid.
397         // If the original parent exits, the pid may have been re-used for
398         // this newer process.
399         // Processes started by the original parent (now dead) will all have
400         // start times less than the start of this newer parent.
401         // Processes started by this newer parent will have start times equal
402         // or after this parent.
403         return children(pid).filter(ph -> startTime <= ((ProcessHandleImpl)ph).startTime);
404     }
405 
406     /**
407      * Returns a Stream of the children of a process or all processes.
408      *
409      * @param pid the pid of the process for which to find the children;
410      *            0 for all processes
411      * @return a stream of ProcessHandles
412      */
413     static Stream<ProcessHandle> children(long pid) {
414         int size = 100;
415         long[] childpids = null;
416         long[] starttimes = null;
417         while (childpids == null || size > childpids.length) {
418             childpids = new long[size];
419             starttimes = new long[size];
420             size = getProcessPids0(pid, childpids, null, starttimes);
421         }
422 
423         final long[] cpids = childpids;
424         final long[] stimes = starttimes;
425         return IntStream.range(0, size).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
426     }
427 
428     @Override
429     public Stream<ProcessHandle> descendants() {
430         int size = 100;
431         long[] pids = null;
432         long[] ppids = null;
433         long[] starttimes = null;
434         while (pids == null || size > pids.length) {
435             pids = new long[size];
436             ppids = new long[size];
437             starttimes = new long[size];
438             size = getProcessPids0(0, pids, ppids, starttimes);
439         }
440 
441         int next = 0;       // index of next process to check
442         int count = -1;     // count of subprocesses scanned
443         long ppid = pid;    // start looking for this parent
444         long ppStart = 0;
445         // Find the start time of the parent
446         for (int i = 0; i < size; i++) {
447             if (pids[i] == ppid) {
448                 ppStart = starttimes[i];
449                 break;
450             }
451         }
452         do {
453             // Scan from next to size looking for ppid with child start time
454             // the same or later than the parent.
455             // If found, exchange it with index next
456             for (int i = next; i < size; i++) {
457                 if (ppids[i] == ppid &&
458                         ppStart <= starttimes[i]) {
459                     swap(pids, i, next);
460                     swap(ppids, i, next);
461                     swap(starttimes, i, next);
462                     next++;
463                 }
464             }
465             ppid = pids[++count];   // pick up the next pid to scan for
466             ppStart = starttimes[count];    // and its start time
467         } while (count < next);
468 
469         final long[] cpids = pids;
470         final long[] stimes = starttimes;
471         return IntStream.range(0, count).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
472     }
473 
474     // Swap two elements in an array
475     private static void swap(long[] array, int x, int y) {
476         long v = array[x];
477         array[x] = array[y];
478         array[y] = v;
479     }
480 
481     @Override
482     public ProcessHandle.Info info() {
483         return ProcessHandleImpl.Info.info(pid, startTime);
484     }
485 
486     @Override
487     public int compareTo(ProcessHandle other) {
488         return Long.compare(pid, ((ProcessHandleImpl) other).pid);
489     }
490 
491     @Override
492     public String toString() {
493         return Long.toString(pid);
494     }
495 
496     @Override
497     public int hashCode() {
498         return Long.hashCode(pid);
499     }
500 
501     @Override
502     public boolean equals(Object obj) {
503         if (this == obj) {
504             return true;
505         }
506         return (obj instanceof ProcessHandleImpl other)
507                 && (pid == other.pid)
508                 && (startTime == other.startTime || startTime == 0 || other.startTime == 0);
509     }
510 
511     /**
512      * Implementation of ProcessHandle.Info.
513      * Information snapshot about a process.
514      * The attributes of a process vary by operating system and are not available
515      * in all implementations.  Additionally, information about other processes
516      * is limited by the operating system privileges of the process making the request.
517      * If a value is not available, either a {@code null} or {@code -1} is stored.
518      * The accessor methods return {@code null} if the value is not available.
519      */
520     static class Info implements ProcessHandle.Info {
521         static {
522             initIDs();
523         }
524 
525         /**
526          * Initialization of JNI fieldIDs.
527          */
528         private static native void initIDs();
529 
530         /**
531          * Fill in this Info instance with information about the native process.
532          * If values are not available the native code does not modify the field.
533          * @param pid  of the native process
534          */
535         private native void info0(long pid);
536 
537         String command;
538         String commandLine;
539         String[] arguments;
540         long startTime;
541         long totalTime;
542         String user;
543 
544         Info() {
545             command = null;
546             commandLine = null;
547             arguments = null;
548             startTime = -1L;
549             totalTime = -1L;
550             user = null;
551         }
552 
553         /**
554          * Returns the Info object with the fields from the process.
555          * Whatever fields are provided by native are returned.
556          * If the startTime of the process does not match the provided
557          * startTime then an empty Info is returned.
558          *
559          * @param pid the native process identifier
560          * @param startTime the startTime of the process being queried
561          * @return ProcessHandle.Info non-null; individual fields may be null
562          *          or -1 if not available.
563          */
564         public static ProcessHandle.Info info(long pid, long startTime) {
565             Info info = new Info();
566             info.info0(pid);
567             if (startTime != info.startTime) {
568                 info.command = null;
569                 info.arguments = null;
570                 info.startTime = -1L;
571                 info.totalTime = -1L;
572                 info.user = null;
573             }
574             return info;
575         }
576 
577         @Override
578         public Optional<String> command() {
579             return Optional.ofNullable(command);
580         }
581 
582         @Override
583         public Optional<String> commandLine() {
584             if (command != null && arguments != null) {
585                 return Optional.of(command + " " + String.join(" ", arguments));
586             } else {
587                 return Optional.ofNullable(commandLine);
588             }
589         }
590 
591         @Override
592         public Optional<String[]> arguments() {
593             return Optional.ofNullable(arguments);
594         }
595 
596         @Override
597         public Optional<Instant> startInstant() {
598             return (startTime > 0)
599                     ? Optional.of(Instant.ofEpochMilli(startTime))
600                     : Optional.empty();
601         }
602 
603         @Override
604         public Optional<Duration> totalCpuDuration() {
605             return (totalTime != -1)
606                     ? Optional.of(Duration.ofNanos(totalTime))
607                     : Optional.empty();
608         }
609 
610         @Override
611         public Optional<String> user() {
612             return Optional.ofNullable(user);
613         }
614 
615         @Override
616         public String toString() {
617             StringBuilder sb = new StringBuilder(60);
618             sb.append('[');
619             if (user != null) {
620                 sb.append("user: ");
621                 sb.append(user());
622             }
623             if (command != null) {
624                 if (sb.length() > 1) sb.append(", ");
625                 sb.append("cmd: ");
626                 sb.append(command);
627             }
628             if (arguments != null && arguments.length > 0) {
629                 if (sb.length() > 1) sb.append(", ");
630                 sb.append("args: ");
631                 sb.append(Arrays.toString(arguments));
632             }
633             if (commandLine != null) {
634                 if (sb.length() > 1) sb.append(", ");
635                 sb.append("cmdLine: ");
636                 sb.append(commandLine);
637             }
638             if (startTime > 0) {
639                 if (sb.length() > 1) sb.append(", ");
640                 sb.append("startTime: ");
641                 sb.append(startInstant());
642             }
643             if (totalTime != -1) {
644                 if (sb.length() > 1) sb.append(", ");
645                 sb.append("totalTime: ");
646                 sb.append(totalCpuDuration().toString());
647             }
648             sb.append(']');
649             return sb.toString();
650         }
651     }
652 }