refactor(src/commands): add timeouthandling and internal daemon guard

Adds 2‑second timeouts around D‑Bus interactions and guards against internal daemon self‑calls; refactors command handling to use a consistent async timeout pattern and improves error reporting.

New Features
- Wrap D‑Bus calls with a 2‑second timeout to prevent hangs
- Early‑exit when $LAPCTL_DAEMON_INTERNAL is set to skip self‑calls- Apply consistent timeout usage across all commands

Enhancements
- Unified error handling using the `matches!` macro
- Reduced race conditions in connection establishment

Bug Fixes
- Prevent silent failures when a connection timeout occurs
This commit is contained in:
sankalp committed 2026-03-23 15:38:57 +05:45
1 parent 46cbcb5b4b
commit c564259a19
6 files changed
+181 -67

No files matched your search

+22 -8
View File
@@ -15,27 +15,41 @@ trait Lapctl {
}
fn try_call_daemon(command: &BatteryCommands) -> bool {
if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() {
return false;
}
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(_) => return false,
};
rt.block_on(async {
let connection = match Connection::system().await {
Ok(conn) => conn,
Err(_) => return false,
};
let connection =
match tokio::time::timeout(std::time::Duration::from_secs(2), Connection::system())
.await
{
Ok(Ok(conn)) => conn,
_ => return false,
};
let proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match command {
let res = match command {
BatteryCommands::Limit { percent } => {
proxy.set_battery_limit(*percent as u32).await.is_ok()
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_battery_limit(*percent as u32),
)
.await
}
_ => false,
}
_ => return false,
};
matches!(res, Ok(Ok(_)))
})
}
+36 -13
View File
@@ -15,32 +15,55 @@ trait Lapctl {
}
fn try_call_daemon(command: &CoolingCommands) -> bool {
if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() {
debug!("Internal daemon call detected. Skipping D-Bus self-call.");
return false;
}
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(_) => return false,
};
rt.block_on(async {
let connection = match Connection::system().await {
Ok(conn) => conn,
Err(_) => return false,
};
let connection =
match tokio::time::timeout(std::time::Duration::from_secs(2), Connection::system())
.await
{
Ok(Ok(conn)) => conn,
_ => return false,
};
let proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match command {
CoolingCommands::Performance => proxy
.set_cooling_profile("performance".to_string())
let res = match command {
CoolingCommands::Performance => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_cooling_profile("performance".to_string()),
)
.await
.is_ok(),
CoolingCommands::Balanced => proxy
.set_cooling_profile("balanced".to_string())
}
CoolingCommands::Balanced => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_cooling_profile("balanced".to_string()),
)
.await
.is_ok(),
CoolingCommands::Quiet => proxy.set_cooling_profile("quiet".to_string()).await.is_ok(),
}
}
CoolingCommands::Quiet => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_cooling_profile("quiet".to_string()),
)
.await
}
};
matches!(res, Ok(Ok(_)))
})
}
+33 -17
View File
@@ -40,24 +40,36 @@ trait Lapctl {
}
fn try_call_daemon(cmd: &GpuCommands) -> bool {
if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() {
return false;
}
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(_) => return false,
};
rt.block_on(async {
let connection = match Connection::system().await {
Ok(conn) => conn,
Err(_) => return false,
};
let connection =
match tokio::time::timeout(std::time::Duration::from_secs(2), Connection::system())
.await
{
Ok(Ok(conn)) => conn,
_ => return false,
};
let proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match cmd {
let res = match cmd {
GpuCommands::Integrated { no_reboot } => {
proxy.switch_gpu_integrated(*no_reboot).await.is_ok()
tokio::time::timeout(
std::time::Duration::from_secs(5),
proxy.switch_gpu_integrated(*no_reboot),
)
.await
}
GpuCommands::Hybrid {
rtd3,
@@ -65,10 +77,11 @@ fn try_call_daemon(cmd: &GpuCommands) -> bool {
no_reboot,
} => {
let rtd3_val = rtd3.map(|v| v as i32).unwrap_or(-1);
proxy
.switch_gpu_hybrid(rtd3_val, *use_nvidia_current, *no_reboot)
.await
.is_ok()
tokio::time::timeout(
std::time::Duration::from_secs(5),
proxy.switch_gpu_hybrid(rtd3_val, *use_nvidia_current, *no_reboot),
)
.await
}
GpuCommands::Nvidia {
dm,
@@ -80,20 +93,23 @@ fn try_call_daemon(cmd: &GpuCommands) -> bool {
} => {
let dm_val = dm.clone().unwrap_or_default();
let coolbits_val = coolbits.map(|v| v as i32).unwrap_or(-1);
proxy
.switch_gpu_nvidia(
tokio::time::timeout(
std::time::Duration::from_secs(5),
proxy.switch_gpu_nvidia(
dm_val,
*force_comp,
coolbits_val,
*use_nvidia_current,
*wayland,
*no_reboot,
)
.await
.is_ok()
),
)
.await
}
_ => false,
}
_ => return false,
};
matches!(res, Ok(Ok(_)))
})
}
+19 -5
View File
@@ -18,22 +18,36 @@ trait Lapctl {
}
fn try_call_daemon_inhibit(active: bool, why: String, who: String) -> bool {
if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() {
return false;
}
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(_) => return false,
};
rt.block_on(async {
let connection = match Connection::system().await {
Ok(conn) => conn,
Err(_) => return false,
};
let connection =
match tokio::time::timeout(std::time::Duration::from_secs(2), Connection::system())
.await
{
Ok(Ok(conn)) => conn,
_ => return false,
};
let proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
proxy.set_system_inhibition(active, why, who).await.is_ok()
let res = tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_system_inhibition(active, why, who),
)
.await;
matches!(res, Ok(Ok(_)))
})
}
+41 -16
View File
@@ -16,36 +16,61 @@ trait Lapctl {
}
fn try_call_daemon(command: &PowerCommands) -> bool {
if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() {
return false;
}
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(_) => return false,
};
rt.block_on(async {
let connection = match Connection::system().await {
Ok(conn) => conn,
Err(_) => return false,
};
let connection =
match tokio::time::timeout(std::time::Duration::from_secs(2), Connection::system())
.await
{
Ok(Ok(conn)) => conn,
_ => return false,
};
let proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match command {
PowerCommands::Performance => proxy
.set_power_profile("performance".to_string())
let res = match command {
PowerCommands::Performance => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_power_profile("performance".to_string()),
)
.await
.is_ok(),
PowerCommands::Balanced => proxy
.set_power_profile("balanced".to_string())
}
PowerCommands::Balanced => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_power_profile("balanced".to_string()),
)
.await
.is_ok(),
PowerCommands::BatterySave => proxy
.set_power_profile("battery-save".to_string())
}
PowerCommands::BatterySave => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_power_profile("battery-save".to_string()),
)
.await
.is_ok(),
PowerCommands::LimitTdp { watts } => proxy.set_tdp_limit(*watts).await.is_ok(),
}
}
PowerCommands::LimitTdp { watts } => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_tdp_limit(*watts),
)
.await
}
};
matches!(res, Ok(Ok(_)))
})
}
+30 -8
View File
@@ -15,25 +15,47 @@ trait Lapctl {
}
fn try_call_daemon(command: &TouchpadCommands) -> bool {
if std::env::var("LAPCTL_DAEMON_INTERNAL").is_ok() {
return false;
}
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(_) => return false,
};
rt.block_on(async {
let connection = match Connection::system().await {
Ok(conn) => conn,
Err(_) => return false,
};
let connection =
match tokio::time::timeout(std::time::Duration::from_secs(2), Connection::system())
.await
{
Ok(Ok(conn)) => conn,
_ => return false,
};
let proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match command {
TouchpadCommands::Enable => proxy.set_touchpad_inhibition(false).await.is_ok(),
TouchpadCommands::Disable => proxy.set_touchpad_inhibition(true).await.is_ok(),
}
let res = match command {
TouchpadCommands::Enable => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_touchpad_inhibition(false),
)
.await
}
TouchpadCommands::Disable => {
tokio::time::timeout(
std::time::Duration::from_secs(2),
proxy.set_touchpad_inhibition(true),
)
.await
}
};
matches!(res, Ok(Ok(_)))
})
}