-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
unix implementation of ProcessHandle
- Loading branch information
1 parent
a818ae0
commit 0d9386f
Showing
9 changed files
with
387 additions
and
32 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
java-api/src/java10/java/xyz/wagyourtail/jvmdg/j10/stub/java_base/J_L_M_RuntimeMXBean.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package xyz.wagyourtail.jvmdg.j10.stub.java_base; | ||
|
||
import xyz.wagyourtail.jvmdg.version.Stub; | ||
|
||
import java.lang.management.RuntimeMXBean; | ||
|
||
public class J_L_M_RuntimeMXBean { | ||
|
||
@Stub | ||
public static long getPid(RuntimeMXBean bean) { | ||
String name = bean.getName(); | ||
name = name.substring(0, name.indexOf('@')); | ||
return Long.parseLong(name); | ||
} | ||
|
||
} |
216 changes: 216 additions & 0 deletions
216
java-api/src/java9/java/xyz/wagyourtail/jvmdg/j9/intl/UnixProcessHandle.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
package xyz.wagyourtail.jvmdg.j9.intl; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
import xyz.wagyourtail.jvmdg.j9.stub.java_base.J_L_ProcessHandle; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.nio.file.FileSystems; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
import java.nio.file.StandardWatchEventKinds; | ||
import java.nio.file.WatchService; | ||
import java.time.Duration; | ||
import java.time.Instant; | ||
import java.util.Arrays; | ||
import java.util.Optional; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.stream.Stream; | ||
|
||
public class UnixProcessHandle implements J_L_ProcessHandle { | ||
private final long pid; | ||
private String[] pidInfo; | ||
private final String[] cmdline; | ||
|
||
public UnixProcessHandle(long pid) { | ||
this.pid = pid; | ||
pidInfo = readPidInfo(); | ||
cmdline = readCmdLine(); | ||
} | ||
|
||
public static boolean isUnix() { | ||
return File.pathSeparatorChar == ':'; | ||
} | ||
|
||
/** | ||
* <a href="https://man7.org/linux/man-pages/man5/proc_pid_stat.5.html">/proc/<pid>/stat</a> | ||
*/ | ||
private String[] readPidInfo() { | ||
Path pth = Paths.get("/proc/" + pid + "/stat"); | ||
if (Files.isReadable(pth)) { | ||
try { | ||
pidInfo = new String(Files.readAllBytes(pth)).split(" "); | ||
} catch (IOException e) { | ||
pidInfo = null; | ||
} | ||
} | ||
return pidInfo; | ||
} | ||
|
||
/** | ||
* <a href="https://man7.org/linux/man-pages/man5/proc_pid_cmdline.5.html">/proc/<pid>/cmdline</a> | ||
*/ | ||
private String[] readCmdLine() { | ||
Path pth = Paths.get("/proc/" + pid + "/cmdline"); | ||
if (Files.isReadable(pth)) { | ||
try { | ||
return new String(Files.readAllBytes(pth)).split("\0"); | ||
} catch (IOException e) { | ||
return null; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public long pid() { | ||
return pid; | ||
} | ||
|
||
@Override | ||
public Optional<J_L_ProcessHandle> parent() { | ||
String[] info = readPidInfo(); | ||
if (info != null) { | ||
return Optional.of(new UnixProcessHandle(Long.parseLong(info[3]))); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Stream<J_L_ProcessHandle> children() { | ||
Path pth = Paths.get("/proc/" + pid + "/task"); | ||
try(Stream<Path> stream = Files.list(pth)) { | ||
return Stream.of(stream.toArray(Path[]::new)).flatMap(e -> { | ||
try { | ||
String s = new String(Files.readAllBytes(e.resolve("children"))); | ||
if (s.isEmpty()) { | ||
return Stream.empty(); | ||
} | ||
return Arrays.stream(s.split(" ")); | ||
} catch (IOException ex) { | ||
return Stream.empty(); | ||
} | ||
}).mapToLong(Long::parseLong).mapToObj(UnixProcessHandle::new); | ||
} catch (IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
|
||
@Override | ||
public Stream<J_L_ProcessHandle> descendants() { | ||
return children().flatMap(e -> Stream.concat(Stream.of(e), e.descendants())); | ||
} | ||
|
||
@Override | ||
public Info info() { | ||
return new Info() { | ||
@Override | ||
public Optional<String> command() { | ||
if (cmdline != null) { | ||
return Optional.ofNullable(cmdline[0]); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Optional<String> commandLine() { | ||
if (cmdline != null) { | ||
return Optional.of(String.join(" ", cmdline)); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Optional<String[]> arguments() { | ||
if (cmdline != null) { | ||
String[] args = new String[cmdline.length - 1]; | ||
System.arraycopy(cmdline, 1, args, 0, cmdline.length - 1); | ||
return Optional.of(args); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Optional<Instant> startInstant() { | ||
String[] info = readPidInfo(); | ||
if (info != null) { | ||
return Optional.of(Instant.ofEpochMilli(Long.parseLong(info[22]))); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Optional<Duration> totalCpuDuration() { | ||
String[] info = readPidInfo(); | ||
if (info != null) { | ||
return Optional.of(Duration.ofMillis(Long.parseLong(info[14]))); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public Optional<String> user() { | ||
try { | ||
return Optional.of(Files.getOwner(Paths.get("/proc/" + pid + "/cmdline")).getName()); | ||
} catch (IOException e) { | ||
return Optional.empty(); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
@Override | ||
public CompletableFuture<J_L_ProcessHandle> onExit() { | ||
return CompletableFuture.supplyAsync(() -> { | ||
try (WatchService ws = FileSystems.getDefault().newWatchService()) { | ||
Path pth = Paths.get("/proc/" + pid + "/status"); | ||
pth.register(ws, StandardWatchEventKinds.ENTRY_DELETE); | ||
ws.take(); | ||
return this; | ||
} catch (IOException | InterruptedException e) { | ||
throw new RuntimeException(e); | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public boolean supportsNormalTermination() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public boolean destroy() { | ||
ProcessBuilder pb = new ProcessBuilder("kill", Long.toString(pid)); | ||
try { | ||
Process p = pb.start(); | ||
p.waitFor(); | ||
return p.exitValue() == 0; | ||
} catch (IOException | InterruptedException e) { | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
public boolean destroyForcibly() { | ||
ProcessBuilder pb = new ProcessBuilder("kill", "-9", Long.toString(pid)); | ||
try { | ||
Process p = pb.start(); | ||
p.waitFor(); | ||
return p.exitValue() == 0; | ||
} catch (IOException | InterruptedException e) { | ||
return false; | ||
} | ||
} | ||
|
||
@Override | ||
public boolean isAlive() { | ||
return Files.exists(Paths.get("/proc/" + pid + "/status")); | ||
} | ||
|
||
@Override | ||
public int compareTo(@NotNull J_L_ProcessHandle other) { | ||
return Long.compare(pid, other.pid()); | ||
} | ||
} |
96 changes: 96 additions & 0 deletions
96
java-api/src/java9/java/xyz/wagyourtail/jvmdg/j9/stub/java_base/J_L_ProcessHandle.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package xyz.wagyourtail.jvmdg.j9.stub.java_base; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
import sun.management.ManagementFactoryHelper; | ||
import sun.management.VMManagement; | ||
import xyz.wagyourtail.jvmdg.exc.MissingStubError; | ||
import xyz.wagyourtail.jvmdg.j9.intl.UnixProcessHandle; | ||
import xyz.wagyourtail.jvmdg.util.Utils; | ||
import xyz.wagyourtail.jvmdg.version.Adapter; | ||
|
||
import java.lang.invoke.MethodHandles; | ||
import java.lang.management.ManagementFactory; | ||
import java.lang.management.RuntimeMXBean; | ||
import java.time.Duration; | ||
import java.time.Instant; | ||
import java.util.Optional; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.stream.Stream; | ||
|
||
/** | ||
* Implementation of ProcessHandle is very incomplete, as can only get this kind of info on the | ||
* current process without access to natives. | ||
* <p> | ||
* If you know some funny sun classes or something to get other processes, lmk | ||
*/ | ||
@Adapter("java/lang/ProcessHandle") | ||
public interface J_L_ProcessHandle extends Comparable<J_L_ProcessHandle> { | ||
RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean(); | ||
|
||
static Optional<J_L_ProcessHandle> of(long pid) { | ||
if (pid == current().pid()) { | ||
return Optional.of(current()); | ||
} | ||
throw new UnsupportedOperationException("Getting arbitrary PID process handles is not supported"); | ||
} | ||
|
||
static J_L_ProcessHandle current() { | ||
String pidName = bean.getName(); | ||
long pid = Long.parseLong(pidName.substring(0, pidName.indexOf('@'))); | ||
if (UnixProcessHandle.isUnix()) { | ||
return new UnixProcessHandle(pid) { | ||
@Override | ||
public CompletableFuture<J_L_ProcessHandle> onExit() { | ||
throw new IllegalStateException(); | ||
} | ||
}; | ||
} | ||
throw MissingStubError.create(); | ||
} | ||
|
||
static Stream<J_L_ProcessHandle> allProcesses() { | ||
return Stream.of(current()); | ||
} | ||
|
||
long pid(); | ||
|
||
Optional<J_L_ProcessHandle> parent(); | ||
|
||
Stream<J_L_ProcessHandle> children(); | ||
|
||
Stream<J_L_ProcessHandle> descendants(); | ||
|
||
Info info(); | ||
|
||
CompletableFuture<J_L_ProcessHandle> onExit(); | ||
|
||
boolean supportsNormalTermination(); | ||
|
||
boolean destroy(); | ||
|
||
boolean destroyForcibly(); | ||
|
||
boolean isAlive(); | ||
|
||
int hashCode(); | ||
|
||
boolean equals(Object obj); | ||
|
||
@Override | ||
int compareTo(@NotNull J_L_ProcessHandle other); | ||
|
||
@Adapter("java/lang/ProcessHandle$Info") | ||
interface Info { | ||
Optional<String> command(); | ||
|
||
Optional<String> commandLine(); | ||
|
||
Optional<String[]> arguments(); | ||
|
||
Optional<Instant> startInstant(); | ||
|
||
Optional<Duration> totalCpuDuration(); | ||
|
||
Optional<String> user(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.