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
35pub fn redirect_stderr_to_file(logfile: Option<String>) -> Option<JoinHandle<()>> {
39 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}
91pub fn println_name_value(name: &str, value: &str) {
93 println!("{}", format_name_value(name, value));
94}
95
96pub 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}