Util: New getEstimatedDowntime() method

This commit is contained in:
zzz
2020-06-07 17:20:45 +00:00
parent 2af7066074
commit e23f671ca3
6 changed files with 132 additions and 8 deletions

View File

@ -1006,4 +1006,22 @@ public class I2PAppContext {
public ClientAppManager clientAppManager() { public ClientAppManager clientAppManager() {
return _appManager; return _appManager;
} }
/**
* How long this router was down before it started, or 0 if unknown.
*
* This may be used for a determination of whether to regenerate keys, for example.
* We use the timestamp of the previous ping file left behind on crash,
* as set by isOnlyRouterRunning(), if present.
* Otherwise, the last STOPPED entry in the event log.
*
* May take a while to run the first time, if it has to go through the event log.
* Once called, the result is cached.
*
* @return 0 always in app context
* @since 0.0.47
*/
public long getEstimatedDowntime() {
return 0L;
}
} }

View File

@ -105,6 +105,7 @@ public class Router implements RouterClock.ClockShiftListener {
private boolean _familyKeyCryptoFail; private boolean _familyKeyCryptoFail;
public final Object _familyKeyLock = new Object(); public final Object _familyKeyLock = new Object();
private UPnPScannerCallback _upnpScannerCallback; private UPnPScannerCallback _upnpScannerCallback;
private long _downtime = -1;
public final static String PROP_CONFIG_FILE = "router.configLocation"; public final static String PROP_CONFIG_FILE = "router.configLocation";
@ -711,7 +712,8 @@ public class Router implements RouterClock.ClockShiftListener {
synchronized(_configFileLock) { synchronized(_configFileLock) {
// persistent key for peer ordering since 0.9.17 // persistent key for peer ordering since 0.9.17
// These will be replaced in CreateRouterInfoJob if we rekey // These will be replaced in CreateRouterInfoJob if we rekey
if (!_config.containsKey(PROP_IB_RANDOM_KEY)) { if (!_config.containsKey(PROP_IB_RANDOM_KEY) ||
getEstimatedDowntime() > 12*60*60*1000L) {
byte rk[] = new byte[32]; byte rk[] = new byte[32];
_context.random().nextBytes(rk); _context.random().nextBytes(rk);
_config.put(PROP_IB_RANDOM_KEY, Base64.encode(rk)); _config.put(PROP_IB_RANDOM_KEY, Base64.encode(rk));
@ -1809,6 +1811,9 @@ public class Router implements RouterClock.ClockShiftListener {
((RouterClock) _context.clock()).removeShiftListener(this); ((RouterClock) _context.clock()).removeShiftListener(this);
// Let's not stop accepting tunnels, etc // Let's not stop accepting tunnels, etc
//_started = _context.clock().now(); //_started = _context.clock().now();
synchronized(_configFileLock) {
_downtime = 1;
}
Thread t = new I2PThread(new Restarter(_context), "Router Restart"); Thread t = new I2PThread(new Restarter(_context), "Router Restart");
t.setPriority(Thread.NORM_PRIORITY + 1); t.setPriority(Thread.NORM_PRIORITY + 1);
t.start(); t.start();
@ -1915,7 +1920,12 @@ public class Router implements RouterClock.ClockShiftListener {
File f = getPingFile(); File f = getPingFile();
if (f.exists()) { if (f.exists()) {
long lastWritten = f.lastModified(); long lastWritten = f.lastModified();
if (System.currentTimeMillis()-lastWritten > LIVELINESS_DELAY) { long downtime = System.currentTimeMillis() - lastWritten;
synchronized(_configFileLock) {
if (downtime > 0 && _downtime < 0)
_downtime = downtime;
}
if (downtime > LIVELINESS_DELAY) {
System.err.println("WARN: Old router was not shut down gracefully, deleting " + f); System.err.println("WARN: Old router was not shut down gracefully, deleting " + f);
f.delete(); f.delete();
} else { } else {
@ -1934,6 +1944,49 @@ public class Router implements RouterClock.ClockShiftListener {
_context.simpleTimer2().addPeriodicEvent(new MarkLiveliness(this, f), 0, LIVELINESS_DELAY - (5*1000)); _context.simpleTimer2().addPeriodicEvent(new MarkLiveliness(this, f), 0, LIVELINESS_DELAY - (5*1000));
} }
/**
* How long this router was down before it started, or 0 if unknown.
*
* This may be used for a determination of whether to regenerate keys, for example.
* We use the timestamp of the previous ping file left behind on crash,
* as set by isOnlyRouterRunning(), if present.
* Otherwise, the last STOPPED entry in the event log.
*
* May take a while to run the first time, if it has to go through the event log.
* Once called, the result is cached.
*
* @since 0.0.47
*/
public long getEstimatedDowntime() {
synchronized(_configFileLock) {
if (_downtime >= 0)
return _downtime;
long begin = System.currentTimeMillis();
long stopped = _eventLog.getLastEvent(EventLog.STOPPED, _context.clock().now() - 365*24*60*60*1000L);
long downtime = stopped > 0 ? _started - stopped : 0;
if (downtime < 0)
downtime = 0;
if (_log.shouldWarn())
_log.warn("Downtime was " + DataHelper.formatDuration(downtime) +
"; calculation took " + DataHelper.formatDuration(System.currentTimeMillis() - begin));
_downtime = downtime;
return downtime;
}
}
/**
* Only for soft restart. Not for external use.
*
* @since 0.0.47
*/
public void setEstimatedDowntime(long downtime) {
if (downtime <= 0)
downtime = 1;
synchronized(_configFileLock) {
_downtime = downtime;
}
}
public static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage"; public static final String PROP_BANDWIDTH_SHARE_PERCENTAGE = "router.sharePercentage";
public static final int DEFAULT_SHARE_PERCENTAGE = 80; public static final int DEFAULT_SHARE_PERCENTAGE = 80;

View File

@ -706,4 +706,24 @@ public class RouterContext extends I2PAppContext {
public ECIESAEADEngine eciesEngine() { public ECIESAEADEngine eciesEngine() {
return _eciesEngine; return _eciesEngine;
} }
/**
* How long this router was down before it started, or 0 if unknown.
*
* This may be used for a determination of whether to regenerate keys, for example.
* We use the timestamp of the previous ping file left behind on crash,
* as set by isOnlyRouterRunning(), if present.
* Otherwise, the last STOPPED entry in the event log.
*
* May take a while to run the first time, if it has to go through the event log.
* Once called, the result is cached.
*
* @return downtime in ms or 0 if unknown
* @since 0.0.47
*/
public long getEstimatedDowntime() {
if (_router == null)
return 0L;
return _router.getEstimatedDowntime();
}
} }

View File

@ -17,6 +17,7 @@ public class Restarter implements Runnable {
} }
public void run() { public void run() {
Long start = System.currentTimeMillis();
_context.router().eventLog().addEvent(EventLog.SOFT_RESTART); _context.router().eventLog().addEvent(EventLog.SOFT_RESTART);
Log log = _context.logManager().getLog(Router.class); Log log = _context.logManager().getLog(Router.class);
log.error("Stopping the router for a restart..."); log.error("Stopping the router for a restart...");
@ -36,6 +37,7 @@ public class Restarter implements Runnable {
log.logAlways(Log.WARN, "Router teardown complete, restarting the router..."); log.logAlways(Log.WARN, "Router teardown complete, restarting the router...");
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {} try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
_context.router().setEstimatedDowntime(System.currentTimeMillis() - start);
log.logAlways(Log.WARN, "Restarting the comm system"); log.logAlways(Log.WARN, "Restarting the comm system");
log.logAlways(Log.WARN, "Restarting the tunnel manager"); log.logAlways(Log.WARN, "Restarting the tunnel manager");

View File

@ -252,12 +252,7 @@ public class NTCPTransport extends TransportImpl {
String b64IV = null; String b64IV = null;
String s = null; String s = null;
// try to determine if we've been down for 30 days or more // try to determine if we've been down for 30 days or more
// no stopping, no crashes, and only one start (this one) boolean shouldRekey = _context.getEstimatedDowntime() >= MIN_DOWNTIME_TO_REKEY;
EventLog el = _context.router().eventLog();
long since = _context.clock().now() - MIN_DOWNTIME_TO_REKEY;
boolean shouldRekey = el.getEvents(EventLog.STOPPED, since).isEmpty() &&
el.getEvents(EventLog.CRASHED, since).isEmpty() &&
el.getEvents(EventLog.STARTED, since).size() <= 1;
if (!shouldRekey) { if (!shouldRekey) {
s = ctx.getProperty(PROP_NTCP2_SP); s = ctx.getProperty(PROP_NTCP2_SP);
if (s != null) { if (s != null) {

View File

@ -190,4 +190,40 @@ public class EventLog {
} }
return rv; return rv;
} }
/**
* Timestamp of last event.
*
* @param event matching this event, case sensitive
* @param since since this time, 0 for all
* @return last event time, or 0 for none
* @since 0.9.47
*/
public synchronized long getLastEvent(String event, long since) {
long rv = 0;
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(
new FileInputStream(_file), "UTF-8"));
String line = null;
while ( (line = br.readLine()) != null) {
try {
String[] s = DataHelper.split(line.trim(), " ", 3);
if (s.length < 2)
continue;
if (!s[1].equals(event))
continue;
long time = Long.parseLong(s[0]);
if (time <= since)
continue;
rv = time;
} catch (NumberFormatException nfe) {
}
}
} catch (IOException ioe) {
} finally {
if (br != null) try { br.close(); } catch (IOException ioe) {}
}
return rv;
}
} }