SubServers-2/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Host/Executable.java

122 lines
5.1 KiB
Java

package net.ME1312.SubServers.Bungee.Host;
import net.ME1312.Galaxi.Library.Platform;
import net.ME1312.Galaxi.Library.Try;
import net.ME1312.Galaxi.Library.Util;
import net.ME1312.SubServers.Bungee.Library.Compatibility.JNA;
import java.io.File;
import java.util.stream.Stream;
/**
* Executable Handler Class
*/
public class Executable {
private Executable() {}
private static final boolean USE_SESSION_TRACKING;
/**
* Format a command to be executed
*
* @param gitbash Git Bash location (optional)
* @param exec Executable String
* @return Formatted Executable
*/
public static String[] parse(String gitbash, String exec) {
if (exec.startsWith("java "))
exec = '\"' + System.getProperty("java.home") + File.separator + "bin" + File.separator + "java" + '\"' + exec.substring(4);
String[] cmd;
if (Platform.getSystem() == Platform.WINDOWS) {
if (gitbash != null && (exec.toLowerCase().startsWith("bash ") || exec.toLowerCase().startsWith("sh ")))
exec = '"' + gitbash + ((gitbash.endsWith(File.separator))?"":File.separator) + "bin" + File.separatorChar + "sh.exe\" -lc \"" +
exec.replace("\\", "/\\").replace("\"", "\\\"").replace("^", "^^").replace("%", "^%").replace("&", "^&").replace("<", "^<").replace(">", "^>").replace("|", "^|") + '"';
cmd = new String[]{"cmd.exe", "/q", "/c", '"'+exec+'"'};
} else if (USE_SESSION_TRACKING) {
cmd = new String[]{"setsid", "sh", "-lc", exec};
} else {
cmd = new String[]{"sh", "-lc", exec};
}
return cmd;
}
static {
USE_SESSION_TRACKING = Platform.getSystem() != Platform.WINDOWS && Try.all.get(() -> {
Process test = Runtime.getRuntime().exec(new String[]{"setsid", "bash", "-c", "exit 0"});
test.waitFor(); // The purpose of this block is to test for the 'setsid' command
return test.exitValue() == 0;
}, false);
}
/**
* Get the PID of a currently running process
*
* @param process Process
* @return Process ID (null if unknown)
*/
@SuppressWarnings("JavaReflectionMemberAccess")
public static Long pid(Process process) {
if (process.isAlive()) {
try { // Java 9 Standard
return (long) Process.class.getMethod("pid").invoke(process);
} catch (Throwable e) {
try { // Java 8 Not-so-standard
Object response = Util.reflect(process.getClass().getDeclaredField("pid"), process);
if (response instanceof Number) {
return ((Number) response).longValue();
} else throw e;
} catch (Throwable e2) {
if (Platform.getSystem() == Platform.WINDOWS) try {
long handle = Util.reflect(process.getClass().getDeclaredField("handle"), process);
ClassLoader jna = JNA.get();
Class<?> pc = jna.loadClass("com.sun.jna.Pointer"),
ntc = jna.loadClass("com.sun.jna.platform.win32.WinNT$HANDLE"),
k32c = jna.loadClass("com.sun.jna.platform.win32.Kernel32");
Object k32 = k32c.getField("INSTANCE").get(null),
nt = ntc.getConstructor().newInstance();
ntc.getMethod("setPointer", pc).invoke(nt, pc.getMethod("createConstant", long.class).invoke(null, handle));
return ((Number) k32c.getMethod("GetProcessId", ntc).invoke(k32, nt)).longValue();
} catch (Throwable e3) {
// No way to find pid, I suppose.
}
}
}
}
return null;
}
/**
* Terminate a currently running process
*
* @param process Process
*/
public static void terminate(Process process) {
if (process.isAlive()) {
Long pid;
if (Platform.getSystem() == Platform.WINDOWS) {
if ((pid = pid(process)) != null) Try.all.run(() -> Runtime.getRuntime().exec(new String[]{"taskkill.exe", "/T", "/F", "/PID", pid.toString()}).waitFor());
} else if (USE_SESSION_TRACKING) {
if ((pid = pid(process)) != null) Try.all.run(() -> Runtime.getRuntime().exec(new String[]{"bash", "-c", "kill -9 $(ps -s " + pid + " -o pid=)"}).waitFor());
}
if (process.isAlive() && terminate9(process)) {
process.destroyForcibly();
}
}
}
private static boolean terminate9(Object handle) {
try { // Attempt iteration over Java 9 ProcessHandle objects
Class<?> clazz = handle.getClass();
Stream<?> children = (Stream<?>) clazz.getMethod("children").invoke(handle);
clazz.getMethod("destroyForcibly").invoke(handle);
children.forEach(Executable::terminate9);
return false;
} catch (Throwable e) {
return true;
}
}
}