Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 4 additions & 82 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 1 addition & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,24 @@ futures = "0.3.32"
async-trait = "0.1.89"
indicatif = "0.18.4"
rpassword = "7.4.0"
directories = "6.0.0"
dirs = "6.0"
chrono = { version = "0.4.44", features = ["serde"] }
glob = "0.3.3"
whoami = "2.1.1"
owo-colors = "4.3.0"
unicode-width = "0.2.2"
terminal_size = "0.4.4"
once_cell = "1.21.4"
zeroize = { version = "1.8.2", features = ["derive"] }
secrecy = { version = "0.10.3", features = ["serde"] }
rustyline = "18.0.0"
crossterm = "0.29"
ratatui = "0.30"
regex = "1.12.3"
lazy_static = "1.5"
ctrlc = "3.5.2"
signal-hook = "0.4.4"
signal-hook = "0.3"
nix = { version = "0.31", features = ["fs", "poll", "process", "signal", "term"] }
arrayvec = "0.7.6"
smallvec = "1.15.1"
lru = "0.17.0"
uuid = { version = "1.23.1", features = ["v4"] }
fastrand = "2.4.1"
tokio-util = "0.7.18"
socket2 = "0.6"
shell-words = "1.1.1"
Expand All @@ -85,7 +79,6 @@ security-framework = "3.7.0"
[dev-dependencies]
tempfile = "3.27.0"
mockito = "1.7.2"
once_cell = "1.21.4"
tokio-test = "0.4"
serial_test = "3.4"
insta = "1.47"
Expand Down
30 changes: 19 additions & 11 deletions src/commands/interactive_signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,29 @@ pub fn reset_interrupt() {
INTERRUPTED.store(false, Ordering::Relaxed);
}

