Permalink
Browse files

Add SIGHUP handling to reload ssl certificates. Close #134

  • Loading branch information...
ShaneMcC committed May 7, 2017
1 parent 77026ce commit b581bcf059cfa445786065e6a3b889888610b267
Showing with 129 additions and 12 deletions.
  1. +1 −1 build.gradle
  2. +1 −1 modules/sockets
  3. +38 −10 src/com/dfbnc/DFBnc.java
  4. +89 −0 src/com/dfbnc/SignalHandler.java
@@ -54,7 +54,7 @@ dependencies {
if (file('modules/sockets/build.gradle').exists()) {
bundle project(":sockets")
} else {
bundle group: 'com.dfbnc', name: 'sockets', version: '0.3'
bundle group: 'com.dfbnc', name: 'sockets', version: '0.3.1'
}
if (file('modules/parser/build.gradle').exists()) {
@@ -108,6 +108,9 @@
/** Shutdown hook. */
private ShutdownHook shutdownHook;
/** Signal Handler. */
private SignalHandler signalHandler;
/** Daemon. */
public final static DFBncDaemon daemon = new DFBncDaemon();
@@ -170,6 +173,8 @@ private void init(final String[] args) {
shutdownHook = new ShutdownHook(this);
Runtime.getRuntime().addShutdownHook(shutdownHook);
signalHandler = new SignalHandler(this);
setupLogging();
if (DFBncDaemon.canFork() && daemon.isDaemonized()) {
@@ -194,6 +199,7 @@ private void init(final String[] args) {
// Before forking, close any sockets and files.
Logger.setLevel(LogLevel.SILENT);
shutdownHook.inactivate();
signalHandler.inactivate();
this.shutdown(true);
// Daemonise.
@@ -377,6 +383,19 @@ public void setupLogging() {
}
}
public void createSSLContextManager() {
if ("pem".equalsIgnoreCase(getConfig().getOption("ssl", "source"))) {
sslContextManager = new SSLContextManager(
getConfig().getOption("ssl", "certificatefile"),
getConfig().getOption("ssl", "privatekeyfile"));
} else {
sslContextManager = new SSLContextManager(
getConfig().getOption("ssl", "keystore"),
getConfig().getOption("ssl", "storepass"),
getConfig().getOption("ssl", "keypass"));
}
}
/**
* Open the listen sockets.
*/
@@ -386,16 +405,7 @@ public void openListenSockets() {
final List<String> listenhosts = config.getOptionList("general", "listenhost");
if (sslContextManager == null) {
if ("pem".equalsIgnoreCase(getConfig().getOption("ssl", "source"))) {
sslContextManager = new SSLContextManager(
getConfig().getOption("ssl", "certificatefile"),
getConfig().getOption("ssl", "privatekeyfile"));
} else {
sslContextManager = new SSLContextManager(
getConfig().getOption("ssl", "keystore"),
getConfig().getOption("ssl", "storepass"),
getConfig().getOption("ssl", "keypass"));
}
createSSLContextManager();
}
for (String listenhost : listenhosts) {
@@ -493,6 +503,20 @@ public MultiWriter getMultiWriter() {
return multiWriter;
}
/**
* Handle signal
*
* @param signal Signal name to handle.
*/
public void signal(final String signal) {
if (signal.equalsIgnoreCase("HUP")) {
Logger.info("Got sighup.");
Logger.info("Resetting SSL Context.");
sslContextManager.setSSLContext(null);
}
}
/**
* Handle shutdown
*/
@@ -555,6 +579,10 @@ public void shutdown(final boolean shuttingDown) {
Logger.info("Deactivating shutdown hook.");
if (shutdownHook != null) { shutdownHook.inactivate(); }
Logger.info("Deactivating signal handler.");
if (signalHandler != null) { signalHandler.inactivate(); }
if (!shuttingDown) {
Logger.info("Exiting.");
System.exit(0);
@@ -0,0 +1,89 @@
/*
* Copyright 2017 Shane Mc Cormack <shanemcc@gmail.com>.
* See LICENSE.txt for licensing details.
*/
package com.dfbnc;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import uk.org.dataforce.libs.logger.Logger;
/**
* Handle signals sent to BNC where possible.
* This relies on sun.misc.Signal and sun.misc.SignalHandler which may not be
* in every JVM, so we use reflection to use them where possible.
*
* @author Shane Mc Cormack <shanemcc@gmail.com>
*/
public class SignalHandler {
/** The DFBnc instance that this SignalHandler is for. */
private final DFBnc myBnc;
/** Ignore signals if we are deactivated. */
private boolean inactive;
/**
* Create the SignalHandler
*
* @param bnc DFBnc instance
*/
public SignalHandler(final DFBnc bnc) {
myBnc = bnc;
inactive = false;
try {
activate();
} catch (final Exception ex) {
Logger.error("Unable to listen for signals: " + ex.getMessage());
}
}
public void activate() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
final Class<?> handlerCl = Class.forName("sun.misc.SignalHandler");
final Class<?> signalCl = Class.forName("sun.misc.Signal");
final Constructor signalCtor = signalCl.getConstructor(String.class);
final Method signalHandle = signalCl.getMethod("handle", signalCl, handlerCl);
// Create a proxy class that implements SignalHandler
final Class<?> proxyClass = Proxy.getProxyClass(signalCl.getClassLoader(), handlerCl);
// This is used by the instance of proxyClass to dispatch method calls
final InvocationHandler invHandler = (final Object proxy, final Method method, final Object[] args) -> {
// proxy is the SignalHandler's "this" rederence
// method will be the handle(Signal) method
// args[0] will be an instance of Signal
// If you're using this object for multiple signals, you'll
// you'll need to use the "getName" method to determine which
// signal you have caught.
try {
final Method signalGetName = signalCl.getMethod("getName");
final String name = (String)signalGetName.invoke(args[0]);
myBnc.signal(name);
} catch (final Throwable t) {
/** Do nothing. */
}
return null;
};
// Get the constructor and create an instance of proxyClass
final Constructor<?> proxyCtor = proxyClass.getConstructor(InvocationHandler.class);
final Object handler = proxyCtor.newInstance(invHandler);
// Create the signal and call Signal.handle to bind handler to signal
signalHandle.invoke(null, signalCtor.newInstance("HUP"), handler);
}
/**
* Inactivates this handler.
*/
public void inactivate() {
inactive = true;
}
}

0 comments on commit b581bcf

Please sign in to comment.