forked from I2P_Developers/i2p.i2p
* ShellCommand: Fix launching all browsers at startup (ticket #453)
This commit is contained in:
@ -16,6 +16,7 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Passes a command to the OS shell for execution and manages the input and
|
* Passes a command to the OS shell for execution and manages the input and
|
||||||
@ -27,55 +28,49 @@ import java.io.OutputStreamWriter;
|
|||||||
*/
|
*/
|
||||||
public class ShellCommand {
|
public class ShellCommand {
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
private static final boolean CONSUME_OUTPUT = true;
|
private static final boolean CONSUME_OUTPUT = true;
|
||||||
private static final boolean NO_CONSUME_OUTPUT = false;
|
private static final boolean NO_CONSUME_OUTPUT = false;
|
||||||
|
|
||||||
private static final boolean WAIT_FOR_EXIT_STATUS = true;
|
private static final boolean WAIT_FOR_EXIT_STATUS = true;
|
||||||
private static final boolean NO_WAIT_FOR_EXIT_STATUS = false;
|
private static final boolean NO_WAIT_FOR_EXIT_STATUS = false;
|
||||||
|
|
||||||
private boolean _commandSuccessful;
|
// Following are unused, only for NO_CONSUME_OUTPUT;
|
||||||
private boolean _commandCompleted;
|
// need synchronization or volatile or something if we start using it.
|
||||||
private CommandThread _commandThread;
|
|
||||||
private InputStream _errorStream;
|
private InputStream _errorStream;
|
||||||
private InputStream _inputStream;
|
private InputStream _inputStream;
|
||||||
private boolean _isTimerRunning;
|
|
||||||
private OutputStream _outputStream;
|
private OutputStream _outputStream;
|
||||||
private Process _process;
|
|
||||||
|
/** @since 0.9.3 */
|
||||||
|
private static class Result {
|
||||||
|
public volatile boolean commandSuccessful;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a shell command in its own thread.
|
* Executes a shell command in its own thread.
|
||||||
* Use caution when repeatedly calling execute methods with the same object
|
|
||||||
* as there are some globals here...
|
|
||||||
*
|
*
|
||||||
* @author hypercubus
|
* @author hypercubus
|
||||||
*/
|
*/
|
||||||
private class CommandThread extends Thread {
|
private class CommandThread extends Thread {
|
||||||
|
|
||||||
private final Object caller;
|
|
||||||
private final boolean consumeOutput;
|
private final boolean consumeOutput;
|
||||||
private final Object shellCommand;
|
private final Object shellCommand;
|
||||||
|
private final Result result;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param shellCommand either a String or a String[] (since 0.8.3)
|
* @param shellCommand either a String or a String[] (since 0.8.3)
|
||||||
|
* @param consumeOutput always true, false is unused, possibly untested
|
||||||
|
* @param result out parameter
|
||||||
*/
|
*/
|
||||||
CommandThread(Object caller, Object shellCommand, boolean consumeOutput) {
|
CommandThread(Object shellCommand, boolean consumeOutput, Result result) {
|
||||||
super("CommandThread");
|
super("ShellCommand Executor");
|
||||||
this.caller = caller;
|
|
||||||
this.shellCommand = shellCommand;
|
this.shellCommand = shellCommand;
|
||||||
this.consumeOutput = consumeOutput;
|
this.consumeOutput = consumeOutput;
|
||||||
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// FIXME these will corrupt the globals if the command times out and the caller
|
result.commandSuccessful = execute(shellCommand, consumeOutput, WAIT_FOR_EXIT_STATUS);
|
||||||
// makes another request with the same object.
|
|
||||||
_commandSuccessful = execute(shellCommand, consumeOutput, WAIT_FOR_EXIT_STATUS);
|
|
||||||
_commandCompleted = true;
|
|
||||||
if (_isTimerRunning) {
|
|
||||||
synchronized(caller) {
|
|
||||||
caller.notifyAll(); // In case the caller is still in the wait() state.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,19 +85,16 @@ public class ShellCommand {
|
|||||||
* @author hypercubus
|
* @author hypercubus
|
||||||
*/
|
*/
|
||||||
private static class StreamConsumer extends Thread {
|
private static class StreamConsumer extends Thread {
|
||||||
|
private final BufferedReader bufferedReader;
|
||||||
private BufferedReader bufferedReader;
|
|
||||||
private InputStreamReader inputStreamReader;
|
|
||||||
|
|
||||||
public StreamConsumer(InputStream inputStream) {
|
public StreamConsumer(InputStream inputStream) {
|
||||||
super("StreamConsumer");
|
super("ShellCommand Consumer");
|
||||||
this.inputStreamReader = new InputStreamReader(inputStream);
|
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
|
||||||
this.bufferedReader = new BufferedReader(inputStreamReader);
|
this.bufferedReader = new BufferedReader(inputStreamReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while ((bufferedReader.readLine()) != null) {
|
while ((bufferedReader.readLine()) != null) {
|
||||||
// Just like a Hoover.
|
// Just like a Hoover.
|
||||||
@ -119,27 +111,25 @@ public class ShellCommand {
|
|||||||
* Reads data from a <code>java.io.InputStream</code> and writes it to
|
* Reads data from a <code>java.io.InputStream</code> and writes it to
|
||||||
* <code>STDOUT</code>.
|
* <code>STDOUT</code>.
|
||||||
*
|
*
|
||||||
|
* UNUSED, only for NO_CONSUME_OUTPUT
|
||||||
|
*
|
||||||
* @author hypercubus
|
* @author hypercubus
|
||||||
*/
|
*/
|
||||||
private static class StreamReader extends Thread {
|
private static class StreamReader extends Thread {
|
||||||
|
private final BufferedReader bufferedReader;
|
||||||
private BufferedReader bufferedReader;
|
|
||||||
private InputStreamReader inputStreamReader;
|
|
||||||
|
|
||||||
public StreamReader(InputStream inputStream) {
|
public StreamReader(InputStream inputStream) {
|
||||||
super("StreamReader");
|
super("ShellCommand Reader");
|
||||||
this.inputStreamReader = new InputStreamReader(inputStream);
|
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
|
||||||
this.bufferedReader = new BufferedReader(inputStreamReader);
|
this.bufferedReader = new BufferedReader(inputStreamReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
char[] buffer = new char[BUFFER_SIZE];
|
char[] buffer = new char[BUFFER_SIZE];
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
while ((bytesRead = bufferedReader.read(buffer, 0, BUFFER_SIZE)) != -1)
|
while ((bytesRead = bufferedReader.read(buffer, 0, BUFFER_SIZE)) != -1)
|
||||||
for (int i = 0; i < bytesRead; i++)
|
for (int i = 0; i < bytesRead; i++)
|
||||||
@ -155,30 +145,26 @@ public class ShellCommand {
|
|||||||
* Reads data from <code>STDIN</code> and writes it to a
|
* Reads data from <code>STDIN</code> and writes it to a
|
||||||
* <code>java.io.OutputStream</code>.
|
* <code>java.io.OutputStream</code>.
|
||||||
*
|
*
|
||||||
|
* UNUSED, only for NO_CONSUME_OUTPUT
|
||||||
|
*
|
||||||
* @author hypercubus
|
* @author hypercubus
|
||||||
*/
|
*/
|
||||||
private static class StreamWriter extends Thread {
|
private static class StreamWriter extends Thread {
|
||||||
|
private final BufferedWriter bufferedWriter;
|
||||||
private BufferedWriter bufferedWriter;
|
|
||||||
private BufferedReader in;
|
|
||||||
private OutputStreamWriter outputStreamWriter;
|
|
||||||
|
|
||||||
public StreamWriter(OutputStream outputStream) {
|
public StreamWriter(OutputStream outputStream) {
|
||||||
super("StreamWriter");
|
super("ShellCommand Writer");
|
||||||
this.outputStreamWriter = new OutputStreamWriter(outputStream);
|
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
|
||||||
this.bufferedWriter = new BufferedWriter(outputStreamWriter);
|
this.bufferedWriter = new BufferedWriter(outputStreamWriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
|
||||||
String input;
|
|
||||||
|
|
||||||
in = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
input = in.readLine() + "\r\n";
|
bufferedWriter.write(in.readLine());
|
||||||
bufferedWriter.write(input, 0, input.length());
|
bufferedWriter.write("\r\n");
|
||||||
bufferedWriter.flush();
|
bufferedWriter.flush();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -226,11 +212,7 @@ public class ShellCommand {
|
|||||||
* else <code>false</code>.
|
* else <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public boolean executeAndWait(String shellCommand) {
|
public boolean executeAndWait(String shellCommand) {
|
||||||
|
return execute(shellCommand, NO_CONSUME_OUTPUT, WAIT_FOR_EXIT_STATUS);
|
||||||
if (execute(shellCommand, NO_CONSUME_OUTPUT, WAIT_FOR_EXIT_STATUS))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -254,29 +236,20 @@ public class ShellCommand {
|
|||||||
* returns an exit status of 0 (indicating success),
|
* returns an exit status of 0 (indicating success),
|
||||||
* else <code>false</code>.
|
* else <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean executeAndWaitTimed(String shellCommand, int seconds) {
|
public boolean executeAndWaitTimed(String shellCommand, int seconds) {
|
||||||
|
Result result = new Result();
|
||||||
_commandThread = new CommandThread(this, shellCommand, NO_CONSUME_OUTPUT);
|
Thread commandThread = new CommandThread(shellCommand, NO_CONSUME_OUTPUT, result);
|
||||||
_commandThread.start();
|
commandThread.start();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (seconds > 0) {
|
if (seconds > 0) {
|
||||||
_isTimerRunning = true;
|
commandThread.join(seconds * 1000);
|
||||||
wait(seconds * 1000);
|
if (commandThread.isAlive())
|
||||||
_isTimerRunning = false;
|
|
||||||
if (!_commandCompleted)
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// Wake up, time to die.
|
// Wake up, time to die.
|
||||||
}
|
}
|
||||||
_isTimerRunning = false;
|
return result.commandSuccessful;
|
||||||
|
|
||||||
if (_commandSuccessful)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -307,11 +280,7 @@ public class ShellCommand {
|
|||||||
* else <code>false</code>.
|
* else <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public boolean executeSilentAndWait(String shellCommand) {
|
public boolean executeSilentAndWait(String shellCommand) {
|
||||||
|
return execute(shellCommand, CONSUME_OUTPUT, WAIT_FOR_EXIT_STATUS);
|
||||||
if (execute(shellCommand, CONSUME_OUTPUT, WAIT_FOR_EXIT_STATUS))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -332,9 +301,10 @@ public class ShellCommand {
|
|||||||
* here disables waiting.
|
* here disables waiting.
|
||||||
* @return <code>true</code> if the spawned shell process
|
* @return <code>true</code> if the spawned shell process
|
||||||
* returns an exit status of 0 (indicating success),
|
* returns an exit status of 0 (indicating success),
|
||||||
|
* OR if the time expires,
|
||||||
* else <code>false</code>.
|
* else <code>false</code>.
|
||||||
*/
|
*/
|
||||||
public synchronized boolean executeSilentAndWaitTimed(String shellCommand, int seconds) {
|
public boolean executeSilentAndWaitTimed(String shellCommand, int seconds) {
|
||||||
return executeSAWT(shellCommand, seconds);
|
return executeSAWT(shellCommand, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,50 +323,65 @@ public class ShellCommand {
|
|||||||
* here disables waiting.
|
* here disables waiting.
|
||||||
* @return <code>true</code> if the spawned shell process
|
* @return <code>true</code> if the spawned shell process
|
||||||
* returns an exit status of 0 (indicating success),
|
* returns an exit status of 0 (indicating success),
|
||||||
|
* OR if the time expires,
|
||||||
* else <code>false</code>.
|
* else <code>false</code>.
|
||||||
* @since 0.8.3
|
* @since 0.8.3
|
||||||
*/
|
*/
|
||||||
public synchronized boolean executeSilentAndWaitTimed(String[] commandArray, int seconds) {
|
public boolean executeSilentAndWaitTimed(String[] commandArray, int seconds) {
|
||||||
return executeSAWT(commandArray, seconds);
|
return executeSAWT(commandArray, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @since 0.8.3 */
|
/** @since 0.8.3 */
|
||||||
private boolean executeSAWT(Object shellCommand, int seconds) {
|
private boolean executeSAWT(Object shellCommand, int seconds) {
|
||||||
_commandThread = new CommandThread(this, shellCommand, CONSUME_OUTPUT);
|
String name = null;
|
||||||
_commandThread.start();
|
long begin = 0;
|
||||||
try {
|
if (DEBUG) {
|
||||||
|
if (shellCommand instanceof String) {
|
||||||
if (seconds > 0) {
|
name = (String) shellCommand;
|
||||||
_isTimerRunning = true;
|
} else if (shellCommand instanceof String[]) {
|
||||||
wait(seconds * 1000);
|
String[] arr = (String[]) shellCommand;
|
||||||
_isTimerRunning = false;
|
name = Arrays.toString(arr);
|
||||||
if (!_commandCompleted)
|
}
|
||||||
return true;
|
begin = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
Result result = new Result();
|
||||||
|
Thread commandThread = new CommandThread(shellCommand, CONSUME_OUTPUT, result);
|
||||||
|
commandThread.start();
|
||||||
|
try {
|
||||||
|
if (seconds > 0) {
|
||||||
|
commandThread.join(seconds * 1000);
|
||||||
|
if (commandThread.isAlive()) {
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("ShellCommand gave up waiting for \"" + name + "\" after " + seconds + " seconds");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// Wake up, time to die.
|
// Wake up, time to die.
|
||||||
}
|
}
|
||||||
_isTimerRunning = false;
|
if (DEBUG)
|
||||||
|
System.out.println("ShellCommand returning " + result.commandSuccessful + " for \"" + name + "\" after " + (System.currentTimeMillis() - begin) + " ms");
|
||||||
if (_commandSuccessful)
|
return result.commandSuccessful;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecated unused */
|
||||||
public InputStream getErrorStream() {
|
public InputStream getErrorStream() {
|
||||||
return _errorStream;
|
return _errorStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecated unused */
|
||||||
public InputStream getInputStream() {
|
public InputStream getInputStream() {
|
||||||
return _inputStream;
|
return _inputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecated unused */
|
||||||
public OutputStream getOutputStream() {
|
public OutputStream getOutputStream() {
|
||||||
return _outputStream;
|
return _outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Just does exec, this is NOT a test of ShellCommand.
|
||||||
|
*/
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
if (args.length <= 0) {
|
if (args.length <= 0) {
|
||||||
System.err.println("Usage: ShellCommand commandline");
|
System.err.println("Usage: ShellCommand commandline");
|
||||||
@ -410,63 +395,80 @@ public class ShellCommand {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param shellCommand either a String or a String[] (since 0.8.3) - quick hack
|
* @param shellCommand either a String or a String[] (since 0.8.3) - quick hack
|
||||||
|
* @param consumeOutput always true, false is unused, possibly untested
|
||||||
*/
|
*/
|
||||||
private boolean execute(Object shellCommand, boolean consumeOutput, boolean waitForExitStatus) {
|
private boolean execute(Object shellCommand, boolean consumeOutput, boolean waitForExitStatus) {
|
||||||
|
Process process;
|
||||||
StreamConsumer processStderrConsumer;
|
String name = null; // for debugging only
|
||||||
StreamConsumer processStdoutConsumer;
|
|
||||||
|
|
||||||
StreamReader processStderrReader;
|
|
||||||
StreamWriter processStdinWriter;
|
|
||||||
StreamReader processStdoutReader;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// easy way so we don't have to copy this whole method
|
// easy way so we don't have to copy this whole method
|
||||||
if (shellCommand instanceof String)
|
if (shellCommand instanceof String) {
|
||||||
_process = Runtime.getRuntime().exec((String)shellCommand);
|
name = (String) shellCommand;
|
||||||
else if (shellCommand instanceof String[])
|
if (DEBUG)
|
||||||
_process = Runtime.getRuntime().exec((String[])shellCommand);
|
System.out.println("ShellCommand exec \"" + name + "\" consume? " + consumeOutput + " wait? " + waitForExitStatus);
|
||||||
else
|
process = Runtime.getRuntime().exec(name);
|
||||||
|
} else if (shellCommand instanceof String[]) {
|
||||||
|
String[] arr = (String[]) shellCommand;
|
||||||
|
if (DEBUG) {
|
||||||
|
name = Arrays.toString(arr);
|
||||||
|
System.out.println("ShellCommand exec \"" + name + "\" consume? " + consumeOutput + " wait? " + waitForExitStatus);
|
||||||
|
}
|
||||||
|
process = Runtime.getRuntime().exec(arr);
|
||||||
|
} else {
|
||||||
throw new ClassCastException("shell command must be a String or a String[]");
|
throw new ClassCastException("shell command must be a String or a String[]");
|
||||||
|
}
|
||||||
if (consumeOutput) {
|
if (consumeOutput) {
|
||||||
processStderrConsumer = new StreamConsumer(_process.getErrorStream());
|
Thread processStderrConsumer = new StreamConsumer(process.getErrorStream());
|
||||||
processStderrConsumer.start();
|
processStderrConsumer.start();
|
||||||
processStdoutConsumer = new StreamConsumer(_process.getInputStream());
|
Thread processStdoutConsumer = new StreamConsumer(process.getInputStream());
|
||||||
processStdoutConsumer.start();
|
processStdoutConsumer.start();
|
||||||
} else {
|
} else {
|
||||||
_errorStream = _process.getErrorStream();
|
// unused, consumeOutput always true
|
||||||
_inputStream = _process.getInputStream();
|
_errorStream = process.getErrorStream();
|
||||||
_outputStream = _process.getOutputStream();
|
_inputStream = process.getInputStream();
|
||||||
processStderrReader = new StreamReader(_errorStream);
|
_outputStream = process.getOutputStream();
|
||||||
|
Thread processStderrReader = new StreamReader(_errorStream);
|
||||||
processStderrReader.start();
|
processStderrReader.start();
|
||||||
processStdinWriter = new StreamWriter(_outputStream);
|
Thread processStdinWriter = new StreamWriter(_outputStream);
|
||||||
processStdinWriter.start();
|
processStdinWriter.start();
|
||||||
processStdoutReader = new StreamReader(_inputStream);
|
Thread processStdoutReader = new StreamReader(_inputStream);
|
||||||
processStdoutReader.start();
|
processStdoutReader.start();
|
||||||
}
|
}
|
||||||
if (waitForExitStatus) {
|
if (waitForExitStatus) {
|
||||||
|
if (DEBUG)
|
||||||
|
System.out.println("ShellCommand waiting for \"" + name + '\"');
|
||||||
try {
|
try {
|
||||||
_process.waitFor();
|
process.waitFor();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("ShellCommand exception waiting for \"" + name + '\"');
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
if (!consumeOutput)
|
if (!consumeOutput)
|
||||||
killStreams();
|
killStreams();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!consumeOutput)
|
if (!consumeOutput)
|
||||||
killStreams();
|
killStreams();
|
||||||
|
|
||||||
if (_process.exitValue() > 0)
|
if (DEBUG)
|
||||||
|
System.out.println("ShellCommand exit value is " + process.exitValue() + " for \"" + name + '\"');
|
||||||
|
if (process.exitValue() > 0)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
// probably IOException, file not found from exec()
|
||||||
|
if (DEBUG) {
|
||||||
|
System.out.println("ShellCommand execute exception for \"" + name + '\"');
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** unused, only for NO_CONSUME_OUTPUT */
|
||||||
private void killStreams() {
|
private void killStreams() {
|
||||||
try {
|
try {
|
||||||
_errorStream.close();
|
_errorStream.close();
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
|
2012-10-10 zzz
|
||||||
|
* ShellCommand: Fix launching all browsers at startup (ticket #453)
|
||||||
|
* stats.jsp: Sort groups by translated name
|
||||||
|
|
||||||
2012-10-09 zzz
|
2012-10-09 zzz
|
||||||
* Console, i2ptunnel: Warn on low ports
|
* Console, i2ptunnel: Warn on low ports
|
||||||
|
* EventLog: Add more events
|
||||||
* NetDB: Increase floodfills again
|
* NetDB: Increase floodfills again
|
||||||
* RouterInfo: Exit 1 on error in main()
|
* RouterInfo: Exit 1 on error in main()
|
||||||
* SSU:
|
* SSU:
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 13;
|
public final static long BUILD = 14;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user