mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-20 14:49:53 +01:00
Read Tor process's stdout until it exits.
On Windows, RunAsDaemon is a no-op so we need to read stdout to find out when Tor has finished starting up, then continue to read and discard stdout until Tor exits.
This commit is contained in:
@@ -107,7 +107,7 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
|
|||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||||
|
|
||||||
private static final Logger LOG = getLogger(TorPlugin.class.getName());
|
protected static final Logger LOG = getLogger(TorPlugin.class.getName());
|
||||||
|
|
||||||
private static final String[] EVENTS = {
|
private static final String[] EVENTS = {
|
||||||
"CIRC",
|
"CIRC",
|
||||||
@@ -124,7 +124,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
private static final int COOKIE_POLLING_INTERVAL_MS = 200;
|
||||||
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
|
||||||
|
|
||||||
private final Executor ioExecutor, wakefulIoExecutor;
|
protected final Executor ioExecutor;
|
||||||
|
private final Executor wakefulIoExecutor;
|
||||||
private final Executor connectionStatusExecutor;
|
private final Executor connectionStatusExecutor;
|
||||||
private final NetworkManager networkManager;
|
private final NetworkManager networkManager;
|
||||||
private final LocationUtils locationUtils;
|
private final LocationUtils locationUtils;
|
||||||
@@ -260,29 +261,9 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
} catch (SecurityException | IOException e) {
|
} catch (SecurityException | IOException e) {
|
||||||
throw new PluginException(e);
|
throw new PluginException(e);
|
||||||
}
|
}
|
||||||
// Log the process's standard output until it detaches
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
|
||||||
Scanner stderr = new Scanner(torProcess.getErrorStream());
|
|
||||||
while (stdout.hasNextLine() || stderr.hasNextLine()) {
|
|
||||||
if (stdout.hasNextLine()) {
|
|
||||||
LOG.info(stdout.nextLine());
|
|
||||||
}
|
|
||||||
if (stderr.hasNextLine()) {
|
|
||||||
LOG.info(stderr.nextLine());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stdout.close();
|
|
||||||
stderr.close();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
// Wait for the process to detach or exit
|
// Wait for the Tor process to start
|
||||||
int exit = torProcess.waitFor();
|
waitForTorToStart(torProcess);
|
||||||
if (exit != 0) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.warning("Tor exited with value " + exit);
|
|
||||||
throw new PluginException();
|
|
||||||
}
|
|
||||||
// Wait for the auth cookie file to be created/updated
|
// Wait for the auth cookie file to be created/updated
|
||||||
long start = clock.currentTimeMillis();
|
long start = clock.currentTimeMillis();
|
||||||
while (cookieFile.length() < 32) {
|
while (cookieFile.length() < 32) {
|
||||||
@@ -398,7 +379,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
return zin;
|
return zin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void append(StringBuilder strb, String name, int value) {
|
private static void append(StringBuilder strb, String name, Object value) {
|
||||||
strb.append(name);
|
strb.append(name);
|
||||||
strb.append(" ");
|
strb.append(" ");
|
||||||
strb.append(value);
|
strb.append(value);
|
||||||
@@ -406,9 +387,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getConfigInputStream() {
|
private InputStream getConfigInputStream() {
|
||||||
|
File dataDirectory = new File(torDirectory, ".tor");
|
||||||
StringBuilder strb = new StringBuilder();
|
StringBuilder strb = new StringBuilder();
|
||||||
append(strb, "ControlPort", torControlPort);
|
append(strb, "ControlPort", torControlPort);
|
||||||
append(strb, "CookieAuthentication", 1);
|
append(strb, "CookieAuthentication", 1);
|
||||||
|
append(strb, "DataDirectory", dataDirectory.getAbsolutePath());
|
||||||
append(strb, "DisableNetwork", 1);
|
append(strb, "DisableNetwork", 1);
|
||||||
append(strb, "RunAsDaemon", 1);
|
append(strb, "RunAsDaemon", 1);
|
||||||
append(strb, "SafeSocks", 1);
|
append(strb, "SafeSocks", 1);
|
||||||
@@ -445,6 +428,23 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void waitForTorToStart(Process torProcess)
|
||||||
|
throws InterruptedException, PluginException {
|
||||||
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
|
// Log the first line of stdout (contains Tor and library versions)
|
||||||
|
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
|
// Read the process's stdout (and redirected stderr) until it detaches
|
||||||
|
while (stdout.hasNextLine()) stdout.nextLine();
|
||||||
|
stdout.close();
|
||||||
|
// Wait for the process to detach or exit
|
||||||
|
int exit = torProcess.waitFor();
|
||||||
|
if (exit != 0) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.warning("Tor exited with value " + exit);
|
||||||
|
throw new PluginException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void bind() {
|
private void bind() {
|
||||||
ioExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
// If there's already a port number stored in config, reuse it
|
// If there's already a port number stored in config, reuse it
|
||||||
|
|||||||
@@ -7,15 +7,21 @@ import org.briarproject.bramble.api.network.NetworkManager;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.Backoff;
|
import org.briarproject.bramble.api.plugin.Backoff;
|
||||||
import org.briarproject.bramble.api.plugin.PluginCallback;
|
import org.briarproject.bramble.api.plugin.PluginCallback;
|
||||||
|
import org.briarproject.bramble.api.plugin.PluginException;
|
||||||
import org.briarproject.bramble.api.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
import org.briarproject.bramble.api.system.LocationUtils;
|
import org.briarproject.bramble.api.system.LocationUtils;
|
||||||
import org.briarproject.bramble.api.system.ResourceProvider;
|
import org.briarproject.bramble.api.system.ResourceProvider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.INFO;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class WindowsTorPlugin extends JavaTorPlugin {
|
class WindowsTorPlugin extends JavaTorPlugin {
|
||||||
|
|
||||||
@@ -49,4 +55,43 @@ class WindowsTorPlugin extends JavaTorPlugin {
|
|||||||
protected int getProcessId() {
|
protected int getProcessId() {
|
||||||
return Kernel32.INSTANCE.GetCurrentProcessId();
|
return Kernel32.INSTANCE.GetCurrentProcessId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void waitForTorToStart(Process torProcess)
|
||||||
|
throws InterruptedException, PluginException {
|
||||||
|
// On Windows the RunAsDaemon option has no effect, so Tor won't detach.
|
||||||
|
// Wait for the control port to be opened, then continue to read its
|
||||||
|
// stdout and stderr in a background thread until it exits.
|
||||||
|
BlockingQueue<Boolean> success = new ArrayBlockingQueue<>(1);
|
||||||
|
ioExecutor.execute(() -> {
|
||||||
|
boolean started = false;
|
||||||
|
// Read the process's stdout (and redirected stderr)
|
||||||
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
|
// Log the first line of stdout (contains Tor and library versions)
|
||||||
|
if (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
|
// Startup has succeeded when the control port is open
|
||||||
|
while (stdout.hasNextLine()) {
|
||||||
|
String line = stdout.nextLine();
|
||||||
|
if (!started && line.contains("Opened Control listener")) {
|
||||||
|
success.add(true);
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stdout.close();
|
||||||
|
// If the control port wasn't opened, startup has failed
|
||||||
|
if (!started) success.add(false);
|
||||||
|
// Wait for the process to exit
|
||||||
|
try {
|
||||||
|
int exit = torProcess.waitFor();
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Tor exited with value " + exit);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
LOG.warning("Interrupted while waiting for Tor to exit");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Wait for the startup result
|
||||||
|
if (!success.take()) throw new PluginException();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user