Author: toad
Date: 2005-12-07 21:55:27 +0000 (Wed, 07 Dec 2005)
New Revision: 7687

Added:
   trunk/freenet/src/freenet/node/TestnetHandler.java
Modified:
   trunk/freenet/src/freenet/io/comm/PeerParseException.java
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/PeerNode.java
   trunk/freenet/src/freenet/node/RealNodePingTest.java
   trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
   trunk/freenet/src/freenet/node/RealNodeRoutingTest.java
   trunk/freenet/src/freenet/node/TextModeClientInterface.java
   trunk/freenet/src/freenet/node/Version.java
   trunk/freenet/src/freenet/support/FileLoggerHook.java
   trunk/freenet/src/freenet/support/Logger.java
Log:
286:
Basic testnet support.
Limit compressed log files to 1GB.
Minor bugfixes.
Note that this will wipe your peers file, as testnet nodes and non-testnet 
nodes are strictly incompatible.

Modified: trunk/freenet/src/freenet/io/comm/PeerParseException.java
===================================================================
--- trunk/freenet/src/freenet/io/comm/PeerParseException.java   2005-12-07 
19:03:04 UTC (rev 7686)
+++ trunk/freenet/src/freenet/io/comm/PeerParseException.java   2005-12-07 
21:55:27 UTC (rev 7687)
@@ -14,4 +14,8 @@
         super();
     }

+       public PeerParseException(String string) {
+               super(string);
+       }
+
 }

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2005-12-07 19:03:04 UTC (rev 
7686)
+++ trunk/freenet/src/freenet/node/Node.java    2005-12-07 21:55:27 UTC (rev 
7687)
@@ -145,6 +145,9 @@
     final NodePinger nodePinger;
     final String filenamesPrefix;
     final FilenameGenerator tempFilenameGenerator;
+    final FileLoggerHook fileLoggerHook;
+    final boolean testnetEnabled;
+    final int testnetPort;
     static short MAX_HTL = 10;
     static final int EXIT_STORE_FILE_NOT_FOUND = 1;
     static final int EXIT_STORE_IOEXCEPTION = 2;
@@ -152,6 +155,7 @@
     static final int EXIT_USM_DIED = 4;
     public static final int EXIT_YARROW_INIT_FAILED = 5;
     static final int EXIT_TEMP_INIT_ERROR = 6;
+    static final int EXIT_TESTNET_FAILED = 7;

     public final long bootID;
     public final long startupTime;
@@ -164,6 +168,7 @@
     final RequestThrottle insertThrottle;
     final RequestStarter insertStarter;
     final File downloadDir;
+    final TestnetHandler testnetHandler;

     // Client stuff that needs to be configged - FIXME
     static final int MAX_ARCHIVE_HANDLERS = 200; // don't take up much RAM... 
FIXME
@@ -281,7 +286,9 @@
         System.out.println("Port number: "+port);
         File logDir = new File("logs-"+port);
         logDir.mkdir();
-        FileLoggerHook logger = new FileLoggerHook(true, new File(logDir, 
"freenet-"+port).getAbsolutePath(), "d (c, t, p): m", "MMM dd, yyyy 
HH:mm:ss:SSS", Logger.MINOR, false, true);
+        FileLoggerHook logger = new FileLoggerHook(true, new File(logDir, 
"freenet-"+port).getAbsolutePath(), 
+                       "d (c, t, p): m", "MMM dd, yyyy HH:mm:ss:SSS", 
Logger.MINOR, false, true, 
+                       1024*1024*1024 /* 1GB of old compressed logfiles */);
         logger.setInterval("5MINUTES");
         Logger.setupChain();
         Logger.globalSetThreshold(Logger.MINOR);
@@ -299,7 +306,7 @@
             }
         }
         DiffieHellman.init(yarrow);
-        Node n = new Node(port, yarrow, overrideIP, "", 1000 / 
packetsPerSecond);
+        Node n = new Node(port, yarrow, overrideIP, "", 1000 / 
packetsPerSecond, true, logger);
         n.start(new StaticSwapRequestInterval(2000));
         new TextModeClientInterface(n);
         Thread t = new Thread(new MemoryChecker(), "Memory checker");
@@ -309,7 +316,18 @@

     // FIXME - the whole overrideIP thing is a hack to avoid config
     // Implement the config!
