From 9f7320fa6720bfd2aeb3d767b4ab7b72e1d0b917 Mon Sep 17 00:00:00 2001 From: jrandom <jrandom> Date: Mon, 23 Aug 2004 07:33:14 +0000 Subject: [PATCH] * 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() --- .../i2p/router/web/ConfigServiceHandler.java | 46 ++++++++++++ apps/routerconsole/jsp/confignav.jsp | 2 + apps/routerconsole/jsp/configservice.jsp | 55 ++++++++++++++ router/java/src/net/i2p/router/Router.java | 71 +++++++++++++++++++ .../net/i2p/router/RouterThrottleImpl.java | 9 ++- 5 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java create mode 100644 apps/routerconsole/jsp/configservice.jsp diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java new file mode 100644 index 0000000000..60ea04742d --- /dev/null +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigServiceHandler.java @@ -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; } +} diff --git a/apps/routerconsole/jsp/confignav.jsp b/apps/routerconsole/jsp/confignav.jsp index c17b54a19a..1da6c88cec 100644 --- a/apps/routerconsole/jsp/confignav.jsp +++ b/apps/routerconsole/jsp/confignav.jsp @@ -1,5 +1,7 @@ <h4><% if (request.getRequestURI().indexOf("config.jsp") != -1) { %>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) { %>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% } if (request.getRequestURI().indexOf("configlogging.jsp") != -1) { diff --git a/apps/routerconsole/jsp/configservice.jsp b/apps/routerconsole/jsp/configservice.jsp new file mode 100644 index 0000000000..771cbfcf7c --- /dev/null +++ b/apps/routerconsole/jsp/configservice.jsp @@ -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> diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index f602d6eb4e..17c02cde5c 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -57,6 +57,7 @@ public class Router { private boolean _isAlive; private I2PThread.OOMEventListener _oomListener; private ShutdownHook _shutdownHook; + private I2PThread _gracefulShutdownDetector; 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_KEYS_FILENAME = "router.keys.location"; public final static String PROP_KEYS_FILENAME_DEFAULT = "router.keys"; + public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress"; static { // grumble about sun's java caching DNS entries *forever* @@ -108,6 +110,8 @@ public class Router { } }; _shutdownHook = new ShutdownHook(); + _gracefulShutdownDetector = new I2PThread(new GracefulShutdown()); + _gracefulShutdownDetector.start(); } /** @@ -184,6 +188,8 @@ public class Router { File f = new File(filename); if (f.canRead()) { DataHelper.loadProps(props, f); + // dont be a wanker + props.remove(PROP_SHUTDOWN_IN_PROGRESS); } else { 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 * successful, false otherwise) diff --git a/router/java/src/net/i2p/router/RouterThrottleImpl.java b/router/java/src/net/i2p/router/RouterThrottleImpl.java index dd33d23c48..05fafe9932 100644 --- a/router/java/src/net/i2p/router/RouterThrottleImpl.java +++ b/router/java/src/net/i2p/router/RouterThrottleImpl.java @@ -122,9 +122,12 @@ class RouterThrottleImpl implements RouterThrottle { int numTunnels = _context.tunnelManager().getParticipatingCount(); double bytesAllocated = (numTunnels + 1) * bytesPerTunnel; - // the max # tunnels throttle is useful for shutting down the router - - // set this to 0, wait a few minutes, and the router can be shut off - // without killing anyone's tunnels + if (_context.getProperty(Router.PROP_SHUTDOWN_IN_PROGRESS) != null) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Refusing tunnel request since we are shutting down ASAP"); + return false; + } + String maxTunnels = _context.getProperty(PROP_MAX_TUNNELS); if (maxTunnels != null) { try { -- GitLab