* new configservice.jsp page that shuts down the router (and has hooks for a few other things)

* new safer way of shutting down the router per discussions with oOo (dealing with a graceful
shutdown where the user updates their config before the shutdown is complete, etc)
* graceful shutdown implemented in the router - shutdownGracefully(), cancelGracefulShutdown(), shutdownInProgress()
This commit is contained in:
jrandom
2004-08-23 07:33:14 +00:00
committed by zzz
parent e9310ee8dd
commit 9f7320fa67
5 changed files with 180 additions and 3 deletions

View File

@ -0,0 +1,46 @@
package net.i2p.router.web;
import net.i2p.router.ClientTunnelSettings;
/**
* Handler to deal with form submissions from the service config form and act
* upon the values.
*
*/
public class ConfigServiceHandler extends FormHandler {
private String _action;
private String _nonce;
public void ConfigNetHandler() {
_action = null;
_nonce = null;
}
protected void processForm() {
if (_action == null) return;
if (_nonce == null) {
addFormError("You trying to mess with me? Huh? Are you?");
return;
}
String nonce = System.getProperty(ConfigServiceHandler.class.getName() + ".nonce");
String noncePrev = System.getProperty(ConfigServiceHandler.class.getName() + ".noncePrev");
if ( (!_nonce.equals(nonce)) && (!_nonce.equals(noncePrev)) ) {
addFormError("Invalid nonce? Hmmm, someone is spoofing you. prev=["+ noncePrev + "] nonce=[" + nonce + "] param=[" + _nonce + "]");
return;
}
if ("Shutdown gracefully".equals(_action)) {
_context.router().shutdownGracefully();
addFormNotice("Graceful shutdown initiated");
} else if ("Shutdown immediately".equals(_action)) {
_context.router().shutdown();
addFormNotice("Shutdown immediately! boom bye bye bad bwoy");
} else if ("Cancel graceful shutdown".equals(_action)) {
_context.router().cancelGracefulShutdown();
addFormNotice("Graceful shutdown cancelled");
} else {
addFormNotice("Blah blah blah. whatever. I'm not going to " + _action);
}
}
public void setAction(String action) { _action = action; }
public void setNonce(String nonce) { _nonce = nonce; }
}

View File

