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 */
25package java.lang;
26
27import java.lang.annotation.Native;
28import java.security.PrivilegedAction;
29import java.time.Duration;
30import java.time.Instant;
31import java.util.Arrays;
32import java.util.Optional;
33import java.util.concurrent.CompletableFuture;
34import java.util.concurrent.ConcurrentHashMap;
35import java.util.concurrent.ConcurrentMap;
36import java.util.concurrent.Executor;
37import java.util.concurrent.Executors;
38import java.util.concurrent.ThreadFactory;
39import java.util.stream.IntStream;
40import java.util.stream.Stream;
41
42import static java.security.AccessController.doPrivileged;
43
44/**
45 * ProcessHandleImpl is the implementation of ProcessHandle.
46 *
47 * @see Process
48 * @since 9
49 */
50final 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 is alive
155                                if (origStart > 0 && startTime != origStart) {
156                                    // start time changed, 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 null} if 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 true} if the process should be terminated forcibly;
333     *     else {@code false} for 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 true} if 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 null} if 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