/// <summary>Ensure <paramref name="process"/> is actually running.</summary> private async Task <bool> EnsureRunningAsync(ProcessAsync process, CancellationToken token) { int i = 0; while (true) { i++; bool isRunning = await TcpConnectionFactory.IsTorRunningAsync().ConfigureAwait(false); if (isRunning) { return(true); } if (process.HasExited) { Logger.LogError("Tor process failed to start!"); return(false); } const int MaxAttempts = 25; if (i >= MaxAttempts) { Logger.LogError($"All {MaxAttempts} attempts to connect to Tor failed."); return(false); } // Wait 250 milliseconds between attempts. await Task.Delay(250, token).ConfigureAwait(false); } }
/// <summary>Starts Tor process if it is not running already.</summary> /// <exception cref="OperationCanceledException"/> public async Task <bool> StartAsync(CancellationToken token = default) { ThrowIfDisposed(); ProcessAsync? process = null; TorControlClient?controlClient = null; try { // Is Tor already running? Either our Tor process from previous Wasabi Wallet run or possibly user's own Tor. bool isAlreadyRunning = await TcpConnectionFactory.IsTorRunningAsync().ConfigureAwait(false); if (isAlreadyRunning) { Logger.LogInfo($"Tor is already running on {Settings.SocksEndpoint.Address}:{Settings.SocksEndpoint.Port}."); TorControlClient = await InitTorControlAsync(token).ConfigureAwait(false); return(true); } string arguments = Settings.GetCmdArguments(); process = StartProcess(arguments); bool isRunning = await EnsureRunningAsync(process, token).ConfigureAwait(false); if (!isRunning) { return(false); } controlClient = await InitTorControlAsync(token).ConfigureAwait(false); Logger.LogInfo("Tor is running."); // Only now we know that Tor process is fully started. TorProcess = process; TorControlClient = controlClient; controlClient = null; process = null; return(true); } catch (OperationCanceledException ex) { Logger.LogDebug("User canceled operation.", ex); throw; } catch (Exception ex) { Logger.LogError("Could not automatically start Tor. Try running Tor manually.", ex); } finally { if (controlClient is not null) { await controlClient.DisposeAsync().ConfigureAwait(false); } process?.Dispose(); } return(false); }