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