Fix terminal resize handling and cached updates
This commit is contained in:
+26
-5
@@ -10,6 +10,7 @@ prefix="${PREFIX:-$HOME/.local}"
|
||||
from_current=0
|
||||
start_server=1
|
||||
force_config=0
|
||||
update_cache="${DOSH_UPDATE_CACHE:-$HOME/.cache/dosh/source}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
@@ -22,11 +23,14 @@ Options:
|
||||
--dosh-host HOST UDP host for Dosh packets when different from SSH target
|
||||
--port PORT Dosh UDP port; default 50000
|
||||
--prefix DIR Install prefix; default ~/.local
|
||||
--update-cache DIR
|
||||
Persistent source checkout for incremental updates
|
||||
--no-start Install server but do not start it
|
||||
--force-config Rewrite existing ~/.config/dosh/*.toml files
|
||||
|
||||
Environment alternatives:
|
||||
DOSH_REPO, DOSH_ROLE, DOSH_SERVER, DOSH_HOST, DOSH_PORT, PREFIX
|
||||
DOSH_REPO, DOSH_ROLE, DOSH_SERVER, DOSH_HOST, DOSH_PORT, PREFIX,
|
||||
DOSH_UPDATE_CACHE
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -55,6 +59,10 @@ while [ "$#" -gt 0 ]; do
|
||||
prefix="$2"
|
||||
shift
|
||||
;;
|
||||
--update-cache)
|
||||
update_cache="$2"
|
||||
shift
|
||||
;;
|
||||
--from-current)
|
||||
from_current=1
|
||||
;;
|
||||
@@ -119,15 +127,28 @@ else
|
||||
echo "DOSH_REPO or --repo is required when running the installer from curl" >&2
|
||||
exit 2
|
||||
fi
|
||||
case "$update_cache" in
|
||||
""|"/"|"$HOME"|"$HOME/"|"$HOME/.cache")
|
||||
echo "refusing unsafe update cache path: $update_cache" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
need git
|
||||
tmpdir="$(mktemp -d)"
|
||||
git clone --depth 1 "$repo" "$tmpdir/dosh" >/dev/null
|
||||
src_dir="$tmpdir/dosh"
|
||||
mkdir -p "$(dirname "$update_cache")"
|
||||
if [ -d "$update_cache/.git" ]; then
|
||||
git -C "$update_cache" remote set-url origin "$repo"
|
||||
git -C "$update_cache" fetch --depth 1 origin main
|
||||
git -C "$update_cache" checkout -q -B main FETCH_HEAD
|
||||
else
|
||||
rm -rf "$update_cache"
|
||||
git clone --depth 1 --branch main "$repo" "$update_cache" >/dev/null
|
||||
fi
|
||||
src_dir="$update_cache"
|
||||
fi
|
||||
|
||||
cd "$src_dir"
|
||||
if [ "$role" = "client" ]; then
|
||||
cargo build --release --bin dosh-client --bin dosh-bench
|
||||
cargo build --release --bin dosh-client
|
||||
else
|
||||
cargo build --release
|
||||
fi
|
||||
|
||||
+65
-4
@@ -9,7 +9,7 @@ use dosh::auth::{
|
||||
use dosh::config::{expand_tilde, load_client_config, load_server_config};
|
||||
use dosh::crypto;
|
||||
use dosh::protocol::{
|
||||
self, AttachOk, BootstrapAttachRequest, CLIENT_TO_SERVER, Frame, Input, PacketKind,
|
||||
self, AttachOk, BootstrapAttachRequest, CLIENT_TO_SERVER, Frame, Input, PacketKind, Resize,
|
||||
ResumeRequest, SERVER_TO_CLIENT, TicketAttachBody, TicketAttachEnvelope,
|
||||
TicketAttachOkEnvelope,
|
||||
};
|
||||
@@ -218,11 +218,16 @@ fn run_update(config: &dosh::config::ClientConfig) -> Result<()> {
|
||||
.unwrap_or_else(|| "https://git.palav.dev/Palav/dosh.git".to_string());
|
||||
let raw_base = raw_base_from_repo(&repo);
|
||||
let installer = format!("{raw_base}/raw/branch/main/install.sh");
|
||||
let update_cache = dirs::cache_dir()
|
||||
.unwrap_or_else(|| expand_tilde("~/.cache"))
|
||||
.join("dosh")
|
||||
.join("source");
|
||||
let mut script = format!(
|
||||
"curl -fsSL {} | DOSH_REPO={} DOSH_PORT={} sh -s -- client",
|
||||
"curl -fsSL {} | DOSH_REPO={} DOSH_PORT={} DOSH_UPDATE_CACHE={} sh -s -- client",
|
||||
shell_word(&installer),
|
||||
shell_word(&repo),
|
||||
config.update_port.unwrap_or(config.dosh_port)
|
||||
config.update_port.unwrap_or(config.dosh_port),
|
||||
shell_word(&update_cache.display().to_string())
|
||||
);
|
||||
if config.server != "user@example.com" {
|
||||
script.push_str(&format!(" --server {}", shell_word(&config.server)));
|
||||
@@ -230,7 +235,6 @@ fn run_update(config: &dosh::config::ClientConfig) -> Result<()> {
|
||||
if let Some(host) = &config.dosh_host {
|
||||
script.push_str(&format!(" --dosh-host {}", shell_word(host)));
|
||||
}
|
||||
script.push_str(" --force-config");
|
||||
let status = Command::new("sh")
|
||||
.arg("-lc")
|
||||
.arg(script)
|
||||
@@ -549,6 +553,8 @@ async fn run_terminal(
|
||||
let mut last_packet_at = Instant::now();
|
||||
let mut status_visible = false;
|
||||
let mut status_tick = tokio::time::interval(Duration::from_secs(1));
|
||||
let mut resize_tick = tokio::time::interval(Duration::from_millis(250));
|
||||
let mut last_size = size().unwrap_or((80, 24));
|
||||
if let Some(frame) = first_frame {
|
||||
render_frame(&frame)?;
|
||||
if frame.closed {
|
||||
@@ -597,6 +603,16 @@ async fn run_terminal(
|
||||
socket.send_to(&packet, addr).await?;
|
||||
}
|
||||
}
|
||||
_ = resize_tick.tick() => {
|
||||
if let Ok((cols, rows)) = size()
|
||||
&& (cols, rows) != last_size
|
||||
{
|
||||
last_size = (cols, rows);
|
||||
if cred.mode != "view-only" {
|
||||
send_resize(&socket, addr, &cred, &mut send_seq, cols, rows).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
recv = socket.recv_from(&mut recv_buf) => {
|
||||
let (n, _) = recv?;
|
||||
last_packet_at = Instant::now();
|
||||
@@ -647,6 +663,29 @@ async fn run_terminal(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_resize(
|
||||
socket: &UdpSocket,
|
||||
addr: SocketAddr,
|
||||
cred: &CachedCredential,
|
||||
send_seq: &mut u64,
|
||||
cols: u16,
|
||||
rows: u16,
|
||||
) -> Result<()> {
|
||||
let body = protocol::to_body(&Resize { cols, rows })?;
|
||||
let packet = protocol::encode_encrypted(
|
||||
PacketKind::Resize,
|
||||
cred.client_id,
|
||||
*send_seq,
|
||||
cred.last_rendered_seq,
|
||||
&cred.session_key,
|
||||
CLIENT_TO_SERVER,
|
||||
&body,
|
||||
)?;
|
||||
*send_seq += 1;
|
||||
socket.send_to(&packet, addr).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_ack(
|
||||
socket: &UdpSocket,
|
||||
addr: SocketAddr,
|
||||
@@ -733,12 +772,34 @@ struct RawMode;
|
||||
impl RawMode {
|
||||
fn enter() -> Result<Self> {
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = std::io::stdout();
|
||||
stdout.write_all(TERMINAL_CLEANUP)?;
|
||||
stdout.flush()?;
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for RawMode {
|
||||
fn drop(&mut self) {
|
||||
let mut stdout = std::io::stdout();
|
||||
let _ = stdout.write_all(TERMINAL_CLEANUP);
|
||||
let _ = stdout.flush();
|
||||
let _ = disable_raw_mode();
|
||||
}
|
||||
}
|
||||
|
||||
const TERMINAL_CLEANUP: &[u8] = concat!(
|
||||
"\x1b[?1l",
|
||||
"\x1b[0m",
|
||||
"\x1b[?25h",
|
||||
"\x1b[?1003l",
|
||||
"\x1b[?1002l",
|
||||
"\x1b[?1001l",
|
||||
"\x1b[?1000l",
|
||||
"\x1b[?1015l",
|
||||
"\x1b[?1006l",
|
||||
"\x1b[?1005l",
|
||||
"\x1b[?2004l",
|
||||
"\x1b[?1049l"
|
||||
)
|
||||
.as_bytes();
|
||||
|
||||
@@ -258,6 +258,10 @@ async fn handle_bootstrap_attach(
|
||||
.sessions
|
||||
.get_mut(&req.bootstrap.session)
|
||||
.expect("session exists");
|
||||
if req.bootstrap.mode != "view-only" {
|
||||
session.pty.resize(req.cols, req.rows)?;
|
||||
session.parser.set_size(req.rows, req.cols);
|
||||
}
|
||||
let client_id = crypto::random_16();
|
||||
let snapshot = session.parser.screen().state_formatted();
|
||||
let screen = session.parser.screen().clone();
|
||||
@@ -362,6 +366,10 @@ async fn handle_ticket_attach(
|
||||
.sessions
|
||||
.get_mut(&req.session)
|
||||
.expect("session exists");
|
||||
if req.mode != "view-only" {
|
||||
session.pty.resize(req.cols, req.rows)?;
|
||||
session.parser.set_size(req.rows, req.cols);
|
||||
}
|
||||
let client_id = crypto::random_16();
|
||||
let snapshot = session.parser.screen().state_formatted();
|
||||
let screen = session.parser.screen().clone();
|
||||
@@ -456,6 +464,10 @@ async fn handle_resume(
|
||||
client.rows = req.rows;
|
||||
client.last_seen = Instant::now();
|
||||
client.send_seq += 1;
|
||||
if client.mode != "view-only" {
|
||||
session.pty.resize(req.cols, req.rows)?;
|
||||
session.parser.set_size(req.rows, req.cols);
|
||||
}
|
||||
let snapshot = session.parser.screen().state_formatted();
|
||||
client.last_screen = Some(session.parser.screen().clone());
|
||||
(client.send_seq, session.output_seq, snapshot)
|
||||
|
||||
Reference in New Issue
Block a user