diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java index 5a9d991c93d73cac0abb926107b622a16d2371dd..d6f46ae8e7293e0daa87fb43b6bccfd6a421927d 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -1,59 +1,115 @@ package net.i2p.router.web; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.Properties; +import java.util.Set; + import net.i2p.data.DataFormatException; +import net.i2p.router.startup.ClientAppConfig; +import net.i2p.router.startup.LoadClientAppsJob; import net.i2p.util.Log; +import org.mortbay.http.HttpListener; +import org.mortbay.jetty.Server; + /** - * + * Saves changes to clients.config or webapps.config */ public class ConfigClientsHandler extends FormHandler { private Log _log; private Map _settings; - private boolean _shouldSave; public ConfigClientsHandler() { - _shouldSave = false; + _log = ContextHelper.getContext(null).logManager().getLog(ConfigClientsHandler.class); } - + protected void processForm() { - if (_shouldSave) { - saveChanges(); + if (_action.startsWith("Save Client")) { + saveClientChanges(); + } else if (_action.startsWith("Save WebApp")) { + saveWebAppChanges(); + } else if (_action.startsWith("Start ")) { + String app = _action.substring(6); + int appnum = -1; + try { + appnum = Integer.parseInt(app); + } catch (NumberFormatException nfe) {} + if (appnum >= 0) + startClient(appnum); + else + startWebApp(app); } else { - // noop - addFormError("Unimplemented"); + addFormError("Unsupported " + _action); } } - public void setShouldsave(String moo) { - if ( (moo != null) && (moo.equals("Save changes")) ) - _shouldSave = true; - } - public void setSettings(Map settings) { _settings = new HashMap(settings); } - /** - * The user made changes to the network config and wants to save them, so - * lets go ahead and do so. - * - */ - private void saveChanges() { - _log = _context.logManager().getLog(ConfigClientsHandler.class); - boolean saveRequired = false; - - int updated = 0; - int index = 0; - - if (updated > 0) - addFormNotice("Updated settings"); - - if (saveRequired) { - boolean saved = _context.router().saveConfig(); - if (saved) - addFormNotice("Exploratory tunnel configuration saved successfully"); - else - addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs"); + private void saveClientChanges() { + List clients = ClientAppConfig.getClientApps(_context); + for (int cur = 0; cur < clients.size(); cur++) { + ClientAppConfig ca = (ClientAppConfig) clients.get(cur); + Object val = _settings.get(cur + ".enabled"); + if (! "webConsole".equals(ca.clientName)) + ca.disabled = val == null; + } + ClientAppConfig.writeClientAppConfig(_context, clients); + addFormNotice("Client configuration saved successfully - restart required to take effect"); + } + + private void startClient(int i) { + List clients = ClientAppConfig.getClientApps(_context); + if (i >= clients.size()) { + addFormError("Bad client index"); + return; + } + ClientAppConfig ca = (ClientAppConfig) clients.get(i); + LoadClientAppsJob.runClient(ca.className, ca.clientName, LoadClientAppsJob.parseArgs(ca.args), _log); + addFormNotice("Client " + ca.clientName + " started"); + } + + private void saveWebAppChanges() { + Properties props = RouterConsoleRunner.webAppProperties(); + Set keys = props.keySet(); + int cur = 0; + for (Iterator iter = keys.iterator(); iter.hasNext(); ) { + String name = (String)iter.next(); + if (! (name.startsWith(RouterConsoleRunner.PREFIX) && name.endsWith(RouterConsoleRunner.ENABLED))) + continue; + String app = name.substring(RouterConsoleRunner.PREFIX.length(), name.lastIndexOf(RouterConsoleRunner.ENABLED)); + Object val = _settings.get(app + ".enabled"); + if (! RouterConsoleRunner.ROUTERCONSOLE.equals(app)) + props.setProperty(name, "" + (val != null)); + } + RouterConsoleRunner.storeWebAppProperties(props); + addFormNotice("WebApp configuration saved successfully - restart required to take effect"); + } + + // Big hack for the moment, not using properties for directory and port + // Go through all the Jetty servers, find the one serving port 7657, + // requested and add the .war to that one + private void startWebApp(String app) { + Collection c = Server.getHttpServers(); + for (int i = 0; i < c.size(); i++) { + Server s = (Server) c.toArray()[i]; + HttpListener[] hl = s.getListeners(); + for (int j = 0; j < hl.length; j++) { + if (hl[j].getPort() == 7657) { + try { + s.addWebApplication("/"+ app, "./webapps/" + app + ".war").start(); + // no passwords... initialize(wac); + addFormNotice("WebApp " + app + " started"); + } catch (Exception ioe) { + addFormError("Failed to start " + app + " " + ioe); + } + return; + } + } } + addFormError("Failed to find server"); } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java index 715899c208f1ca1ee27498637d7eecbf23f0feaa..a678b48a5a426030497da103f14ea780f9ec90da 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java @@ -26,16 +26,15 @@ public class ConfigClientsHelper { public ConfigClientsHelper() {} - public String getForm1() { StringBuffer buf = new StringBuffer(1024); buf.append("<table border=\"1\">\n"); - buf.append("<tr><td>Client</td><td>Enabled?</td><td>Class and arguments</td></tr>\n"); + buf.append("<tr><td>Client</td><td>Run at Startup?</td><td>Start Now</td><td>Class and arguments</td></tr>\n"); List clients = ClientAppConfig.getClientApps(_context); for (int cur = 0; cur < clients.size(); cur++) { ClientAppConfig ca = (ClientAppConfig) clients.get(cur); - renderForm(buf, cur, ca.clientName, false, !ca.disabled, "webConsole".equals(ca.clientName), ca.className + " " + ca.args); + renderForm(buf, ""+cur, ca.clientName, false, !ca.disabled, "webConsole".equals(ca.clientName), ca.className + " " + ca.args); } buf.append("</table>\n"); @@ -45,24 +44,22 @@ public class ConfigClientsHelper { public String getForm2() { StringBuffer buf = new StringBuffer(1024); buf.append("<table border=\"1\">\n"); - buf.append("<tr><td>WebApp</td><td>Enabled?</td><td>Description</td></tr>\n"); + buf.append("<tr><td>WebApp</td><td>Run at Startup?</td><td>Start Now</td><td>Description</td></tr>\n"); Properties props = RouterConsoleRunner.webAppProperties(); Set keys = new TreeSet(props.keySet()); - int cur = 0; for (Iterator iter = keys.iterator(); iter.hasNext(); ) { String name = (String)iter.next(); if (name.startsWith(RouterConsoleRunner.PREFIX) && name.endsWith(RouterConsoleRunner.ENABLED)) { - String app = name.substring(8, name.lastIndexOf(RouterConsoleRunner.ENABLED)); + String app = name.substring(RouterConsoleRunner.PREFIX.length(), name.lastIndexOf(RouterConsoleRunner.ENABLED)); String val = props.getProperty(name); - renderForm(buf, cur, app, !"addressbook".equals(app), "true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war"); - cur++; + renderForm(buf, app, app, !"addressbook".equals(app), "true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war"); } } buf.append("</table>\n"); return buf.toString(); } - private void renderForm(StringBuffer buf, int index, String name, boolean urlify, boolean enabled, boolean ro, String desc) { + private void renderForm(StringBuffer buf, String index, String name, boolean urlify, boolean enabled, boolean ro, String desc) { buf.append("<tr><td>"); if (urlify && enabled) { String link = "/"; @@ -72,12 +69,16 @@ public class ConfigClientsHelper { } else { buf.append(name); } - buf.append("</td><td align=\"center\"><input type=\"checkbox\" name=\"enable\" value=\"").append(index).append(".enabled\" "); + buf.append("</td><td align=\"center\"><input type=\"checkbox\" name=\"").append(index).append(".enabled\" value=\"true\" "); if (enabled) { buf.append("checked=\"true\" "); if (ro) buf.append("disabled=\"true\" "); } - buf.append("/><td>").append(desc).append("</td></tr>\n"); + buf.append("/></td><td> "); + if (!enabled) { + buf.append("<button type=\"submit\" name=\"action\" value=\"Start ").append(index).append("\" />Start</button>"); + } + buf.append(" </td><td>").append(desc).append("</td></tr>\n"); } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java index 0085d7b401c78caef4f18f5443f52ceefdd276ac..dec2d7891553eacbaf9f28c92301a3101f826fd0 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/SummaryHelper.java @@ -10,6 +10,7 @@ import java.util.Set; import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.data.LeaseSet; +import net.i2p.data.RouterAddress; import net.i2p.stat.Rate; import net.i2p.stat.RateStat; import net.i2p.router.CommSystemFacade; @@ -17,6 +18,7 @@ import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.router.RouterVersion; import net.i2p.router.TunnelPoolSettings; +import net.i2p.router.transport.ntcp.NTCPAddress; /** * Simple helper to query the appropriate router for data necessary to render @@ -120,7 +122,10 @@ public class SummaryHelper { int status = _context.commSystem().getReachabilityStatus(); switch (status) { case CommSystemFacade.STATUS_OK: - return "OK"; + RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP"); + if (ra == null || (new NTCPAddress(ra)).isPubliclyRoutable()) + return "OK"; + return "ERR-Private TCP Address"; case CommSystemFacade.STATUS_DIFFERENT: return "ERR-SymmetricNAT"; case CommSystemFacade.STATUS_REJECT_UNSOLICITED: @@ -130,6 +135,8 @@ public class SummaryHelper { return "WARN-Firewalled and Fast"; else return "Firewalled"; + case CommSystemFacade.STATUS_HOSED: + return "ERR-UDP Port In Use - Set i2np.udp.internalPort=xxxx in advanced config and restart"; case CommSystemFacade.STATUS_UNKNOWN: // fallthrough default: return "Testing"; @@ -512,6 +519,28 @@ public class SummaryHelper { return String.valueOf(_context.tunnelManager().getInboundBuildQueueSize()); } + public String getPRNGStatus() { + Rate r = _context.statManager().getRate("prng.bufferWaitTime").getRate(60*1000); + int use = (int) r.getLastEventCount(); + int i = (int) (r.getAverageValue() + 0.5); + if (i <= 0) { + r = _context.statManager().getRate("prng.bufferWaitTime").getRate(10*60*1000); + i = (int) (r.getAverageValue() + 0.5); + } + String rv = i + "/"; + r = _context.statManager().getRate("prng.bufferFillTime").getRate(60*1000); + i = (int) (r.getAverageValue() + 0.5); + if (i <= 0) { + r = _context.statManager().getRate("prng.bufferFillTime").getRate(10*60*1000); + i = (int) (r.getAverageValue() + 0.5); + } + rv = rv + i + "ms"; + // margin == fill time / use time + if (use > 0 && i > 0) + rv = rv + ' ' + (60*1000 / (use * i)) + 'x'; + return rv; + } + public boolean updateAvailable() { return NewsFetcher.getInstance(_context).updateAvailable(); } diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp index 8f4f6c8e2733a27cc8614bb1944d7319156010cd..7240281988b926fed4dbfab859f521fd15a957e5 100644 --- a/apps/routerconsole/jsp/configclients.jsp +++ b/apps/routerconsole/jsp/configclients.jsp @@ -18,7 +18,6 @@ <jsp:useBean class="net.i2p.router.web.ConfigClientsHandler" id="formhandler" scope="request" /> <jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" /> - <jsp:setProperty name="formhandler" property="shouldsave" value="<%=request.getParameter("shouldsave")%>" /> <jsp:setProperty name="formhandler" property="action" value="<%=request.getParameter("action")%>" /> <jsp:setProperty name="formhandler" property="nonce" value="<%=request.getParameter("nonce")%>" /> <jsp:setProperty name="formhandler" property="settings" value="<%=request.getParameterMap()%>" /> @@ -30,7 +29,6 @@ if (prev != null) System.setProperty("net.i2p.router.web.ConfigClientsHandler.noncePrev", prev); System.setProperty("net.i2p.router.web.ConfigClientsHandler.nonce", new java.util.Random().nextLong()+""); %> <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigClientsHandler.nonce")%>" /> - <input type="hidden" name="action" value="blah" /> <h3>Client Configuration</h3> <p> The Java clients listed below are started by the router and run in the same JVM. @@ -39,7 +37,7 @@ </p><p> <input type="submit" name="action" value="Save Client Configuration" /> </p><p> - <i>All changes require restart to take effect. For other changes edit the clients.config file.</i> + <i>All changes require restart to take effect. To change other client options, edit the clients.config file.</i> </p> <hr /> <h3>WebApp Configuration</h3> @@ -50,11 +48,15 @@ front-ends to another client or application which must be separately enabled (e.g. susidns, i2ptunnel), or have no web interface at all (e.g. addressbook). </p><p> + A web app may also be disabled by removing the .war file from the webapps directory; + however the .war file and web app will reappear when you update your router to a newer version, + so disabling the web app here is the preferred method. + </p><p> <jsp:getProperty name="clientshelper" property="form2" /> </p><p> <input type="submit" name="action" value="Save WebApp Configuration" /> </p><p> - <i>All changes require restart to take effect. For other changes edit the webapps.config file.</i> + <i>All changes require restart to take effect. To change other webapp options, edit the webapps.config file.</i> </p> </form> </div> diff --git a/apps/routerconsole/jsp/confignav.jsp b/apps/routerconsole/jsp/confignav.jsp index 8625ca6877b87b0910fad00283c0e50f5fcb5359..7ab47f76be1956ccd754f6c1281e9fe5c37eefc7 100644 --- a/apps/routerconsole/jsp/confignav.jsp +++ b/apps/routerconsole/jsp/confignav.jsp @@ -4,10 +4,10 @@ %>Service | <% } else { %><a href="configservice.jsp">Service</a> | <% } if (request.getRequestURI().indexOf("configupdate.jsp") != -1) { %>Update | <% } else { %><a href="configupdate.jsp">Update</a> | <% } - if (request.getRequestURI().indexOf("configclients.jsp") != -1) { - %>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% } if (request.getRequestURI().indexOf("configtunnels.jsp") != -1) { %>Tunnels | <% } else { %><a href="configtunnels.jsp">Tunnels</a> | <% } + if (request.getRequestURI().indexOf("configclients.jsp") != -1) { + %>Clients | <% } else { %><a href="configclients.jsp">Clients</a> | <% } if (request.getRequestURI().indexOf("configlogging.jsp") != -1) { %>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% } if (request.getRequestURI().indexOf("configstats.jsp") != -1) { diff --git a/apps/routerconsole/jsp/summary.jsp b/apps/routerconsole/jsp/summary.jsp index 1f284c64baad8931ae75a7ce8328d3f8e2c1b1a4..7bd85aaf92b39ee26223f5e3513b27a62602dc28 100644 --- a/apps/routerconsole/jsp/summary.jsp +++ b/apps/routerconsole/jsp/summary.jsp @@ -95,6 +95,7 @@ <b>Message delay:</b> <jsp:getProperty name="helper" property="messageDelay" /><br /> <b>Tunnel lag:</b> <jsp:getProperty name="helper" property="tunnelLag" /><br /> <b>Handle backlog:</b> <jsp:getProperty name="helper" property="inboundBacklog" /><br /> + <b>PRNG wait/fill:</b> <jsp:getProperty name="helper" property="PRNGStatus" /><br /> <b><jsp:getProperty name="helper" property="tunnelStatus" /></b><br /> <hr /> diff --git a/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java b/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java index 90d8d97d73c0a7073bb27501e2aafad0c10187b4..c7b5c36e5d4895d91de8d14a0e8475085ec7e424 100644 --- a/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java +++ b/core/java/src/gnu/crypto/prng/AsyncFortunaStandalone.java @@ -2,6 +2,8 @@ package gnu.crypto.prng; import java.util.*; +import net.i2p.I2PAppContext; + /** * fortuna instance that tries to avoid blocking if at all possible by using separate * filled buffer segments rather than one buffer (and blocking when that buffer's data @@ -13,16 +15,20 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl private final byte asyncBuffers[][] = new byte[BUFFERS][BUFSIZE]; private final int status[] = new int[BUFFERS]; private int nextBuf = 0; + private I2PAppContext _context; private static final int STATUS_NEED_FILL = 0; private static final int STATUS_FILLING = 1; private static final int STATUS_FILLED = 2; private static final int STATUS_LIVE = 3; - public AsyncFortunaStandalone() { + public AsyncFortunaStandalone(I2PAppContext context) { super(); for (int i = 0; i < BUFFERS; i++) status[i] = STATUS_NEED_FILL; + _context = context; + context.statManager().createRateStat("prng.bufferWaitTime", "", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } ); + context.statManager().createRateStat("prng.bufferFillTime", "", "Encryption", new long[] { 60*1000, 10*60*1000, 60*60*1000 } ); } public void startup() { @@ -61,6 +67,7 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl } catch (InterruptedException ie) {} waited = System.currentTimeMillis()-before; } + _context.statManager().addRateData("prng.bufferWaitTime", waited, 0); if (waited > 10*1000) System.out.println(Thread.currentThread().getName() + ": Took " + waited + "ms for a full PRNG buffer to be found"); @@ -108,6 +115,7 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl //System.out.println(Thread.currentThread().getName() + ": Prng buffer " + toFill + " filled after " + (after-before)); asyncBuffers.notifyAll(); } + _context.statManager().addRateData("prng.bufferFillTime", after - before, 0); Thread.yield(); long waitTime = (after-before)*5; if (waitTime <= 0) // somehow postman saw waitTime show up as negative @@ -147,7 +155,7 @@ public class AsyncFortunaStandalone extends FortunaStandalone implements Runnabl public static void main(String args[]) { try { - AsyncFortunaStandalone rand = new AsyncFortunaStandalone(); + AsyncFortunaStandalone rand = new AsyncFortunaStandalone(null); // Will cause NPEs above; fix this if you want to test! Sorry... byte seed[] = new byte[1024]; rand.seed(seed); diff --git a/core/java/src/net/i2p/stat/StatManager.java b/core/java/src/net/i2p/stat/StatManager.java index 07cce24efb617a3fa97589a384549e9c837136b3..3f3ab4a7230c9fca05a2249849ae31a3b2f1e38f 100644 --- a/core/java/src/net/i2p/stat/StatManager.java +++ b/core/java/src/net/i2p/stat/StatManager.java @@ -37,13 +37,15 @@ public class StatManager { public static final String PROP_STAT_REQUIRED = "stat.required"; /** * These are all the stats published in netDb, plus those required for the operation of - * the router (many in RouterThrottleImpl), plus those that are on graphs.jsp by default. + * the router (many in RouterThrottleImpl), plus those that are on graphs.jsp by default, + * plus those used on the summary bar (SummaryHelper.java). * Wildcard ('*') allowed at end of stat only. * Ignore all the rest of the stats unless stat.full=true. */ public static final String DEFAULT_STAT_REQUIRED = "bw.recvRate,bw.sendBps,bw.sendRate,client.sendAckTime,clock.skew,crypto.elGamal.encrypt," + "jobQueue.jobLag,netDb.successTime,router.fastPeers," + + "prng.bufferFillTime,prng.bufferWaitTime," + "transport.receiveMessageSize,transport.sendMessageSize,transport.sendProcessingTime," + "tunnel.acceptLoad,tunnel.buildRequestTime,tunnel.rejectOverloaded,tunnel.rejectTimeout" + "tunnel.buildClientExpire,tunnel.buildClientReject,tunnel.buildClientSuccess," + diff --git a/core/java/src/net/i2p/util/FortunaRandomSource.java b/core/java/src/net/i2p/util/FortunaRandomSource.java index 2d1a6919659825d204801827f184ad4ea32c2ddb..865cc0bb44c36d7dbbfe26ae62c143c836f7a8b1 100644 --- a/core/java/src/net/i2p/util/FortunaRandomSource.java +++ b/core/java/src/net/i2p/util/FortunaRandomSource.java @@ -32,7 +32,7 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste public FortunaRandomSource(I2PAppContext context) { super(context); - _fortuna = new AsyncFortunaStandalone(); + _fortuna = new AsyncFortunaStandalone(context); byte seed[] = new byte[1024]; if (initSeed(seed)) { _fortuna.seed(seed); diff --git a/history.txt b/history.txt index e202dd2064c1a5ab818e5437cc7a4c7e09d9d98b..a3e13b7e9dfdf8773cd8ae031f76cbeed0d0e579 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,17 @@ +2008-06-23 zzz + * configclients.jsp: Add start button for clients and webapps. + * PRNG: Add two stats + * Summary bar: + - Display Warning for TCP private IP address + - Display PRNG stats + * OutNetMessage: Change cache logging from WARN to INFO + +2008-06-17 zzz + * Comm System: Add new STATUS_HOSED for use when UDP bind fails + * Summary bar: Display helpful errror message when UDP bind fails + * UDP: Don't bid when UDP bind fails + * configclients.jsp: Implement saves for clients and webapps. + 2008-06-16 zzz * UDP: Prevent 100% CPU when UDP bind fails; change bind fail message from ERROR to CRIT @@ -12,7 +26,6 @@ * configclients.jsp: New. For both clients and webapps. Saves are not yet implemented. - 2008-06-10 zzz * Floodfill: Add new FloodfillMonitorJob, which tracks active floodfills, and automatically enables/disables floodfill on diff --git a/router/java/src/net/i2p/router/CommSystemFacade.java b/router/java/src/net/i2p/router/CommSystemFacade.java index 2978ce9286206977dad7328518089a39fa179d84..3fb14186547cd030f6c85d7a5efd377366e2643f 100644 --- a/router/java/src/net/i2p/router/CommSystemFacade.java +++ b/router/java/src/net/i2p/router/CommSystemFacade.java @@ -61,6 +61,9 @@ public abstract class CommSystemFacade implements Service { */ public void notifyReplaceAddress(RouterAddress UDPAddr) {} /** + * These must be increasing in "badness" (see TransportManager.java), + * but UNKNOWN must be last. + * * We are able to receive unsolicited connections */ public static final short STATUS_OK = 0; @@ -75,10 +78,14 @@ public abstract class CommSystemFacade implements Service { * cannot receive unsolicited connections */ public static final short STATUS_REJECT_UNSOLICITED = 2; + /** + * Our detection system is broken (SSU bind port failed) + */ + public static final short STATUS_HOSED = 3; /** * Our reachability is unknown */ - public static final short STATUS_UNKNOWN = 3; + public static final short STATUS_UNKNOWN = 4; } diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index da77a17e2f4600814eba8fed4e936b3eb8b34a40..9cea22605ce6224fc49a34d7acbae56bd94b66a7 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -17,7 +17,7 @@ import net.i2p.CoreVersion; public class RouterVersion { public final static String ID = "$Revision: 1.548 $ $Date: 2008-06-07 23:00:00 $"; public final static String VERSION = "0.6.2"; - public final static long BUILD = 3; + public final static long BUILD = 5; public static void main(String args[]) { System.out.println("I2P Router version: " + VERSION + "-" + BUILD); System.out.println("Router ID: " + RouterVersion.ID); diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index 8dba7275dc2bbecb7c21d1f3dc679fd4e8bb5396..02e2efd401cde7bd809161418428571bdad12d5a 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -247,8 +247,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { return null; //} } else { - if (_log.shouldLog(Log.WARN)) - _log.warn("Expired from cache - reply leaseset for " + _toString); + if (_log.shouldLog(Log.INFO)) + _log.info("Expired from cache - reply leaseset for " + _toString); // will get overwritten below // _leaseSetCache.remove(hashPair()); } @@ -256,8 +256,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { } _leaseSetCache.put(hashPair(), newLS); } - if (_log.shouldLog(Log.WARN)) - _log.warn("Added to cache - reply leaseset for " + _toString); + if (_log.shouldLog(Log.INFO)) + _log.info("Added to cache - reply leaseset for " + _toString); return newLS; } @@ -329,8 +329,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { } } } - if (_log.shouldLog(Log.WARN)) - _log.warn("Expired from cache - lease for " + _toString); + if (_log.shouldLog(Log.INFO)) + _log.info("Expired from cache - lease for " + _toString); _leaseCache.remove(_to); } } @@ -340,8 +340,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { for (int i = 0; i < _leaseSet.getLeaseCount(); i++) { Lease lease = _leaseSet.getLease(i); if (lease.isExpired(Router.CLOCK_FUDGE_FACTOR)) { - if (_log.shouldLog(Log.WARN)) - _log.warn(getJobId() + ": getNextLease() - expired lease! - " + lease + " for " + _toString); + if (_log.shouldLog(Log.INFO)) + _log.info(getJobId() + ": getNextLease() - expired lease! - " + lease + " for " + _toString); continue; } else { leases.add(lease); @@ -403,8 +403,8 @@ public class OutboundClientMessageOneShotJob extends JobImpl { synchronized (_leaseCache) { _leaseCache.put(hashPair(), _lease); } - if (_log.shouldLog(Log.WARN)) - _log.warn("Added to cache - lease for " + _toString); + if (_log.shouldLog(Log.INFO)) + _log.info("Added to cache - lease for " + _toString); return true; } diff --git a/router/java/src/net/i2p/router/startup/ClientAppConfig.java b/router/java/src/net/i2p/router/startup/ClientAppConfig.java index 8c54ea1fa9effb471a172a9fe1e89fe09db5319e..0a994069f9234de2fc7cc1d06ea482616dc2eb64 100644 --- a/router/java/src/net/i2p/router/startup/ClientAppConfig.java +++ b/router/java/src/net/i2p/router/startup/ClientAppConfig.java @@ -2,6 +2,7 @@ package net.i2p.router.startup; import java.io.IOException; import java.io.File; +import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -22,6 +23,7 @@ public class ClientAppConfig { private static final String PROP_CLIENT_CONFIG_FILENAME = "router.clientConfigFile"; private static final String DEFAULT_CLIENT_CONFIG_FILENAME = "clients.config"; + private static final String PREFIX = "clientApp."; // let's keep this really simple public String className; @@ -63,14 +65,14 @@ public class ClientAppConfig { List rv = new ArrayList(5); int i = 0; while (true) { - String className = clientApps.getProperty("clientApp."+i+".main"); + String className = clientApps.getProperty(PREFIX + i + ".main"); if (className == null) break; - String clientName = clientApps.getProperty("clientApp."+i+".name"); - String args = clientApps.getProperty("clientApp."+i+".args"); - String delayStr = clientApps.getProperty("clientApp." + i + ".delay"); - String onBoot = clientApps.getProperty("clientApp." + i + ".onBoot"); - String disabled = clientApps.getProperty("clientApp." + i + ".startOnLoad"); + String clientName = clientApps.getProperty(PREFIX + i + ".name"); + String args = clientApps.getProperty(PREFIX + i + ".args"); + String delayStr = clientApps.getProperty(PREFIX + i + ".delay"); + String onBoot = clientApps.getProperty(PREFIX + i + ".onBoot"); + String disabled = clientApps.getProperty(PREFIX + i + ".startOnLoad"); i++; boolean dis = disabled != null && "false".equals(disabled); @@ -87,5 +89,25 @@ public class ClientAppConfig { return rv; } + public static void writeClientAppConfig(RouterContext ctx, List apps) { + String clientConfigFile = ctx.getProperty(PROP_CLIENT_CONFIG_FILENAME, DEFAULT_CLIENT_CONFIG_FILENAME); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(clientConfigFile); + StringBuffer buf = new StringBuffer(2048); + for(int i = 0; i < apps.size(); i++) { + ClientAppConfig app = (ClientAppConfig) apps.get(i); + buf.append(PREFIX).append(i).append(".main=").append(app.className).append("\n"); + buf.append(PREFIX).append(i).append(".name=").append(app.clientName).append("\n"); + buf.append(PREFIX).append(i).append(".args=").append(app.args).append("\n"); + buf.append(PREFIX).append(i).append(".delay=").append(app.delay / 1000).append("\n"); + buf.append(PREFIX).append(i).append(".startOnLoad=").append(!app.disabled).append("\n"); + } + fos.write(buf.toString().getBytes()); + } catch (IOException ioe) { + } finally { + if (fos != null) try { fos.close(); } catch (IOException ioe) {} + } + } } diff --git a/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java b/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java index 62e7132382f6733234849bf2d14b8b4b54eb742f..0770241d6b4f66886ef5efce791a3f6e9e25fafd 100644 --- a/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java +++ b/router/java/src/net/i2p/router/startup/LoadClientAppsJob.java @@ -15,7 +15,7 @@ import net.i2p.util.Log; * it'll get queued up for starting 2 minutes later. * */ -class LoadClientAppsJob extends JobImpl { +public class LoadClientAppsJob extends JobImpl { private Log _log; private static boolean _loaded = false; @@ -36,7 +36,7 @@ class LoadClientAppsJob extends JobImpl { String argVal[] = parseArgs(app.args); if (app.delay == 0) { // run this guy now - runClient(app.className, app.clientName, argVal); + runClient(app.className, app.clientName, argVal, _log); } else { // wait before firing it up getContext().jobQueue().addJob(new DelayedRunClient(getContext(), app.className, app.clientName, argVal, app.delay)); @@ -56,11 +56,11 @@ class LoadClientAppsJob extends JobImpl { } public String getName() { return "Delayed client job"; } public void runJob() { - runClient(_className, _clientName, _args); + runClient(_className, _clientName, _args, _log); } } - static String[] parseArgs(String args) { + public static String[] parseArgs(String args) { List argList = new ArrayList(4); if (args != null) { char data[] = args.toCharArray(); @@ -109,9 +109,9 @@ class LoadClientAppsJob extends JobImpl { return rv; } - private void runClient(String className, String clientName, String args[]) { - _log.info("Loading up the client application " + clientName + ": " + className + " " + args); - I2PThread t = new I2PThread(new RunApp(className, clientName, args)); + public static void runClient(String className, String clientName, String args[], Log log) { + log.info("Loading up the client application " + clientName + ": " + className + " " + args); + I2PThread t = new I2PThread(new RunApp(className, clientName, args, log)); if (clientName == null) clientName = className + " client"; t.setName(clientName); @@ -119,17 +119,19 @@ class LoadClientAppsJob extends JobImpl { t.start(); } - private final class RunApp implements Runnable { + private final static class RunApp implements Runnable { private String _className; private String _appName; private String _args[]; - public RunApp(String className, String appName, String args[]) { + private Log _log; + public RunApp(String className, String appName, String args[], Log log) { _className = className; _appName = appName; if (args == null) _args = new String[0]; else _args = args; + _log = log; } public void run() { try { diff --git a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java index cb57dcf0fcd2a7c986183d3af40f45d18a0f8a92..aefe18b8ba5d1084db4fc24b5ba6527a859b031d 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java @@ -5,6 +5,7 @@ import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; +import net.i2p.router.CommSystemFacade; import net.i2p.router.RouterContext; import net.i2p.util.Log; @@ -44,6 +45,7 @@ public class UDPEndpoint { _sender.startup(); _receiver.startup(); } catch (SocketException se) { + _transport.setReachabilityStatus(CommSystemFacade.STATUS_HOSED); _log.log(Log.CRIT, "Unable to bind on port " + _listenPort, se); } } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 4e5e770304fa338a4f71b13623dff72cb7368b5d..040fac7615572004b35779e280aa5ba468e74a37 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -858,6 +858,12 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority else return _fastBid; } else { + // If we don't have a port, all is lost + if ( _reachabilityStatus == CommSystemFacade.STATUS_HOSED) { + markUnreachable(to); + return null; + } + // Validate his SSU address RouterAddress addr = toAddress.getTargetAddress(STYLE); if (addr == null) { @@ -1870,6 +1876,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority buf.append(" </tr>\n"); buf.append("<tr><td colspan=\"15\" valign=\"top\" align=\"left\">"); long bytesTransmitted = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes(); + // NPE here early double averagePacketSize = _context.statManager().getRate("udp.sendPacketSize").getLifetimeAverageValue(); // lifetime value, not just the retransmitted packets of current connections resentTotal = (long)_context.statManager().getRate("udp.packetsRetransmitted").getLifetimeEventCount(); @@ -2005,6 +2012,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority break; case CommSystemFacade.STATUS_REJECT_UNSOLICITED: _context.statManager().addRateData("udp.statusReject", 1, 0); + // fall through... + case CommSystemFacade.STATUS_HOSED: _reachabilityStatus = status; _reachabilityStatusLastUpdated = now; break; @@ -2021,6 +2030,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority break; } if ( (status != old) && (status != CommSystemFacade.STATUS_UNKNOWN) ) { + if (_log.shouldLog(Log.INFO)) + _log.info("Old status: " + old + " New status: " + status + " from: ", new Exception("traceback")); if (needsRebuild()) rebuildExternalAddress(); }