/** * 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"); } }
/** * 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. * * <p>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"); File jbigiJar = new File(context.getBaseDir(), "lib/jbigi.jar"); if (goodOS && jbigiJar.exists()) { String libPrefix = (isWin ? "" : "lib"); String libSuffix = (isWin ? ".dll" : isMac ? ".jnilib" : ".so"); if (isX86) { 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"); } } } } if (isX86 || SystemVersion.isARM()) { 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"); } } } } } }
/** * @param action "start" or "stop" or "uninstall" * @throws just about anything if an app has a delay less than zero, caller would be wise to catch * Throwable If no apps have a delay less than zero, it shouldn't throw anything */ private static void runClientApps( RouterContext ctx, File pluginDir, List<ClientAppConfig> apps, String action) throws Exception { Log log = ctx.logManager().getLog(PluginStarter.class); // initialize pluginThreadGroup and _pendingPluginClients String pluginName = pluginDir.getName(); if (!pluginThreadGroups.containsKey(pluginName)) pluginThreadGroups.put(pluginName, new ThreadGroup(pluginName)); ThreadGroup pluginThreadGroup = pluginThreadGroups.get(pluginName); if (action.equals("start")) _pendingPluginClients.put(pluginName, new ConcurrentHashSet<SimpleTimer2.TimedEvent>()); for (ClientAppConfig app : apps) { // If the client is a running ClientApp that we want to stop, // bypass all the logic below. if (action.equals("stop")) { String[] argVal = LoadClientAppsJob.parseArgs(app.args); // We must do all the substitution just as when started, so the // argument array comparison in getClientApp() works. // Do this after parsing so we don't need to worry about quoting for (int i = 0; i < argVal.length; i++) { if (argVal[i].indexOf("$") >= 0) { argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath()); } } ClientApp ca = ctx.routerAppManager().getClientApp(app.className, argVal); if (ca != null) { // even if (ca.getState() != ClientAppState.RUNNING), we do this, we don't want to fall // thru try { ca.shutdown(LoadClientAppsJob.parseArgs(app.stopargs)); } catch (Throwable t) { throw new Exception(t); } continue; } } if (action.equals("start") && app.disabled) continue; String argVal[]; if (action.equals("start")) { // start argVal = LoadClientAppsJob.parseArgs(app.args); } else { String args; if (action.equals("stop")) args = app.stopargs; else if (action.equals("uninstall")) args = app.uninstallargs; else throw new IllegalArgumentException("bad action"); // args must be present if (args == null || args.length() <= 0) continue; argVal = LoadClientAppsJob.parseArgs(args); } // do this after parsing so we don't need to worry about quoting for (int i = 0; i < argVal.length; i++) { if (argVal[i].indexOf("$") >= 0) { argVal[i] = argVal[i].replace("$I2P", ctx.getBaseDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$CONFIG", ctx.getConfigDir().getAbsolutePath()); argVal[i] = argVal[i].replace("$PLUGIN", pluginDir.getAbsolutePath()); } } ClassLoader cl = null; if (app.classpath != null) { String cp = app.classpath; if (cp.indexOf("$") >= 0) { cp = cp.replace("$I2P", ctx.getBaseDir().getAbsolutePath()); cp = cp.replace("$CONFIG", ctx.getConfigDir().getAbsolutePath()); cp = cp.replace("$PLUGIN", pluginDir.getAbsolutePath()); } // Old way - add for the whole JVM // addToClasspath(cp, app.clientName, log); // New way - add only for this client // We cache the ClassLoader we start the client with, so // we can reuse it for stopping and uninstalling. // If we don't, the client won't be able to find its // static members. String clCacheKey = pluginName + app.className + app.args; if (!action.equals("start")) cl = _clCache.get(clCacheKey); if (cl == null) { URL[] urls = classpathToURLArray(cp, app.clientName, log); if (urls != null) { cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader()); if (action.equals("start")) _clCache.put(clCacheKey, cl); } } } if (app.delay < 0 && action.equals("start")) { // this will throw exceptions LoadClientAppsJob.runClientInline(app.className, app.clientName, argVal, log, cl); } else if (app.delay == 0 || !action.equals("start")) { // quick check, will throw ClassNotFoundException on error LoadClientAppsJob.testClient(app.className, cl); // run this guy now LoadClientAppsJob.runClient( app.className, app.clientName, argVal, ctx, log, pluginThreadGroup, cl); } else { // If there is some delay, there may be a really good reason for it. // Loading a class would be one of them! // So we do a quick check first, If it bombs out, we delay and try again. // If it bombs after that, then we throw the ClassNotFoundException. try { // quick check LoadClientAppsJob.testClient(app.className, cl); } catch (ClassNotFoundException ex) { // Try again 1 or 2 seconds later. // This should be enough time. Although it is a lousy hack // it should work for most cases. // Perhaps it may be even better to delay a percentage // if > 1, and reduce the delay time. // Under normal circumstances there will be no delay at all. try { if (app.delay > 1) { Thread.sleep(2000); } else { Thread.sleep(1000); } } catch (InterruptedException ie) { } // quick check, will throw ClassNotFoundException on error LoadClientAppsJob.testClient(app.className, cl); } // wait before firing it up SimpleTimer2.TimedEvent evt = new TrackedDelayedClient( pluginName, ctx.simpleTimer2(), ctx, app.className, app.clientName, argVal, pluginThreadGroup, cl); evt.schedule(app.delay); } } }
/** * http://irc.codehaus.org/display/JETTY/Porting+to+jetty6 * * <pre> * Server * HandlerCollection * ContextHandlerCollection * WebAppContext (i.e. ContextHandler) * SessionHandler * SecurityHandler * ServletHandler * servlets... * WebAppContext * ... * DefaultHandler * RequestLogHandler (opt) * </pre> */ public void startConsole() { File workDir = new SecureDirectory(_context.getTempDir(), "jetty-work"); boolean workDirRemoved = FileUtil.rmdir(workDir, false); if (!workDirRemoved) System.err.println("ERROR: Unable to remove Jetty temporary work directory"); boolean workDirCreated = workDir.mkdirs(); if (!workDirCreated) System.err.println("ERROR: Unable to create Jetty temporary work directory"); // try { // Log.setLog(new I2PLogger(_context)); // } catch (Throwable t) { // System.err.println("INFO: I2P Jetty logging class not found, logging to wrapper log"); // } // This way it doesn't try to load Slf4jLog first System.setProperty("org.mortbay.log.class", "net.i2p.jetty.I2PLogger"); // so Jetty can find WebAppConfiguration System.setProperty("jetty.class.path", _context.getBaseDir() + "/lib/routerconsole.jar"); _server = new Server(); _server.setGracefulShutdown(1000); try { ThreadPool ctp = new CustomThreadPoolExecutor(); ctp.prestartAllCoreThreads(); _server.setThreadPool(ctp); } catch (Throwable t) { // class not found... System.out.println("INFO: Jetty concurrent ThreadPool unavailable, using QueuedThreadPool"); QueuedThreadPool qtp = new QueuedThreadPool(MAX_THREADS); qtp.setMinThreads(MIN_THREADS); qtp.setMaxIdleTimeMs(MAX_IDLE_TIME); _server.setThreadPool(qtp); } HandlerCollection hColl = new HandlerCollection(); ContextHandlerCollection chColl = new ContextHandlerCollection(); _server.addHandler(hColl); hColl.addHandler(chColl); hColl.addHandler(new DefaultHandler()); String log = _context.getProperty("routerconsole.log"); if (log != null) { File logFile = new File(log); if (!logFile.isAbsolute()) logFile = new File(_context.getLogDir(), "logs/" + log); try { RequestLogHandler rhl = new RequestLogHandler(); rhl.setRequestLog(new NCSARequestLog(logFile.getAbsolutePath())); hColl.addHandler(rhl); } catch (Exception ioe) { System.err.println("ERROR: Unable to create Jetty log: " + ioe); } } boolean rewrite = false; Properties props = webAppProperties(); if (props.isEmpty()) { props.setProperty(PREFIX + ROUTERCONSOLE + ENABLED, "true"); rewrite = true; } // Get an absolute path with a trailing slash for the webapps dir // We assume relative to the base install dir for backward compatibility File app = new File(_webAppsDir); if (!app.isAbsolute()) { app = new File(_context.getBaseDir(), _webAppsDir); try { _webAppsDir = app.getCanonicalPath(); } catch (IOException ioe) { } } if (!_webAppsDir.endsWith("/")) _webAppsDir += '/'; WebAppContext rootWebApp = null; ServletHandler rootServletHandler = null; List<Connector> connectors = new ArrayList(4); try { int boundAddresses = 0; Set addresses = Addresses.getAllAddresses(); boolean hasIPV4 = addresses.contains("0.0.0.0"); boolean hasIPV6 = addresses.contains("0:0:0:0:0:0:0:0"); // add standard listeners int lport = 0; if (_listenPort != null) { try { lport = Integer.parseInt(_listenPort); } catch (NumberFormatException nfe) { } if (lport <= 0) System.err.println("Bad routerconsole port " + _listenPort); } if (lport > 0) { StringTokenizer tok = new StringTokenizer(_listenHost, " ,"); while (tok.hasMoreTokens()) { String host = tok.nextToken().trim(); try { // Test before we add the connector, because Jetty 6 won't start if any of the // connectors are bad InetAddress test = InetAddress.getByName(host); if ((!hasIPV6) && (!(test instanceof Inet4Address))) throw new IOException("IPv6 addresses unsupported"); if ((!hasIPV4) && (test instanceof Inet4Address)) throw new IOException("IPv4 addresses unsupported"); ServerSocket testSock = null; try { // On Windows, this was passing and Jetty was still failing, // possibly due to %scope_id ??? // https://issues.apache.org/jira/browse/ZOOKEEPER-667 // testSock = new ServerSocket(0, 0, test); // so do exactly what Jetty does in SelectChannelConnector.open() testSock = new ServerSocket(); InetSocketAddress isa = new InetSocketAddress(host, 0); testSock.bind(isa); } finally { if (testSock != null) try { testSock.close(); } catch (IOException ioe) { } } // if (host.indexOf(":") >= 0) // IPV6 - requires patched Jetty 5 // _server.addListener('[' + host + "]:" + _listenPort); // else // _server.addListener(host + ':' + _listenPort); AbstractConnector lsnr; if (SystemVersion.isJava6() && !SystemVersion.isGNU()) { SelectChannelConnector slsnr = new SelectChannelConnector(); slsnr.setUseDirectBuffers(false); // default true seems to be leaky lsnr = slsnr; } else { // Jetty 6 and NIO on Java 5 don't get along that well // Also: http://jira.codehaus.org/browse/JETTY-1238 // "Do not use GCJ with Jetty, it will not work." // Actually it does if you don't use NIO lsnr = new SocketConnector(); } lsnr.setHost(host); lsnr.setPort(lport); lsnr.setMaxIdleTime(90 * 1000); // default 10 sec lsnr.setName("ConsoleSocket"); // all with same name will use the same thread pool // _server.addConnector(lsnr); connectors.add(lsnr); boundAddresses++; } catch (Exception ioe) { System.err.println( "Unable to bind routerconsole to " + host + " port " + _listenPort + ": " + ioe); System.err.println( "You may ignore this warning if the console is still available at http://localhost:" + _listenPort); } } // XXX: what if listenhosts do not include 127.0.0.1? (Should that ever even happen?) _context.portMapper().register(PortMapper.SVC_CONSOLE, lport); } // add SSL listeners int sslPort = 0; if (_sslListenPort != null) { try { sslPort = Integer.parseInt(_sslListenPort); } catch (NumberFormatException nfe) { } if (sslPort <= 0) System.err.println("Bad routerconsole SSL port " + _sslListenPort); } if (sslPort > 0) { File keyStore = new File(_context.getConfigDir(), "keystore/console.ks"); if (verifyKeyStore(keyStore)) { StringTokenizer tok = new StringTokenizer(_sslListenHost, " ,"); while (tok.hasMoreTokens()) { String host = tok.nextToken().trim(); // doing it this way means we don't have to escape an IPv6 host with [] try { // Test before we add the connector, because Jetty 6 won't start if any of the // connectors are bad InetAddress test = InetAddress.getByName(host); if ((!hasIPV6) && (!(test instanceof Inet4Address))) throw new IOException("IPv6 addresses unsupported"); if ((!hasIPV4) && (test instanceof Inet4Address)) throw new IOException("IPv4 addresses unsupported"); ServerSocket testSock = null; try { // see comments above // testSock = new ServerSocket(0, 0, test); testSock = new ServerSocket(); InetSocketAddress isa = new InetSocketAddress(host, 0); testSock.bind(isa); } finally { if (testSock != null) try { testSock.close(); } catch (IOException ioe) { } } // TODO if class not found use SslChannelConnector // Sadly there's no common base class with the ssl methods in it AbstractConnector ssll; if (SystemVersion.isJava6() && !SystemVersion.isGNU()) { SslSelectChannelConnector sssll = new SslSelectChannelConnector(); // the keystore path and password sssll.setKeystore(keyStore.getAbsolutePath()); sssll.setPassword( _context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD)); // the X.509 cert password (if not present, verifyKeyStore() returned false) sssll.setKeyPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork")); sssll.setUseDirectBuffers(false); // default true seems to be leaky ssll = sssll; } else { // Jetty 6 and NIO on Java 5 don't get along that well SslSocketConnector sssll = new SslSocketConnector(); // the keystore path and password sssll.setKeystore(keyStore.getAbsolutePath()); sssll.setPassword( _context.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD)); // the X.509 cert password (if not present, verifyKeyStore() returned false) sssll.setKeyPassword(_context.getProperty(PROP_KEY_PASSWORD, "thisWontWork")); ssll = sssll; } ssll.setHost(host); ssll.setPort(sslPort); ssll.setMaxIdleTime(90 * 1000); // default 10 sec ssll.setName("ConsoleSocket"); // all with same name will use the same thread pool // _server.addConnector(ssll); connectors.add(ssll); boundAddresses++; } catch (Exception e) { System.err.println( "Unable to bind routerconsole to " + host + " port " + sslPort + " for SSL: " + e); if (SystemVersion.isGNU()) System.err.println("Probably because GNU classpath does not support Sun keystores"); System.err.println( "You may ignore this warning if the console is still available at https://localhost:" + sslPort); } } _context.portMapper().register(PortMapper.SVC_HTTPS_CONSOLE, sslPort); } else { System.err.println( "Unable to create or access keystore for SSL: " + keyStore.getAbsolutePath()); } } if (boundAddresses <= 0) { System.err.println( "Unable to bind routerconsole to any address on port " + _listenPort + (sslPort > 0 ? (" or SSL port " + sslPort) : "")); return; } rootWebApp = new LocaleWebAppHandler(_context, "/", _webAppsDir + ROUTERCONSOLE + ".war"); File tmpdir = new SecureDirectory( workDir, ROUTERCONSOLE + "-" + (_listenPort != null ? _listenPort : _sslListenPort)); tmpdir.mkdir(); rootWebApp.setTempDirectory(tmpdir); rootWebApp.setExtractWAR(false); rootWebApp.setSessionHandler(new SessionHandler()); rootServletHandler = new ServletHandler(); rootWebApp.setServletHandler(rootServletHandler); initialize(_context, rootWebApp); chColl.addHandler(rootWebApp); } catch (Exception ioe) { ioe.printStackTrace(); } try { // start does a mapContexts() _server.start(); } catch (Throwable me) { // NoClassFoundDefError from a webapp is a throwable, not an exception System.err.println("Error starting the Router Console server: " + me); me.printStackTrace(); } if (_server.isRunning()) { // Add and start the connectors one-by-one boolean error = false; for (Connector conn : connectors) { try { _server.addConnector(conn); // start after adding so it gets the right thread pool conn.start(); } catch (Throwable me) { try { _server.removeConnector(conn); } catch (Throwable t) { t.printStackTrace(); } System.err.println("WARNING: Error starting " + conn + ": " + me); me.printStackTrace(); error = true; } } if (error) { System.err.println( "WARNING: Error starting one or more listeners of the Router Console server.\n" + "If your console is still accessible at http://127.0.0.1:" + _listenPort + "/,\n" + "this may be a problem only with binding to the IPV6 address ::1.\n" + "If so, you may ignore this error, or remove the\n" + "\"::1,\" in the \"clientApp.0.args\" line of the clients.config file."); } } // Start all the other webapps after the server is up, // so things start faster. // Jetty 6 starts the connector before the router console is ready // This also prevents one webapp from breaking the whole thing List<String> notStarted = new ArrayList(); if (_server.isRunning()) { File dir = new File(_webAppsDir); String fileNames[] = dir.list(WarFilenameFilter.instance()); if (fileNames != null) { for (int i = 0; i < fileNames.length; i++) { String appName = fileNames[i].substring(0, fileNames[i].lastIndexOf(".war")); String enabled = props.getProperty(PREFIX + appName + ENABLED); if (!"false".equals(enabled)) { try { String path = new File(dir, fileNames[i]).getCanonicalPath(); WebAppStarter.startWebApp(_context, chColl, appName, path); if (enabled == null) { // do this so configclients.jsp knows about all apps from reading the config props.setProperty(PREFIX + appName + ENABLED, "true"); rewrite = true; } } catch (Throwable t) { System.err.println("ERROR: Failed to start " + appName + ' ' + t); t.printStackTrace(); notStarted.add(appName); } } else { notStarted.add(appName); } } changeState(RUNNING); } } else { System.err.println("ERROR: Router console did not start, not starting webapps"); changeState(START_FAILED); } if (rewrite) storeWebAppProperties(_context, props); if (rootServletHandler != null && notStarted.size() > 0) { // map each not-started webapp to the error page ServletHolder noWebApp = rootServletHandler.getServlet("net.i2p.router.web.jsp.nowebapp_jsp"); for (int i = 0; i < notStarted.size(); i++) { // we want a new handler for each one since if the webapp is started we remove the // handler??? try { if (noWebApp != null) { String path = '/' + notStarted.get(i); // LocaleWebAppsHandler adds a .jsp rootServletHandler.addServletWithMapping(noWebApp, path + ".jsp"); rootServletHandler.addServletWithMapping(noWebApp, path + "/*"); } else { System.err.println("Can't find nowebapp.jsp?"); } } catch (Throwable me) { System.err.println(me); me.printStackTrace(); } } } Thread t = new I2PAppThread(new StatSummarizer(), "StatSummarizer", true); t.setPriority(Thread.NORM_PRIORITY - 1); t.start(); ConsoleUpdateManager um = new ConsoleUpdateManager(_context); um.start(); if (PluginStarter.pluginsEnabled(_context)) { t = new I2PAppThread(new PluginStarter(_context), "PluginStarter", true); t.setPriority(Thread.NORM_PRIORITY - 1); t.start(); _context.addShutdownTask(new PluginStopper(_context)); } // stat summarizer registers its own hook _context.addShutdownTask(new ServerShutdown()); ConfigServiceHandler.registerSignalHandler(_context); }
/** * Context must be available. Unzip update file found in the router dir OR base dir, to the base * dir * * <p>If successful, will call exit() and never return. * * <p>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); } }