@ -1,5 +1,7 @@
<h4><% if (request.getRequestURI().indexOf("config.jsp") != -1) { <h4><% if (request.getRequestURI().indexOf("config.jsp") != -1) {
%>Network | <% } else { %><a href="config.jsp">Network</a> | <% } %>Network | <% } else { %><a href="config.jsp">Network</a> | <% }
if (request.getRequestURI().indexOf("configservice.jsp") != -1) {
%>Service | <% } else { %><a href="configservice.jsp">Service</a> | <% }
if (request.getRequestURI().indexOf("configclients.jsp") != -1) { if (request.getRequestURI().indexOf("configclients.jsp") != -1) {
%>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% } %>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% }
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) { if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {

View File

@ -0,0 +1,55 @@
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<title>I2P Router Console - config clients</title>
<link rel="stylesheet" href="default.css" type="text/css" />
</head><body>
<%@include file="nav.jsp" %>
<%@include file="summary.jsp" %>
<div class="main" id="main">
<%@include file="confignav.jsp" %>
<jsp:useBean class="net.i2p.router.web.ConfigServiceHandler" id="formhandler" scope="request" />
<jsp:setProperty name="formhandler" property="*" />
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
<i><jsp:getProperty name="formhandler" property="notices" /></i>
<form action="configservice.jsp" method="POST">
<% String prev = System.getProperty("net.i2p.router.web.ConfigServiceHandler.nonce");
if (prev != null) System.setProperty("net.i2p.router.web.ConfigServiceHandler.noncePrev", prev);
System.setProperty("net.i2p.router.web.ConfigServiceHandler.nonce", new java.util.Random().nextLong()+""); %>
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigServiceHandler.nonce")%>" />
<h4>Shutdown the router</h4>
Graceful shutdown lets the router satisfy the agreements it has already made
before shutting down, but may take a few minutes. If you need to kill the
router immediately, that option is available as well.<br />
<input type="submit" name="action" value="Shutdown gracefully" />
<input type="submit" name="action" value="Shutdown immediately" />
<input type="submit" name="action" value="Cancel graceful shutdown" />
<h4>Systray integration</h4>
On the windows platform, there is a small application to sit in the system
tray, allowing you to view the router's status (later on, I2P client applications
will be able to integrate their own functionality into the system tray as well).
If you are on windows, you can either enable or disable that icon here. <br />
<input type="submit" name="action" value="Enable systray icon" />
<input type="submit" name="action" value="Disable systray icon" />
<h4>Run on startup</h4>
On the windows platform, you can control whether I2P is run on startup or not by
selecting one of the following options - I2P will install (or remove) a service
accordingly. On *nix machines, you need root permissions to add a script
to be run on startup (we hope you know better than to run I2P as root ;). To
have I2P run (or not run) at startup on *nix machines, please run
<code>install_i2p_service_unix</code> or <code>install_i2p_service_unix</code>
as root.<br />
<input type="submit" name="action" value="Run I2P on startup" />
<input type="submit" name="action" value="Don't run I2P on startup" />
</form>
</div>
</body>
</html>

View File

@ -57,6 +57,7 @@ public class Router {
private boolean _isAlive; private boolean _isAlive;
private I2PThread.OOMEventListener _oomListener; private I2PThread.OOMEventListener _oomListener;
private ShutdownHook _shutdownHook; private ShutdownHook _shutdownHook;
private I2PThread _gracefulShutdownDetector;
public final static String PROP_CONFIG_FILE = "router.configLocation"; public final static String PROP_CONFIG_FILE = "router.configLocation";
@ -67,6 +68,7 @@ public class Router {
public final static String PROP_INFO_FILENAME_DEFAULT = "router.info"; public final static String PROP_INFO_FILENAME_DEFAULT = "router.info";
public final static String PROP_KEYS_FILENAME = "router.keys.location"; public final static String PROP_KEYS_FILENAME = "router.keys.location";
public final static String PROP_KEYS_FILENAME_DEFAULT = "router.keys"; public final static String PROP_KEYS_FILENAME_DEFAULT = "router.keys";
public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
static { static {
// grumble about sun's java caching DNS entries *forever* // grumble about sun's java caching DNS entries *forever*
@ -108,6 +110,8 @@ public class Router {
} }
}; };
_shutdownHook = new ShutdownHook(); _shutdownHook = new ShutdownHook();
_gracefulShutdownDetector = new I2PThread(new GracefulShutdown());
_gracefulShutdownDetector.start();
} }
/** /**
@ -184,6 +188,8 @@ public class Router {
File f = new File(filename); File f = new File(filename);
if (f.canRead()) { if (f.canRead()) {
DataHelper.loadProps(props, f); DataHelper.loadProps(props, f);
// dont be a wanker
props.remove(PROP_SHUTDOWN_IN_PROGRESS);
} else { } else {
log.warn("Configuration file " + filename + " does not exist"); log.warn("Configuration file " + filename + " does not exist");
} }
@ -552,6 +558,71 @@ public class Router {
} }
} }
/**
* Call this if we want the router to kill itself as soon as we aren't
* participating in any more tunnels (etc). This will not block and doesn't
* guarantee any particular time frame for shutting down. To shut the
* router down immediately, use {@link #shutdown}. If you want to cancel
* the graceful shutdown (prior to actual shutdown ;), call
* {@link #cancelGracefulShutdown}.
*
*/
public void shutdownGracefully() {
_config.setProperty(PROP_SHUTDOWN_IN_PROGRESS, "true");
synchronized (_gracefulShutdownDetector) {
_gracefulShutdownDetector.notifyAll();
}
}
/**
* Cancel any prior request to shut the router down gracefully.
*
*/
public void cancelGracefulShutdown() {
_config.remove(PROP_SHUTDOWN_IN_PROGRESS);
synchronized (_gracefulShutdownDetector) {
_gracefulShutdownDetector.notifyAll();
}
}
public boolean gracefulShutdownInProgress() {
return (null != _config.getProperty(PROP_SHUTDOWN_IN_PROGRESS));
}
/**
* Simple thread that sits and waits forever, managing the
* graceful shutdown "process" (describing it would take more text
* than just reading the code...)
*
*/
private class GracefulShutdown implements Runnable {
public void run() {
while (true) {
boolean shutdown = (null != _config.getProperty(PROP_SHUTDOWN_IN_PROGRESS));
if (shutdown) {
if (_context.tunnelManager().getParticipatingCount() <= 0) {
if (_log.shouldLog(Log.CRIT))
_log.log(Log.CRIT, "Graceful shutdown progress - no more tunnels, safe to die");
shutdown();
return;
} else {
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait(10*1000);
}
} catch (InterruptedException ie) {}
}
} else {
try {
synchronized (Thread.currentThread()) {
Thread.currentThread().wait();
}
} catch (InterruptedException ie) {}
}
}
}
}
/** /**
* Save the current config options (returning true if save was * Save the current config options (returning true if save was
* successful, false otherwise) * successful, false otherwise)

View File

@ -122,9 +122,12 @@ class RouterThrottleImpl implements RouterThrottle {
int numTunnels = _context.tunnelManager().getParticipatingCount(); int numTunnels = _context.tunnelManager().getParticipatingCount();
double bytesAllocated = (numTunnels + 1) * bytesPerTunnel; double bytesAllocated = (numTunnels + 1) * bytesPerTunnel;
// the max # tunnels throttle is useful for shutting down the router - if (_context.getProperty(Router.PROP_SHUTDOWN_IN_PROGRESS) != null) {
// set this to 0, wait a few minutes, and the router can be shut off if (_log.shouldLog(Log.WARN))
// without killing anyone's tunnels _log.warn("Refusing tunnel request since we are shutting down ASAP");
return false;
}
String maxTunnels = _context.getProperty(PROP_MAX_TUNNELS); String maxTunnels = _context.getProperty(PROP_MAX_TUNNELS);
if (maxTunnels != null) { if (maxTunnels != null) {
try { try {