solana_validator/
lib.rs

1#![allow(clippy::arithmetic_side_effects)]
2pub use solana_test_validator as test_validator;
3use {
4    console::style,
5    fd_lock::{RwLock, RwLockWriteGuard},
6    indicatif::{ProgressDrawTarget, ProgressStyle},
7    std::{
8        borrow::Cow,
9        env,
10        fmt::Display,
11        fs::{File, OpenOptions},
12        path::Path,
13        process::exit,
14        thread::JoinHandle,
15        time::Duration,
16    },
17};
18
19pub mod admin_rpc_service;
20pub mod bootstrap;
21pub mod cli;
22pub mod dashboard;
23
24#[cfg(unix)]
25fn redirect_stderr(filename: &str) {
26    use std::os::unix::io::AsRawFd;
27    match OpenOptions::new().create(true).append(true).open(filename) {
28        Ok(file) => unsafe {
29            libc::dup2(file.as_raw_fd(), libc::STDERR_FILENO);
30        },
31        Err(err) => eprintln!("Unable to open {filename}: {err}"),
32    }
33}
34
35// Redirect stderr to a file with support for logrotate by sending a SIGUSR1 to the process.
36//
37// Upon success, future `log` macros and `eprintln!()` can be found in the specified log file.
38pub fn redirect_stderr_to_file(logfile: Option<String>) -> Option<JoinHandle<()>> {
39    // Default to RUST_BACKTRACE=1 for more informative validator logs
40    if env::var_os("RUST_BACKTRACE").is_none() {
41        env::set_var("RUST_BACKTRACE", "1")
42    }
43
44    let filter = "solana=info";
45    match logfile {
46        None => {
47            solana_logger::setup_with_default(filter);
48            None
49        }
50        Some(logfile) => {
51            #[cfg(unix)]
52            {
53                use log::info;
54                let mut signals =
55                    signal_hook::iterator::Signals::new([signal_hook::consts::SIGUSR1])
56                        .unwrap_or_else(|err| {
57                            eprintln!("Unable to register SIGUSR1 handler: {err:?}");
58                            exit(1);
59                        });
60
61                solana_logger::setup_with_default(filter);
62                redirect_stderr(&logfile);
63                Some(
64                    std::thread::Builder::new()
65                        .name("solSigUsr1".into())
66                        .spawn(move || {
67                            for signal in signals.forever() {
68                                info!(
69                                    "received SIGUSR1 ({}), reopening log file: {:?}",
70                                    signal, logfile
71                                );
72                                redirect_stderr(&logfile);
73                            }
74                        })
75                        .unwrap(),
76                )
77            }
78            #[cfg(not(unix))]
79            {
80                println!("logrotate is not supported on this platform");
81                solana_logger::setup_file_with_default(&logfile, filter);
82                None
83            }
84        }
85    }
86}
87
88pub fn format_name_value(name: &str, value: &str) -> String {
89    format!("{} {}", style(name).bold(), value)
90}
91/// Pretty print a "name value"
92pub fn println_name_value(name: &str, value: &str) {
93    println!("{}", format_name_value(name, value));
94}
95
96/// Creates a new process bar for processing that will take an unknown amount of time
97pub fn new_spinner_progress_bar() -> ProgressBar {
98    let progress_bar = indicatif::ProgressBar::new(42);
99    progress_bar.set_draw_target(ProgressDrawTarget::stdout());
100    progress_bar.set_style(
101        ProgressStyle::default_spinner()
102            .template("{spinner:.green} {wide_msg}")
103            .expect("ProgresStyle::template direct input to be correct"),
104    );
105    progress_bar.enable_steady_tick(Duration::from_millis(100));
106
107    ProgressBar {
108        progress_bar,
109        is_term: console::Term::stdout().is_term(),
110    }
111}
112
113pub struct ProgressBar {
114    progress_bar: indicatif::ProgressBar,
115    is_term: bool,
116}
117
118impl ProgressBar {
119    pub fn set_message<T: Into<Cow<'static, str>> + Display>(&self, msg: T) {
120        if self.is_term {
121            self.progress_bar.set_message(msg);
122        } else {
123            println!("{msg}");
124        }
125    }
126
127    pub fn println<I: AsRef<str>>(&self, msg: I) {
128        self.progress_bar.println(msg);
129    }
130
131    pub fn abandon_with_message<T: Into<Cow<'static, str>> + Display>(&self, msg: T) {
132        if self.is_term {
133            self.progress_bar.abandon_with_message(msg);
134        } else {
135            println!("{msg}");
136        }
137    }
138}
139
140pub fn ledger_lockfile(ledger_path: &Path) -> RwLock<File> {
141    let lockfile = ledger_path.join("ledger.lock");
142    fd_lock::RwLock::new(
143        OpenOptions::new()
144            .write(true)
145            .create(true)
146            .open(lockfile)
147            .unwrap(),
148    )
149}
150
151pub fn lock_ledger<'lock>(
152    ledger_path: &Path,
153    ledger_lockfile: &'lock mut RwLock<File>,
154) -> RwLockWriteGuard<'lock, File> {
155    ledger_lockfile.try_write().unwrap_or_else(|_| {
156        println!(
157            "Error: Unable to lock {} directory. Check if another validator is running",
158            ledger_path.display()
159        );
160        exit(1);
161    })
162}