Skip to content

Commit

Permalink
Add SIGHUP handling to reload ssl certificates. Close #134
Browse files Browse the repository at this point in the history
  • Loading branch information
ShaneMcC committed May 7, 2017
1 parent 77026ce commit b581bcf
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 12 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ dependencies {
if (file('modules/sockets/build.gradle').exists()) { if (file('modules/sockets/build.gradle').exists()) {
bundle project(":sockets") bundle project(":sockets")
} else { } 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()) { if (file('modules/parser/build.gradle').exists()) {
Expand Down
2 changes: 1 addition & 1 deletion modules/sockets
48 changes: 38 additions & 10 deletions src/com/dfbnc/DFBnc.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ public class DFBnc implements NewSocketReadyHandler {
/** Shutdown hook. */ /** Shutdown hook. */
private ShutdownHook shutdownHook; private ShutdownHook shutdownHook;


/** Signal Handler. */
private SignalHandler signalHandler;

/** Daemon. */ /** Daemon. */
public final static DFBncDaemon daemon = new DFBncDaemon(); public final static DFBncDaemon daemon = new DFBncDaemon();


Expand Down Expand Up @@ -170,6 +173,8 @@ private void init(final String[] args) {
shutdownHook = new ShutdownHook(this); shutdownHook = new ShutdownHook(this);
Runtime.getRuntime().addShutdownHook(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownHook);


signalHandler = new SignalHandler(this);

setupLogging(); setupLogging();


if (DFBncDaemon.canFork() && daemon.isDaemonized()) { if (DFBncDaemon.canFork() && daemon.isDaemonized()) {
Expand All @@ -194,6 +199,7 @@ private void init(final String[] args) {
// Before forking, close any sockets and files. // Before forking, close any sockets and files.
Logger.setLevel(LogLevel.SILENT); Logger.setLevel(LogLevel.SILENT);
shutdownHook.inactivate(); shutdownHook.inactivate();
signalHandler.inactivate();
this.shutdown(true); this.shutdown(true);


// Daemonise. // Daemonise.
Expand Down Expand Up @@ -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. * Open the listen sockets.
*/ */
Expand All @@ -386,16 +405,7 @@ public void openListenSockets() {
final List<String> listenhosts = config.getOptionList("general", "listenhost"); final List<String> listenhosts = config.getOptionList("general", "listenhost");


if (sslContextManager == null) { if (sslContextManager == null) {
if ("pem".equalsIgnoreCase(getConfig().getOption("ssl", "source"))) { createSSLContextManager();
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"));
}
} }


for (String listenhost : listenhosts) { for (String listenhost : listenhosts) {
Expand Down Expand Up @@ -493,6 +503,20 @@ public MultiWriter getMultiWriter() {
return multiWriter; 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 * Handle shutdown
*/ */
Expand Down Expand Up @@ -555,6 +579,10 @@ public void shutdown(final boolean shuttingDown) {


Logger.info("Deactivating shutdown hook."); Logger.info("Deactivating shutdown hook.");
if (shutdownHook != null) { shutdownHook.inactivate(); } if (shutdownHook != null) { shutdownHook.inactivate(); }

Logger.info("Deactivating signal handler.");
if (signalHandler != null) { signalHandler.inactivate(); }

if (!shuttingDown) { if (!shuttingDown) {
Logger.info("Exiting."); Logger.info("Exiting.");
System.exit(0); System.exit(0);
Expand Down
89 changes: 89 additions & 0 deletions src/com/dfbnc/SignalHandler.java
Original file line number Original file line Diff line number Diff line change
@@ -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.