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 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