Router: Move update extraction code to new class in tasks/

This commit is contained in:
zzz
2015-04-08 12:33:16 +00:00
parent 6aa1284848
commit 4705f01bc5
3 changed files with 218 additions and 195 deletions

View File

@@ -8,15 +8,10 @@ package net.i2p.router;
* *
*/ */
import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
@@ -49,7 +44,6 @@ import net.i2p.router.util.EventLog;
import net.i2p.stat.RateStat; import net.i2p.stat.RateStat;
import net.i2p.stat.StatManager; import net.i2p.stat.StatManager;
import net.i2p.util.ByteCache; import net.i2p.util.ByteCache;
import net.i2p.util.FileUtil;
import net.i2p.util.FortunaRandomSource; import net.i2p.util.FortunaRandomSource;
import net.i2p.util.I2PAppThread; import net.i2p.util.I2PAppThread;
import net.i2p.util.I2PThread; import net.i2p.util.I2PThread;
@@ -113,6 +107,7 @@ public class Router implements RouterClock.ClockShiftListener {
private final static String DNS_CACHE_TIME = "" + (5*60); private final static String DNS_CACHE_TIME = "" + (5*60);
private static final String EVENTLOG = "eventlog.txt"; private static final String EVENTLOG = "eventlog.txt";
private static final String PROP_JBIGI = "jbigi.loadedResource"; private static final String PROP_JBIGI = "jbigi.loadedResource";
public static final String UPDATE_FILE = "i2pupdate.zip";
private static final String originalTimeZoneID; private static final String originalTimeZoneID;
static { static {
@@ -1495,199 +1490,11 @@ public class Router implements RouterClock.ClockShiftListener {
// If it does an update, it never returns. // If it does an update, it never returns.
// I guess it's better to have the other-router check above this, we don't want to // I guess it's better to have the other-router check above this, we don't want to
// overwrite an existing running router's jar files. Other than ours. // overwrite an existing running router's jar files. Other than ours.
r.installUpdates(); InstallUpdate.installUpdates(r);
// ********* Start no threads before here ********* // // ********* Start no threads before here ********* //
r.runRouter(); r.runRouter();
} }
} }
public static final String UPDATE_FILE = "i2pupdate.zip";
private static final String DELETE_FILE = "deletelist.txt";
/**
* Context must be available.
* Unzip update file found in the router dir OR base dir, to the base dir
*
* If successfull, will call exit() and never return.
*
* If we can't write to the base dir, complain.
* Note: _log not available here.
*/
private void installUpdates() {
File updateFile = new File(_context.getRouterDir(), UPDATE_FILE);
boolean exists = updateFile.exists();
if (!exists) {
updateFile = new File(_context.getBaseDir(), UPDATE_FILE);
exists = updateFile.exists();
}
if (exists) {
// do a simple permissions test, if it fails leave the file in place and don't restart
File test = new File(_context.getBaseDir(), "history.txt");
if ((test.exists() && !test.canWrite()) || (!_context.getBaseDir().canWrite())) {
System.out.println("ERROR: No write permissions on " + _context.getBaseDir() +
" to extract software update file");
// carry on
return;
}
System.out.println("INFO: Update file exists [" + UPDATE_FILE + "] - installing");
// verify the whole thing first
// we could remember this fails, and not bother restarting, but who cares...
boolean ok = FileUtil.verifyZip(updateFile);
if (ok) {
// This may be useful someday. First added in 0.8.2
// Moved above the extract so we don't NCDFE
_config.put("router.updateLastInstalled", "" + System.currentTimeMillis());
// Set the last version to the current version, since 0.8.13
_config.put("router.previousVersion", RouterVersion.VERSION);
_config.put("router.previousFullVersion", RouterVersion.FULL_VERSION);
saveConfig();
ok = FileUtil.extractZip(updateFile, _context.getBaseDir());
}
// Very important - we have now trashed our jars.
// After this point, do not use any new I2P classes, or they will fail to load
// and we will die with NCDFE.
// Ideally, do not use I2P classes at all, new or not.
try {
if (ok) {
// We do this here so we may delete old jars before we restart
deleteListedFiles();
System.out.println("INFO: Update installed");
} else {
System.out.println("ERROR: Update failed!");
}
if (!ok) {
// we can't leave the file in place or we'll continually restart, so rename it
File bad = new File(_context.getRouterDir(), "BAD-" + UPDATE_FILE);
boolean renamed = updateFile.renameTo(bad);
if (renamed) {
System.out.println("Moved update file to " + bad.getAbsolutePath());
} else {
System.out.println("Deleting file " + updateFile.getAbsolutePath());
ok = true; // so it will be deleted
}
}
if (ok) {
boolean deleted = updateFile.delete();
if (!deleted) {
System.out.println("ERROR: Unable to delete the update file!");
updateFile.deleteOnExit();
}
}
// exit whether ok or not
if (_context.hasWrapper())
System.out.println("INFO: Restarting after update");
else
System.out.println("WARNING: Exiting after update, restart I2P");
} catch (Throwable t) {
// hide the NCDFE
// hopefully the update file got deleted or we will loop
}
System.exit(EXIT_HARD_RESTART);
} else {
deleteJbigiFiles();
// It was here starting in 0.8.12 so it could be used the very first time
// Now moved up so it is usually run only after an update
// But the first time before jetty 6 it will run here...
// Here we can't remove jars
deleteListedFiles();
}
}
/**
* Remove extracted libjbigi.so and libjcpuid.so files if we have a newer jbigi.jar,
* so the new ones will be extracted.
* We do this after the restart, not after the extract, because it's safer, and
* because people may upgrade their jbigi.jar file manually.
*
* Copied from NativeBigInteger, which we can't access here or the
* libs will get loaded.
*/
private void deleteJbigiFiles() {
boolean isX86 = SystemVersion.isX86();
String osName = System.getProperty("os.name").toLowerCase(Locale.US);
boolean isWin = SystemVersion.isWindows();
boolean isMac = SystemVersion.isMac();
// only do this on these OSes
boolean goodOS = isWin || isMac ||
osName.contains("linux") || osName.contains("freebsd");
// only do this on these x86
File jbigiJar = new File(_context.getBaseDir(), "lib/jbigi.jar");
if (isX86 && goodOS && jbigiJar.exists()) {
String libPrefix = (isWin ? "" : "lib");
String libSuffix = (isWin ? ".dll" : isMac ? ".jnilib" : ".so");
File jcpuidLib = new File(_context.getBaseDir(), libPrefix + "jcpuid" + libSuffix);
if (jcpuidLib.canWrite() && jbigiJar.lastModified() > jcpuidLib.lastModified()) {
String path = jcpuidLib.getAbsolutePath();
boolean success = FileUtil.copy(path, path + ".bak", true, true);
if (success) {
boolean success2 = jcpuidLib.delete();
if (success2) {
System.out.println("New jbigi.jar detected, moved jcpuid library to " +
path + ".bak");
System.out.println("Check logs for successful installation of new library");
}
}
}
File jbigiLib = new File(_context.getBaseDir(), libPrefix + "jbigi" + libSuffix);
if (jbigiLib.canWrite() && jbigiJar.lastModified() > jbigiLib.lastModified()) {
String path = jbigiLib.getAbsolutePath();
boolean success = FileUtil.copy(path, path + ".bak", true, true);
if (success) {
boolean success2 = jbigiLib.delete();
if (success2) {
System.out.println("New jbigi.jar detected, moved jbigi library to " +
path + ".bak");
System.out.println("Check logs for successful installation of new library");
}
}
}
}
}
/**
* Delete all files listed in the delete file.
* Format: One file name per line, comment lines start with '#'.
* All file names must be relative to $I2P, absolute file names not allowed.
* We probably can't remove old jars this way.
* Fails silently.
* Use no new I2P classes here so it may be called after zip extraction.
* @since 0.8.12
*/
private void deleteListedFiles() {
File deleteFile = new File(_context.getBaseDir(), DELETE_FILE);
if (!deleteFile.exists())
return;
// this is similar to FileUtil.readTextFile() but we can't use any I2P classes here
FileInputStream fis = null;
BufferedReader in = null;
try {
fis = new FileInputStream(deleteFile);
in = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
String line;
while ( (line = in.readLine()) != null) {
String fl = line.trim();
if (fl.contains("..") || fl.startsWith("#") || fl.length() == 0)
continue;
File df = new File(fl);
if (df.isAbsolute())
continue;
df = new File(_context.getBaseDir(), fl);
if (df.exists() && !df.isDirectory()) {
if (df.delete())
System.out.println("INFO: File [" + fl + "] deleted");
}
}
} catch (IOException ioe) {}
finally {
if (in != null) try { in.close(); } catch(IOException ioe) {}
if (deleteFile.delete())
System.out.println("INFO: File [" + DELETE_FILE + "] deleted");
}
}
/******* /*******
private static void verifyWrapperConfig() { private static void verifyWrapperConfig() {

View File

@@ -0,0 +1,215 @@
package net.i2p.router.tasks;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.RouterVersion;
import net.i2p.util.FileUtil;
import net.i2p.util.SystemVersion;
/**
* If the i2pupdate.zip file is present,
* unzip it and JVM exit.
*
* @since 0.9.20 moved from Router.java
*/
public class InstallUpdate {
private static final String DELETE_FILE = "deletelist.txt";
/**
* Context must be available.
* Unzip update file found in the router dir OR base dir, to the base dir
*
* If successful, will call exit() and never return.
*
* If we can't write to the base dir, write message to System.out and return.
* Note: _log not available here.
*/
public static void installUpdates(Router r) {
RouterContext context = r.getContext();
File updateFile = new File(context.getRouterDir(), Router.UPDATE_FILE);
boolean exists = updateFile.exists();
if (!exists) {
updateFile = new File(context.getBaseDir(), Router.UPDATE_FILE);
exists = updateFile.exists();
}
if (exists) {
// do a simple permissions test, if it fails leave the file in place and don't restart
File test = new File(context.getBaseDir(), "history.txt");
if ((test.exists() && !test.canWrite()) || (!context.getBaseDir().canWrite())) {
System.out.println("ERROR: No write permissions on " + context.getBaseDir() +
" to extract software update file");
// carry on
return;
}
System.out.println("INFO: Update file exists [" + Router.UPDATE_FILE + "] - installing");
// verify the whole thing first
// we could remember this fails, and not bother restarting, but who cares...
boolean ok = FileUtil.verifyZip(updateFile);
if (ok) {
// This may be useful someday. First added in 0.8.2
// Moved above the extract so we don't NCDFE
Map<String, String> config = new HashMap<String, String>(4);
config.put("router.updateLastInstalled", "" + System.currentTimeMillis());
// Set the last version to the current version, since 0.8.13
config.put("router.previousVersion", RouterVersion.VERSION);
config.put("router.previousFullVersion", RouterVersion.FULL_VERSION);
r.saveConfig(config, null);
ok = FileUtil.extractZip(updateFile, context.getBaseDir());
}
// Very important - we have now trashed our jars.
// After this point, do not use any new I2P classes, or they will fail to load
// and we will die with NCDFE.
// Ideally, do not use I2P classes at all, new or not.
try {
if (ok) {
// We do this here so we may delete old jars before we restart
deleteListedFiles(context);
System.out.println("INFO: Update installed");
} else {
System.out.println("ERROR: Update failed!");
}
if (!ok) {
// we can't leave the file in place or we'll continually restart, so rename it
File bad = new File(context.getRouterDir(), "BAD-" + Router.UPDATE_FILE);
boolean renamed = updateFile.renameTo(bad);
if (renamed) {
System.out.println("Moved update file to " + bad.getAbsolutePath());
} else {
System.out.println("Deleting file " + updateFile.getAbsolutePath());
ok = true; // so it will be deleted
}
}
if (ok) {
boolean deleted = updateFile.delete();
if (!deleted) {
System.out.println("ERROR: Unable to delete the update file!");
updateFile.deleteOnExit();
}
}
// exit whether ok or not
if (context.hasWrapper())
System.out.println("INFO: Restarting after update");
else
System.out.println("WARNING: Exiting after update, restart I2P");
} catch (Throwable t) {
// hide the NCDFE
// hopefully the update file got deleted or we will loop
}
System.exit(Router.EXIT_HARD_RESTART);
} else {
deleteJbigiFiles(context);
// It was here starting in 0.8.12 so it could be used the very first time
// Now moved up so it is usually run only after an update
// But the first time before jetty 6 it will run here...
// Here we can't remove jars
deleteListedFiles(context);
}
}
/**
* Remove extracted libjbigi.so and libjcpuid.so files if we have a newer jbigi.jar,
* so the new ones will be extracted.
* We do this after the restart, not after the extract, because it's safer, and
* because people may upgrade their jbigi.jar file manually.
*
* Copied from NativeBigInteger, which we can't access here or the
* libs will get loaded.
*/
private static void deleteJbigiFiles(RouterContext context) {
boolean isX86 = SystemVersion.isX86();
String osName = System.getProperty("os.name").toLowerCase(Locale.US);
boolean isWin = SystemVersion.isWindows();
boolean isMac = SystemVersion.isMac();
// only do this on these OSes
boolean goodOS = isWin || isMac ||
osName.contains("linux") || osName.contains("freebsd");
// only do this on these x86
File jbigiJar = new File(context.getBaseDir(), "lib/jbigi.jar");
if (isX86 && goodOS && jbigiJar.exists()) {
String libPrefix = (isWin ? "" : "lib");
String libSuffix = (isWin ? ".dll" : isMac ? ".jnilib" : ".so");
File jcpuidLib = new File(context.getBaseDir(), libPrefix + "jcpuid" + libSuffix);
if (jcpuidLib.canWrite() && jbigiJar.lastModified() > jcpuidLib.lastModified()) {
String path = jcpuidLib.getAbsolutePath();
boolean success = FileUtil.copy(path, path + ".bak", true, true);
if (success) {
boolean success2 = jcpuidLib.delete();
if (success2) {
System.out.println("New jbigi.jar detected, moved jcpuid library to " +
path + ".bak");
System.out.println("Check logs for successful installation of new library");
}
}
}
File jbigiLib = new File(context.getBaseDir(), libPrefix + "jbigi" + libSuffix);
if (jbigiLib.canWrite() && jbigiJar.lastModified() > jbigiLib.lastModified()) {
String path = jbigiLib.getAbsolutePath();
boolean success = FileUtil.copy(path, path + ".bak", true, true);
if (success) {
boolean success2 = jbigiLib.delete();
if (success2) {
System.out.println("New jbigi.jar detected, moved jbigi library to " +
path + ".bak");
System.out.println("Check logs for successful installation of new library");
}
}
}
}
}
/**
* Delete all files listed in the delete file.
* Format: One file name per line, comment lines start with '#'.
* All file names must be relative to $I2P, absolute file names not allowed.
* We probably can't remove old jars this way.
* Fails silently.
* Use no new I2P classes here so it may be called after zip extraction.
* @since 0.8.12
*/
private static void deleteListedFiles(RouterContext context) {
File deleteFile = new File(context.getBaseDir(), DELETE_FILE);
if (!deleteFile.exists())
return;
// this is similar to FileUtil.readTextFile() but we can't use any I2P classes here
FileInputStream fis = null;
BufferedReader in = null;
try {
fis = new FileInputStream(deleteFile);
in = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
String line;
while ( (line = in.readLine()) != null) {
String fl = line.trim();
if (fl.contains("..") || fl.startsWith("#") || fl.length() == 0)
continue;
File df = new File(fl);
if (df.isAbsolute())
continue;
df = new File(context.getBaseDir(), fl);
if (df.exists() && !df.isDirectory()) {
if (df.delete())
System.out.println("INFO: File [" + fl + "] deleted");
}
}
} catch (IOException ioe) {}
finally {
if (in != null) try { in.close(); } catch(IOException ioe) {}
if (deleteFile.delete())
System.out.println("INFO: File [" + DELETE_FILE + "] deleted");
}
}
}

View File

@@ -4,6 +4,7 @@
Miscellaneous classes, mostly things that are executed periodically as Miscellaneous classes, mostly things that are executed periodically as
Jobs, Threads, and SimpleTimer.TimedEvents. Jobs, Threads, and SimpleTimer.TimedEvents.
These are used only by Router.java. These are used only by Router.java.
Nothing here is to be used externally, not a part of the public API.
</p> </p>
</body> </body>
</html> </html>