diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index eb5195d2e7..0e52f17a1c 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -43,6 +43,7 @@ import java.io.OutputStream; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -306,19 +307,33 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { " -w, -wait, --wait : do not run the command line interface or GUI"; } - /** @return A copy, non-null */ + /** + * @return A copy, unmodifiable, non-null + */ List getSessions() { - return new ArrayList(_sessions); + if (_sessions.isEmpty()) + return Collections.emptyList(); + return new ArrayList(_sessions); } + /** + * @param session null ok + */ void addSession(I2PSession session) { if (session == null) return; - _sessions.add(session); + boolean added = _sessions.add(session); + if (added && _log.shouldLog(Log.INFO)) + _log.info(getPrefix() + " session added: " + session, new Exception()); } + /** + * @param session null ok + */ void removeSession(I2PSession session) { if (session == null) return; - _sessions.remove(session); + boolean removed = _sessions.remove(session); + if (removed && _log.shouldLog(Log.INFO)) + _log.info(getPrefix() + " session removed: " + session, new Exception()); } /** @@ -334,6 +349,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { tsk.setId(next_task_id); next_task_id++; tasks.add(tsk); + if (_log.shouldLog(Log.INFO)) + _log.info(getPrefix() + " adding task: " + tsk); + } else { + if (_log.shouldLog(Log.INFO)) + _log.info(getPrefix() + " not adding task that isn't open: " + tsk); } } @@ -1521,6 +1541,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { } if (args[argindex].equalsIgnoreCase("all")) { boolean error = false; + if (tasks.isEmpty()) { + if (_log.shouldLog(Log.INFO)) + _log.info(getPrefix() + " runClose(all) no tasks"); + } for (I2PTunnelTask t : tasks) { if (!closetask(t, forced, l)) { notifyEvent("closeResult", "error"); @@ -1693,7 +1717,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { */ public void log(String s) { System.out.println(s); - _log.info(getPrefix() + "Display: " + s); + if (_log.shouldLog(Log.INFO)) + _log.info(getPrefix() + "Display: " + s); } /** diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index 84e803ff6b..7a7328c3ca 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -346,6 +346,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna } else { if (_log.shouldLog(Log.INFO)) _log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Not building a new socket manager since the old one is open [s=" + s + "]"); + // If some other tunnel created the session, we need to add it + // as our session too. + // It's a Set in I2PTunnel + tunnel.addSession(s); } } else { if (_log.shouldLog(Log.INFO)) @@ -767,6 +771,12 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna if (!chained) { I2PSession session = sockMgr.getSession(); getTunnel().removeSession(session); + if (_ownDest) { + try { + session.destroySession(); + } catch (I2PException ex) {} + } + // TCG will try to destroy it too } // else the app chaining to this one closes it! } l.log("Stopping client " + toString()); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 806d2d7149..22c496f10b 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -4,9 +4,12 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.Set; import net.i2p.I2PAppContext; import net.i2p.I2PException; @@ -387,10 +390,9 @@ public class TunnelController implements Logging { * Note the fact that we are no longer using some sessions, and if * no other tunnels are using them, close them. */ - private void release() { - if (_sessions != null && !_sessions.isEmpty()) { - for (int i = 0; i < _sessions.size(); i++) { - I2PSession s = _sessions.get(i); + private void release(Collection sessions) { + if (!sessions.isEmpty()) { + for (I2PSession s : sessions) { if (_log.shouldLog(Log.INFO)) _log.info("Releasing session " + s); TunnelControllerGroup group = TunnelControllerGroup.getInstance(); @@ -403,6 +405,22 @@ public class TunnelController implements Logging { _log.warn("No sessions to release? for " + getName()); } } + + /** + * Get all the sessions we may be using. + * + * @return a copy, non-null + * @since 0.9.15 + */ + private Collection getAllSessions() { + // We use _sessions AND the tunnel sessions as + // _sessions will be null for delay-open tunnels - see acquire(). + // We want the current sessions. + Set sessions = new HashSet(_tunnel.getSessions()); + if (_sessions != null) + sessions.addAll(_sessions); + return sessions; + } private void startClient() { setListenOn(); @@ -522,8 +540,11 @@ public class TunnelController implements Logging { } public void stopTunnel() { + // I2PTunnel removes the session in close(), + // so save the sessions to pass to release() and TCG + Collection sessions = getAllSessions(); _tunnel.runClose(new String[] { "forced", "all" }, this); - release(); + release(sessions); _running = false; } @@ -568,12 +589,8 @@ public class TunnelController implements Logging { // tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager setSessionOptions(); - // we use the tunnel sessions, not _sessions, as - // _sessions will be null for delay-open tunnels - see acquire(). - // We want the current sessions. - //List sessions = _sessions; - List sessions = _tunnel.getSessions(); - if (_running && sessions != null) { + if (_running) { + Collection sessions = getAllSessions(); if (sessions.isEmpty()) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Running but no sessions to update"); @@ -591,10 +608,7 @@ public class TunnelController implements Logging { } } else { if (_log.shouldLog(Log.DEBUG)) { - if (!_running) - _log.debug("Not running, not updating sessions"); - if (sessions == null) - _log.debug("null sessions, nothing to update"); + _log.debug("Not running, not updating sessions"); } } } @@ -683,6 +697,7 @@ public class TunnelController implements Logging { public boolean getIsRunning() { return _running; } public boolean getIsStarting() { return _starting; } + /** if running but no open sessions, we are in standby */ public boolean getIsStandby() { if (!_running) @@ -835,4 +850,12 @@ public class TunnelController implements Logging { } return rv; } + + /** + * @since 0.9.15 + */ + @Override + public String toString() { + return "TC " + getType() + ' ' + getName() + " for " + _tunnel; + } } diff --git a/history.txt b/history.txt index 860144a1cd..afb96e314a 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,8 @@ +2014-09-13 zzz + * i2ptunnel: + - Fixes for stopping client tunnels + - Fix status display for shared clients + 2014-09-12 zzz * i2psnark: Escape fixes * i2ptunnel: Fix updating session options on a running delay-open client tunnel diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 14408f1ed4..987fc2f753 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 18; + public final static long BUILD = 19; /** for example "-test" */ public final static String EXTRA = "-rc";