diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index f98e45d49..34da8c3ed 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -33,6 +33,7 @@ public class PeerCoordinator implements PeerListener private final Log _log = new Log(PeerCoordinator.class); final MetaInfo metainfo; final Storage storage; + final Snark snark; // package local for access by CheckDownLoadersTask final static long CHECK_PERIOD = 20*1000; // 20 seconds @@ -70,12 +71,13 @@ public class PeerCoordinator implements PeerListener public int trackerSeenPeers = 0; public PeerCoordinator(byte[] id, MetaInfo metainfo, Storage storage, - CoordinatorListener listener) + CoordinatorListener listener, Snark torrent) { this.id = id; this.metainfo = metainfo; this.storage = storage; this.listener = listener; + this.snark = torrent; // Make a list of pieces wantedPieces = new ArrayList(); @@ -400,8 +402,9 @@ public class PeerCoordinator implements PeerListener } catch (IOException ioe) { - Snark.fatal("Error reading storage", ioe); - return null; // Never reached. + snark.stopTorrent(); + _log.error("Error reading the storage for " + metainfo.getName(), ioe); + throw new RuntimeException("B0rked"); } } @@ -476,7 +479,9 @@ public class PeerCoordinator implements PeerListener } catch (IOException ioe) { - Snark.fatal("Error writing storage", ioe); + snark.stopTorrent(); + _log.error("Error writing storage for " + metainfo.getName(), ioe); + throw new RuntimeException("B0rked"); } wantedPieces.remove(p); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index a5917fd82..09d438bc2 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -333,7 +333,7 @@ public class Snark } debug(meta.toString(), INFO); - + // When the metainfo torrent was created from an existing file/dir // it already exists. if (storage == null) @@ -346,12 +346,15 @@ public class Snark } catch (IOException ioe) { + try { storage.close(); } catch (IOException ioee) { + ioee.printStackTrace(); + } fatal("Could not create storage", ioe); } } activity = "Collecting pieces"; - coordinator = new PeerCoordinator(id, meta, storage, clistener); + coordinator = new PeerCoordinator(id, meta, storage, clistener, this); PeerCoordinatorSet set = PeerCoordinatorSet.instance(); set.add(coordinator); PeerAcceptor peeracceptor = new PeerAcceptor(set); //coordinator); @@ -374,7 +377,7 @@ public class Snark PeerCoordinatorSet set = PeerCoordinatorSet.instance(); set.remove(coordinator); PeerCoordinator newCoord = new PeerCoordinator(coordinator.getID(), coordinator.getMetaInfo(), - coordinator.getStorage(), coordinator.getListener()); + coordinator.getStorage(), coordinator.getListener(), this); set.add(newCoord); coordinator = newCoord; coordinatorChanged = true; @@ -569,7 +572,7 @@ public class Snark /** * Aborts program abnormally. */ - public static void fatal(String s) + public void fatal(String s) { fatal(s, null); } @@ -577,12 +580,13 @@ public class Snark /** * Aborts program abnormally. */ - public static void fatal(String s, Throwable t) + public void fatal(String s, Throwable t) { I2PSnarkUtil.instance().debug(s, ERROR, t); //System.err.println("snark: " + s + ((t == null) ? "" : (": " + t))); //if (debug >= INFO && t != null) // t.printStackTrace(); + stopTorrent(); throw new RuntimeException("die bart die"); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 8a2c78793..2661d1fa2 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -42,7 +42,7 @@ public class SnarkManager implements Snark.CompleteListener { monitor.start(); } - private static final int MAX_MESSAGES = 10; + private static final int MAX_MESSAGES = 5; public void addMessage(String message) { synchronized (_messages) { _messages.add(message); @@ -243,6 +243,9 @@ public class SnarkManager implements Snark.CompleteListener { public Properties getConfig() { return _config; } + /** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */ + private static final int MAX_FILES_PER_TORRENT = 128; + /** set of filenames that we are dealing with */ public Set listTorrentFiles() { synchronized (_snarks) { return new HashSet(_snarks.keySet()); } } /** @@ -250,6 +253,14 @@ public class SnarkManager implements Snark.CompleteListener { */ public Snark getTorrent(String filename) { synchronized (_snarks) { return (Snark)_snarks.get(filename); } } public void addTorrent(String filename) { + if (!I2PSnarkUtil.instance().connected()) { + addMessage("Connecting to I2P"); + boolean ok = I2PSnarkUtil.instance().connect(); + if (!ok) { + addMessage("Error connecting to I2P - check your I2CP settings"); + return; + } + } File sfile = new File(filename); try { filename = sfile.getCanonicalPath(); @@ -263,9 +274,31 @@ public class SnarkManager implements Snark.CompleteListener { synchronized (_snarks) { torrent = (Snark)_snarks.get(filename); if (torrent == null) { - torrent = new Snark(filename, null, -1, null, null, false, dataDir.getPath()); - torrent.completeListener = this; - _snarks.put(filename, torrent); + FileInputStream fis = null; + try { + fis = new FileInputStream(sfile); + MetaInfo info = new MetaInfo(fis); + fis.close(); + fis = null; + + List files = info.getFiles(); + if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) { + sfile.delete(); + addMessage("Too many files in " + sfile.getName() + " (" + files.size() + "), deleting it"); + return; + } else { + torrent = new Snark(filename, null, -1, null, null, false, dataDir.getPath()); + torrent.completeListener = this; + _snarks.put(filename, torrent); + } + } catch (IOException ioe) { + addMessage("Torrent in " + sfile.getName() + " is invalid: " + ioe.getMessage()); + if (sfile.exists()) + sfile.delete(); + return; + } finally { + if (fis != null) try { fis.close(); } catch (IOException ioe) {} + } } else { return; } @@ -330,6 +363,12 @@ public class SnarkManager implements Snark.CompleteListener { private class DirMonitor implements Runnable { public void run() { try { Thread.sleep(60*1000*getStartupDelayMinutes()); } catch (InterruptedException ie) {} + // the first message was a "We are starting up in 1m" + synchronized (_messages) { + if (_messages.size() == 1) + _messages.remove(0); + } + while (true) { File dir = getDataDir(); _log.debug("Directory Monitor loop over " + dir.getAbsolutePath()); diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java index 2b58628bb..fe5bd32ad 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkShutdown.java @@ -72,7 +72,8 @@ public class SnarkShutdown extends Thread } catch(IOException ioe) { - Snark.fatal("Couldn't properly close storage", ioe); + I2PSnarkUtil.instance().debug("Couldn't properly close storage", Snark.ERROR, ioe); + throw new RuntimeException("b0rking"); } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java index 09f702175..7dc1fa1c8 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java @@ -404,10 +404,15 @@ public class Storage { for (int i = 0; i < rafs.length; i++) { - synchronized(rafs[i]) - { - rafs[i].close(); - } + try { + synchronized(rafs[i]) + { + rafs[i].close(); + } + } catch (IOException ioe) { + I2PSnarkUtil.instance().debug("Error closing " + rafs[i], Snark.ERROR, ioe); + // gobble gobble + } } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index df2e24377..7cd0e7406 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -254,8 +254,9 @@ public class TrackerClient extends Thread throw new IOException("Error fetching " + s); } + InputStream in = null; try { - InputStream in = new FileInputStream(fetched); + in = new FileInputStream(fetched); TrackerInfo info = new TrackerInfo(in, coordinator.getID(), coordinator.getMetaInfo()); @@ -270,6 +271,7 @@ public class TrackerClient extends Thread interval = info.getInterval() * 1000; return info; } finally { + if (in != null) try { in.close(); } catch (IOException ioe) {} fetched.delete(); } } 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 b6255bedc..4594ac9de 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -53,14 +53,18 @@ public class I2PSnarkServlet extends HttpServlet { // we want it to go to the base URI so we don't refresh with some funky action= value out.write("\n"); out.write(HEADER); - out.write("Refresh\n"); - out.write("\n"); + out.write("\n"); out.write(TABLE_HEADER); @@ -302,6 +306,7 @@ public class I2PSnarkServlet extends HttpServlet { boolean isRunning = !snark.stopped; boolean isValid = snark.meta != null; + boolean singleFile = snark.meta.getFiles() == null; String err = snark.coordinator.trackerProblems; int curPeers = snark.coordinator.getPeerCount(); @@ -330,7 +335,15 @@ public class I2PSnarkServlet extends HttpServlet { out.write(""); out.write(statusString + "\n\t"); out.write(""); - out.write(filename + "\n\t"); + + if (remaining == 0) + out.write(""); + out.write(filename); + if (remaining == 0) + out.write(""); + out.write("\n\t"); + out.write(""); if (remaining > 0) { out.write(formatSize(total-remaining) + "/" + formatSize(total)); // 18MB/3GB @@ -380,7 +393,8 @@ public class I2PSnarkServlet extends HttpServlet { // not supporting from file at the moment, since the file name passed isn't always absolute (so it may not resolve) //out.write("From file:
\n"); out.write("
\n"); - out.write("Alternately, you can copy .torrent files to " + _manager.getDataDir().getAbsolutePath() + "
\n"); + out.write("Alternately, you can copy .torrent files to " + _manager.getDataDir().getAbsolutePath() + "
\n"); + out.write("Removing that .torrent file will cause the torrent to stop.
\n"); out.write("\n\n"); } @@ -466,12 +480,22 @@ public class I2PSnarkServlet extends HttpServlet { " font-size: 16pt;\n" + " font-weight: bold;\n" + "}\n" + + ".snarkRefresh {\n" + + " font-size: 10pt;\n" + + "}\n" + ".snarkMessages {\n" + " border: none;\n" + " background-color: #CECFC6;\n" + " font-family: monospace;\n" + " font-size: 10pt;\n" + " font-weight: 100;\n" + + " width: 100%;\n" + + " text-align: left;\n" + + " margin: 0px 0px 0px 0px;\n" + + " border: 0px;\n" + + " padding: 5px;\n" + + " border-width: 0px;\n" + + " border-spacing: 0px;\n" + "}\n" + "table {\n" + " margin: 0px 0px 0px 0px;\n" + @@ -495,14 +519,16 @@ public class I2PSnarkServlet extends HttpServlet { " font-family: monospace;\n" + " background-color: #ADAE9;\n" + "}\n" + + ".snarkAddInfo {\n" + + " font-size: 10pt;\n" + + "}\n" + ".snarkConfigTitle {\n" + " font-size: 16pt;\n" + " font-weight: bold;\n" + "}\n" + "\n" + "\n" + - "\n" + - "

I2PSnark 

\n"; + "\n"; private static final String TABLE_HEADER = "\n" + diff --git a/history.txt b/history.txt index e1d33d12b..e6408c8cf 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,9 @@ -$Id: history.txt,v 1.356 2005/12/15 03:58:31 jrandom Exp $ +$Id: history.txt,v 1.357 2005/12/15 22:00:48 jrandom Exp $ + +2005-12-16 jrandom + * Refuse torrents with too many files (128), avoiding ulimit errors. + * Remove an fd leak in I2PSnark + * Further I2PSnark web UI cleanup 2005-12-15 jrandom * Added a first pass to the I2PSnark web UI (see /i2psnark/)