diff --git a/apps/i2psnark/java/src/org/klomp/snark/IdleChecker.java b/apps/i2psnark/java/src/org/klomp/snark/IdleChecker.java index d7b47a343..adfc42833 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/IdleChecker.java +++ b/apps/i2psnark/java/src/org/klomp/snark/IdleChecker.java @@ -26,10 +26,12 @@ class IdleChecker extends SimpleTimer2.TimedEvent { private final PeerCoordinatorSet _pcs; private final Log _log; private int _consec; + private int _consecNotRunning; private boolean _isIdle; private static final long CHECK_TIME = 63*1000; private static final int MAX_CONSEC_IDLE = 4; + private static final int MAX_CONSEC_NOT_RUNNING = 20; /** * Caller must schedule @@ -43,13 +45,30 @@ class IdleChecker extends SimpleTimer2.TimedEvent { public void timeReached() { if (_util.connected()) { + boolean torrentRunning = false; boolean hasPeers = false; for (PeerCoordinator pc : _pcs) { - if (pc.getPeers() > 0) { - hasPeers = true; - break; + if (!pc.halted()) { + torrentRunning = true; + if (pc.getPeers() > 0) { + hasPeers = true; + break; + } } } + + if (torrentRunning) { + _consecNotRunning = 0; + } else { + if (_consecNotRunning++ >= MAX_CONSEC_NOT_RUNNING) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Closing tunnels on idle"); + _util.disconnect(); + schedule(3 * CHECK_TIME); + return; + } + } + if (hasPeers) { if (_isIdle) restoreTunnels(); @@ -62,6 +81,7 @@ class IdleChecker extends SimpleTimer2.TimedEvent { } else { _isIdle = false; _consec = 0; + _consecNotRunning = 0; } schedule(CHECK_TIME); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index c92b42210..6e04d29ad 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -237,6 +237,7 @@ public class Snark private final PeerCoordinatorSet _peerCoordinatorSet; private String trackerProblems; private int trackerSeenPeers; + private boolean _autoStoppable; /** from main() via parseArguments() single torrent */ @@ -905,6 +906,16 @@ public class Snark return additionalTrackerURL; } + /** + * @since 0.9.9 + */ + public boolean isAutoStoppable() { return _autoStoppable; } + + /** + * @since 0.9.9 + */ + public void setAutoStoppable(boolean yes) { _autoStoppable = yes; } + /** * Sets debug, ip and torrent variables then creates a Snark * instance. Calls usage(), which terminates the program, if diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 4450d4d74..38ea1c094 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -1666,8 +1666,8 @@ public class SnarkManager implements CompleteListener { if (_log.shouldLog(Log.DEBUG)) _log.debug("DirMon found: " + DataHelper.toString(foundNames) + " existing: " + DataHelper.toString(existingNames)); // lets find new ones first... - for (int i = 0; i < foundNames.size(); i++) { - if (existingNames.contains(foundNames.get(i))) { + for (String name : foundNames) { + if (existingNames.contains(name)) { // already known. noop } else { if (shouldAutoStart() && !_util.connect()) @@ -1675,9 +1675,10 @@ public class SnarkManager implements CompleteListener { try { // Snark.fatal() throws a RuntimeException // don't let one bad torrent kill the whole loop - addTorrent(foundNames.get(i), !shouldAutoStart()); + addTorrent(name, !shouldAutoStart()); } catch (Exception e) { - addMessage(_("Unable to add {0}", foundNames.get(i)) + ": " + e); + addMessage(_("Error: Could not add the torrent {0}", name) + ": " + e.getMessage()); + _log.error("Unable to add the torrent " + name, e); } } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index 9157c582f..91f68ce0b 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -113,6 +113,7 @@ public class TrackerClient implements Runnable { private long lastDHTAnnounce; private final List trackers; private final List backupTrackers; + private long _startedOn; /** * Call start() to start it. @@ -334,6 +335,7 @@ public class TrackerClient implements Runnable { } } this.completed = coordinator.getLeft() == 0; + _startedOn = _util.getContext().clock().now(); } /** @@ -482,12 +484,30 @@ public class TrackerClient implements Runnable { consecutiveFails = 0; runStarted = true; tr.started = true; - - Set peers = info.getPeers(); tr.seenPeers = info.getPeerCount(); if (snark.getTrackerSeenPeers() < tr.seenPeers) // update rising number quickly snark.setTrackerSeenPeers(tr.seenPeers); + // auto stop + // These are very high thresholds for now, not configurable, + // just for update torrent + if (completed && + tr.isPrimary && + snark.isAutoStoppable() && + !snark.isChecking() && + info.getSeedCount() > 100 && + coordinator.getPeerCount() <= 0 && + _util.getContext().clock().now() > _startedOn + 2*60*60*1000 && + uploaded >= 2 * snark.getTotalLength()) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Auto stopping " + snark.getBaseName()); + snark.setAutoStoppable(false); + snark.stopTorrent(); + return tr.seenPeers; + } + + Set peers = info.getPeers(); + // pass everybody over to our tracker DHT dht = _util.getDHT(); if (dht != null) { diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java index 6399706fc..b17d787be 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java @@ -190,6 +190,12 @@ class TrackerInfo return Math.max(pc, complete + incomplete - 1); } + /** @since 0.9.9 */ + public int getSeedCount() + { + return complete; + } + public String getFailureReason() { return failure_reason; diff --git a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java index 63d9670f1..e92ed4774 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java +++ b/apps/i2psnark/java/src/org/klomp/snark/UpdateRunner.java @@ -264,6 +264,7 @@ class UpdateRunner implements UpdateTask, CompleteListener { } _hasMetaInfo = true; notifyProgress(); + snark.setAutoStoppable(true); return _smgr.gotMetaInfo(snark); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index f538da98c..b04b6f23a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -976,7 +976,8 @@ public class I2PSnarkServlet extends BasicServlet { if (announceURL != null && !_manager.util().getOpenTrackers().contains(announceURL)) _manager.addMessage(_("Many I2P trackers require you to register new torrents before seeding - please do so before starting \"{0}\"", baseFile.getName())); } catch (IOException ioe) { - _manager.addMessage(_("Error creating a torrent for \"{0}\"", baseFile.getAbsolutePath()) + ": " + ioe.getMessage()); + _manager.addMessage(_("Error creating a torrent for \"{0}\"", baseFile.getAbsolutePath()) + ": " + ioe); + _log.error("Error creating a torrent", ioe); } } else { _manager.addMessage(_("Cannot create a torrent for the nonexistent data: {0}", baseFile.getAbsolutePath()));