mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-29 23:40:00 +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.volmlib.util.localization.BukkitLanguageSwitcher;
|
||||||
import art.arcane.iris.integration.MultiverseCoreLink;
|
import art.arcane.iris.integration.MultiverseCoreLink;
|
||||||
import art.arcane.iris.platform.agent.Agent;
|
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.iris.platform.bukkit.nms.INMS;
|
||||||
import art.arcane.volmlib.nativelib.terrain.ServerShutdownBoundary;
|
import art.arcane.volmlib.nativelib.terrain.ServerShutdownBoundary;
|
||||||
import art.arcane.iris.studio.view.BukkitGuiHost;
|
import art.arcane.iris.studio.view.BukkitGuiHost;
|
||||||
@@ -579,6 +580,17 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware {
|
|||||||
this::getIrisVersion,
|
this::getIrisVersion,
|
||||||
this::getMCVersion));
|
this::getMCVersion));
|
||||||
SlimJar.load();
|
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) {
|
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