papermc/CraftBukkit-Patches/0057-Watchdog-Thread.patch
2014-04-29 09:24:36 +01:00

562 lines
20 KiB
Diff

From 91e137f00b53a82e6acc86c8eb6f773e79aa8875 Mon Sep 17 00:00:00 2001
From: md_5 <md_5@live.com.au>
Date: Sat, 23 Feb 2013 12:33:20 +1100
Subject: [PATCH] Watchdog Thread.
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
new file mode 100644
index 0000000..d7b8891
--- /dev/null
+++ b/dependency-reduced-pom.xml
@@ -0,0 +1,262 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>spigot-parent</artifactId>
+ <groupId>org.spigotmc</groupId>
+ <version>dev-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.spigotmc</groupId>
+ <artifactId>spigot</artifactId>
+ <name>Spigot</name>
+ <version>1.7.9-R0.1-SNAPSHOT</version>
+ <url>http://www.spigotmc.org</url>
+ <build>
+ <defaultGoal>install</defaultGoal>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>com.lukegb.mojo</groupId>
+ <artifactId>gitdescribe-maven-plugin</artifactId>
+ <versionRange>[1.3,)</versionRange>
+ <goals>
+ <goal>gitdescribe</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore />
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>com.lukegb.mojo</groupId>
+ <artifactId>gitdescribe-maven-plugin</artifactId>
+ <version>1.3</version>
+ <executions>
+ <execution>
+ <phase>compile</phase>
+ <goals>
+ <goal>gitdescribe</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <outputPrefix>git-Spigot-</outputPrefix>
+ <outputPostfix />
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.1</version>
+ <configuration>
+ <archive>
+ <manifestEntries>
+ <Main-Class>org.bukkit.craftbukkit.Main</Main-Class>
+ <Implementation-Title>CraftBukkit</Implementation-Title>
+ <Implementation-Version>${describe}</Implementation-Version>
+ <Implementation-Vendor>Bukkit Team</Implementation-Vendor>
+ <Specification-Title>Bukkit</Specification-Title>
+ <Specification-Version>${api.version}</Specification-Version>
+ <Specification-Vendor>Bukkit Team</Specification-Vendor>
+ <Sealed>true</Sealed>
+ </manifestEntries>
+ <manifestSections>
+ <manifestSection>
+ <name>net/bukkit/</name>
+ <manifestEntries>
+ <Sealed>true</Sealed>
+ </manifestEntries>
+ </manifestSection>
+ <manifestSection>
+ <name>com/bukkit/</name>
+ <manifestEntries>
+ <Sealed>true</Sealed>
+ </manifestEntries>
+ </manifestSection>
+ <manifestSection>
+ <name>org/bukkit/</name>
+ <manifestEntries>
+ <Sealed>true</Sealed>
+ </manifestEntries>
+ </manifestSection>
+ </manifestSections>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.1</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <relocations>
+ <relocation>
+ <pattern>org.bouncycastle</pattern>
+ <shadedPattern>net.minecraft.v${minecraft_version}.org.bouncycastle</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>joptsimple</pattern>
+ <shadedPattern>org.bukkit.craftbukkit.libs.joptsimple</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>jline</pattern>
+ <shadedPattern>org.bukkit.craftbukkit.libs.jline</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.ibex</pattern>
+ <shadedPattern>org.bukkit.craftbukkit.libs.org.ibex</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.gjt</pattern>
+ <shadedPattern>org.bukkit.craftbukkit.libs.org.gjt</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>com.google.gson</pattern>
+ <shadedPattern>org.bukkit.craftbukkit.libs.com.google.gson</shadedPattern>
+ </relocation>
+ <relocation>
+ <pattern>org.bukkit.craftbukkit</pattern>
+ <shadedPattern>org.bukkit.craftbukkit.v${minecraft_version}</shadedPattern>
+ <excludes>
+ <exclude>org.bukkit.craftbukkit.Main*</exclude>
+ </excludes>
+ </relocation>
+ <relocation>
+ <pattern>net.minecraft.server</pattern>
+ <shadedPattern>net.minecraft.server.v${minecraft_version}</shadedPattern>
+ </relocation>
+ </relocations>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.11</version>
+ <configuration>
+ <workingDirectory>${basedir}/target/test-server</workingDirectory>
+ <excludes>
+ <exclude>org/bukkit/craftbukkit/updater/BukkitDLUpdaterServiceTest.java</exclude>
+ <exclude>org/bukkit/craftbukkit/inventory/ItemStack*Test.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.4.3</version>
+ <executions>
+ <execution>
+ <id>maps-file</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/target</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${basedir}</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>maps.yml</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.wolvereness</groupId>
+ <artifactId>overmapped</artifactId>
+ <version>0.0.2</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>map</goal>
+ </goals>
+ <configuration>
+ <maps>${basedir}/target/maps.yml</maps>
+ <input>${basedir}/target/${project.artifactId}-${project.version}.jar</input>
+ <original>${basedir}/target/unmapped-${project.artifactId}-${project.version}.jar</original>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <repositories>
+ <repository>
+ <id>repobo-snap</id>
+ <url>http://repo.bukkit.org/content/groups/public</url>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>bukkit-plugins</id>
+ <url>http://repo.bukkit.org/content/groups/public</url>
+ </pluginRepository>
+ </pluginRepositories>
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>hamcrest-core</artifactId>
+ <groupId>org.hamcrest</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-library</artifactId>
+ <version>1.3</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>hamcrest-core</artifactId>
+ <groupId>org.hamcrest</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+ <properties>
+ <minecraft_version>1_7_R3</minecraft_version>
+ <buildtag.prefix>git-Bukkit-</buildtag.prefix>
+ <api.version>unknown</api.version>
+ <minecraft.version>1.7.9</minecraft.version>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <junit.version>4.11</junit.version>
+ </properties>
+</project>
+
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 007fb86..51f1761 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -498,6 +498,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
this.a(crashreport);
} finally {
try {
+ org.spigotmc.WatchdogThread.doStop();
this.stop();
this.isStopped = true;
} catch (Throwable throwable1) {
@@ -688,6 +689,7 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
SpigotTimings.tickablesTimer.stopTiming(); // Spigot
this.methodProfiler.b();
+ org.spigotmc.WatchdogThread.tick(); // Spigot
SpigotTimings.serverTickTimer.stopTiming(); // Spigot
org.spigotmc.CustomTimingsHandler.tick(); // Spigot
}
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
new file mode 100644
index 0000000..3e4203f
--- /dev/null
+++ b/src/main/java/org/spigotmc/RestartCommand.java
@@ -0,0 +1,113 @@
+package org.spigotmc;
+
+import java.io.File;
+import java.util.List;
+import net.minecraft.server.EntityPlayer;
+import net.minecraft.server.MinecraftServer;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+public class RestartCommand extends Command
+{
+
+ public RestartCommand(String name)
+ {
+ super( name );
+ this.description = "Restarts the server";
+ this.usageMessage = "/restart";
+ this.setPermission( "bukkit.command.restart" );
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String currentAlias, String[] args)
+ {
+ if ( testPermission( sender ) )
+ {
+ restart();
+ }
+ return true;
+ }
+
+ public static void restart()
+ {
+ AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
+ try
+ {
+ final File file = new File( SpigotConfig.restartScript );
+ if ( file.isFile() )
+ {
+ System.out.println( "Attempting to restart with " + SpigotConfig.restartScript );
+
+ // Disable Watchdog
+ WatchdogThread.doStop();
+
+ // Kick all players
+ for ( EntityPlayer p : (List< EntityPlayer>) MinecraftServer.getServer().getPlayerList().players )
+ {
+ p.playerConnection.disconnect(SpigotConfig.restartMessage);
+ }
+ // Give the socket a chance to send the packets
+ try
+ {
+ Thread.sleep( 100 );
+ } catch ( InterruptedException ex )
+ {
+ }
+ // Close the socket so we can rebind with the new process
+ MinecraftServer.getServer().getServerConnection().b();
+
+ // Give time for it to kick in
+ try
+ {
+ Thread.sleep( 100 );
+ } catch ( InterruptedException ex )
+ {
+ }
+
+ // Actually shutdown
+ try
+ {
+ MinecraftServer.getServer().stop();
+ } catch ( Throwable t )
+ {
+ }
+
+ // This will be done AFTER the server has completely halted
+ Thread shutdownHook = new Thread()
+ {
+ @Override
+ public void run()
+ {
+ try
+ {
+ String os = System.getProperty( "os.name" ).toLowerCase();
+ if ( os.contains( "win" ) )
+ {
+ Runtime.getRuntime().exec( "cmd /c start " + file.getPath() );
+ } else
+ {
+ Runtime.getRuntime().exec( new String[]
+ {
+ "sh", file.getPath()
+ } );
+ }
+ } catch ( Exception e )
+ {
+ e.printStackTrace();
+ }
+ }
+ };
+
+ shutdownHook.setDaemon( true );
+ Runtime.getRuntime().addShutdownHook( shutdownHook );
+ } else
+ {
+ System.out.println( "Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server." );
+ }
+ System.exit( 0 );
+ } catch ( Exception ex )
+ {
+ ex.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
index efcd193..2b499fe 100644
--- a/src/main/java/org/spigotmc/SpigotConfig.java
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
@@ -178,4 +178,18 @@ public class SpigotConfig
outdatedClientMessage = transform( getString( "messages.outdated-client", outdatedClientMessage ) );
outdatedServerMessage = transform( getString( "messages.outdated-server", outdatedServerMessage ) );
}
+
+ public static int timeoutTime = 60;
+ public static boolean restartOnCrash = true;
+ public static String restartScript = "./start.sh";
+ public static String restartMessage;
+ private static void watchdog()
+ {
+ timeoutTime = getInt( "settings.timeout-time", timeoutTime );
+ restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash );
+ restartScript = getString( "settings.restart-script", restartScript );
+ restartMessage = transform( getString( "messages.restart", "Server is restarting" ) );
+ commands.put( "restart", new RestartCommand( "restart" ) );
+ WatchdogThread.doStart( timeoutTime, restartOnCrash );
+ }
}
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
new file mode 100644
index 0000000..de08ad6
--- /dev/null
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -0,0 +1,117 @@
+package org.spigotmc;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.minecraft.server.MinecraftServer;
+import org.bukkit.Bukkit;
+
+public class WatchdogThread extends Thread
+{
+
+ private static WatchdogThread instance;
+ private final long timeoutTime;
+ private final boolean restart;
+ private volatile long lastTick;
+ private volatile boolean stopping;
+
+ private WatchdogThread(long timeoutTime, boolean restart)
+ {
+ super( "Spigot Watchdog Thread" );
+ this.timeoutTime = timeoutTime;
+ this.restart = restart;
+ }
+
+ public static void doStart(int timeoutTime, boolean restart)
+ {
+ if ( instance == null )
+ {
+ instance = new WatchdogThread( timeoutTime * 1000L, restart );
+ instance.start();
+ }
+ }
+
+ public static void tick()
+ {
+ instance.lastTick = System.currentTimeMillis();
+ }
+
+ public static void doStop()
+ {
+ if ( instance != null )
+ {
+ instance.stopping = true;
+ }
+ }
+
+ @Override
+ public void run()
+ {
+ while ( !stopping )
+ {
+ //
+ if ( lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime )
+ {
+ Logger log = Bukkit.getServer().getLogger();
+ log.log( Level.SEVERE, "The server has stopped responding!" );
+ log.log( Level.SEVERE, "Please report this to http://www.spigotmc.org/" );
+ log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" );
+ log.log( Level.SEVERE, "Spigot version: " + Bukkit.getServer().getVersion() );
+ //
+ log.log( Level.SEVERE, "------------------------------" );
+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Spigot!):" );
+ dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE ), log );
+ log.log( Level.SEVERE, "------------------------------" );
+ //
+ log.log( Level.SEVERE, "Entire Thread Dump:" );
+ ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads( true, true );
+ for ( ThreadInfo thread : threads )
+ {
+ dumpThread( thread, log );
+ }
+ log.log( Level.SEVERE, "------------------------------" );
+
+ if ( restart )
+ {
+ RestartCommand.restart();
+ }
+ break;
+ }
+
+ try
+ {
+ sleep( 10000 );
+ } catch ( InterruptedException ex )
+ {
+ interrupt();
+ }
+ }
+ }
+
+ private static void dumpThread(ThreadInfo thread, Logger log)
+ {
+ log.log( Level.SEVERE, "------------------------------" );
+ //
+ log.log( Level.SEVERE, "Current Thread: " + thread.getThreadName() );
+ log.log( Level.SEVERE, "\tPID: " + thread.getThreadId()
+ + " | Suspended: " + thread.isSuspended()
+ + " | Native: " + thread.isInNative()
+ + " | State: " + thread.getThreadState() );
+ if ( thread.getLockedMonitors().length != 0 )
+ {
+ log.log( Level.SEVERE, "\tThread is waiting on monitor(s):" );
+ for ( MonitorInfo monitor : thread.getLockedMonitors() )
+ {
+ log.log( Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame() );
+ }
+ }
+ log.log( Level.SEVERE, "\tStack:" );
+ //
+ for ( StackTraceElement stack : thread.getStackTrace() )
+ {
+ log.log( Level.SEVERE, "\t\t" + stack );
+ }
+ }
+}
--
1.9.1