fix: connect() no longer falls through from mux to USB serial

When a mux socket file exists, connect(None, timeout) and try_connect()
now commit to the mux — returning an error if unreachable instead of
silently falling through to USB serial. This prevents a race condition
where a client could steal the serial port from the mux during reconnect.

Callers with explicit port = Some(...) are unaffected.
This commit is contained in:
swaits committed 2026-04-07 18:04:07 -06:00
1 parent a7e93ab56b
commit 77addfa698
4 files changed
+29 -33

No files matched your search

+11
View File
@@ -1,5 +1,16 @@
# Changelog
## 0.2.1 — 2026-04-07
### Fixed
- **`connect()` no longer falls through from mux to USB serial.** If a mux
socket file exists, `connect(None, timeout)` and `try_connect(timeout)` now
commit to the mux and return an error if the mux is unreachable — instead of
silently bypassing it and grabbing the USB port directly. This prevents a
race condition where a client could steal the serial port from the mux during
reconnect. Callers with explicit `port = Some(...)` are unaffected.
## 0.2.0 — 2026-04-07
### Features
Generated
+1 -1
View File
@@ -44,7 +44,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "donglora-client"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"anyhow",
"serialport",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "donglora-client"
version = "0.2.0"
version = "0.2.1"
edition = "2024"
rust-version = "1.85"
authors = ["Stephen Waits <steve@waits.net>"]
+16 -31
View File
@@ -100,26 +100,19 @@ pub fn connect(port: Option<&str>, timeout: Duration) -> anyhow::Result<Client<A
return Ok(client);
}
// Try Unix socket mux
// Try Unix socket mux — if a mux socket exists, commit to it. Never fall through
// to USB serial when a mux is (or was) running. This prevents a client from
// stealing the serial port while the mux is temporarily reconnecting.
#[cfg(unix)]
if let Some(path) = find_mux_socket() {
match MuxTransport::unix(&path, timeout) {
Ok(transport) => {
debug!("connected to mux socket at {path}");
let mut client = Client::new(AnyTransport::Mux(transport));
if client.validate().is_ok() {
return Ok(client);
}
debug!("mux at {path} did not validate, falling back to USB");
}
Err(_) => {
// Stale socket or connection refused — fall through
debug!("mux socket at {path} not reachable, falling back to USB");
}
}
debug!("mux socket found at {path} — connecting via mux only");
let transport = MuxTransport::unix(&path, timeout)?;
let mut client = Client::new(AnyTransport::Mux(transport));
client.validate()?;
return Ok(client);
}
// Direct USB serial — auto-detect or wait
// Direct USB serial — only reached when no mux socket exists at all
let port_path = discovery::find_port().unwrap_or_else(discovery::wait_for_device);
debug!("opening serial port {port_path}");
let transport = SerialTransport::open(&port_path, timeout)?;
@@ -183,25 +176,17 @@ pub fn try_connect(timeout: Duration) -> anyhow::Result<Client<AnyTransport>> {
return Ok(client);
}
// Try Unix socket mux
// Try Unix socket mux — commit to it if socket exists (same rationale as connect())
#[cfg(unix)]
if let Some(path) = find_mux_socket() {
match MuxTransport::unix(&path, timeout) {
Ok(transport) => {
debug!("connected to mux socket at {path}");
let mut client = Client::new(AnyTransport::Mux(transport));
if client.validate().is_ok() {
return Ok(client);
}
debug!("mux socket at {path} did not validate, falling back to USB");
}
Err(_) => {
debug!("mux socket at {path} not reachable, falling back to USB");
}
}
debug!("mux socket found at {path} — connecting via mux only");
let transport = MuxTransport::unix(&path, timeout)?;
let mut client = Client::new(AnyTransport::Mux(transport));
client.validate()?;
return Ok(client);
}
// Direct USB serial — single non-blocking scan
// Direct USB serial — single non-blocking scan (only when no mux socket exists)
let port_path =
discovery::find_port().ok_or_else(|| anyhow::anyhow!("no DongLoRa device found (no mux, no USB device)"))?;
debug!("opening serial port {port_path}");