* 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:
@ -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; }
|
||||||
|
}
|
@ -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) {
|
||||||
|
55
apps/routerconsole/jsp/configservice.jsp
Normal file
55
apps/routerconsole/jsp/configservice.jsp
Normal 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>
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
Reference in New Issue
Block a user