#![allow(clippy::integer_arithmetic)] pub use solana_test_validator as test_validator; use { console::style, fd_lock::{RwLock, RwLockWriteGuard}, indicatif::{ProgressDrawTarget, ProgressStyle}, solana_net_utils::MINIMUM_VALIDATOR_PORT_RANGE_WIDTH, solana_sdk::quic::QUIC_PORT_OFFSET, std::{ borrow::Cow, env, fmt::Display, fs::{File, OpenOptions}, path::Path, process::exit, thread::JoinHandle, time::Duration, }, }; pub mod admin_rpc_service; pub mod bootstrap; pub mod dashboard; #[cfg(unix)] fn redirect_stderr(filename: &str) { use std::os::unix::io::AsRawFd; match OpenOptions::new() .write(true) .create(true) .append(true) .open(filename) { Ok(file) => unsafe { libc::dup2(file.as_raw_fd(), libc::STDERR_FILENO); }, Err(err) => eprintln!("Unable to open {}: {}", filename, err), } } // Redirect stderr to a file with support for logrotate by sending a SIGUSR1 to the process. // // Upon success, future `log` macros and `eprintln!()` can be found in the specified log file. pub fn redirect_stderr_to_file(logfile: Option) -> Option> { // Default to RUST_BACKTRACE=1 for more informative validator logs if env::var_os("RUST_BACKTRACE").is_none() { env::set_var("RUST_BACKTRACE", "1") } let filter = "solana=info"; match logfile { None => { solana_logger::setup_with_default(filter); None } Some(logfile) => { #[cfg(unix)] { use log::info; let mut signals = signal_hook::iterator::Signals::new([signal_hook::consts::SIGUSR1]) .unwrap_or_else(|err| { eprintln!("Unable to register SIGUSR1 handler: {:?}", err); exit(1); }); solana_logger::setup_with_default(filter); redirect_stderr(&logfile); Some( std::thread::Builder::new() .name("solSigUsr1".into()) .spawn(move || { for signal in signals.forever() { info!( "received SIGUSR1 ({}), reopening log file: {:?}", signal, logfile ); redirect_stderr(&logfile); } }) .unwrap(), ) } #[cfg(not(unix))] { println!("logrotate is not supported on this platform"); solana_logger::setup_file_with_default(&logfile, filter); None } } } } pub fn port_validator(port: String) -> Result<(), String> { port.parse::() .map(|_| ()) .map_err(|e| format!("{:?}", e)) } pub fn port_range_validator(port_range: String) -> Result<(), String> { if let Some((start, end)) = solana_net_utils::parse_port_range(&port_range) { if end - start < MINIMUM_VALIDATOR_PORT_RANGE_WIDTH { Err(format!( "Port range is too small. Try --dynamic-port-range {}-{}", start, start + MINIMUM_VALIDATOR_PORT_RANGE_WIDTH )) } else if end.checked_add(QUIC_PORT_OFFSET).is_none() { Err("Invalid dynamic_port_range.".to_string()) } else { Ok(()) } } else { Err("Invalid port range".to_string()) } } pub fn format_name_value(name: &str, value: &str) -> String { format!("{} {}", style(name).bold(), value) } /// Pretty print a "name value" pub fn println_name_value(name: &str, value: &str) { println!("{}", format_name_value(name, value)); } /// Creates a new process bar for processing that will take an unknown amount of time pub fn new_spinner_progress_bar() -> ProgressBar { let progress_bar = indicatif::ProgressBar::new(42); progress_bar.set_draw_target(ProgressDrawTarget::stdout()); progress_bar.set_style( ProgressStyle::default_spinner() .template("{spinner:.green} {wide_msg}") .expect("ProgresStyle::template direct input to be correct"), ); progress_bar.enable_steady_tick(Duration::from_millis(100)); ProgressBar { progress_bar, is_term: console::Term::stdout().is_term(), } } pub struct ProgressBar { progress_bar: indicatif::ProgressBar, is_term: bool, } impl ProgressBar { pub fn set_message> + Display>(&self, msg: T) { if self.is_term { self.progress_bar.set_message(msg); } else { println!("{}", msg); } } pub fn println>(&self, msg: I) { self.progress_bar.println(msg); } pub fn abandon_with_message> + Display>(&self, msg: T) { if self.is_term { self.progress_bar.abandon_with_message(msg); } else { println!("{}", msg); } } } pub fn ledger_lockfile(ledger_path: &Path) -> RwLock { let lockfile = ledger_path.join("ledger.lock"); fd_lock::RwLock::new( OpenOptions::new() .write(true) .create(true) .open(lockfile) .unwrap(), ) } pub fn lock_ledger<'path, 'lock>( ledger_path: &'path Path, ledger_lockfile: &'lock mut RwLock, ) -> RwLockWriteGuard<'lock, File> { ledger_lockfile.try_write().unwrap_or_else(|_| { println!( "Error: Unable to lock {} directory. Check if another validator is running", ledger_path.display() ); exit(1); }) }