Skip to content
Snippets Groups Projects
Commit 467842e6 authored by akwizgran's avatar akwizgran
Browse files

If a Tor process is left behind after a crash, kill it when restarting.

parent 413b0468
No related branches found
No related tags found
No related merge requests found
...@@ -334,7 +334,8 @@ public class HomeScreenActivity extends RoboActivity { ...@@ -334,7 +334,8 @@ public class HomeScreenActivity extends RoboActivity {
syncButton.setText(R.string.synchronize_button); syncButton.setText(R.string.synchronize_button);
syncButton.setOnClickListener(new OnClickListener() { syncButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) { public void onClick(View view) {
// FIXME: Hook this button up to an activity // FIXME: Crash testing, remove this
throw new RuntimeException();
} }
}); });
buttons.add(syncButton); buttons.add(syncButton);
......
...@@ -114,10 +114,20 @@ class TorPlugin implements DuplexPlugin, EventHandler { ...@@ -114,10 +114,20 @@ class TorPlugin implements DuplexPlugin, EventHandler {
public boolean start() throws IOException { public boolean start() throws IOException {
// Try to connect to an existing Tor process if there is one // Try to connect to an existing Tor process if there is one
boolean startProcess = false;
try { try {
controlSocket = new Socket("127.0.0.1", CONTROL_PORT); controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
if(LOG.isLoggable(INFO)) LOG.info("Tor is already running"); if(LOG.isLoggable(INFO)) LOG.info("Tor is already running");
if(readPidFile() == -1) {
controlSocket.close();
killZombieProcess();
startProcess = true;
}
} catch(IOException e) { } catch(IOException e) {
if(LOG.isLoggable(INFO)) LOG.info("Tor is not running");
startProcess = true;
}
if(startProcess) {
// Install the binary, GeoIP database and config file if necessary // Install the binary, GeoIP database and config file if necessary
if(!isInstalled() && !install()) { if(!isInstalled() && !install()) {
if(LOG.isLoggable(INFO)) LOG.info("Could not install Tor"); if(LOG.isLoggable(INFO)) LOG.info("Could not install Tor");
...@@ -171,13 +181,7 @@ class TorPlugin implements DuplexPlugin, EventHandler { ...@@ -171,13 +181,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
controlSocket = new Socket("127.0.0.1", CONTROL_PORT); controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
} }
// Read the PID of the Tor process so we can kill it if necessary // Read the PID of the Tor process so we can kill it if necessary
try { pid = readPidFile();
pid = Integer.parseInt(new String(read(pidFile), "UTF-8").trim());
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning("Could not read PID file");
} catch(NumberFormatException e) {
if(LOG.isLoggable(WARNING)) LOG.warning("Could not parse PID file");
}
// Create a shutdown hook to ensure the Tor process is killed // Create a shutdown hook to ensure the Tor process is killed
shutdownManager.addShutdownHook(new Runnable() { shutdownManager.addShutdownHook(new Runnable() {
public void run() { public void run() {
...@@ -332,6 +336,58 @@ class TorPlugin implements DuplexPlugin, EventHandler { ...@@ -332,6 +336,58 @@ class TorPlugin implements DuplexPlugin, EventHandler {
} }
} }
private int readPidFile() {
// Read the PID of the Tor process so we can kill it if necessary
try {
return Integer.parseInt(new String(read(pidFile), "UTF-8").trim());
} catch(IOException e) {
if(LOG.isLoggable(WARNING)) LOG.warning("Could not read PID file");
} catch(NumberFormatException e) {
if(LOG.isLoggable(WARNING)) LOG.warning("Could not parse PID file");
}
return -1;
}
/*
* If the app crashes, leaving a Tor process running, and the user clears
* the app's data, removing the PID file and auth cookie file, it's no
* longer possible to communicate with the zombie process and it must be
* killed. ActivityManager.killBackgroundProcesses() doesn't seem to work
* in this case, so we must parse the output of ps to get the PID.
* <p>
* On all tested devices, the output consists of a header line followed by
* one line per process. The second column is the PID and the last column
* is the process name, which includes the app's package name.
*/
private void killZombieProcess() {
String packageName = "/" + appContext.getPackageName() + "/";
try {
// Parse the output of ps
Process ps = Runtime.getRuntime().exec("ps");
Scanner scanner = new Scanner(ps.getInputStream());
// Discard the header line
if(scanner.hasNextLine()) scanner.nextLine();
// Look for a Tor process with our package name
while(scanner.hasNextLine()) {
String[] columns = scanner.nextLine().split("\\s+");
if(columns.length < 3) break;
int pid = Integer.parseInt(columns[1]);
String name = columns[columns.length - 1];
if(name.contains(packageName) && name.endsWith("/tor")) {
if(LOG.isLoggable(INFO))
LOG.info("Killing zombie process " + pid);
android.os.Process.killProcess(pid);
}
}
scanner.close();
} catch(IOException e) {
if(LOG.isLoggable(WARNING))
LOG.warning("Could not parse ps output");
} catch(SecurityException e) {
if(LOG.isLoggable(WARNING)) LOG.warning("Could not execute ps");
}
}
private void bind() { private void bind() {
// If there's already a port number stored in config, reuse it // If there's already a port number stored in config, reuse it
String portString = callback.getConfig().get("port"); String portString = callback.getConfig().get("port");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment