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