1 /*
2  * Copyright (c) 2014, 2020, 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.PrivilegedAction;
29 import java.time.Duration;
30 import java.time.Instant;
31 import java.util.Arrays;
32 import java.util.Optional;
33 import java.util.concurrent.CompletableFuture;
34 import java.util.concurrent.ConcurrentHashMap;
35 import java.util.concurrent.ConcurrentMap;
36 import java.util.concurrent.Executor;
37 import java.util.concurrent.Executors;
38 import java.util.concurrent.ThreadFactory;
39 import java.util.concurrent.ThreadLocalRandom;
40 import java.util.stream.IntStream;
41 import java.util.stream.Stream;
42 
43 import static java.security.AccessController.doPrivileged;
44 
45 /**
46  * ProcessHandleImpl is the implementation of ProcessHandle.
47  *
48  * @see Process
49  * @since 9
50  */
51 @jdk.internal.ValueBased
52 final class ProcessHandleImpl implements ProcessHandle {
53     /**
54      * Default size of stack for reaper processes.
55      */
56     private static long REAPER_DEFAULT_STACKSIZE = 128 * 1024;
57 
58     /**
59      * Return value from waitForProcessExit0 indicating the process is not a child.
60      */
61     @Native
62     private static final int NOT_A_CHILD = -2;
63 
64     /**
65      * Cache the ProcessHandle of this process.
66      */
67     private static final ProcessHandleImpl current;
68 
69     /**
70      * Map of pids to ExitCompletions.
71      */
72     private static final ConcurrentMap<Long, ExitCompletion>
73             completions = new ConcurrentHashMap<>();
74 
75     static {
initNative()76         initNative();
77         long pid = getCurrentPid0();
78         current = new ProcessHandleImpl(pid, isAlive0(pid));
79     }
80 
initNative()81     private static native void initNative();
82 
83     /**
84      * The thread pool of "process reaper" daemon threads.
85      */
86     private static final Executor processReaperExecutor =
87             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         SecurityManager sm = System.getSecurityManager();
242         if (sm != null) {
243             sm.checkPermission(new RuntimePermission("manageProcess"));
244         }
245         long start = isAlive0(pid);
246         return (start >= 0)
247                 ? Optional.of(new ProcessHandleImpl(pid, start))
248                 : Optional.empty();
249     }
250 
251     /**
252      * Returns a ProcessHandle for an existing native process known to be alive.
253      * The startTime of the process is retrieved and stored in the ProcessHandle.
254      * It does not perform a security check since it is called from ProcessImpl.
255      * @param pid of the known to exist process
256      * @return a ProcessHandle corresponding to an existing Process instance
257      */
getInternal(long pid)258     static ProcessHandleImpl getInternal(long pid) {
259         return new ProcessHandleImpl(pid, isAlive0(pid));
260     }
261 
262     /**
263      * Returns the native process ID.
264      * A {@code long} is used to be able to fit the system specific binary values
265      * for the process.
266      *
267      * @return the native process ID
268      */
269     @Override
pid()270     public long pid() {
271         return pid;
272     }
273 
274     /**
275      * Returns the ProcessHandle for the current native process.
276      *
277      * @return The ProcessHandle for the OS process.
278      * @throws SecurityException if RuntimePermission("manageProcess") is not granted
279      */
current()280     public static ProcessHandleImpl current() {
281         SecurityManager sm = System.getSecurityManager();
282         if (sm != null) {
283             sm.checkPermission(new RuntimePermission("manageProcess"));
284         }
285         return current;
286     }
287 
288     /**
289      * Return the pid of the current process.
290      *
291      * @return the pid of the  current process
292      */
getCurrentPid0()293     private static native long getCurrentPid0();
294 
295     /**
296      * Returns a ProcessHandle for the parent process.
297      *
298      * @return a ProcessHandle of the parent process; {@code null} is returned
299      *         if the child process does not have a parent
300      * @throws SecurityException           if permission is not granted by the
301      *                                     security policy
302      */
parent()303     public Optional<ProcessHandle> parent() {
304         SecurityManager sm = System.getSecurityManager();
305         if (sm != null) {
306             sm.checkPermission(new RuntimePermission("manageProcess"));
307         }
308         long ppid = parent0(pid, startTime);
309         if (ppid <= 0) {
310             return Optional.empty();
311         }
312         return get(ppid);
313     }
314 
315     /**
316      * Returns the parent of the native pid argument.
317      *
318      * @param pid the process id
319      * @param startTime the startTime of the process
320      * @return the parent of the native pid; if any, otherwise -1
321      */
parent0(long pid, long startTime)322     private static native long parent0(long pid, long startTime);
323 
324     /**
325      * Returns the number of pids filled in to the array.
326      * @param pid if {@code pid} equals zero, then all known processes are returned;
327      *      otherwise only direct child process pids are returned
328      * @param pids an allocated long array to receive the pids
329      * @param ppids an allocated long array to receive the parent pids; may be null
330      * @param starttimes an allocated long array to receive the child start times; may be null
331      * @return if greater than or equal to zero is the number of pids in the array;
332      *      if greater than the length of the arrays, the arrays are too small
333      */
getProcessPids0(long pid, long[] pids, long[] ppids, long[] starttimes)334     private static native int getProcessPids0(long pid, long[] pids,
335                                               long[] ppids, long[] starttimes);
336 
337     /**
338      * Destroy the process for this ProcessHandle.
339      * The native code checks the start time before sending the termination request.
340      *
341      * @param force {@code true} if the process should be terminated forcibly;
342      *     else {@code false} for a normal termination
343      */
destroyProcess(boolean force)344     boolean destroyProcess(boolean force) {
345         if (this.equals(current)) {
346             throw new IllegalStateException("destroy of current process not allowed");
347         }
348         return destroy0(pid, startTime, force);
349     }
350 
351     /**
352      * Signal the process to terminate.
353      * The process is signaled only if its start time matches the known start time.
354      *
355      * @param pid  process id to kill
356      * @param startTime the start time of the process
357      * @param forcibly true to forcibly terminate (SIGKILL vs SIGTERM)
358      * @return true if the process was signaled without error; false otherwise
359      */
destroy0(long pid, long startTime, boolean forcibly)360     private static native boolean destroy0(long pid, long startTime, boolean forcibly);
361 
362     @Override
destroy()363     public boolean destroy() {
364         return destroyProcess(false);
365     }
366 
367     @Override
destroyForcibly()368     public boolean destroyForcibly() {
369         return destroyProcess(true);
370     }
371 
372 
373     @Override
supportsNormalTermination()374     public boolean supportsNormalTermination() {
375         return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
376     }
377 
378     /**
379      * Tests whether the process represented by this {@code ProcessHandle} is alive.
380      *
381      * @return {@code true} if the process represented by this
382      * {@code ProcessHandle} object has not yet terminated.
383      * @since 9
384      */
385     @Override
isAlive()386     public boolean isAlive() {
387         long start = isAlive0(pid);
388         return (start >= 0 && (start == startTime || start == 0 || startTime == 0));
389     }
390 
391     /**
392      * Returns the process start time depending on whether the pid is alive.
393      * This must not reap the exitValue.
394      *
395      * @param pid the pid to check
396      * @return the start time in milliseconds since 1970,
397      *         0 if the start time cannot be determined,
398      *         -1 if the pid does not exist.
399      */
isAlive0(long pid)400     private static native long isAlive0(long pid);
401 
402     @Override
children()403     public Stream<ProcessHandle> children() {
404         // The native OS code selects based on matching the requested parent pid.
405         // If the original parent exits, the pid may have been re-used for
406         // this newer process.
407         // Processes started by the original parent (now dead) will all have
408         // start times less than the start of this newer parent.
409         // Processes started by this newer parent will have start times equal
410         // or after this parent.
411         return children(pid).filter(ph -> startTime <= ((ProcessHandleImpl)ph).startTime);
412     }
413 
414     /**
415      * Returns a Stream of the children of a process or all processes.
416      *
417      * @param pid the pid of the process for which to find the children;
418      *            0 for all processes
419      * @return a stream of ProcessHandles
420      */
children(long pid)421     static Stream<ProcessHandle> children(long pid) {
422         SecurityManager sm = System.getSecurityManager();
423         if (sm != null) {
424             sm.checkPermission(new RuntimePermission("manageProcess"));
425         }
426         int size = 100;
427         long[] childpids = null;
428         long[] starttimes = null;
429         while (childpids == null || size > childpids.length) {
430             childpids = new long[size];
431             starttimes = new long[size];
432             size = getProcessPids0(pid, childpids, null, starttimes);
433         }
434 
435         final long[] cpids = childpids;
436         final long[] stimes = starttimes;
437         return IntStream.range(0, size).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
438     }
439 
440     @Override
descendants()441     public Stream<ProcessHandle> descendants() {
442         SecurityManager sm = System.getSecurityManager();
443         if (sm != null) {
444             sm.checkPermission(new RuntimePermission("manageProcess"));
445         }
446         int size = 100;
447         long[] pids = null;
448         long[] ppids = null;
449         long[] starttimes = null;
450         while (pids == null || size > pids.length) {
451             pids = new long[size];
452             ppids = new long[size];
453             starttimes = new long[size];
454             size = getProcessPids0(0, pids, ppids, starttimes);
455         }
456 
457         int next = 0;       // index of next process to check
458         int count = -1;     // count of subprocesses scanned
459         long ppid = pid;    // start looking for this parent
460         long ppStart = 0;
461         // Find the start time of the parent
462         for (int i = 0; i < size; i++) {
463             if (pids[i] == ppid) {
464                 ppStart = starttimes[i];
465                 break;
466             }
467         }
468         do {
469             // Scan from next to size looking for ppid with child start time
470             // the same or later than the parent.
471             // If found, exchange it with index next
472             for (int i = next; i < size; i++) {
473                 if (ppids[i] == ppid &&
474                         ppStart <= starttimes[i]) {
475                     swap(pids, i, next);
476                     swap(ppids, i, next);
477                     swap(starttimes, i, next);
478                     next++;
479                 }
480             }
481             ppid = pids[++count];   // pick up the next pid to scan for
482             ppStart = starttimes[count];    // and its start time
483         } while (count < next);
484 
485         final long[] cpids = pids;
486         final long[] stimes = starttimes;
487         return IntStream.range(0, count).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
488     }
489 
490     // Swap two elements in an array
swap(long[] array, int x, int y)491     private static void swap(long[] array, int x, int y) {
492         long v = array[x];
493         array[x] = array[y];
494         array[y] = v;
495     }
496 
497     @Override
info()498     public ProcessHandle.Info info() {
499         return ProcessHandleImpl.Info.info(pid, startTime);
500     }
501 
502     @Override
compareTo(ProcessHandle other)503     public int compareTo(ProcessHandle other) {
504         return Long.compare(pid, ((ProcessHandleImpl) other).pid);
505     }
506 
507     @Override
toString()508     public String toString() {
509         return Long.toString(pid);
510     }
511 
512     @Override
hashCode()513     public int hashCode() {
514         return Long.hashCode(pid);
515     }
516 
517     @Override
equals(Object obj)518     public boolean equals(Object obj) {
519         if (this == obj) {
520             return true;
521         }
522         if (obj instanceof ProcessHandleImpl) {
523             ProcessHandleImpl other = (ProcessHandleImpl) obj;
524             return (pid == other.pid) &&
525                     (startTime == other.startTime
526                         || startTime == 0
527                         || other.startTime == 0);
528         }
529         return false;
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