Esempio n. 1
0
        /// <summary>
        /// Stops bitcoin daemon process when PID file exists.
        /// </summary>
        /// <remarks>If there is not PID file, no process is stopped.</remarks>
        /// <param name="onlyOwned">Only stop if this node owns the process.</param>
        public async Task StopAsync(bool onlyOwned)
        {
            Logger.LogDebug($"> {nameof(onlyOwned)}={onlyOwned}");

            if (Process is null)
            {
                Logger.LogDebug("< Process is null.");
                return;
            }

            // "process" variable is guaranteed to be non-null at this point.
            ProcessAsync process = Process;

            using var cts = new CancellationTokenSource(_reasonableCoreShutdownTimeout);
            int?pid = await PidFile.TryReadAsync().ConfigureAwait(false);

            // If the cached PID is PID, then we own the process.
            if (pid.HasValue && (!onlyOwned || CachedPid == pid))
            {
                Logger.LogDebug($"User is responsible for the daemon process with PID {pid}. Stop it.");

                try
                {
                    bool isKilled = false;

                    try
                    {
                        // Stop Bitcoin daemon using RPC "stop" command.
                        // The command actually only initiates the bitcoind graceful shutdown procedure.
                        // Our time budget for the bitcoind to stop is given by "ReasonableCoreShutdownTimeout".
                        await RpcClient.StopAsync().ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogWarning(ex);
                        process.Kill();
                        isKilled = true;
                    }

                    if (!isKilled)
                    {
                        Logger.LogDebug($"Wait until the process is stopped.");
                        await process.WaitForExitAsync(cts.Token).ConfigureAwait(false);
                    }
                }
                finally
                {
                    Logger.LogDebug($"Wait until the process is stopped.");
                    process.Dispose();
                    Process = null;
                    PidFile.TryDelete();
                }
            }
            else
            {
                Logger.LogDebug("User is NOT responsible for the daemon process.");
            }

            Logger.LogDebug("<");
        }
Esempio n. 2
0
        /// <summary>
        /// Stops bitcoin daemon process when PID file exists.
        /// </summary>
        /// <remarks>If there is not PID file, no process is stopped.</remarks>
        /// <param name="onlyOwned">Only stop if this node owns the process.</param>
        public async Task StopAsync(bool onlyOwned)
        {
            Logger.LogDebug($"> {nameof(onlyOwned)}={onlyOwned}");

            if (Process is null)
            {
                Logger.LogDebug("< Process is null.");
                return;
            }

            ProcessAsync process = Process;             // process is guaranteed to be non-null at this point.

            using var cts = new CancellationTokenSource(ReasonableCoreShutdownTimeout);
            int?pid = await PidFile.TryReadAsync().ConfigureAwait(false);

            // If the cached PID is PID, then we own the process.
            if (pid.HasValue && (!onlyOwned || CachedPid == pid))
            {
                Logger.LogDebug($"User is responsible for the daemon process with PID {pid}. Stop it.");

                try
                {
                    try
                    {
                        await RpcClient.StopAsync().ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        Logger.LogWarning(ex);
                        process.Kill();
                    }

                    Logger.LogDebug($"Wait until the process is stopped.");
                    await process.WaitForExitAsync(cts.Token).ConfigureAwait(false);
                }
                finally
                {
                    Logger.LogDebug($"Wait until the process is stopped.");
                    process.Dispose();
                    Process = null;
                    PidFile.TryDelete();
                }
            }
            else
            {
                Logger.LogDebug("User is NOT responsible for the daemon process.");
            }

            Logger.LogDebug("<");
        }
Esempio n. 3
0
        /// <summary>
        /// This method can be called only once.
        /// </summary>
        public async Task StartAsync(CancellationToken cancel)
        {
            int    ptcv            = PrintToConsole ? 1 : 0;
            string processPath     = MicroserviceHelpers.GetBinaryPath("bitcoind");
            string networkArgument = NetworkTranslator.GetCommandLineArguments(Network);

            string args = $"{networkArgument} -datadir=\"{DataDir}\" -printtoconsole={ptcv}";

            // Start bitcoind process.
            Process = new ProcessAsync(ProcessStartInfoFactory.Make(processPath, args));
            Process.Start();

            // Store PID in PID file.
            await PidFile.WriteFileAsync(Process.Id).ConfigureAwait(false);

            CachedPid = Process.Id;

            try
            {
                var exceptionTracker = new LastExceptionTracker();

                // Try to connect to bitcoin daemon RPC until we succeed.
                while (true)
                {
                    try
                    {
                        TimeSpan timeSpan = await RpcClient.UptimeAsync(cancel).ConfigureAwait(false);

                        Logger.LogInfo("RPC connection is successfully established.");
                        Logger.LogDebug($"RPC uptime is: {timeSpan}.");

                        // Bitcoin daemon is started. We are done.
                        break;
                    }
                    catch (Exception ex)
                    {
                        ExceptionInfo exceptionInfo = exceptionTracker.Process(ex);

                        // Don't log extensively.
                        if (exceptionInfo.IsFirst)
                        {
                            Logger.LogInfo($"{Constants.BuiltinBitcoinNodeName} is not yet ready... Reason: {exceptionInfo.Exception.Message}");
                        }

                        if (Process is { } p&& p.HasExited)
                        {
                            throw new BitcoindException($"Failed to start daemon, location: '{p.StartInfo.FileName} {p.StartInfo.Arguments}'", ex);
                        }
                    }

                    if (cancel.IsCancellationRequested)
                    {
                        Logger.LogDebug("Bitcoin daemon was not started yet and user requested to cancel the operation.");
                        await StopAsync(onlyOwned : true).ConfigureAwait(false);

                        cancel.ThrowIfCancellationRequested();
                    }

                    // Wait a moment before the next check.
                    await Task.Delay(100, cancel).ConfigureAwait(false);
                }
            }
            catch (Exception)
            {
                Process?.Dispose();
                throw;
            }
        }
Esempio n. 4
0
        /// <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);
        }