1 /*
2  * Copyright (c) 2014, 2017, 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.stream.IntStream;
40 import java.util.stream.Stream;
41
42 import static java.security.AccessController.doPrivileged;
43
44 /**
45  * ProcessHandleImpl is the implementation of ProcessHandle.
46  *
47  * @see Process
48  * @since 9
49  */

50 final class ProcessHandleImpl implements ProcessHandle {
51     /**
52      * Default size of stack for reaper processes.
53      */

54     private static long REAPER_DEFAULT_STACKSIZE = 128 * 1024;
55
56     /**
57      * Return value from waitForProcessExit0 indicating the process is not a child.
58      */

59     @Native
60     private static final int NOT_A_CHILD = -2;
61
62     /**
63      * Cache the ProcessHandle of this process.
64      */

65     private static final ProcessHandleImpl current;
66
67     /**
68      * Map of pids to ExitCompletions.
69      */

70     private static final ConcurrentMap<Long, ExitCompletion>
71             completions = new ConcurrentHashMap<>();
72
73     static {
74         initNative();
75         long pid = getCurrentPid0();
76         current = new ProcessHandleImpl(pid, isAlive0(pid));
77     }
78
79     private static native void initNative();
80
81     /**
82      * The thread pool of "process reaper" daemon threads.
83      */

84     private static final Executor processReaperExecutor =
85             doPrivileged((PrivilegedAction<Executor>) () -> {
86
87                 ThreadGroup tg = Thread.currentThread().getThreadGroup();
88                 while (tg.getParent() != null) tg = tg.getParent();
89                 ThreadGroup systemThreadGroup = tg;
90                 final long stackSize = Boolean.getBoolean("jdk.lang.processReaperUseDefaultStackSize")
91                         ? 0 : REAPER_DEFAULT_STACKSIZE;
92
93                 ThreadFactory threadFactory = grimReaper -> {
94                     Thread t = new Thread(systemThreadGroup, grimReaper,
95                             "process reaper", stackSize, false);
96                     t.setDaemon(true);
97                     // A small attempt (probably futile) to avoid priority inversion
98                     t.setPriority(Thread.MAX_PRIORITY);
99                     return t;
100                 };
101
102                 return Executors.newCachedThreadPool(threadFactory);
103             });
104
105     private static class ExitCompletion extends CompletableFuture<Integer> {
106         final boolean isReaping;
107
108         ExitCompletion(boolean isReaping) {
109             this.isReaping = isReaping;
110         }
111     }
112
113     /**
114      * Returns a CompletableFuture that completes with process exit status when
115      * the process completes.
116      *
117      * @param shouldReap true if the exit value should be reaped
118      */

119     static CompletableFuture<Integer> completion(long pid, boolean shouldReap) {
120         // check canonicalizing cache 1st
121         ExitCompletion completion = completions.get(pid);
122         // re-try until we get a completion that shouldReap => isReaping
123         while (completion == null || (shouldReap && !completion.isReaping)) {
124             ExitCompletion newCompletion = new ExitCompletion(shouldReap);
125             if (completion == null) {
126                 completion = completions.putIfAbsent(pid, newCompletion);
127             } else {
128                 completion = completions.replace(pid, completion, newCompletion)
129                     ? null : completions.get(pid);
130             }
131             if (completion == null) {
132                 // newCompletion has just been installed successfully
133                 completion = newCompletion;
134                 // spawn a thread to wait for and deliver the exit value
135                 processReaperExecutor.execute(new Runnable() {
136                     // Use inner class to avoid lambda stack overhead
137                     public void run() {
138                         int exitValue = waitForProcessExit0(pid, shouldReap);
139                         if (exitValue == NOT_A_CHILD) {
140                             // pid not alive or not a child of this process
141                             // If it is alive wait for it to terminate
142                             long sleep = 300;     // initial milliseconds to sleep
143                             int incr = 30;        // increment to the sleep time
144
145                             long startTime = isAlive0(pid);
146                             long origStart = startTime;
147                             while (startTime >= 0) {
148                                 try {
149                                     Thread.sleep(Math.min(sleep, 5000L)); // no more than 5 sec
150                                     sleep += incr;
151                                 } catch (InterruptedException ie) {
152                                     // ignore and retry
153                                 }
154                                 startTime = isAlive0(pid);  // recheck if it is alive
155                                 if (startTime > 0 && origStart > 0 && startTime != origStart) {
156                                     // start time changed (and is not zero), pid is not the same process
157                                     break;
158                                 }
159                             }
160                             exitValue = 0;
161                         }
162                         newCompletion.complete(exitValue);
163                         // remove from cache afterwards
164                         completions.remove(pid, newCompletion);
165                     }
166                 });
167             }
168         }
169         return completion;
170     }
171
172     @Override
173     public CompletableFuture<ProcessHandle> onExit() {
174         if (this.equals(current)) {
175             throw new IllegalStateException("onExit for current process not allowed");
176         }
177
178         return ProcessHandleImpl.completion(pid(), false)
179                 .handleAsync((exitStatus, unusedThrowable) -> this);
180     }
181
182     /**
183      * Wait for the process to exit, return the value.
184      * Conditionally reap the value if requested
185      * @param pid the processId
186      * @param reapvalue if true, the value is retrieved,
187      *                   else return the value and leave the process waitable
188      *
189      * @return the value or -1 if an error occurs
190      */

191     private static native int waitForProcessExit0(long pid, boolean reapvalue);
192
193     /**
194      * The pid of this ProcessHandle.
195      */

196     private final long pid;
197
198     /**
199      * The start time of this process.
200      * If STARTTIME_ANY, the start time of the process is not available from the os.
201      * If greater than zero, the start time of the process.
202      */

203     private final long startTime;
204
205     /* The start time should match any value.
206      * Typically, this is because the OS can not supply it.
207      * The process is known to exist but not the exact start time.
208      */

209     private final long STARTTIME_ANY = 0L;
210
211     /* The start time of a Process that does not exist. */
212     private final long STARTTIME_PROCESS_UNKNOWN = -1;
213
214     /**
215      * Private constructor.  Instances are created by the {@code get(long)} factory.
216      * @param pid the pid for this instance
217      */

218     private ProcessHandleImpl(long pid, long startTime) {
219         this.pid = pid;
220         this.startTime = startTime;
221     }
222
223     /**
224      * Returns a ProcessHandle for an existing native process.
225      *
226      * @param pid the native process identifier
227      * @return The ProcessHandle for the pid if the process is alive;
228      *      or {@code nullif the process ID does not exist in the native system.
229      * @throws SecurityException if RuntimePermission("manageProcess") is not granted
230      */

231     static Optional<ProcessHandle> get(long pid) {
232         SecurityManager sm = System.getSecurityManager();
233         if (sm != null) {
234             sm.checkPermission(new RuntimePermission("manageProcess"));
235         }
236         long start = isAlive0(pid);
237         return (start >= 0)
238                 ? Optional.of(new ProcessHandleImpl(pid, start))
239                 : Optional.empty();
240     }
241
242     /**
243      * Returns a ProcessHandle for an existing native process known to be alive.
244      * The startTime of the process is retrieved and stored in the ProcessHandle.
245      * It does not perform a security check since it is called from ProcessImpl.
246      * @param pid of the known to exist process
247      * @return a ProcessHandle corresponding to an existing Process instance
248      */

249     static ProcessHandleImpl getInternal(long pid) {
250         return new ProcessHandleImpl(pid, isAlive0(pid));
251     }
252
253     /**
254      * Returns the native process ID.
255      * A {@code long} is used to be able to fit the system specific binary values
256      * for the process.
257      *
258      * @return the native process ID
259      */

260     @Override
261     public long pid() {
262         return pid;
263     }
264
265     /**
266      * Returns the ProcessHandle for the current native process.
267      *
268      * @return The ProcessHandle for the OS process.
269      * @throws SecurityException if RuntimePermission("manageProcess") is not granted
270      */

271     public static ProcessHandleImpl current() {
272         SecurityManager sm = System.getSecurityManager();
273         if (sm != null) {
274             sm.checkPermission(new RuntimePermission("manageProcess"));
275         }
276         return current;
277     }
278
279     /**
280      * Return the pid of the current process.
281      *
282      * @return the pid of the  current process
283      */

284     private static native long getCurrentPid0();
285
286     /**
287      * Returns a ProcessHandle for the parent process.
288      *
289      * @return a ProcessHandle of the parent process; {@code null} is returned
290      *         if the child process does not have a parent
291      * @throws SecurityException           if permission is not granted by the
292      *                                     security policy
293      */

294     public Optional<ProcessHandle> parent() {
295         SecurityManager sm = System.getSecurityManager();
296         if (sm != null) {
297             sm.checkPermission(new RuntimePermission("manageProcess"));
298         }
299         long ppid = parent0(pid, startTime);
300         if (ppid <= 0) {
301             return Optional.empty();
302         }
303         return get(ppid);
304     }
305
306     /**
307      * Returns the parent of the native pid argument.
308      *
309      * @param pid the process id
310      * @param startTime the startTime of the process
311      * @return the parent of the native pid; if any, otherwise -1
312      */

313     private static native long parent0(long pid, long startTime);
314
315     /**
316      * Returns the number of pids filled in to the array.
317      * @param pid if {@code pid} equals zero, then all known processes are returned;
318      *      otherwise only direct child process pids are returned
319      * @param pids an allocated long array to receive the pids
320      * @param ppids an allocated long array to receive the parent pids; may be null
321      * @param starttimes an allocated long array to receive the child start times; may be null
322      * @return if greater than or equals to zero is the number of pids in the array;
323      *      if greater than the length of the arrays, the arrays are too small
324      */

325     private static native int getProcessPids0(long pid, long[] pids,
326                                               long[] ppids, long[] starttimes);
327
328     /**
329      * Destroy the process for this ProcessHandle.
330      * The native code checks the start time before sending the termination request.
331      *
332      * @param force {@code trueif the process should be terminated forcibly;
333      *     else {@code falsefor a normal termination
334      */

335     boolean destroyProcess(boolean force) {
336         if (this.equals(current)) {
337             throw new IllegalStateException("destroy of current process not allowed");
338         }
339         return destroy0(pid, startTime, force);
340     }
341
342     /**
343       * Signal the process to terminate.
344       * The process is signaled only if its start time matches the known start time.
345       *
346       * @param pid  process id to kill
347       * @param startTime the start time of the process
348       * @param forcibly true to forcibly terminate (SIGKILL vs SIGTERM)
349       * @return true if the process was signaled without error; false otherwise
350       */

351     private static native boolean destroy0(long pid, long startTime, boolean forcibly);
352
353     @Override
354     public boolean destroy() {
355         return destroyProcess(false);
356     }
357
358     @Override
359     public boolean destroyForcibly() {
360         return destroyProcess(true);
361     }
362
363
364     @Override
365     public boolean supportsNormalTermination() {
366         return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
367     }
368
369     /**
370      * Tests whether the process represented by this {@code ProcessHandle} is alive.
371      *
372      * @return {@code trueif the process represented by this
373      * {@code ProcessHandle} object has not yet terminated.
374      * @since 9
375      */

376     @Override
377     public boolean isAlive() {
378         long start = isAlive0(pid);
379         return (start >= 0 && (start == startTime || start == 0 || startTime == 0));
380     }
381
382     /**
383      * Returns the process start time depending on whether the pid is alive.
384      * This must not reap the exitValue.
385      *
386      * @param pid the pid to check
387      * @return the start time in milliseconds since 1970,
388      *         0 if the start time cannot be determined,
389      *         -1 if the pid does not exist.
390      */

391     private static native long isAlive0(long pid);
392
393     @Override
394     public Stream<ProcessHandle> children() {
395         // The native OS code selects based on matching the requested parent pid.
396         // If the original parent exits, the pid may have been re-used for
397         // this newer process.
398         // Processes started by the original parent (now dead) will all have
399         // start times less than the start of this newer parent.
400         // Processes started by this newer parent will have start times equal
401         // or after this parent.
402         return children(pid).filter(ph -> startTime <= ((ProcessHandleImpl)ph).startTime);
403     }
404
405     /**
406      * Returns a Stream of the children of a process or all processes.
407      *
408      * @param pid the pid of the process for which to find the children;
409      *            0 for all processes
410      * @return a stream of ProcessHandles
411      */

412     static Stream<ProcessHandle> children(long pid) {
413         SecurityManager sm = System.getSecurityManager();
414         if (sm != null) {
415             sm.checkPermission(new RuntimePermission("manageProcess"));
416         }
417         int size = 100;
418         long[] childpids = null;
419         long[] starttimes = null;
420         while (childpids == null || size > childpids.length) {
421             childpids = new long[size];
422             starttimes = new long[size];
423             size = getProcessPids0(pid, childpids, null, starttimes);
424         }
425
426         final long[] cpids = childpids;
427         final long[] stimes = starttimes;
428         return IntStream.range(0, size).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
429     }
430
431     @Override
432     public Stream<ProcessHandle> descendants() {
433         SecurityManager sm = System.getSecurityManager();
434         if (sm != null) {
435             sm.checkPermission(new RuntimePermission("manageProcess"));
436         }
437         int size = 100;
438         long[] pids = null;
439         long[] ppids = null;
440         long[] starttimes = null;
441         while (pids == null || size > pids.length) {
442             pids = new long[size];
443             ppids = new long[size];
444             starttimes = new long[size];
445             size = getProcessPids0(0, pids, ppids, starttimes);
446         }
447
448         int next = 0;       // index of next process to check
449         int count = -1;     // count of subprocesses scanned
450         long ppid = pid;    // start looking for this parent
451         long ppStart = 0;
452         // Find the start time of the parent
453         for (int i = 0; i < size; i++) {
454             if (pids[i] == ppid) {
455                 ppStart = starttimes[i];
456                 break;
457             }
458         }
459         do {
460             // Scan from next to size looking for ppid with child start time
461             // the same or later than the parent.
462             // If found, exchange it with index next
463             for (int i = next; i < size; i++) {
464                 if (ppids[i] == ppid &&
465                         ppStart <= starttimes[i]) {
466                     swap(pids, i, next);
467                     swap(ppids, i, next);
468                     swap(starttimes, i, next);
469                     next++;
470                 }
471             }
472             ppid = pids[++count];   // pick up the next pid to scan for
473             ppStart = starttimes[count];    // and its start time
474         } while (count < next);
475
476         final long[] cpids = pids;
477         final long[] stimes = starttimes;
478         return IntStream.range(0, count).mapToObj(i -> new ProcessHandleImpl(cpids[i], stimes[i]));
479     }
480
481     // Swap two elements in an array
482     private static void swap(long[] array, int x, int y) {
483         long v = array[x];
484         array[x] = array[y];
485         array[y] = v;
486     }
487
488     @Override
489     public ProcessHandle.Info info() {
490         return ProcessHandleImpl.Info.info(pid, startTime);
491     }
492
493     @Override
494     public int compareTo(ProcessHandle other) {
495         return Long.compare(pid, ((ProcessHandleImpl) other).pid);
496     }
497
498     @Override
499     public String toString() {
500         return Long.toString(pid);
501     }
502
503     @Override
504     public int hashCode() {
505         return Long.hashCode(pid);
506     }
507
508     @Override
509     public boolean equals(Object obj) {
510         if (this == obj) {
511             return true;
512         }
513         if (obj instanceof ProcessHandleImpl) {
514             ProcessHandleImpl other = (ProcessHandleImpl) obj;
515             return (pid == other.pid) &&
516                     (startTime == other.startTime
517                         || startTime == 0
518                         || other.startTime == 0);
519         }
520         return false;
521     }
522
523     /**
524      * Implementation of ProcessHandle.Info.
525      * Information snapshot about a process.
526      * The attributes of a process vary by operating system and are not available
527      * in all implementations.  Additionally, information about other processes
528      * is limited by the operating system privileges of the process making the request.
529      * If a value is not available, either a {@code null} or {@code -1} is stored.
530      * The accessor methods return {@code nullif the value is not available.
531      */

532     static class Info implements ProcessHandle.Info {
533         static {
534             initIDs();
535         }
536
537         /**
538          * Initialization of JNI fieldIDs.
539          */

540         private static native void initIDs();
541
542         /**
543          * Fill in this Info instance with information about the native process.
544          * If values are not available the native code does not modify the field.
545          * @param pid  of the native process
546          */

547         private native void info0(long pid);
548
549         String command;
550         String commandLine;
551         String[] arguments;
552         long startTime;
553         long totalTime;
554         String user;
555
556         Info() {
557             command = null;
558             commandLine = null;
559             arguments = null;
560             startTime = -1L;
561             totalTime = -1L;
562             user = null;
563         }
564
565         /**
566          * Returns the Info object with the fields from the process.
567          * Whatever fields are provided by native are returned.
568          * If the startTime of the process does not match the provided
569          * startTime then an empty Info is returned.
570          *
571          * @param pid the native process identifier
572          * @param startTime the startTime of the process being queried
573          * @return ProcessHandle.Info non-null; individual fields may be null
574          *          or -1 if not available.
575          */

576         public static ProcessHandle.Info info(long pid, long startTime) {
577             Info info = new Info();
578             info.info0(pid);
579             if (startTime != info.startTime) {
580                 info.command = null;
581                 info.arguments = null;
582                 info.startTime = -1L;
583                 info.totalTime = -1L;
584                 info.user = null;
585             }
586             return info;
587         }
588
589         @Override
590         public Optional<String> command() {
591             return Optional.ofNullable(command);
592         }
593
594         @Override
595         public Optional<String> commandLine() {
596             if (command != null && arguments != null) {
597                 return Optional.of(command + " " + String.join(" ", arguments));
598             } else {
599                 return Optional.ofNullable(commandLine);
600             }
601         }
602
603         @Override
604         public Optional<String[]> arguments() {
605             return Optional.ofNullable(arguments);
606         }
607
608         @Override
609         public Optional<Instant> startInstant() {
610             return (startTime > 0)
611                     ? Optional.of(Instant.ofEpochMilli(startTime))
612                     : Optional.empty();
613         }
614
615         @Override
616         public Optional<Duration> totalCpuDuration() {
617             return (totalTime != -1)
618                     ? Optional.of(Duration.ofNanos(totalTime))
619                     : Optional.empty();
620         }
621
622         @Override
623         public Optional<String> user() {
624             return Optional.ofNullable(user);
625         }
626
627         @Override
628         public String toString() {
629             StringBuilder sb = new StringBuilder(60);
630             sb.append('[');
631             if (user != null) {
632                 sb.append("user: ");
633                 sb.append(user());
634             }
635             if (command != null) {
636                 if (sb.length() != 0) sb.append(", ");
637                 sb.append("cmd: ");
638                 sb.append(command);
639             }
640             if (arguments != null && arguments.length > 0) {
641                 if (sb.length() != 0) sb.append(", ");
642                 sb.append("args: ");
643                 sb.append(Arrays.toString(arguments));
644             }
645             if (commandLine != null) {
646                 if (sb.length() != 0) sb.append(", ");
647                 sb.append("cmdLine: ");
648                 sb.append(commandLine);
649             }
650             if (startTime > 0) {
651                 if (sb.length() != 0) sb.append(", ");
652                 sb.append("startTime: ");
653                 sb.append(startInstant());
654             }
655             if (totalTime != -1) {
656                 if (sb.length() != 0) sb.append(", ");
657                 sb.append("totalTime: ");
658                 sb.append(totalCpuDuration().toString());
659             }
660             sb.append(']');
661             return sb.toString();
662         }
663     }
664 }
665