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