Merge #1696: feat: asynchronous management of internal bitcoind
8c0c23e688b02a73393a7172b3f41bfaa2a9c143 Use gui panic hook (edouardparis)
40dc9ae8c01da0b250bf0e8ea40e275ef77bd23c Delete process internal bitcoind locks in case of panic (edouardparis)
0488ed37a3651add672212664d59f637be17c716 feat: asynchronous management of internal bitcoind (edouardparis)
Pull request description:
This PR introduces a feature allowing multiple liana processes to the same internally managed bitcoind binary,
by having the first process running a wallet relying a managed bitcoind to start it and the last process standing stopping it when closing.
A new directory `./liana/bitcoind/locks` is introduces where a Liana process creates a file `pid-timestamp` when it starts or uses the internally managed bitcoind binary and removes it when it does not use it anymore.
Another Liana process can infer that it can stop the bitcoind binary by looking if any other lock file is present in this directory.
In order to make the bitcoind child process survive once the Liana process that started it is closed, the `libc::setsid();` method for unix and the `DETACHED_PROCESS` flag for windows are added to the process command.
The current panic hook of the gui was override by the one present in lianad daemon start method. The latter was moved to the daemon main.rs file.
ACKs for top commit:
edouardparis:
Self-ACK 8c0c23e688b02a73393a7172b3f41bfaa2a9c143
Tree-SHA512: b8ea415750c656786b64bc7ea97194f27db37267820f1a413ce6639e48dae542e644958856ffaddd53ab1ad047072a9bcc11ad304c060417b9542d4ee61089a3
This commit is contained in:
commit
b9a200af0b
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3025,6 +3025,7 @@ dependencies = [
|
||||
"liana",
|
||||
"liana-ui",
|
||||
"lianad",
|
||||
"libc",
|
||||
"log",
|
||||
"reqwest",
|
||||
"rfd",
|
||||
|
||||
@ -47,6 +47,7 @@ toml = "0.5"
|
||||
chrono = "0.4.38"
|
||||
|
||||
# Used for managing internal bitcoind
|
||||
libc = "0.2"
|
||||
base64 = "0.21"
|
||||
bitcoin_hashes = "0.12"
|
||||
reqwest = { version = "0.11", default-features=false, features = ["json", "rustls-tls", "stream"] }
|
||||
|
||||
@ -11,6 +11,7 @@ pub struct Config {
|
||||
/// Use iced debug feature if true.
|
||||
pub debug: Option<bool>,
|
||||
/// Start internal bitcoind executable.
|
||||
/// Legacy field, replaced by settings.json start_internal_bitcoind field
|
||||
#[serde(default)]
|
||||
pub start_internal_bitcoind: bool,
|
||||
}
|
||||
|
||||
@ -300,7 +300,7 @@ impl App {
|
||||
} else {
|
||||
info!("Internal daemon stopped");
|
||||
}
|
||||
if let Some(bitcoind) = &self.internal_bitcoind {
|
||||
if let Some(bitcoind) = self.internal_bitcoind.take() {
|
||||
bitcoind.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,6 +96,9 @@ pub struct WalletSetting {
|
||||
#[serde(default)]
|
||||
pub hardware_wallets: Vec<HardwareWalletConfig>,
|
||||
pub remote_backend_auth: Option<AuthConfig>,
|
||||
/// Start internal bitcoind executable.
|
||||
/// if None, the app must refer to the gui.toml start_internal_bitcoind field.
|
||||
pub start_internal_bitcoind: Option<bool>,
|
||||
}
|
||||
|
||||
impl WalletSetting {
|
||||
|
||||
@ -132,6 +132,7 @@ impl Wallet {
|
||||
// Only local wallet from previous version of Liana GUI may not have a
|
||||
// settings.json file
|
||||
remote_backend_auth: None,
|
||||
start_internal_bitcoind: None,
|
||||
}],
|
||||
};
|
||||
|
||||
|
||||
@ -188,10 +188,9 @@ impl Installer {
|
||||
.expect("There is always a step")
|
||||
.stop();
|
||||
// Now use context to determine what to stop.
|
||||
if let Some(bitcoind) = &self.context.internal_bitcoind {
|
||||
if let Some(bitcoind) = self.context.internal_bitcoind.take() {
|
||||
bitcoind.stop();
|
||||
}
|
||||
self.context.internal_bitcoind = None;
|
||||
}
|
||||
|
||||
fn skip_steps(&mut self) {
|
||||
@ -675,6 +674,7 @@ pub async fn extract_remote_gui_settings(ctx: &Context, backend: &BackendWalletC
|
||||
backend.user_email().to_string(),
|
||||
backend.wallet_id(),
|
||||
)),
|
||||
start_internal_bitcoind: None,
|
||||
}],
|
||||
}
|
||||
}
|
||||
@ -708,6 +708,7 @@ pub fn extract_local_gui_settings(ctx: &Context) -> Settings {
|
||||
keys: ctx.keys.values().cloned().collect(),
|
||||
hardware_wallets,
|
||||
remote_backend_auth: None,
|
||||
start_internal_bitcoind: Some(ctx.internal_bitcoind.is_some()),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ pub trait Step {
|
||||
true
|
||||
}
|
||||
fn revert(&self, _ctx: &mut Context) {}
|
||||
fn stop(&self) {}
|
||||
fn stop(&mut self) {}
|
||||
}
|
||||
|
||||
pub struct Final {
|
||||
|
||||
@ -554,10 +554,9 @@ impl Step for InternalBitcoindStep {
|
||||
if let Message::InternalBitcoind(msg) = message {
|
||||
match msg {
|
||||
message::InternalBitcoindMsg::Previous => {
|
||||
if let Some(bitcoind) = &self.internal_bitcoind {
|
||||
if let Some(bitcoind) = self.internal_bitcoind.take() {
|
||||
bitcoind.stop();
|
||||
}
|
||||
self.internal_bitcoind = None;
|
||||
if let Some(download) = self.exe_download.as_ref() {
|
||||
// Clear exe_download if not Finished.
|
||||
if let DownloadState::Finished { .. } = download.state {
|
||||
@ -707,7 +706,8 @@ impl Step for InternalBitcoindStep {
|
||||
.as_ref()
|
||||
.expect("already added")
|
||||
.clone();
|
||||
match Bitcoind::start(&self.network, bitcoind_config, &self.liana_datadir) {
|
||||
match Bitcoind::maybe_start(self.network, bitcoind_config, &self.liana_datadir)
|
||||
{
|
||||
Err(e) => {
|
||||
self.started =
|
||||
Some(Err(StartInternalBitcoindError::CommandError(e.to_string())));
|
||||
@ -785,9 +785,9 @@ impl Step for InternalBitcoindStep {
|
||||
)
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
fn stop(&mut self) {
|
||||
// In case the installer is closed before changes written to context, stop bitcoind.
|
||||
if let Some(bitcoind) = &self.internal_bitcoind {
|
||||
if let Some(bitcoind) = self.internal_bitcoind.take() {
|
||||
bitcoind.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ use lianad::{
|
||||
};
|
||||
|
||||
use crate::app;
|
||||
use crate::app::settings::WalletSetting;
|
||||
use crate::backup::Backup;
|
||||
use crate::dir::LianaDirectory;
|
||||
use crate::export::RestoreBackupError;
|
||||
@ -33,9 +34,7 @@ use crate::{
|
||||
wallet::{Wallet, WalletError},
|
||||
},
|
||||
daemon::{client, embedded::EmbeddedDaemon, model::*, Daemon, DaemonError},
|
||||
node::bitcoind::{
|
||||
internal_bitcoind_debug_log_path, stop_bitcoind, Bitcoind, StartInternalBitcoindError,
|
||||
},
|
||||
node::bitcoind::{internal_bitcoind_debug_log_path, Bitcoind, StartInternalBitcoindError},
|
||||
};
|
||||
|
||||
const SYNCING_PROGRESS_1: &str = "Bitcoin Core is synchronising the blockchain. A full synchronisation typically takes a few days and is resource-intensive. Once the initial synchronisation is done, the next ones will be much faster.";
|
||||
@ -60,6 +59,7 @@ pub struct Loader {
|
||||
pub internal_bitcoind: Option<Bitcoind>,
|
||||
pub waiting_daemon_bitcoind: bool,
|
||||
pub backup: Option<Backup>,
|
||||
pub wallet_setting: Option<WalletSetting>,
|
||||
step: Step,
|
||||
}
|
||||
|
||||
@ -119,6 +119,7 @@ impl Loader {
|
||||
network: bitcoin::Network,
|
||||
internal_bitcoind: Option<Bitcoind>,
|
||||
backup: Option<Backup>,
|
||||
wallet_setting: Option<WalletSetting>,
|
||||
) -> (Self, Task<Message>) {
|
||||
let path = socket_path(&datadir_path, network);
|
||||
(
|
||||
@ -130,12 +131,27 @@ impl Loader {
|
||||
daemon_started: false,
|
||||
internal_bitcoind,
|
||||
waiting_daemon_bitcoind: false,
|
||||
wallet_setting,
|
||||
backup,
|
||||
},
|
||||
Task::perform(connect(path), Message::Loaded),
|
||||
)
|
||||
}
|
||||
|
||||
fn start_bitcoind(&self) -> bool {
|
||||
if self.internal_bitcoind.is_some() {
|
||||
false
|
||||
} else if let Some(start) = self
|
||||
.wallet_setting
|
||||
.as_ref()
|
||||
.and_then(|setting| setting.start_internal_bitcoind)
|
||||
{
|
||||
start
|
||||
} else {
|
||||
self.gui_config.start_internal_bitcoind
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_skip_syncing(
|
||||
&mut self,
|
||||
daemon: Arc<dyn Daemon + Sync + Send>,
|
||||
@ -189,8 +205,7 @@ impl Loader {
|
||||
return Task::perform(
|
||||
start_bitcoind_and_daemon(
|
||||
self.datadir_path.clone(),
|
||||
self.gui_config.start_internal_bitcoind
|
||||
&& self.internal_bitcoind.is_none(),
|
||||
self.start_bitcoind(),
|
||||
self.network,
|
||||
),
|
||||
Message::Started,
|
||||
@ -281,25 +296,7 @@ impl Loader {
|
||||
// NOTE: we take() the internal_bitcoind here to make sure the debug.log reader
|
||||
// subscription is dropped.
|
||||
if let Some(bitcoind) = self.internal_bitcoind.take() {
|
||||
log::info!("Stopping managed bitcoind..");
|
||||
bitcoind.stop();
|
||||
log::info!("Managed bitcoind stopped.");
|
||||
} else if self.waiting_daemon_bitcoind && self.gui_config.start_internal_bitcoind {
|
||||
let mut daemon_config_path = self
|
||||
.datadir_path
|
||||
.network_directory(self.network)
|
||||
.path()
|
||||
.to_path_buf();
|
||||
daemon_config_path.push("daemon.toml");
|
||||
if let Ok(config) = Config::from_file(Some(daemon_config_path)) {
|
||||
if let Some(BitcoinBackend::Bitcoind(bitcoind_config)) = &config.bitcoin_backend {
|
||||
let mut retry = 0;
|
||||
while !stop_bitcoind(bitcoind_config) && retry < 10 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(500));
|
||||
retry += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,6 +309,7 @@ impl Loader {
|
||||
self.network,
|
||||
self.internal_bitcoind.clone(),
|
||||
self.backup.clone(),
|
||||
self.wallet_setting.clone(),
|
||||
);
|
||||
*self = loader;
|
||||
cmd
|
||||
@ -562,26 +560,17 @@ pub async fn start_bitcoind_and_daemon(
|
||||
.to_path_buf();
|
||||
config_path.push("daemon.toml");
|
||||
let config = Config::from_file(Some(config_path)).map_err(Error::Config)?;
|
||||
let mut bitcoind: Option<Bitcoind> = None;
|
||||
if start_internal_bitcoind {
|
||||
if let Some(BitcoinBackend::Bitcoind(bitcoind_config)) = &config.bitcoin_backend {
|
||||
// Check if bitcoind is already running before trying to start it.
|
||||
if lianad::BitcoinD::new(bitcoind_config, "internal_bitcoind_start".to_string()).is_ok()
|
||||
{
|
||||
info!("Internal bitcoind is already running");
|
||||
} else {
|
||||
info!("Starting internal bitcoind");
|
||||
bitcoind = Some(
|
||||
Bitcoind::start(
|
||||
&config.bitcoin_config.network,
|
||||
bitcoind_config.clone(),
|
||||
&liana_datadir_path,
|
||||
)
|
||||
.map_err(Error::Bitcoind)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let bitcoind = match (start_internal_bitcoind, &config.bitcoin_backend) {
|
||||
(true, Some(BitcoinBackend::Bitcoind(bitcoind_config))) => Some(
|
||||
Bitcoind::maybe_start(
|
||||
config.bitcoin_config.network,
|
||||
bitcoind_config.clone(),
|
||||
&liana_datadir_path,
|
||||
)
|
||||
.map_err(Error::Bitcoind)?,
|
||||
),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
debug!("starting liana daemon");
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ use liana_gui::{
|
||||
launcher::{self, Launcher},
|
||||
loader::{self, Loader},
|
||||
logger::Logger,
|
||||
node::bitcoind::delete_all_bitcoind_locks_for_process,
|
||||
services::connect::{
|
||||
client::backend::{api, BackendWalletClient},
|
||||
login,
|
||||
@ -208,10 +209,9 @@ impl GUI {
|
||||
);
|
||||
let network_dir = datadir_path.network_directory(network);
|
||||
if let Ok(settings) = app::settings::Settings::from_file(&network_dir) {
|
||||
if let Some(setting) = settings
|
||||
.wallets
|
||||
.into_iter()
|
||||
.find_map(|w| w.remote_backend_auth)
|
||||
let setting = settings.wallets.into_iter().next();
|
||||
if let Some(setting) =
|
||||
setting.as_ref().and_then(|w| w.remote_backend_auth.clone())
|
||||
{
|
||||
let (login, command) =
|
||||
login::LianaLiteLogin::new(datadir_path, network, setting);
|
||||
@ -219,12 +219,13 @@ impl GUI {
|
||||
command.map(|msg| Message::Login(Box::new(msg)))
|
||||
} else {
|
||||
let (loader, command) =
|
||||
Loader::new(datadir_path, cfg, network, None, None);
|
||||
Loader::new(datadir_path, cfg, network, None, None, setting);
|
||||
self.state = State::Loader(Box::new(loader));
|
||||
command.map(|msg| Message::Load(Box::new(msg)))
|
||||
}
|
||||
} else {
|
||||
let (loader, command) = Loader::new(datadir_path, cfg, network, None, None);
|
||||
let (loader, command) =
|
||||
Loader::new(datadir_path, cfg, network, None, None, None);
|
||||
self.state = State::Loader(Box::new(loader));
|
||||
command.map(|msg| Message::Load(Box::new(msg)))
|
||||
}
|
||||
@ -280,10 +281,9 @@ impl GUI {
|
||||
let network_dir = i.datadir.network_directory(i.network);
|
||||
let settings = app::settings::Settings::from_file(&network_dir)
|
||||
.expect("A settings file was created");
|
||||
if let Some(setting) = settings
|
||||
.wallets
|
||||
.into_iter()
|
||||
.find_map(|w| w.remote_backend_auth)
|
||||
let setting = settings.wallets.into_iter().next();
|
||||
if let Some(setting) =
|
||||
setting.as_ref().and_then(|w| w.remote_backend_auth.clone())
|
||||
{
|
||||
let (login, command) =
|
||||
login::LianaLiteLogin::new(i.datadir.clone(), i.network, setting);
|
||||
@ -308,6 +308,7 @@ impl GUI {
|
||||
i.network,
|
||||
internal_bitcoind,
|
||||
i.context.backup.take(),
|
||||
setting,
|
||||
);
|
||||
self.state = State::Loader(Box::new(loader));
|
||||
command.map(|msg| Message::Load(Box::new(msg)))
|
||||
@ -537,7 +538,7 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
None
|
||||
};
|
||||
|
||||
setup_panic_hook();
|
||||
setup_panic_hook(&config.liana_directory);
|
||||
|
||||
let settings = Settings {
|
||||
id: Some("Liana".to_string()),
|
||||
@ -584,8 +585,13 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
// A panic in any thread should stop the main thread, and print the panic.
|
||||
fn setup_panic_hook() {
|
||||
fn setup_panic_hook(liana_directory: &LianaDirectory) {
|
||||
let bitcoind_dir = liana_directory.bitcoind_directory();
|
||||
std::panic::set_hook(Box::new(move |panic_info| {
|
||||
error!("Panic occured");
|
||||
if let Err(e) = delete_all_bitcoind_locks_for_process(bitcoind_dir.clone()) {
|
||||
error!("Failed to delete internal bitcoind locks: {}", e);
|
||||
}
|
||||
let file = panic_info
|
||||
.location()
|
||||
.map(|l| l.file())
|
||||
|
||||
@ -10,20 +10,23 @@ use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use tracing::{info, warn};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
use crate::dir::LianaDirectory;
|
||||
use crate::dir::{BitcoindDirectory, LianaDirectory};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const DETACHED_PROCESS: u32 = 0x00000008;
|
||||
|
||||
/// Current and previous managed bitcoind versions, in order of descending version.
|
||||
pub const VERSIONS: [&str; 7] = ["29.0", "28.0", "27.1", "26.1", "26.0", "25.1", "25.0"];
|
||||
|
||||
@ -371,6 +374,7 @@ impl InternalBitcoindConfig {
|
||||
/// Possible errors when starting bitcoind.
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
pub enum StartInternalBitcoindError {
|
||||
Lock(String),
|
||||
CommandError(String),
|
||||
CouldNotCanonicalizeDataDir(String),
|
||||
BitcoinDError(String),
|
||||
@ -381,6 +385,9 @@ pub enum StartInternalBitcoindError {
|
||||
impl std::fmt::Display for StartInternalBitcoindError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Lock(e) => {
|
||||
write!(f, "lock file error: {}", e)
|
||||
}
|
||||
Self::CommandError(e) => {
|
||||
write!(f, "Command to start bitcoind returned an error: {}", e)
|
||||
}
|
||||
@ -397,17 +404,25 @@ impl std::fmt::Display for StartInternalBitcoindError {
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bitcoind {
|
||||
_process: Arc<std::process::Child>,
|
||||
pub config: BitcoindConfig,
|
||||
lock: LockFile,
|
||||
}
|
||||
|
||||
impl Bitcoind {
|
||||
/// Start internal bitcoind for the given network.
|
||||
pub fn start(
|
||||
network: &bitcoin::Network,
|
||||
pub fn maybe_start(
|
||||
network: bitcoin::Network,
|
||||
config: BitcoindConfig,
|
||||
liana_datadir: &LianaDirectory,
|
||||
) -> Result<Self, StartInternalBitcoindError> {
|
||||
if lianad::BitcoinD::new(&config, "internal_bitcoind_start".to_string()).is_ok() {
|
||||
info!("Internal bitcoind is already running");
|
||||
return Ok(Bitcoind {
|
||||
config,
|
||||
lock: LockFile::create(liana_datadir.bitcoind_directory(), network)
|
||||
.map_err(|e| StartInternalBitcoindError::Lock(format!("{:?}", e)))?,
|
||||
});
|
||||
}
|
||||
let bitcoind_datadir = internal_bitcoind_datadir(liana_datadir);
|
||||
// Find most recent bitcoind version available.
|
||||
let bitcoind_exe_path = VERSIONS
|
||||
@ -448,7 +463,19 @@ impl Bitcoind {
|
||||
let mut command = std::process::Command::new(bitcoind_exe_path);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let command = command.creation_flags(CREATE_NO_WINDOW);
|
||||
let command = command.creation_flags(CREATE_NO_WINDOW | DETACHED_PROCESS);
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::process::CommandExt;
|
||||
// Create a new session to detach the child from the main process.
|
||||
unsafe {
|
||||
command.pre_exec(|| {
|
||||
libc::setsid();
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut process = command
|
||||
.args(&args)
|
||||
@ -474,7 +501,8 @@ impl Bitcoind {
|
||||
log::info!("Bitcoind seems to have successfully started.");
|
||||
return Ok(Self {
|
||||
config,
|
||||
_process: Arc::new(process),
|
||||
lock: LockFile::create(liana_datadir.bitcoind_directory(), network)
|
||||
.map_err(|e| StartInternalBitcoindError::Lock(format!("{:?}", e)))?,
|
||||
});
|
||||
}
|
||||
Err(lianad::BitcoindError::CookieFile(_)) => {
|
||||
@ -498,26 +526,127 @@ impl Bitcoind {
|
||||
}
|
||||
|
||||
/// Stop (internal) bitcoind.
|
||||
pub fn stop(&self) {
|
||||
stop_bitcoind(&self.config);
|
||||
pub fn stop(self) {
|
||||
match self.lock.delete() {
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to release bitcoind lock: {}", e);
|
||||
}
|
||||
Ok(false) => {
|
||||
info!("Other processes are using internal bitcoind. Process lock has been deleted");
|
||||
}
|
||||
Ok(true) => {
|
||||
match lianad::BitcoinD::new(&self.config, "internal_bitcoind_stop".to_string()) {
|
||||
Ok(bitcoind) => {
|
||||
info!("Stopping internal bitcoind...");
|
||||
bitcoind.stop();
|
||||
info!("Stopped liana managed bitcoind");
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Could not create interface to internal bitcoind: '{}'.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop_bitcoind(config: &BitcoindConfig) -> bool {
|
||||
match lianad::BitcoinD::new(config, "internal_bitcoind_stop".to_string()) {
|
||||
Ok(bitcoind) => {
|
||||
info!("Stopping internal bitcoind...");
|
||||
bitcoind.stop();
|
||||
info!("Stopped liana managed bitcoind");
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Could not create interface to internal bitcoind: '{}'.", e);
|
||||
false
|
||||
const LOCK_DIRECTORY_NAME: &str = "locks";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct LockFile {
|
||||
path: PathBuf,
|
||||
directory: BitcoindDirectory,
|
||||
network: Network,
|
||||
}
|
||||
|
||||
impl LockFile {
|
||||
fn create(
|
||||
directory: BitcoindDirectory,
|
||||
network: Network,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let mut path = directory.clone().path().to_path_buf();
|
||||
path.push(LOCK_DIRECTORY_NAME);
|
||||
path.push(network.to_string());
|
||||
std::fs::create_dir_all(&path)?;
|
||||
|
||||
path.push(format!(
|
||||
"{}-{}.lock",
|
||||
std::process::id(),
|
||||
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs()
|
||||
));
|
||||
|
||||
std::fs::File::create(&path)?;
|
||||
Ok(Self {
|
||||
path,
|
||||
directory,
|
||||
network,
|
||||
})
|
||||
}
|
||||
|
||||
// returns true if the lock directory is removed because empty.
|
||||
fn delete(self) -> Result<bool, Box<dyn std::error::Error>> {
|
||||
std::fs::remove_file(self.path)?;
|
||||
if std::fs::read_dir(
|
||||
self.directory
|
||||
.path()
|
||||
.join(LOCK_DIRECTORY_NAME)
|
||||
.join(self.network.to_string()),
|
||||
)?
|
||||
.next()
|
||||
.is_none()
|
||||
{
|
||||
std::fs::remove_dir(
|
||||
self.directory
|
||||
.path()
|
||||
.join(LOCK_DIRECTORY_NAME)
|
||||
.join(self.network.to_string()),
|
||||
)?;
|
||||
|
||||
if std::fs::read_dir(self.directory.path().join(LOCK_DIRECTORY_NAME))?
|
||||
.next()
|
||||
.is_none()
|
||||
{
|
||||
std::fs::remove_dir(self.directory.path().join(LOCK_DIRECTORY_NAME))?;
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In case of panic, we remove all the bitcoind locks created by the process.
|
||||
pub fn delete_all_bitcoind_locks_for_process(
|
||||
directory: BitcoindDirectory,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let locks_directory = directory.path().join(LOCK_DIRECTORY_NAME);
|
||||
if !locks_directory.exists() {
|
||||
tracing::debug!("No internal bitcoind locks for the current process");
|
||||
return Ok(());
|
||||
}
|
||||
tracing::info!("Deleting all internal bitcoind locks for the current process");
|
||||
let process_prefix = format!("{}-", std::process::id());
|
||||
for network_dir in std::fs::read_dir(&locks_directory)? {
|
||||
let dir = network_dir?.path();
|
||||
for lock_file in std::fs::read_dir(&dir)? {
|
||||
let file = lock_file?.path();
|
||||
if let Some(name) = file.file_name().and_then(|n| n.to_str()) {
|
||||
if name.starts_with(&process_prefix) {
|
||||
std::fs::remove_file(file)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if std::fs::read_dir(&dir)?.next().is_none() {
|
||||
std::fs::remove_dir(dir)?;
|
||||
}
|
||||
}
|
||||
if std::fs::read_dir(&locks_directory)?.next().is_none() {
|
||||
std::fs::remove_dir(locks_directory)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum RpcAuthType {
|
||||
CookieFile,
|
||||
|
||||
@ -5,7 +5,7 @@ use std::{
|
||||
process, thread, time,
|
||||
};
|
||||
|
||||
use lianad::{config::Config, DaemonHandle, VERSION};
|
||||
use lianad::{config::Config, setup_panic_hook, DaemonHandle, VERSION};
|
||||
|
||||
fn print_help_exit(code: i32) {
|
||||
eprintln!("lianad version {}", VERSION);
|
||||
@ -80,6 +80,8 @@ fn main() {
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
setup_panic_hook();
|
||||
|
||||
let handle = DaemonHandle::start_default(config, cfg!(unix)).unwrap_or_else(|e| {
|
||||
log::error!("Error starting Liana daemon: {}", e);
|
||||
process::exit(1);
|
||||
|
||||
@ -40,7 +40,7 @@ use miniscript::bitcoin::{constants::ChainHash, hashes::Hash, secp256k1, BlockHa
|
||||
use std::panic;
|
||||
// A panic in any thread should stop the main thread, and print the panic.
|
||||
#[cfg(not(test))]
|
||||
fn setup_panic_hook() {
|
||||
pub fn setup_panic_hook() {
|
||||
panic::set_hook(Box::new(move |panic_info| {
|
||||
let file = panic_info
|
||||
.location()
|
||||
@ -396,9 +396,6 @@ impl DaemonHandle {
|
||||
db: Option<impl DatabaseInterface + 'static>,
|
||||
with_rpc_server: bool,
|
||||
) -> Result<Self, StartupError> {
|
||||
#[cfg(not(test))]
|
||||
setup_panic_hook();
|
||||
|
||||
let secp = secp256k1::Secp256k1::verification_only();
|
||||
|
||||
// First, check the data directory
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user