/// Set up signal handlers for interactive mode
/// Set up signal handlers for interactive mode.
///
/// Spawns a tokio task that waits for a Ctrl+C signal and flips both the
/// global interrupt flag and the returned shutdown flag. Callers must invoke
/// this from within a tokio runtime; without one the spawn is skipped and the
/// shutdown flag is returned unarmed (matching the prior best-effort semantics).
pub fn setup_signal_handlers() -> Result<Arc<AtomicBool>> {
let shutdown = Arc::new(AtomicBool::new(false));
let shutdown_clone = Arc::clone(&shutdown);

// Handle Ctrl+C
// Note: set_handler can only be called once per process, so we ignore errors in tests
if let Err(e) = ctrlc::set_handler(move || {
info!("Received Ctrl+C signal");
INTERRUPTED.store(true, Ordering::Relaxed);
shutdown_clone.store(true, Ordering::Relaxed);
}) {
// In tests, this might already be registered
debug!("Could not set Ctrl-C handler: {}", e);
// Return the shutdown flag anyway for use in the code
if tokio::runtime::Handle::try_current().is_ok() {
tokio::spawn(async move {
match signal::ctrl_c().await {
Ok(()) => {
info!("Received Ctrl+C signal");
INTERRUPTED.store(true, Ordering::Relaxed);
shutdown_clone.store(true, Ordering::Relaxed);
}
Err(e) => debug!("Failed to install Ctrl-C handler: {}", e),
}
});
} else {
debug!("No tokio runtime active; Ctrl-C handler not installed");
}

Ok(shutdown)
Expand Down
5 changes: 2 additions & 3 deletions src/config/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
//! Configuration loading and priority management.

use anyhow::{Context, Result};
use directories::ProjectDirs;
use std::env;
use std::path::{Path, PathBuf};
use tokio::fs;
Expand Down Expand Up @@ -222,8 +221,8 @@ impl Config {
.join("bssh")
.join("config.yaml");
return Ok(xdg_config);
} else if let Some(proj_dirs) = ProjectDirs::from("", "", "bssh") {
let xdg_config = proj_dirs.config_dir().join("config.yaml");
} else if let Some(config_dir) = dirs::config_dir() {
let xdg_config = config_dir.join("bssh").join("config.yaml");
return Ok(xdg_config);
}

Expand Down
7 changes: 3 additions & 4 deletions src/executor/output_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@
//! Thread-safe output synchronization for preventing race conditions
//! when multiple nodes write to stdout/stderr simultaneously.

use once_cell::sync::Lazy;
use std::io::{self, Write};
use std::sync::Mutex;
use std::sync::{LazyLock, Mutex};

/// Global stdout mutex to prevent interleaved output
static STDOUT_MUTEX: Lazy<Mutex<io::Stdout>> = Lazy::new(|| Mutex::new(io::stdout()));
static STDOUT_MUTEX: LazyLock<Mutex<io::Stdout>> = LazyLock::new(|| Mutex::new(io::stdout()));

/// Global stderr mutex to prevent interleaved output
static STDERR_MUTEX: Lazy<Mutex<io::Stderr>> = Lazy::new(|| Mutex::new(io::stderr()));
static STDERR_MUTEX: LazyLock<Mutex<io::Stderr>> = LazyLock::new(|| Mutex::new(io::stderr()));

/// Thread-safe println! that prevents output interleaving
///
Expand Down
2 changes: 1 addition & 1 deletion src/forwarding/dynamic/forwarder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ impl DynamicForwarder {
);

// Add jitter to avoid thundering herd
let jitter = Duration::from_millis(fastrand::u64(
let jitter = Duration::from_millis(rand::random_range(
0..=retry_delay.as_millis() as u64 / 4,
));
retry_delay += jitter;
Expand Down
2 changes: 1 addition & 1 deletion src/forwarding/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ impl LocalForwarder {
);

// Add jitter to avoid thundering herd
let jitter = Duration::from_millis(fastrand::u64(
let jitter = Duration::from_millis(rand::random_range(
0..=retry_delay.as_millis() as u64 / 4,
));
retry_delay += jitter;
Expand Down
2 changes: 1 addition & 1 deletion src/forwarding/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ impl RemoteForwarder {
);

// Add jitter to avoid thundering herd
let jitter = Duration::from_millis(fastrand::u64(
let jitter = Duration::from_millis(rand::random_range(
0..=retry_delay.as_millis() as u64 / 4,
));
retry_delay += jitter;
Expand Down
5 changes: 2 additions & 3 deletions src/pty/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

use std::io::Write;
use std::sync::{
Arc, Mutex,
Arc, LazyLock, Mutex,
atomic::{AtomicBool, Ordering},
};

Expand All @@ -26,11 +26,10 @@ use crossterm::{
execute,
terminal::{disable_raw_mode, enable_raw_mode},
};
use once_cell::sync::Lazy;

/// Global terminal cleanup synchronization
/// Ensures only one cleanup attempt happens even with multiple guards
static TERMINAL_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
static TERMINAL_MUTEX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
static RAW_MODE_ACTIVE: AtomicBool = AtomicBool::new(false);

/// Terminal state information that needs to be preserved and restored
Expand Down
4 changes: 2 additions & 2 deletions src/ssh/config_cache/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

use super::config::CacheConfig;
use super::manager::SshConfigCache;
use once_cell::sync::Lazy;
use std::sync::LazyLock;
use tracing::debug;

/// Global SSH config cache instance
pub static GLOBAL_CACHE: Lazy<SshConfigCache> = Lazy::new(|| {
pub static GLOBAL_CACHE: LazyLock<SshConfigCache> = LazyLock::new(|| {
let config = CacheConfig::from_env();

debug!(
Expand Down
3 changes: 1 addition & 2 deletions src/ssh/known_hosts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@
// limitations under the License.

use super::tokio_client::ServerCheckMethod;
use directories::BaseDirs;
use std::path::PathBuf;
use std::str::FromStr;

/// Get the default known_hosts file path
pub fn get_default_known_hosts_path() -> Option<PathBuf> {
BaseDirs::new().map(|dirs| dirs.home_dir().join(".ssh").join("known_hosts"))
dirs::home_dir().map(|home| home.join(".ssh").join("known_hosts"))
}

/// Create a ServerCheckMethod based on strict host key checking mode
Expand Down
Loading