Add Dosh status command
ci / test (push) Has been cancelled
ci / fuzz-smoke (push) Has been cancelled
ci / windows-client (push) Has been cancelled
ci / package-release (linux-x86_64, ubuntu-latest) (push) Has been cancelled
ci / package-release (macos-aarch64, macos-14) (push) Has been cancelled
ci / package-release (macos-x86_64, macos-13) (push) Has been cancelled
ci / package-release (windows-x86_64, windows-latest) (push) Has been cancelled
ci / remote-bench (push) Has been cancelled
ci / test (push) Has been cancelled
ci / fuzz-smoke (push) Has been cancelled
ci / windows-client (push) Has been cancelled
ci / package-release (linux-x86_64, ubuntu-latest) (push) Has been cancelled
ci / package-release (macos-aarch64, macos-14) (push) Has been cancelled
ci / package-release (macos-x86_64, macos-13) (push) Has been cancelled
ci / package-release (windows-x86_64, windows-latest) (push) Has been cancelled
ci / remote-bench (push) Has been cancelled
This commit is contained in:
@@ -45,6 +45,7 @@ dosh setup HOST # import SSH config and trust the Dosh host key
|
|||||||
dosh HOST # connect
|
dosh HOST # connect
|
||||||
dosh HOST COMMAND # connect and run a command
|
dosh HOST COMMAND # connect and run a command
|
||||||
dosh update # update Dosh
|
dosh update # update Dosh
|
||||||
|
dosh status HOST # show remote tmux sessions and Dosh service status
|
||||||
dosh doctor HOST # check config and connectivity
|
dosh doctor HOST # check config and connectivity
|
||||||
dosh recover HOST # clear cached attach state and re-check
|
dosh recover HOST # clear cached attach state and re-check
|
||||||
```
|
```
|
||||||
|
|||||||
+84
-9
@@ -213,6 +213,9 @@ async fn main() -> Result<()> {
|
|||||||
if matches!(args.server.as_deref(), Some("recover" | "repair")) {
|
if matches!(args.server.as_deref(), Some("recover" | "repair")) {
|
||||||
return run_recover_command(&config, &args).await;
|
return run_recover_command(&config, &args).await;
|
||||||
}
|
}
|
||||||
|
if matches!(args.server.as_deref(), Some("status" | "sessions")) {
|
||||||
|
return run_status_command(&config, &args);
|
||||||
|
}
|
||||||
if args.server.as_deref() == Some("forward") {
|
if args.server.as_deref() == Some("forward") {
|
||||||
args = rewrite_forward_command(args)?;
|
args = rewrite_forward_command(args)?;
|
||||||
}
|
}
|
||||||
@@ -727,6 +730,54 @@ async fn run_recover_command(config: &dosh::config::ClientConfig, args: &Args) -
|
|||||||
run_doctor_for_host(config, args, &requested).await
|
run_doctor_for_host(config, args, &requested).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_status_command(config: &dosh::config::ClientConfig, args: &Args) -> Result<()> {
|
||||||
|
if args.command.len() != 1 {
|
||||||
|
return Err(anyhow!("usage: dosh status <host>"));
|
||||||
|
}
|
||||||
|
let requested = args.command[0].clone();
|
||||||
|
let hosts = load_hosts_config(None).unwrap_or_default();
|
||||||
|
let host_config = hosts.hosts.get(&requested).cloned().unwrap_or_default();
|
||||||
|
let server = status_ssh_target(&requested, &host_config);
|
||||||
|
let ssh_port = args.ssh_port.or(host_config.ssh_port).or(config.ssh_port);
|
||||||
|
println!("Dosh status for {requested}");
|
||||||
|
if is_local_status_target(&server) {
|
||||||
|
return run_local_sessions_command();
|
||||||
|
}
|
||||||
|
run_sessions_command(
|
||||||
|
&server,
|
||||||
|
ssh_port,
|
||||||
|
args.ssh_key.as_deref(),
|
||||||
|
args.ssh_known_hosts.as_deref(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status_ssh_target(requested: &str, host_config: &dosh::config::HostConfig) -> String {
|
||||||
|
let raw_server = host_config
|
||||||
|
.ssh
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| requested.to_string());
|
||||||
|
ssh_with_user(&raw_server, host_config.user.as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_local_status_target(server: &str) -> bool {
|
||||||
|
let host = server.rsplit('@').next().unwrap_or(server);
|
||||||
|
matches!(host, "localhost" | "127.0.0.1" | "::1")
|
||||||
|
}
|
||||||
|
|
||||||
|
const SESSIONS_STATUS_SCRIPT: &str = r#"printf 'tmux sessions:\n'; tmux ls 2>/dev/null || printf ' none\n'; printf '\nDosh service:\n'; systemctl --user --no-pager --plain status dosh-server.service 2>/dev/null | sed -n '1,8p' || pgrep -af dosh-server || printf ' not running\n'"#;
|
||||||
|
|
||||||
|
fn run_local_sessions_command() -> Result<()> {
|
||||||
|
let status = Command::new("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(SESSIONS_STATUS_SCRIPT)
|
||||||
|
.status()
|
||||||
|
.context("run local status command")?;
|
||||||
|
if !status.success() {
|
||||||
|
return Err(anyhow!("local status command failed with status {status}"));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn run_doctor_for_host(
|
async fn run_doctor_for_host(
|
||||||
config: &dosh::config::ClientConfig,
|
config: &dosh::config::ClientConfig,
|
||||||
args: &Args,
|
args: &Args,
|
||||||
@@ -1328,8 +1379,7 @@ fn run_sessions_command(
|
|||||||
.arg("-o")
|
.arg("-o")
|
||||||
.arg(format!("UserKnownHostsFile={}", known_hosts.display()));
|
.arg(format!("UserKnownHostsFile={}", known_hosts.display()));
|
||||||
}
|
}
|
||||||
let script = r#"printf 'tmux sessions:\n'; tmux ls 2>/dev/null || printf ' none\n'; printf '\nDosh service:\n'; systemctl --user --no-pager --plain status dosh-server.service 2>/dev/null | sed -n '1,8p' || pgrep -af dosh-server || printf ' not running\n'"#;
|
let remote_command = format!("sh -c {}", shell_word(SESSIONS_STATUS_SCRIPT));
|
||||||
let remote_command = format!("sh -c {}", shell_word(script));
|
|
||||||
let status = command
|
let status = command
|
||||||
.arg(server)
|
.arg(server)
|
||||||
.arg(remote_command)
|
.arg(remote_command)
|
||||||
@@ -4996,13 +5046,13 @@ mod tests {
|
|||||||
DisconnectStatus, DynamicForward, FrameBuffer, LocalForward, MAX_PENDING_USER_INPUT_BYTES,
|
DisconnectStatus, DynamicForward, FrameBuffer, LocalForward, MAX_PENDING_USER_INPUT_BYTES,
|
||||||
PredictMode, Predictor, RemoteForward, SshConfig, StatusAction, auth_allows, cache_key,
|
PredictMode, Predictor, RemoteForward, SshConfig, StatusAction, auth_allows, cache_key,
|
||||||
cache_server_prefix, clear_cached_credentials, ensure_tui_safe_status_overlay,
|
cache_server_prefix, clear_cached_credentials, ensure_tui_safe_status_overlay,
|
||||||
input_matches_escape, latest_release_download_url, load_first_native_identity_with_prompt,
|
input_matches_escape, is_local_status_target, latest_release_download_url,
|
||||||
parse_dynamic_forward, parse_escape_key, parse_local_forward, parse_remote_forward,
|
load_first_native_identity_with_prompt, parse_dynamic_forward, parse_escape_key,
|
||||||
parse_ssh_config, queue_pending_user_input, raw_contains_host_table, recv_response_until,
|
parse_local_forward, parse_remote_forward, parse_ssh_config, queue_pending_user_input,
|
||||||
render_status_clear, render_status_overlay, requested_env, resolved_startup_command,
|
raw_contains_host_table, recv_response_until, render_status_clear, render_status_overlay,
|
||||||
rewrite_forward_command, selected_predict_mode, selected_udp_host, ssh_destination_host,
|
requested_env, resolved_startup_command, rewrite_forward_command, selected_predict_mode,
|
||||||
ssh_username, ssh_with_user, startup_command, toml_bare_key_or_quoted,
|
selected_udp_host, ssh_destination_host, ssh_username, ssh_with_user, startup_command,
|
||||||
update_check_requested, valid_forward_host,
|
status_ssh_target, toml_bare_key_or_quoted, update_check_requested, valid_forward_host,
|
||||||
};
|
};
|
||||||
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
use dosh::config::{ClientConfig, CommandExtension, HostConfig};
|
||||||
use dosh::native::EnvVar;
|
use dosh::native::EnvVar;
|
||||||
@@ -5659,6 +5709,31 @@ mod tests {
|
|||||||
assert!(update_check_requested(&["--check".to_string(), "extra".to_string()]).is_err());
|
assert!(update_check_requested(&["--check".to_string(), "extra".to_string()]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn status_uses_host_alias_and_user() {
|
||||||
|
let host = HostConfig {
|
||||||
|
ssh: Some("server.example.com".to_string()),
|
||||||
|
user: Some("palav".to_string()),
|
||||||
|
..HostConfig::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
status_ssh_target("palav", &host),
|
||||||
|
"palav@server.example.com"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
status_ssh_target("rawhost", &HostConfig::default()),
|
||||||
|
"rawhost"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn status_detects_local_targets() {
|
||||||
|
assert!(is_local_status_target("localhost"));
|
||||||
|
assert!(is_local_status_target("palav@127.0.0.1"));
|
||||||
|
assert!(is_local_status_target("::1"));
|
||||||
|
assert!(!is_local_status_target("server.example.com"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn release_download_url_uses_latest_release_asset() {
|
fn release_download_url_uses_latest_release_asset() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
Reference in New Issue
Block a user