-    Node(int port, RandomSource rand, InetAddress overrideIP, String prefix, 
int throttleInterval) {
+    Node(int port, RandomSource rand, InetAddress overrideIP, String prefix, 
int throttleInterval, boolean enableTestnet, FileLoggerHook logger) {
+       this.fileLoggerHook = logger;
+       if(enableTestnet) {
+               Logger.error(this, "WARNING: ENABLING TESTNET CODE! This may 
seriously jeopardize your anonymity!");
+               testnetEnabled = true;
+               testnetPort = 1024 + (port-1024+1000) % (65536 - 1024);
+               testnetHandler = new TestnetHandler(this, testnetPort);
+       } else {
+               testnetEnabled = false;
+               testnetPort = -1;
+               testnetHandler = null;
+       }
         portNumber = port;
         startupTime = System.currentTimeMillis();
         recentlyCompletedIDs = new LRUQueue();
@@ -318,6 +336,7 @@
         filenamesPrefix = prefix;
         this.overrideIPAddress = overrideIP;
         downloadDir = new File("downloads");
+        downloadDir.mkdir();
         try {
             datastore = new 
BaseFreenetStore(prefix+"freenet-"+portNumber,16384); // 512MB
         } catch (FileNotFoundException e1) {
@@ -647,6 +666,9 @@
         fs.put("identity", HexUtil.bytesToHex(myIdentity));
         fs.put("location", Double.toString(lm.getLocation().getValue()));
         fs.put("version", Version.getVersionString());
+        fs.put("testnet", Boolean.toString(testnetEnabled));
+        if(testnetEnabled)
+               fs.put("testnetPort", Integer.toString(testnetPort));
         fs.put("myName", myName);
         Logger.minor(this, "My reference: "+fs);
         return fs;

Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java        2005-12-07 19:03:04 UTC 
(rev 7686)
+++ trunk/freenet/src/freenet/node/PeerNode.java        2005-12-07 21:55:27 UTC 
(rev 7687)
@@ -54,6 +54,9 @@
     /** My low-level address for SocketManager purposes */
     private Peer peer;

+    /** Is this a testnet node? */
+    public final boolean testnetEnabled;
+    
     /** Name of this node */
     String myName;

@@ -194,6 +197,12 @@
         String name = fs.get("myName");
         if(name == null) throw new FSParseException("No name");
         myName = name;
+        String testnet = fs.get("testnet");
+        testnetEnabled = Boolean.getBoolean(testnet);
+        if(testnetEnabled != node.testnetEnabled) {
+               Logger.error(this, "Ignoring incompatible node "+peer+" - 
peer.testnet="+testnetEnabled+" but node.testnet="+node.testnetEnabled);
+               throw new PeerParseException("Ignoring incompatible node 
"+peer+" - peer.testnet="+testnetEnabled+" but 
node.testnet="+node.testnetEnabled);
+        }

         // Setup incoming and outgoing setup ciphers
         byte[] nodeKey = node.identityHash;

Modified: trunk/freenet/src/freenet/node/RealNodePingTest.java
===================================================================
--- trunk/freenet/src/freenet/node/RealNodePingTest.java        2005-12-07 
19:03:04 UTC (rev 7686)
+++ trunk/freenet/src/freenet/node/RealNodePingTest.java        2005-12-07 
21:55:27 UTC (rev 7687)
@@ -4,6 +4,7 @@
 import freenet.crypt.Yarrow;
 import freenet.io.comm.NotConnectedException;
 import freenet.io.comm.PeerParseException;
+import freenet.support.FileLoggerHook;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;

@@ -20,12 +21,12 @@
 public class RealNodePingTest {

     public static void main(String[] args) throws FSParseException, 
PeerParseException, InterruptedException {
-        Logger.setupStdoutLogging(Logger.MINOR, "");
+        FileLoggerHook fh = Logger.setupStdoutLogging(Logger.MINOR, "");
         Yarrow yarrow = new Yarrow();
         DiffieHellman.init(yarrow);
         // Create 2 nodes
-        Node node1 = new Node(5001, yarrow, null, "pingtest-", 0);
-        Node node2 = new Node(5002, yarrow, null, "pingtest-", 0);
+        Node node1 = new Node(5001, yarrow, null, "pingtest-", 0, false, fh);
+        Node node2 = new Node(5002, yarrow, null, "pingtest-", 0, false, fh);
         SimpleFieldSet node1ref = node1.exportFieldSet();
         SimpleFieldSet node2ref = node2.exportFieldSet();
         // Connect

Modified: trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java
===================================================================
--- trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java       
2005-12-07 19:03:04 UTC (rev 7686)
+++ trunk/freenet/src/freenet/node/RealNodeRequestInsertTest.java       
2005-12-07 21:55:27 UTC (rev 7687)
@@ -12,6 +12,7 @@
 import freenet.keys.ClientKey;
 import freenet.node.PeerNode;
 import freenet.support.Fields;
+import freenet.support.FileLoggerHook;
 import freenet.support.HexUtil;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
@@ -32,7 +33,7 @@
         new File(wd).mkdir();
         // Don't clobber nearby nodes!
         Node.MAX_HTL = 5;
-        Logger.setupStdoutLogging(Logger.DEBUG, 
"freenet.store:minor,freenet.node.Location:normal" 
/*"freenet.node.LocationManager:debug,freenet.node.FNPPacketManager:normal,freenet.io.comm.UdpSocketManager:debug"*/);
+        FileLoggerHook fh = Logger.setupStdoutLogging(Logger.DEBUG, 
"freenet.store:minor,freenet.node.Location:normal" 
/*"freenet.node.LocationManager:debug,freenet.node.FNPPacketManager:normal,freenet.io.comm.UdpSocketManager:debug"*/);
         Logger.globalSetThreshold(Logger.DEBUG);
         System.out.println("Insert/retrieve test");
         System.out.println();
@@ -41,7 +42,7 @@
         Node[] nodes = new Node[NUMBER_OF_NODES];
         Logger.normal(RealNodeRoutingTest.class, "Creating nodes...");
         for(int i=0;i<NUMBER_OF_NODES;i++) {
-            nodes[i] = new Node(5000+i, random, null, wd+File.separator, 0);
+            nodes[i] = new Node(5000+i, random, null, wd+File.separator, 0, 
false, fh);
             nodes[i].usm.setDropProbability(20); // 5%
             Logger.normal(RealNodeRoutingTest.class, "Created node "+i);
         }

Modified: trunk/freenet/src/freenet/node/RealNodeRoutingTest.java
===================================================================
--- trunk/freenet/src/freenet/node/RealNodeRoutingTest.java     2005-12-07 
19:03:04 UTC (rev 7686)
+++ trunk/freenet/src/freenet/node/RealNodeRoutingTest.java     2005-12-07 
21:55:27 UTC (rev 7687)
@@ -5,6 +5,7 @@
 import freenet.crypt.DiffieHellman;
 import freenet.crypt.DummyRandomSource;
 import freenet.io.comm.PeerParseException;
+import freenet.support.FileLoggerHook;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
 import freenet.support.math.BootstrappingDecayingRunningAverage;
@@ -25,7 +26,7 @@
     public static void main(String[] args) throws FSParseException, 
PeerParseException {
         PeerNode.disableProbabilisticHTLs = true;
         Node.MAX_HTL = 6;
-        Logger.setupStdoutLogging(Logger.NORMAL, 
"freenet.node.CPUAdjustingSwapRequestInterval:minor" 
/*"freenet.node.LocationManager:debug,freenet.node.FNPPacketManager:normal,freenet.io.comm.UdpSocketManager:debug"*/);
+        FileLoggerHook fh = Logger.setupStdoutLogging(Logger.NORMAL, 
"freenet.node.CPUAdjustingSwapRequestInterval:minor" 
/*"freenet.node.LocationManager:debug,freenet.node.FNPPacketManager:normal,freenet.io.comm.UdpSocketManager:debug"*/);
         System.out.println("Routing test using real nodes:");
         System.out.println();
         String wd = "realNodeRequestInsertTest";
@@ -35,7 +36,7 @@
         Node[] nodes = new Node[NUMBER_OF_NODES];
         Logger.normal(RealNodeRoutingTest.class, "Creating nodes...");
         for(int i=0;i<NUMBER_OF_NODES;i++) {
-            nodes[i] = new Node(5000+i, random, null, wd+File.separator, 0);
+            nodes[i] = new Node(5000+i, random, null, wd+File.separator, 0, 
false, fh);
             Logger.normal(RealNodeRoutingTest.class, "Created node "+i);
         }
         SimpleFieldSet refs[] = new SimpleFieldSet[NUMBER_OF_NODES];

Added: trunk/freenet/src/freenet/node/TestnetHandler.java
===================================================================
--- trunk/freenet/src/freenet/node/TestnetHandler.java  2005-12-07 19:03:04 UTC 
(rev 7686)
+++ trunk/freenet/src/freenet/node/TestnetHandler.java  2005-12-07 21:55:27 UTC 
(rev 7687)
@@ -0,0 +1,130 @@
+package freenet.node;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import freenet.support.Logger;
+
+/**
+ * Testnet handler.
+ * This is a simple server used for debugging. It allows remote developers
+ * to access logfiles stored on this node, and may in future give more options
+ * such as triggering an auto-update.
+ * 
+ * NOTE THAT IF THIS IS ENABLED, YOU HAVE NO ANONYMITY! It may also be possible
+ * to exploit this for denial of service, as it is not authenticated in any 
way.
+ * 
+ * Currently provides two simple commands:
+ * LIST\n - list all currently available logfiles, with size etc.
+ * GET <date> - get the log file containing the given date.
+ * No headers are sent, so you can simply capture netcat's output.
+ * The idea is that this should be as simple as possible...
+ */
+public class TestnetHandler implements Runnable {
+
+       public TestnetHandler(Node node2, int testnetPort) {
+               this.node = node2;
+               this.testnetPort = testnetPort;
+               Logger.error(this, "STARTING TESTNET SERVER!");
+               Logger.error(this, "ANONYMITY MODE: OFF");
+               System.err.println("STARTING TESTNET SERVER!");
+               System.err.println("ANONYMITY MODE: OFF");
+               System.err.println("You have no anonymity. Thank you for 
running a testnet node, this will help the developers to efficiently debug 
Freenet.");
+               System.err.println("We repeat: YOU HAVE NO ANONYMITY 
WHATSOEVER. DO NOT POST ANYTHING YOU DO NOT WANT TO BE ASSOCIATED WITH.");
+               System.err.println("If you want a real freenet node, with 
anonymity, turn off testnet mode.");
+               System.err.println("Note, this node will not connect to 
non-testnet nodes, for security reasons. You can of course run a testnet node 
and a non-testnet node.");
+               serverThread = new Thread(this, "Testnet handler thread");
+               serverThread.setDaemon(true);
+               serverThread.start();
+       }
+       
+       private final Node node;
+       private final Thread serverThread;
+       private final int testnetPort;
+       
+       public void run() {
+               // Set up server socket
+               ServerSocket server;
+               try {
+                       server = new ServerSocket(testnetPort);
+               } catch (IOException e) {
+                       Logger.error(this, "Could not bind to testnet port: 
"+testnetPort);
+                       System.err.println("Could not bind to testnet port: 
"+testnetPort);
+                       System.exit(Node.EXIT_TESTNET_FAILED);
+                       return;
+               }
+               while(true) {
+                       try {
+                               Socket s = server.accept();
+                               TestnetSocketHandler tsh = new 
TestnetSocketHandler(s);
+                       } catch (IOException e) {
+                               Logger.error(this, "Testnet failed to accept 
socket: "+e, e);
+                       }
+                       
+               }
+       }
+       
+       public class TestnetSocketHandler implements Runnable {
+
+               private Socket s;
+               
+               public TestnetSocketHandler(Socket s2) {
+                       this.s = s2;
+               }
+
+               public void run() {
+                       InputStream is = null;
+                       OutputStream os = null;
+                       try {
+                               is = s.getInputStream();
+                               os = s.getOutputStream();
+                               // Read command
+                               InputStreamReader isr = new 
InputStreamReader(is, "ISO-8859-1");
+                               BufferedReader br = new BufferedReader(isr);
+                               String command = br.readLine();
+                               if(command.equalsIgnoreCase("LIST")) {
+                                       
node.fileLoggerHook.listAvailableLogs(new OutputStreamWriter(os, "ISO-8859-1"));
+                               } else if(command.startsWith("GET:")) {
+                                       String date = 
command.substring("GET:".length());
+                                       DateFormat df = 
DateFormat.getDateTimeInstance();
+                                       
df.setTimeZone(TimeZone.getTimeZone("GMT"));
+                                       Date d;
+                                       try {
+                                               d = df.parse(date);
+                                       } catch (ParseException e) {
+                                               return;
+                                       }
+                                       
node.fileLoggerHook.sendLogByContainedDate(d.getTime(), os);
+                               }
+                       } catch (IOException e) {
+                               Logger.normal(this, "Failure handling testnet 
connection: "+e);
+                       } finally {
+                               if(is != null)
+                                       try {
+                                               is.close();
+                                       } catch (IOException e) {
+                                               // Ignore
+                                       }
+                               if(os != null)
+                                       try {
+                                               os.close();
+                                       } catch (IOException e) {
+                                               // Ignore
+                                       }
+                       }
+               }
+
+       }
+
+}

Modified: trunk/freenet/src/freenet/node/TextModeClientInterface.java
===================================================================
--- trunk/freenet/src/freenet/node/TextModeClientInterface.java 2005-12-07 
19:03:04 UTC (rev 7686)
+++ trunk/freenet/src/freenet/node/TextModeClientInterface.java 2005-12-07 
21:55:27 UTC (rev 7687)
@@ -102,7 +102,10 @@
 //        System.out.println("SAY:<text> - send text to the last 
created/pushed stream");
         System.out.println("STATUS - display some status information on the 
node including its reference and connections.");
         System.out.println("QUIT - exit the program");
-       }
+        if(n.testnetEnabled) {
+               System.out.println("WARNING: TESTNET MODE ENABLED. YOU HAVE NO 
ANONYMITY.");
+        }
+    }

        /**
      * Process a single command.

Modified: trunk/freenet/src/freenet/node/Version.java
===================================================================
--- trunk/freenet/src/freenet/node/Version.java 2005-12-07 19:03:04 UTC (rev 
7686)
+++ trunk/freenet/src/freenet/node/Version.java 2005-12-07 21:55:27 UTC (rev 
7687)
@@ -20,10 +20,10 @@
        public static final String protocolVersion = "1.0";

        /** The build number of the current revision */
-       public static final int buildNumber = 285;
+       public static final int buildNumber = 286;

        /** Oldest build of Fred we will talk to */
-       public static final int lastGoodBuild = 285;
+       public static final int lastGoodBuild = 286;

        /** The highest reported build of fred */
        public static int highestSeenBuild = buildNumber;

Modified: trunk/freenet/src/freenet/support/FileLoggerHook.java
===================================================================
--- trunk/freenet/src/freenet/support/FileLoggerHook.java       2005-12-07 
19:03:04 UTC (rev 7686)
+++ trunk/freenet/src/freenet/support/FileLoggerHook.java       2005-12-07 
21:55:27 UTC (rev 7687)
@@ -2,10 +2,14 @@

 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.net.InetAddress;
@@ -14,12 +18,15 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.GregorianCalendar;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.StringTokenizer;
 import java.util.TimeZone;
 import java.util.Vector;
 import java.util.zip.GZIPOutputStream;

+import freenet.node.Version;
+
 /**
  * Converted the old StandardLogger to Ian's loggerhook interface.
  * 
@@ -82,13 +89,30 @@
         * Something wierd happens when the disk gets full, also we don't want 
to
         * block So run the actual write on another thread
         */
-       protected LinkedList list = new LinkedList();
+       protected final LinkedList list = new LinkedList();
        protected long listBytes = 0;

        protected int MAX_LIST_SIZE = 100000;
        protected long MAX_LIST_BYTES = 10 * (1 << 20);
        // FIXME: should reimplement LinkedList with minimal locking

+       final long maxOldLogfilesDiskUsage;
+       protected final LinkedList logFiles = new LinkedList();
+       private long oldLogFilesDiskSpaceUsage = 0;
+
+       class OldLogFile {
+               public OldLogFile(File currentFilename, long startTime, long 
endTime, long length) {
+                       this.filename = currentFilename;
+                       this.start = startTime;
+                       this.end = endTime;
+                       this.size = length;
+               }
+               final File filename;
+               final long start; // inclusive
+               final long end; // exclusive
+               final long size;
+       }
+       
        public void setMaxListLength(int len) {
                MAX_LIST_SIZE = len;
        }
@@ -134,6 +158,7 @@
        protected String getHourLogName(Calendar c, boolean compressed) {
                StringBuffer buf = new StringBuffer(50);
                buf.append(baseFilename).append('-');
+               buf.append(Version.buildNumber);
                buf.append(c.get(Calendar.YEAR)).append('-');
                pad2digits(buf, c.get(Calendar.MONTH) + 1);
                buf.append('-');
@@ -164,8 +189,11 @@
                }

                public void run() {
+                       findOldLogFiles();
+                       File currentFilename = null;
                        Object o = null;
                        long thisTime = System.currentTimeMillis();
+                       long startTime = -1;
                        long nextHour = -1;
                        GregorianCalendar gc = null;
                        String filename = null;
@@ -194,11 +222,13 @@
                                        gc.set(INTERVAL, (x / 
INTERVAL_MULTIPLIER) * INTERVAL_MULTIPLIER);
                                }
                                filename = getHourLogName(gc, true);
-                               logStream = openNewLogFile(new File(filename), 
true);
+                               currentFilename = new File(filename);
+                               logStream = openNewLogFile(currentFilename, 
true);
                                if(latestFilename != null) {
                                        altLogStream = 
openNewLogFile(latestFilename, false);
                                }
                                System.err.println("Created log files");
+                               startTime = gc.getTimeInMillis();
                                gc.add(INTERVAL, INTERVAL_MULTIPLIER);
                                nextHour = gc.getTimeInMillis();
                        }
@@ -216,14 +246,21 @@
                                                                        
"Flushing on change caught " + e);
                                                        }
                                                        String oldFilename = 
filename;
-                                                       // Rotate primary log 
stream
-                                                       filename = 
getHourLogName(gc, true);
+                                                       long length = 
currentFilename.length();
+                                                       OldLogFile olf = new 
OldLogFile(currentFilename, startTime, thisTime, length);
+                                                       synchronized(logFiles) {
+                                                               
logFiles.addLast(olf);
+                                                       }
+                                                       
oldLogFilesDiskSpaceUsage += length;
+                                                       trimOldLogFiles();
                                                        try {
                                                                
logStream.close();
                                                        } catch (IOException e) 
{
                                                                
System.err.println(
                                                                                
"Closing on change caught " + e);
                                                        }
+                                                       // Rotate primary log 
stream
+                                                       filename = 
getHourLogName(gc, true);
                                                        logStream = 
openNewLogFile(new File(filename), true);
                                                        if(latestFilename != 
null) {
                                                                try {
@@ -370,7 +407,8 @@
                String dfmt,
                int threshold,
                boolean assumeWorking,
-               boolean logOverwrite)
+               boolean logOverwrite,
+               long maxOldLogfilesDiskUsage)
                throws IOException {
                this(
                        false,
@@ -379,23 +417,127 @@
                        dfmt,
                        threshold,
                        assumeWorking,
-                       logOverwrite);
+                       logOverwrite,
+                       maxOldLogfilesDiskUsage);
        }

+       public void trimOldLogFiles() {
+               while(oldLogFilesDiskSpaceUsage > maxOldLogfilesDiskUsage) {
+                       OldLogFile olf;
+                       synchronized(logFiles) {
+                               olf = (OldLogFile) logFiles.removeFirst();
+                       }
+                       olf.filename.delete();
+                       oldLogFilesDiskSpaceUsage -= olf.size;
+                       Logger.minor(this, "Deleting "+olf.filename+" - saving 
"+olf.size+
+                                       " bytes, disk usage now: 
"+oldLogFilesDiskSpaceUsage+" of "+maxOldLogfilesDiskUsage);
+               }
+       }
+
+       /** Initialize oldLogFiles */
+       public void findOldLogFiles() {
+               int slashIndex = baseFilename.lastIndexOf(File.separatorChar);
+               File dir;
+               String prefix;
+               if(slashIndex == -1) {
+                       dir = new File(System.getProperty("user.dir"));
+                       prefix = baseFilename.toLowerCase();
+               } else {
+                       dir = new File(baseFilename.substring(0, slashIndex));
+                       prefix = 
baseFilename.substring(slashIndex+1).toLowerCase();
+               }
+               File[] files = dir.listFiles();
+               java.util.Arrays.sort(files);
+               long lastStartTime = -1;
+               File oldFile = null;
+               for(int i=0;i<files.length;i++) {
+                       File f = files[i];
+                       String name = f.getName();
+                       if(name.toLowerCase().startsWith(prefix)) {
+                               if(name.equals(previousFilename)) {
+                                       f.delete();
+                                       continue;
+                               } else if(name.equals(latestFilename)) {
+                                       f.renameTo(previousFilename);
+                                       continue;
+                               }
+                               if(!name.endsWith(".log.gz")) {
+                                       f.delete();
+                                       continue;
+                               } else {
+                                       name = name.substring(0, 
name.length()-".log.gz".length());
+                               }
+                               String[] tokens = name.split("-");
+                               int[] nums = new int[tokens.length];
+                               for(int j=0;j<tokens.length;j++) {
+                                       try {
+                                               nums[j] = 
Integer.parseInt(tokens[j]);
+                                       } catch (NumberFormatException e) {
+                                               // Broken
+                                               f.delete();
+                                               continue;
+                                       }
+                               }
+                               // First field: version
+                               if(nums.length < 1) {
+                                       f.delete();
+                                       continue;
+                               }
+                               if(nums[0] != Version.buildNumber) {
+                                       // Logs that old are useless
+                                       f.delete();
+                                       continue;
+                               }
+                               GregorianCalendar gc = new GregorianCalendar();
+                               if(nums.length > 1)
+                                       gc.set(Calendar.YEAR, nums[1]);
+                               if(nums.length > 2)
+                                       gc.set(Calendar.MONTH, nums[2]);
+                               if(nums.length > 3)
+                                       gc.set(Calendar.DAY_OF_MONTH, nums[3]);
+                               if(nums.length > 4)
+                                       gc.set(Calendar.HOUR_OF_DAY, nums[4]);
+                               if(nums.length > 5)
+                                       gc.set(Calendar.MINUTE, nums[5]);
+                               long startTime = gc.getTimeInMillis();
+                               if(oldFile != null) {
+                                       long l = oldFile.length();
+                                       OldLogFile olf = new 
OldLogFile(oldFile, lastStartTime, startTime, l);
+                                       logFiles.addLast(olf);
+                                       oldLogFilesDiskSpaceUsage += l;
+                               }
+                               lastStartTime = -1;
+                               oldFile = f;
+                       } else {
+                               // Nothing to do with us
+                               Logger.normal(this, "Unknown file: "+name+" in 
our log directory");
+                       }
+               }
+               if(oldFile != null) {
+                       long l = oldFile.length();
+                       OldLogFile olf = new OldLogFile(oldFile, lastStartTime, 
System.currentTimeMillis(), l);
+                       logFiles.addLast(olf);
+                       oldLogFilesDiskSpaceUsage += l;
+               }
+               trimOldLogFiles();
+       }
+
        public FileLoggerHook(
                        String filename,
                        String fmt,
                        String dfmt,
                        String threshold,
                        boolean assumeWorking,
-                       boolean logOverwrite)
+                       boolean logOverwrite,
+                       long maxOldLogFilesDiskUsage)
                        throws IOException {
                        this(filename,
                                fmt,
                                dfmt,
                                priorityOf(threshold),
                                assumeWorking,
-                               logOverwrite);
+                               logOverwrite,
+                               maxOldLogFilesDiskUsage);
                }

        private void checkStdStreams() {
@@ -447,7 +589,7 @@
                String dfmt,
                int threshold,
                boolean overwrite) {
-               this(fmt, dfmt, threshold, overwrite);
+               this(fmt, dfmt, threshold, overwrite, -1);
                logStream = stream;
        }

@@ -470,9 +612,10 @@
                String dfmt,
                int threshold,
                boolean assumeWorking,
-               boolean logOverwrite)
+               boolean logOverwrite,
+               long maxOldLogfilesDiskUsage)
                throws IOException {
-               this(fmt, dfmt, threshold, logOverwrite);
+               this(fmt, dfmt, threshold, logOverwrite, 
maxOldLogfilesDiskUsage);
                //System.err.println("Creating FileLoggerHook with threshold
                // "+threshold);
                if (!assumeWorking)
@@ -491,12 +634,14 @@
                        String dfmt,
                        String threshold,
                        boolean assumeWorking,
-                       boolean logOverwrite) throws IOException{
-               
this(rotate,baseFilename,fmt,dfmt,priorityOf(threshold),assumeWorking,logOverwrite);
+                       boolean logOverwrite,
+                       long maxOldLogFilesDiskUsage) throws IOException{
+               
this(rotate,baseFilename,fmt,dfmt,priorityOf(threshold),assumeWorking,logOverwrite,maxOldLogFilesDiskUsage);
        }

-       private FileLoggerHook(String fmt, String dfmt, int threshold, boolean 
overwrite) {
+       private FileLoggerHook(String fmt, String dfmt, int threshold, boolean 
overwrite, long maxOldLogfilesDiskUsage) {
                super(threshold);
+               this.maxOldLogfilesDiskUsage = maxOldLogfilesDiskUsage;
                this.logOverwrite = overwrite;
                if (dfmt != null && dfmt.length() != 0) {
                        try {
@@ -686,4 +831,53 @@
                        closed = true;
                }
        }
+
+       /**
+        * Print a human- and script- readable list of available log files.
+        * @throws IOException 
+        */
+       public void listAvailableLogs(OutputStreamWriter writer) throws 
IOException {
+               OldLogFile[] oldLogFiles;
+               synchronized(logFiles) {
+                       oldLogFiles = (OldLogFile[]) logFiles.toArray(new 
OldLogFile[logFiles.size()]);
+               }
+               DateFormat df = DateFormat.getDateTimeInstance();
+               df.setTimeZone(TimeZone.getTimeZone("GMT"));
+               for(int i=0;i<oldLogFiles.length;i++) {
+                       OldLogFile olf = oldLogFiles[i];
+                       writer.write(olf.filename.getName()+" : "+df.format(new 
Date(olf.start))+" to "+df.format(new Date(olf.end))+ " - "+olf.size+" bytes");
+               }
+       }
+
+       public void sendLogByContainedDate(long time, OutputStream os) throws 
IOException {
+               OldLogFile toReturn = null;
+               synchronized(logFiles) {
+                       Iterator i = logFiles.iterator();
+                       while(i.hasNext()) {
+                               OldLogFile olf = (OldLogFile) i.next();
+                               if(time >= olf.start && time < olf.end) {
+                                       toReturn = olf;
+                                       break;
+                               }
+                       }
+                       if(toReturn == null)
+                               return; // couldn't find it
+               }
+               FileInputStream fis = new FileInputStream(toReturn.filename);
+               DataInputStream dis = new DataInputStream(fis);
+               long written = 0;
+               long size = toReturn.size;
+               byte[] buf = new byte[4096];
+               while(written < size) {
+                       int toRead = (int) Math.min(buf.length, (size - 
written));
+                       try {
+                               dis.readFully(buf, 0, toRead);
+                       } catch (IOException e) {
+                               Logger.error(this, "Could not read bytes 
"+written+" to "+(written + toRead)+" from file "+toReturn.filename+" which is 
supposed to be "+size+" bytes ("+toReturn.filename.length()+")");
+                               return;
+                       }
+                       os.write(buf, 0, toRead);
+                       written += toRead;
+               }
+       }
 }

Modified: trunk/freenet/src/freenet/support/Logger.java
===================================================================
--- trunk/freenet/src/freenet/support/Logger.java       2005-12-07 19:03:04 UTC 
(rev 7686)
+++ trunk/freenet/src/freenet/support/Logger.java       2005-12-07 21:55:27 UTC 
(rev 7687)
@@ -28,7 +28,7 @@
         */
        static Logger logger = new VoidLogger();

-       public static void setupStdoutLogging(int level, String detail) {
+       public static FileLoggerHook setupStdoutLogging(int level, String 
detail) {
            setupChain();
            logger.setThreshold(level);
            logger.setDetailedThresholds(detail);
@@ -38,6 +38,7 @@
                fh.setDetailedThresholds(detail);
            ((LoggerHookChain) logger).addHook(fh);
            fh.start();
+           return fh;
        }

     public static void setupChain() {


Reply via email to