Skip to content

Commit 19f12fe

Browse files
author
Alex J Lennon
committed
fix(proxy): single active session for remote_ssh (Cursor dual TCP)
cppdbg can open two connections to the listen port; each spawned a full SSH/gdbserver backend and broke the session. Gate remote_ssh to one client at a time; document in board_test_app README. Made-with: Cursor
1 parent 4c73a0e commit 19f12fe

2 files changed

Lines changed: 51 additions & 6 deletions

File tree

examples/board_test_app/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ Use the **repository root** as the workspace folder so `${workspaceFolder}` reso
123123
**`Connection reset by peer` / `Unexpected GDB output` on `-target-select`:**
124124
The first debug session runs **`scp` + `ssh` + `gdbserver`** on the board; that can take **tens of seconds**. GDB’s default **remote timeout** is short, so it may close the connection before rsgdb finishes. [`.vscode/launch.json`](../../.vscode/launch.json) sets **`miDebuggerArgs`** to **`set remotetimeout 120`**, and [`rsgdb.remote.toml`](rsgdb.remote.toml) raises **`ready_timeout_secs`**. If it still fails, confirm the board is reachable (**`ping`**, **`ssh user@host`**, **`scp`**), read **`/tmp/rsgdb-cursor-3333.log`**, and run **`RUST_LOG=info ./target/release/rsgdb --config …`** in a terminal to see rsgdb errors (failed **scp**/SSH/TCP to gdbserver closes the proxy side and shows up as reset in the IDE).
125125

126+
Some IDEs open **two TCP connections** to the proxy; **`transport = remote_ssh`** must only run **one** scp/ssh/gdbserver chain. Current **rsgdb** rejects a second client while a session is active (see log: `rejecting GDB client: transport=remote_ssh already has an active session`).
127+
126128
### Manual gdbserver on target
127129

128130
On the **target**:

src/proxy/server.rs

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,31 @@ use crate::rtos;
1414
use crate::svd::SvdIndex;
1515
use futures::StreamExt;
1616
use std::net::SocketAddr;
17+
use std::sync::atomic::{AtomicBool, Ordering};
1718
use std::sync::Arc;
1819
use tokio::net::{TcpListener, TcpStream};
1920
use tokio::sync::Mutex;
2021
use tokio_util::codec::Framed;
2122
use tracing::{debug, error, info, warn};
2223

24+
/// Clears `remote_ssh_busy` when dropped so a second GDB client can connect after the session ends.
25+
struct RemoteSshSessionSlot(Arc<AtomicBool>);
26+
27+
impl Drop for RemoteSshSessionSlot {
28+
fn drop(&mut self) {
29+
self.0.store(false, Ordering::SeqCst);
30+
}
31+
}
32+
2333
/// Proxy server that bridges GDB clients and debug backends
2434
pub struct ProxyServer {
2535
config: ProxyConfig,
2636
backend: BackendConfig,
2737
recording: RecordingConfig,
2838
svd: Option<Arc<SvdIndex>>,
2939
listener: TcpListener,
40+
/// When `transport = remote_ssh`, only one GDB client may use the proxy at a time (one scp/ssh/gdbserver chain).
41+
remote_ssh_busy: Option<Arc<AtomicBool>>,
3042
}
3143

3244
impl ProxyServer {
@@ -44,12 +56,19 @@ impl ProxyServer {
4456

4557
info!("Proxy server listening on {}", addr);
4658

59+
let remote_ssh_busy = if backend.transport == BackendTransport::RemoteSsh {
60+
Some(Arc::new(AtomicBool::new(false)))
61+
} else {
62+
None
63+
};
64+
4765
Ok(Self {
4866
config,
4967
backend,
5068
recording,
5169
svd,
5270
listener,
71+
remote_ssh_busy,
5372
})
5473
}
5574

@@ -69,14 +88,38 @@ impl ProxyServer {
6988
let recording = self.recording.clone();
7089
let svd = self.svd.clone();
7190

72-
// Spawn a new task to handle this connection
73-
tokio::spawn(async move {
74-
if let Err(e) =
75-
handle_connection(socket, config, backend, recording, svd).await
91+
if let Some(ref busy) = self.remote_ssh_busy {
92+
match busy.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
7693
{
77-
error!("Connection error: {}", e);
94+
Ok(_) => {
95+
let slot = RemoteSshSessionSlot(Arc::clone(busy));
96+
tokio::spawn(async move {
97+
let _slot = slot;
98+
if let Err(e) =
99+
handle_connection(socket, config, backend, recording, svd)
100+
.await
101+
{
102+
error!("Connection error: {}", e);
103+
}
104+
});
105+
}
106+
Err(_) => {
107+
warn!(
108+
peer = %addr,
109+
"rejecting GDB client: transport=remote_ssh already has an active session (IDEs may open two TCP connections; only one is allowed)"
110+
);
111+
drop(socket);
112+
}
78113
}
79-
});
114+
} else {
115+
tokio::spawn(async move {
116+
if let Err(e) =
117+
handle_connection(socket, config, backend, recording, svd).await
118+
{
119+
error!("Connection error: {}", e);
120+
}
121+
});
122+
}
80123
}
81124
Err(e) => {
82125
error!("Failed to accept connection: {}", e);

0 commit comments

Comments
 (0)