diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
index 08ac271b7..00a210a8a 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java
@@ -99,7 +99,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
t.start();
open = true;
synchronized (this) {
- while (!listenerReady) {
+ while (!listenerReady && open) {
try {
wait();
} catch (InterruptedException e) {
@@ -112,7 +112,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
l.log("Ready! Port " + getLocalPort());
notifyEvent("openBaseClientResult", "ok");
} else {
- l.log("Error!");
+ l.log("Error listening - please see the logs!");
notifyEvent("openBaseClientResult", "error");
}
}
@@ -232,7 +232,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
public final void run() {
try {
InetAddress addr = getListenHost(l);
- if (addr == null) return;
+ if (addr == null) {
+ open = false;
+ synchronized (this) {
+ notifyAll();
+ }
+ return;
+ }
ss = new ServerSocket(localPort, 0, addr);
// If a free port was requested, find out what we got
@@ -263,11 +269,15 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
manageConnection(s);
}
} catch (IOException ex) {
- _log.error("Error listening for connections", ex);
+ _log.error("Error listening for connections on " + localPort, ex);
notifyEvent("openBaseClientResult", "error");
synchronized (sockLock) {
mySockets.clear();
}
+ open = false;
+ synchronized (this) {
+ notifyAll();
+ }
}
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
index b486185c6..03379accf 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java
@@ -31,6 +31,7 @@ public class TunnelController implements Logging {
private List _messages;
private List _sessions;
private boolean _running;
+ private boolean _starting;
/**
* Create a new controller for a tunnel out of the specific config options.
@@ -58,8 +59,7 @@ public class TunnelController implements Logging {
_running = false;
if (createKey && ("server".equals(getType())) )
createPrivateKey();
- if (getStartOnLoad())
- startTunnel();
+ _starting = getStartOnLoad();
}
private void createPrivateKey() {
@@ -104,12 +104,14 @@ public class TunnelController implements Logging {
*
*/
public void startTunnel() {
+ _starting = true;
try {
doStartTunnel();
} catch (Exception e) {
_log.error("Error starting up the tunnel", e);
log("Error starting up the tunnel - " + e.getMessage());
}
+ _starting = false;
}
private void doStartTunnel() {
if (_running) {
@@ -299,6 +301,7 @@ public class TunnelController implements Logging {
public boolean getStartOnLoad() { return "true".equalsIgnoreCase(_config.getProperty("startOnLoad", "true")); }
public boolean getIsRunning() { return _running; }
+ public boolean getIsStarting() { return _starting; }
public void getSummary(StringBuffer buf) {
String type = getType();
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java
index eb1a42676..2978c9921 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java
@@ -8,6 +8,7 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -20,6 +21,8 @@ import java.util.TreeMap;
import net.i2p.I2PAppContext;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
+import net.i2p.data.DataHelper;
+import net.i2p.util.I2PThread;
import net.i2p.util.Log;
/**
@@ -43,28 +46,34 @@ public class TunnelControllerGroup {
*/
private Map _sessions;
- public static TunnelControllerGroup getInstance() { return _instance; }
+ public static TunnelControllerGroup getInstance() {
+ synchronized (TunnelControllerGroup.class) {
+ if (_instance == null)
+ _instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
+ return _instance;
+ }
+ }
private TunnelControllerGroup(String configFile) {
_log = I2PAppContext.getGlobalContext().logManager().getLog(TunnelControllerGroup.class);
- _instance = this;
- _controllers = new ArrayList();
+ _controllers = Collections.synchronizedList(new ArrayList());
_configFile = configFile;
_sessions = new HashMap(4);
loadControllers(_configFile);
}
public static void main(String args[]) {
- if ( (args == null) || (args.length <= 0) ) {
- new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
- } else if (args.length == 1) {
- if (DEFAULT_CONFIG_FILE.equals(args[0]))
- new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
- else
- new TunnelControllerGroup(args[0]);
- } else {
- System.err.println("Usage: TunnelControllerGroup [filename]");
- return;
+ synchronized (TunnelControllerGroup.class) {
+ if (_instance != null) return; // already loaded through the web
+
+ if ( (args == null) || (args.length <= 0) ) {
+ _instance = new TunnelControllerGroup(DEFAULT_CONFIG_FILE);
+ } else if (args.length == 1) {
+ _instance = new TunnelControllerGroup(args[0]);
+ } else {
+ System.err.println("Usage: TunnelControllerGroup [filename]");
+ return;
+ }
}
}
@@ -89,10 +98,24 @@ public class TunnelControllerGroup {
_controllers.add(controller);
i++;
}
+ I2PThread startupThread = new I2PThread(new StartControllers(), "Startup tunnels");
+ startupThread.start();
+
if (_log.shouldLog(Log.INFO))
_log.info(i + " controllers loaded from " + configFile);
}
+ private class StartControllers implements Runnable {
+ public void run() {
+ for (int i = 0; i < _controllers.size(); i++) {
+ TunnelController controller = (TunnelController)_controllers.get(i);
+ if (controller.getStartOnLoad())
+ controller.startTunnel();
+ }
+ }
+ }
+
+
public void reloadControllers() {
unloadControllers();
loadControllers(_configFile);
@@ -143,7 +166,6 @@ public class TunnelControllerGroup {
controller.stopTunnel();
msgs.addAll(controller.clearMessages());
}
-
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers stopped");
return msgs;
@@ -161,7 +183,7 @@ public class TunnelControllerGroup {
controller.startTunnel();
msgs.addAll(controller.clearMessages());
}
-
+
if (_log.shouldLog(Log.INFO))
_log.info(_controllers.size() + " controllers started");
return msgs;
@@ -259,33 +281,13 @@ public class TunnelControllerGroup {
}
Properties props = new Properties();
- FileInputStream fis = null;
try {
- fis = new FileInputStream(cfgFile);
- BufferedReader in = new BufferedReader(new InputStreamReader(fis));
- String line = null;
- while ( (line = in.readLine()) != null) {
- line = line.trim();
- if (line.length() <= 0) continue;
- if (line.startsWith("#") || line.startsWith(";"))
- continue;
- int eq = line.indexOf('=');
- if ( (eq <= 0) || (eq >= line.length() - 1) )
- continue;
- String key = line.substring(0, eq);
- String val = line.substring(eq+1);
- props.setProperty(key, val);
- }
-
- if (_log.shouldLog(Log.INFO))
- _log.info("Props loaded with " + props.size() + " lines");
+ DataHelper.loadProps(props, cfgFile);
return props;
} catch (IOException ioe) {
if (_log.shouldLog(Log.ERROR))
_log.error("Error reading the controllers from " + configFile, ioe);
return null;
- } finally {
- if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}
}
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebStatusPageHelper.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebStatusPageHelper.java
index c186a1857..07d55f88a 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebStatusPageHelper.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/WebStatusPageHelper.java
@@ -68,6 +68,8 @@ public class WebStatusPageHelper {
if (controller.getIsRunning()) {
buf.append("running ");
buf.append("stop ");
+ } else if (controller.getIsStarting()) {
+ buf.append("startup in progress (please be patient)");
} else {
buf.append("not running ");
buf.append("start ");
diff --git a/apps/sam/java/src/net/i2p/sam/SAMBridge.java b/apps/sam/java/src/net/i2p/sam/SAMBridge.java
index fe95b297d..c1dd7fe8a 100644
--- a/apps/sam/java/src/net/i2p/sam/SAMBridge.java
+++ b/apps/sam/java/src/net/i2p/sam/SAMBridge.java
@@ -250,6 +250,7 @@ public class SAMBridge implements Runnable {
}
public void run() {
+ if (serverSocket == null) return;
try {
while (acceptConnections) {
Socket s = serverSocket.accept();
@@ -293,7 +294,8 @@ public class SAMBridge implements Runnable {
try {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Shutting down, closing server socket");
- serverSocket.close();
+ if (serverSocket != null)
+ serverSocket.close();
} catch (IOException e) {}
}
}
diff --git a/core/java/src/net/i2p/util/Clock.java b/core/java/src/net/i2p/util/Clock.java
index 61ae7c402..0a1c0662b 100644
--- a/core/java/src/net/i2p/util/Clock.java
+++ b/core/java/src/net/i2p/util/Clock.java
@@ -17,6 +17,7 @@ import net.i2p.time.Timestamper;
public class Clock implements Timestamper.UpdateListener {
private I2PAppContext _context;
private Timestamper _timestamper;
+ private long _startedOn;
public Clock(I2PAppContext context) {
_context = context;
@@ -24,6 +25,7 @@ public class Clock implements Timestamper.UpdateListener {
_alreadyChanged = false;
_listeners = new HashSet(64);
_timestamper = new Timestamper(context, this);
+ _startedOn = System.currentTimeMillis();
}
public static Clock getInstance() {
return I2PAppContext.getGlobalContext().clock();
@@ -40,6 +42,8 @@ public class Clock implements Timestamper.UpdateListener {
/** if the clock is skewed by 3+ days, fuck 'em */
public final static long MAX_OFFSET = 3 * 24 * 60 * 60 * 1000;
+ /** after we've started up and shifted the clock, don't allow shifts of more than a minute */
+ public final static long MAX_LIVE_OFFSET = 60 * 1000;
/** if the clock skewed changes by less than 1s, ignore the update (so we don't slide all over the place) */
public final static long MIN_OFFSET_CHANGE = 10 * 1000;
@@ -60,6 +64,15 @@ public class Clock implements Timestamper.UpdateListener {
return;
}
+ // only allow substantial modifications before the first 10 minutes
+ if (_alreadyChanged && (System.currentTimeMillis() - _startedOn > 10 * 60 * 1000)) {
+ if ( (offsetMs > MAX_LIVE_OFFSET) || (offsetMs < 0 - MAX_LIVE_OFFSET) ) {
+ getLog().log(Log.CRIT, "The clock has already been updated, but you want to change it by "
+ + offsetMs + "? Did something break?");
+ return;
+ }
+ }
+
if ((delta < MIN_OFFSET_CHANGE) && (delta > 0 - MIN_OFFSET_CHANGE)) {
getLog().debug("Not changing offset since it is only " + delta + "ms");
return;
diff --git a/history.txt b/history.txt
index 49e9ce30b..b4f7cae87 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,16 @@
-$Id: history.txt,v 1.77 2004/11/20 23:08:14 jrandom Exp $
+$Id: history.txt,v 1.78 2004/11/21 14:42:57 jrandom Exp $
+
+2004-11-21 jrandom
+ * Only allow small clock skews after the first 10 minutes of operation
+ (to prevent later network lag bouncing us way off course - yes, we
+ really need an NTP impl to balance out the network burps...)
+ * Revamp the I2PTunnel web interface startup process so that everything
+ is shown immediately, so that different pieces hanging don't hang
+ the rest, and other minor bugfixes.
+ * Take note of SAM startup error (in case you're already running a SAM
+ bridge...)
+ * Increase the bandwidth limiter burst values available to 10-60s (or
+ whatever is placed in /configadvanced.jsp, of course)
2004-11-21 jrandom
* Allow end of line comments in the hosts.txt and other config files,
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index bf30d4d53..f53712c3c 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
- public final static String ID = "$Revision: 1.82 $ $Date: 2004/11/20 23:08:14 $";
+ public final static String ID = "$Revision: 1.83 $ $Date: 2004/11/21 14:42:58 $";
public final static String VERSION = "0.4.1.4";
- public final static long BUILD = 11;
+ public final static long BUILD = 12;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION);
System.out.println("Router ID: " + RouterVersion.ID);