mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-30 07:50:03 +00:00
Fixed Spigot agent attach
- Iris restores the JVM SIGQUIT handler when the server console replaced it - The Java agent attaches on Spigot servers with a terminal console - Tests cover restoring the handler after a console resets it
This commit is contained in:
1 parent
58ab3fcbbe
commit
21ab5baf27
3 files changed
+119
No files matched your search
@@ -58,6 +58,7 @@ import art.arcane.volmlib.util.director.help.DirectorMiniMenu;
|
||||
import art.arcane.volmlib.util.localization.BukkitLanguageSwitcher;
|
||||
import art.arcane.iris.integration.MultiverseCoreLink;
|
||||
import art.arcane.iris.platform.agent.Agent;
|
||||
import art.arcane.iris.platform.agent.AttachSignal;
|
||||
import art.arcane.iris.platform.bukkit.nms.INMS;
|
||||
import art.arcane.volmlib.nativelib.terrain.ServerShutdownBoundary;
|
||||
import art.arcane.iris.studio.view.BukkitGuiHost;
|
||||
@@ -579,6 +580,17 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
||||
this::getIrisVersion,
|
||||
this::getMCVersion));
|
||||
SlimJar.load();
|
||||
restoreAttachSignal();
|
||||
}
|
||||
|
||||
private static void restoreAttachSignal() {
|
||||
try {
|
||||
if (AttachSignal.restoreJvmHandler()) {
|
||||
info("Restored the JVM SIGQUIT handler that the server console replaced");
|
||||
}
|
||||
} catch (RuntimeException failure) {
|
||||
reportError("Could not restore the JVM SIGQUIT handler; the Iris agent may fail to attach.", failure);
|
||||
}
|
||||
}
|
||||
|
||||
private static void bridgeLog(LogLevel level, String message) {
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package art.arcane.iris.platform.agent;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class AttachSignal {
|
||||
private static final int SIGQUIT = 3;
|
||||
private static final int SIGTERM = 15;
|
||||
private static final long ACTION_BYTES = 256;
|
||||
private static final long SIG_IGN = 1L;
|
||||
|
||||
private AttachSignal() {
|
||||
}
|
||||
|
||||
public static boolean restoreJvmHandler() {
|
||||
if (System.getProperty("os.name", "").toLowerCase(Locale.ROOT).contains("win")) {
|
||||
return false;
|
||||
}
|
||||
Linker linker = Linker.nativeLinker();
|
||||
MethodHandle sigaction = linker.downcallHandle(
|
||||
linker.defaultLookup().find("sigaction")
|
||||
.orElseThrow(() -> new IllegalStateException("sigaction is unavailable")),
|
||||
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS));
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment jvm = arena.allocate(ACTION_BYTES);
|
||||
MemorySegment quit = arena.allocate(ACTION_BYTES);
|
||||
if ((int) sigaction.invokeExact(SIGTERM, MemorySegment.NULL, jvm) != 0
|
||||
|| (int) sigaction.invokeExact(SIGQUIT, MemorySegment.NULL, quit) != 0) {
|
||||
throw new IllegalStateException("sigaction could not read the SIGTERM and SIGQUIT handlers");
|
||||
}
|
||||
long handler = jvm.get(ValueLayout.JAVA_LONG, 0);
|
||||
if (handler == 0L || handler == SIG_IGN || quit.get(ValueLayout.JAVA_LONG, 0) == handler) {
|
||||
return false;
|
||||
}
|
||||
if ((int) sigaction.invokeExact(SIGQUIT, jvm, MemorySegment.NULL) != 0) {
|
||||
throw new IllegalStateException("sigaction could not restore the JVM SIGQUIT handler");
|
||||
}
|
||||
return true;
|
||||
} catch (RuntimeException | Error failure) {
|
||||
throw failure;
|
||||
} catch (Throwable failure) {
|
||||
throw new IllegalStateException("Unable to restore the JVM SIGQUIT handler", failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package art.arcane.iris.platform.agent;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
|
||||
public class AttachSignalTest {
|
||||
private static final int SIGQUIT = 3;
|
||||
private static final int SIGTERM = 15;
|
||||
private static final long ACTION_BYTES = 256;
|
||||
private static final MethodHandle SIGACTION = Linker.nativeLinker().downcallHandle(
|
||||
Linker.nativeLinker().defaultLookup().find("sigaction").orElseThrow(),
|
||||
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, ValueLayout.ADDRESS));
|
||||
|
||||
@Test
|
||||
public void aConsoleThatResetsQuitGetsTheJvmHandlerBack() throws Throwable {
|
||||
assumeFalse(System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("win"));
|
||||
long jvmHandler = handler(SIGTERM);
|
||||
assertNotEquals(0L, jvmHandler);
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment original = arena.allocate(ACTION_BYTES);
|
||||
assertEquals(0, (int) SIGACTION.invokeExact(SIGQUIT, MemorySegment.NULL, original));
|
||||
try {
|
||||
MemorySegment reset = arena.allocate(ACTION_BYTES);
|
||||
assertEquals(0, (int) SIGACTION.invokeExact(SIGQUIT, reset, MemorySegment.NULL));
|
||||
assertEquals(0L, handler(SIGQUIT));
|
||||
|
||||
assertTrue(AttachSignal.restoreJvmHandler());
|
||||
|
||||
assertEquals(jvmHandler, handler(SIGQUIT));
|
||||
assertFalse(AttachSignal.restoreJvmHandler());
|
||||
} finally {
|
||||
assertEquals(0, (int) SIGACTION.invokeExact(SIGQUIT, original, MemorySegment.NULL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static long handler(int signal) throws Throwable {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment action = arena.allocate(ACTION_BYTES);
|
||||
assertEquals(0, (int) SIGACTION.invokeExact(signal, MemorySegment.NULL, action));
|
||||
return action.get(ValueLayout.JAVA_LONG, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user