fuck it forgot to commit all those changes

This commit is contained in:
sankalp committed 2026-03-22 15:05:45 +05:45
1 parent c20a5d3a06
commit b0fdc0d58e
17 files changed
+2007 -111

No files matched your search

+41
View File
@@ -2,8 +2,49 @@ use crate::cli::BatteryCommands;
use log::{debug, error, info};
use std::fs;
use std::path::Path;
use zbus::Connection;
use zbus::proxy;
#[proxy(
interface = "org.lapctl1",
default_service = "org.lapctl",
default_path = "/org/lapctl"
)]
trait Lapctl {
async fn set_battery_limit(&self, percent: u32) -> zbus::Result<()>;
}
fn try_call_daemon(command: &BatteryCommands) -> bool {
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 proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match command {
BatteryCommands::Limit { percent } => {
proxy.set_battery_limit(*percent as u32).await.is_ok()
}
_ => false,
}
})
}
pub fn execute(command: &BatteryCommands) {
if matches!(command, BatteryCommands::Limit { .. }) && try_call_daemon(command) {
println!("Request handled by lapctld daemon.");
return;
}
match command {
BatteryCommands::Limit { percent } => {
if !(&1..=&100).contains(&percent) {
+46
View File
@@ -2,6 +2,47 @@ use crate::cli::CoolingCommands;
use log::{debug, info};
use std::fs;
use std::path::Path;
use zbus::Connection;
use zbus::proxy;
#[proxy(
interface = "org.lapctl1",
default_service = "org.lapctl",
default_path = "/org/lapctl"
)]
trait Lapctl {
async fn set_cooling_profile(&self, profile: String) -> zbus::Result<()>;
}
fn try_call_daemon(command: &CoolingCommands) -> bool {
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 proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match command {
CoolingCommands::Performance => proxy
.set_cooling_profile("performance".to_string())
.await
.is_ok(),
CoolingCommands::Balanced => proxy
.set_cooling_profile("balanced".to_string())
.await
.is_ok(),
CoolingCommands::Quiet => proxy.set_cooling_profile("quiet".to_string()).await.is_ok(),
}
})
}
fn set_ideapad_fan_mode(mode: &str) -> bool {
let mut success = false;
@@ -38,6 +79,11 @@ fn set_asus_throttle_policy(mode: &str) -> bool {
}
pub fn execute(command: &CoolingCommands) {
if try_call_daemon(command) {
println!("Request handled by lapctld daemon.");
return;
}
let (ideapad_mode, asus_mode, desc) = match command {
CoolingCommands::Performance => ("1", "1", "Performance"),
CoolingCommands::Balanced => ("0", "0", "Balanced"),
+257 -40
View File
@@ -1,13 +1,101 @@
use crate::cli::GpuCommands;
use crate::hardware::gpu::{
get_amd_igpu_name, get_current_mode, get_igpu_vendor, get_nvidia_gpu_pci_bus,
get_amd_igpu_name, get_current_mode, get_igpu_vendor, get_nvidia_gpu_pci_addr,
get_nvidia_gpu_pci_bus, kill_gpu_processes, remove_gpu, rescan_pci, unbind_gpu,
};
use crate::utils::system::{
assert_root, create_file, get_active_graphical_sessions, get_display_manager,
is_service_active, manage_service, rebuild_initramfs, terminate_session,
};
use crate::utils::system::{assert_root, create_file, get_display_manager, rebuild_initramfs};
use log::{error, info};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use std::process::Command;
use zbus::Connection;
use zbus::proxy;
#[proxy(
interface = "org.lapctl1",
default_service = "org.lapctl",
default_path = "/org/lapctl"
)]
trait Lapctl {
async fn switch_gpu_integrated(&self, no_reboot: bool) -> zbus::Result<()>;
async fn switch_gpu_hybrid(
&self,
rtd3: i32,
use_nvidia_current: bool,
no_reboot: bool,
) -> zbus::Result<()>;
async fn switch_gpu_nvidia(
&self,
dm: String,
force_comp: bool,
coolbits: i32,
use_nvidia_current: bool,
wayland: bool,
no_reboot: bool,
) -> zbus::Result<()>;
}
fn try_call_daemon(cmd: &GpuCommands) -> bool {
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 proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match cmd {
GpuCommands::Integrated { no_reboot } => {
proxy.switch_gpu_integrated(*no_reboot).await.is_ok()
}
GpuCommands::Hybrid {
rtd3,
use_nvidia_current,
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()
}
GpuCommands::Nvidia {
dm,
force_comp,
coolbits,
use_nvidia_current,
wayland,
no_reboot,
} => {
let dm_val = dm.clone().unwrap_or_default();
let coolbits_val = coolbits.map(|v| v as i32).unwrap_or(-1);
proxy
.switch_gpu_nvidia(
dm_val,
*force_comp,
coolbits_val,
*use_nvidia_current,
*wayland,
*no_reboot,
)
.await
.is_ok()
}
_ => false,
}
})
}
const CACHE_FILE_PATH: &str = "/var/cache/lapctl/cache.json";
const BLACKLIST_PATH: &str = "/etc/modprobe.d/blacklist-nvidia.conf";
@@ -258,10 +346,12 @@ fn create_cache_obj(bus_id: String) -> Cache {
}
fn create_cache_file() {
if get_current_mode() != "hybrid" {
error!("--cache-create requires that the system be in the hybrid Optimus mode");
let pci_addr = get_nvidia_gpu_pci_addr();
if pci_addr.is_none() {
log::debug!("NVIDIA GPU not found, skipping cache creation");
return;
}
let bus_id = get_nvidia_gpu_pci_bus();
let cache = create_cache_obj(bus_id);
@@ -272,7 +362,7 @@ fn create_cache_file() {
if let Ok(json) = serde_json::to_string_pretty(&cache)
&& fs::write(CACHE_FILE_PATH, json).is_ok()
{
log::debug!("Created file {}", CACHE_FILE_PATH);
log::debug!("Created/Updated cache file {}", CACHE_FILE_PATH);
}
}
@@ -290,10 +380,84 @@ fn read_cache_file() -> Result<Cache, String> {
}
}
fn switch_integrated() {
fn prepare_no_reboot(dm_flag: Option<String>) -> Result<Option<String>, String> {
let dm_name = dm_flag.or_else(get_display_manager);
if let Some(ref name) = dm_name
&& is_service_active(name)
{
info!("Stopping display manager: {}", name);
manage_service(name, "stop")?;
return Ok(Some(name.clone()));
}
// If no active DM found, check for graphical sessions
let sessions = get_active_graphical_sessions();
if !sessions.is_empty() {
println!("No active display manager found, but graphical sessions are running.");
println!("Terminating graphical sessions for no-reboot GPU switching...");
for session_id in sessions {
if let Err(e) = terminate_session(&session_id) {
error!("Failed to terminate session {}: {}", session_id, e);
}
}
// Give some time for sessions to terminate
std::thread::sleep(std::time::Duration::from_secs(2));
}
Ok(None)
}
fn finalize_no_reboot(stopped_dm: Option<String>) {
if let Some(name) = stopped_dm {
info!("Starting display manager: {}", name);
let _ = manage_service(&name, "start");
}
}
fn switch_integrated(no_reboot: bool) {
assert_root();
println!("Switching to integrated mode");
if !Path::new(CACHE_FILE_PATH).exists() {
create_cache_file();
}
cleanup();
create_file(BLACKLIST_PATH, BLACKLIST_CONTENT, false);
create_file(UDEV_INTEGRATED_PATH, UDEV_INTEGRATED, false);
let mut stopped_dm = None;
if no_reboot {
match prepare_no_reboot(None) {
Ok(dm) => stopped_dm = dm,
Err(e) => {
error!("{}", e);
return;
}
}
if let Err(e) = kill_gpu_processes() {
error!("Failed to kill GPU processes: {}", e);
}
// Try to unload modules
let modules = ["nvidia_uvm", "nvidia_modeset", "nvidia_drm", "nvidia"];
for module in modules {
let _ = Command::new("modprobe").args(["-r", module]).status();
}
if let Some(pci_addr) = get_nvidia_gpu_pci_addr() {
if let Err(e) = unbind_gpu(&pci_addr) {
error!("Failed to unbind GPU: {}", e);
}
if let Err(e) = remove_gpu(&pci_addr) {
error!("Failed to remove GPU: {}", e);
}
}
}
let is_debug = log::log_enabled!(log::Level::Debug);
let mut dis_cmd = Command::new("systemctl");
dis_cmd.args(["disable", "nvidia-persistenced.service"]);
@@ -306,21 +470,17 @@ fn switch_integrated() {
_ => error!("An error ocurred while disabling service"),
}
if get_current_mode() == "hybrid" && !Path::new(CACHE_FILE_PATH).exists() {
create_cache_file();
if no_reboot {
println!("Operation completed successfully");
finalize_no_reboot(stopped_dm);
} else {
rebuild_initramfs();
println!("Operation completed successfully");
println!("Please reboot your computer for changes to take effect!");
}
cleanup();
create_file(BLACKLIST_PATH, BLACKLIST_CONTENT, false);
create_file(UDEV_INTEGRATED_PATH, UDEV_INTEGRATED, false);
rebuild_initramfs();
println!("Operation completed successfully");
println!("Please reboot your computer for changes to take effect!");
}
fn switch_hybrid(rtd3: Option<u8>, use_nvidia_current: bool) {
fn switch_hybrid(rtd3: Option<u8>, use_nvidia_current: bool, no_reboot: bool) {
assert_root();
println!("Switching to hybrid mode");
println!(
@@ -328,12 +488,31 @@ fn switch_hybrid(rtd3: Option<u8>, use_nvidia_current: bool) {
rtd3.is_some()
);
if get_current_mode() == "hybrid" && !Path::new(CACHE_FILE_PATH).exists() {
if no_reboot && let Err(e) = rescan_pci() {
error!("Failed to rescan PCI bus: {}", e);
}
if !Path::new(CACHE_FILE_PATH).exists() {
create_cache_file();
}
cleanup();
let mut stopped_dm = None;
if no_reboot {
match prepare_no_reboot(None) {
Ok(dm) => stopped_dm = dm,
Err(e) => {
error!("{}", e);
return;
}
}
if let Err(e) = kill_gpu_processes() {
error!("Failed to kill GPU processes: {}", e);
}
}
// persistenced enable
let is_debug = log::log_enabled!(log::Level::Debug);
let mut enable_cmd = Command::new("systemctl");
@@ -370,9 +549,14 @@ fn switch_hybrid(rtd3: Option<u8>, use_nvidia_current: bool) {
create_file(MODESET_PATH, &modeset_content, false);
}
rebuild_initramfs();
println!("Operation completed successfully");
println!("Please reboot your computer for changes to take effect!");
if no_reboot {
println!("Operation completed successfully");
finalize_no_reboot(stopped_dm);
} else {
rebuild_initramfs();
println!("Operation completed successfully");
println!("Please reboot your computer for changes to take effect!");
}
}
fn switch_nvidia(
@@ -381,26 +565,18 @@ fn switch_nvidia(
coolbits: Option<u32>,
use_nvidia_current: bool,
wayland: bool,
no_reboot: bool,
) {
assert_root();
println!("Switching to nvidia mode");
println!("Enable ForceCompositionPipeline: {}", force_comp);
println!("Enable Coolbits: {}", coolbits.is_some());
// persistenced enable
let is_debug = log::log_enabled!(log::Level::Debug);
let mut enable_cmd = Command::new("systemctl");
enable_cmd.args(["enable", "nvidia-persistenced.service"]);
if !is_debug {
enable_cmd.stdout(std::process::Stdio::null());
enable_cmd.stderr(std::process::Stdio::null());
}
match enable_cmd.status() {
Ok(s) if s.success() => println!("Successfully enabled nvidia-persistenced.service"),
_ => error!("An error ocurred while enabling service"),
if no_reboot && let Err(e) = rescan_pci() {
error!("Failed to rescan PCI bus: {}", e);
}
if get_current_mode() == "hybrid" && !Path::new(CACHE_FILE_PATH).exists() {
if !Path::new(CACHE_FILE_PATH).exists() {
create_cache_file();
}
@@ -454,7 +630,7 @@ fn switch_nvidia(
create_file(EXTRA_XORG_PATH, &extra_xorg, false);
}
let display_manager = dm.or_else(get_display_manager);
let display_manager = dm.clone().or_else(get_display_manager);
if let Some(cdm) = display_manager {
if cdm == "sddm" {
@@ -480,9 +656,42 @@ fn switch_nvidia(
}
}
rebuild_initramfs();
println!("Operation completed successfully");
println!("Please reboot your computer for changes to take effect!");
let mut stopped_dm = None;
if no_reboot {
match prepare_no_reboot(dm.clone()) {
Ok(dm_name) => stopped_dm = dm_name,
Err(e) => {
error!("{}", e);
return;
}
}
if let Err(e) = kill_gpu_processes() {
error!("Failed to kill GPU processes: {}", e);
}
}
// persistenced enable
let is_debug = log::log_enabled!(log::Level::Debug);
let mut enable_cmd = Command::new("systemctl");
enable_cmd.args(["enable", "nvidia-persistenced.service"]);
if !is_debug {
enable_cmd.stdout(std::process::Stdio::null());
enable_cmd.stderr(std::process::Stdio::null());
}
match enable_cmd.status() {
Ok(s) if s.success() => println!("Successfully enabled nvidia-persistenced.service"),
_ => error!("An error ocurred while enabling service"),
}
if no_reboot {
println!("Operation completed successfully");
finalize_no_reboot(stopped_dm);
} else {
rebuild_initramfs();
println!("Operation completed successfully");
println!("Please reboot your computer for changes to take effect!");
}
}
fn run_on_dgpu(command: &[String]) {
@@ -523,6 +732,11 @@ fn run_on_dgpu(command: &[String]) {
}
pub fn execute(cmd: &GpuCommands) {
if try_call_daemon(cmd) {
println!("Request handled by lapctld daemon.");
return;
}
match cmd {
GpuCommands::Query => {
let mode = get_current_mode();
@@ -570,23 +784,26 @@ pub fn execute(cmd: &GpuCommands) {
create_file(SDDM_XSETUP_PATH, SDDM_XSETUP_CONTENT, true);
println!("Operation completed successfully");
}
GpuCommands::Integrated => switch_integrated(),
GpuCommands::Integrated { no_reboot } => switch_integrated(*no_reboot),
GpuCommands::Hybrid {
rtd3,
use_nvidia_current,
} => switch_hybrid(*rtd3, *use_nvidia_current),
no_reboot,
} => switch_hybrid(*rtd3, *use_nvidia_current, *no_reboot),
GpuCommands::Nvidia {
dm,
force_comp,
coolbits,
use_nvidia_current,
wayland,
no_reboot,
} => switch_nvidia(
dm.clone(),
*force_comp,
*coolbits,
*use_nvidia_current,
*wayland,
*no_reboot,
),
GpuCommands::Run { command } => run_on_dgpu(command),
}
+57 -1
View File
@@ -1,7 +1,63 @@
use log::{error, info};
use std::process::Command;
use zbus::Connection;
use zbus::proxy;
#[proxy(
interface = "org.lapctl1",
default_service = "org.lapctl",
default_path = "/org/lapctl"
)]
trait Lapctl {
async fn set_system_inhibition(
&self,
active: bool,
why: String,
who: String,
) -> zbus::Result<()>;
}
fn try_call_daemon_inhibit(active: bool, why: String, who: String) -> bool {
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 proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
proxy.set_system_inhibition(active, why, who).await.is_ok()
})
}
pub fn execute(command_args: &[String], why: &str, who: &str, daemon: bool, stop: bool) {
if stop {
if try_call_daemon_inhibit(false, why.to_string(), who.to_string()) {
println!("Persistent system inhibition deactivated via lapctld.");
} else {
error!("Failed to deactivate inhibition via daemon. Is lapctld running?");
}
return;
}
if daemon && command_args.is_empty() {
if try_call_daemon_inhibit(true, why.to_string(), who.to_string()) {
println!("Persistent system inhibition activated via lapctld.");
println!("The system will stay awake until you run: lapctl inhibit --stop");
return;
} else {
error!("Failed to activate inhibition via daemon. Is lapctld running?");
// Fallback to local systemd-inhibit if daemon call fails
}
}
pub fn execute(command_args: &[String], why: &str, who: &str, daemon: bool) {
let args = build_args(command_args, why, who);
if command_args.is_empty() {
+94 -50
View File
@@ -2,74 +2,118 @@ use crate::utils::system::{assert_root, create_file};
use log::info;
use std::process::Command;
const LAPCTL_UDEV_RULES: &str = r#"# /etc/udev/rules.d/99-lapctl.rules
# Automatically generated by lapctl
# This file grants read/write permissions to all local users
# for the SysFS endpoints required by lapctl's power, cooling, and battery commands.
// No longer needed: all privileged operations are now handled by the lapctld daemon via D-Bus.
const LAPCTL_UDEV_RULES: &str = "# lapctl udev rules (placeholder)\n";
# ACPI Platform Profiles & CPU Scaling
ACTION=="add", SUBSYSTEM=="firmware", RUN+="/bin/sh -c 'chmod a+w /sys/firmware/acpi/platform_profile 2>/dev/null'"
ACTION=="add", SUBSYSTEM=="cpu", RUN+="/bin/sh -c 'chmod a+w /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor 2>/dev/null'"
ACTION=="add", SUBSYSTEM=="cpu", RUN+="/bin/sh -c 'chmod a+w /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference 2>/dev/null'"
const POLKIT_POLICY: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
"-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<vendor>lapctl</vendor>
<vendor_url>https://github.com/S4NKALP/lapctl</vendor_url>
<action id="org.s4nkalp.lapctl.execute">
<description>Elevated execution for lapctl</description>
<message>Authentication is required to perform privileged lapctl operations</message>
<defaults>
<allow_any>auth_admin</allow_any>
<allow_inactive>auth_admin</allow_inactive>
<allow_active>yes</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">/usr/bin/lapctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">false</annotate>
</action>
</policyconfig>
"#;
# Battery Charging Limits
ACTION=="add", SUBSYSTEM=="power_supply", RUN+="/bin/sh -c 'chmod a+w /sys/class/power_supply/BAT*/charge_control_end_threshold 2>/dev/null'"
const POLKIT_RULES: &str = r#"/* /etc/polkit-1/rules.d/10-lapctl.rules
* Automatically generated by lapctl
* This rule allows users in the 'wheel' or 'sudo' groups to
* perform privileged lapctl operations via pkexec without a password.
*/
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.policykit.exec" &&
action.lookup("program").indexOf("lapctl") !== -1 &&
(subject.isInGroup("wheel") || subject.isInGroup("sudo"))) {
return polkit.Result.YES;
}
});
"#;
# Lenovo Ideapad Thermal and Conservation Mode
ACTION=="add", SUBSYSTEM=="platform", RUN+="/bin/sh -c 'chmod a+w /sys/bus/platform/drivers/ideapad_acpi/VPC*/fan_mode 2>/dev/null'"
ACTION=="add", SUBSYSTEM=="platform", RUN+="/bin/sh -c 'chmod a+w /sys/bus/platform/drivers/ideapad_acpi/VPC*/conservation_mode 2>/dev/null'"
const DBUS_POLICY: &str = r#"<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="org.lapctl"/>
<allow send_destination="org.lapctl"/>
<allow receive_sender="org.lapctl"/>
</policy>
<policy group="wheel">
<allow send_destination="org.lapctl"/>
<allow receive_sender="org.lapctl"/>
</policy>
<policy group="sudo">
<allow send_destination="org.lapctl"/>
<allow receive_sender="org.lapctl"/>
</policy>
</busconfig>
"#;
# ASUS WMI Thermal Policy
ACTION=="add", SUBSYSTEM=="platform", RUN+="/bin/sh -c 'chmod a+w /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy 2>/dev/null'"
const SYSTEMD_SERVICE: &str = r#"[Unit]
Description=lapctl hardware management daemon
After=dbus.service
# Intel RAPL TDP Control
ACTION=="add", SUBSYSTEM=="powercap", RUN+="/bin/sh -c 'chmod a+w /sys/class/powercap/intel-rapl:0/constraint_*_power_limit_uw 2>/dev/null'"
[Service]
Type=simple
ExecStart=/usr/bin/lapctl daemon
Restart=on-failure
# AMD HWMon Power Capping
ACTION=="add", SUBSYSTEM=="hwmon", RUN+="/bin/sh -c 'find /sys/class/hwmon -name \"power1_cap\" -exec chmod a+w {} + 2>/dev/null'"
# Input Device Inhibition (Touchpad)
ACTION=="add", SUBSYSTEM=="input", RUN+="/bin/sh -c 'find /sys/class/input -name \"inhibited\" -exec chmod a+w {} + 2>/dev/null'"
[Install]
WantedBy=multi-user.target
"#;
pub fn execute() {
assert_root();
println!("Installing lapctl udev rules for rootless hardware configuration...");
println!("Installing lapctl configuration for daemon communication...");
let dbus_policy_path = "/etc/dbus-1/system.d/org.lapctl.conf";
create_file(dbus_policy_path, DBUS_POLICY, false);
let service_path = "/etc/systemd/system/lapctld.service";
create_file(service_path, SYSTEMD_SERVICE, false);
// Also install the udev rules file (even if empty now) to avoid errors if it was previously there
let rules_path = "/etc/udev/rules.d/99-lapctl.rules";
create_file(rules_path, LAPCTL_UDEV_RULES, false);
println!("Successfully wrote rules to {}", rules_path);
println!("Loading rules and applying immediate session permissions...");
// Polkit fallbacks for rootless execution without the daemon
let polkit_policy_path = "/usr/share/polkit-1/actions/org.s4nkalp.lapctl.policy";
create_file(polkit_policy_path, POLKIT_POLICY, false);
let _ = Command::new("udevadm")
.args(["control", "--reload-rules"])
let polkit_rules_path = "/etc/polkit-1/rules.d/10-lapctl.rules";
create_file(polkit_rules_path, POLKIT_RULES, false);
println!("Successfully installed configuration files.");
println!("Activating background daemon (lapctld)...");
// Reload systemd to recognize the new service
let _ = Command::new("systemctl").arg("daemon-reload").status();
// Enable and start the daemon
let status = Command::new("systemctl")
.args(["enable", "--now", "lapctld"])
.status();
let _ = Command::new("udevadm").args(["trigger"]).status();
// The udevadm trigger often fails to catch already-initialized ACPI nodes without a reboot,
// so we manually apply the permissions for the current session right now.
let commands = vec![
"chmod a+w /sys/firmware/acpi/platform_profile 2>/dev/null",
"chmod a+w /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor 2>/dev/null",
"chmod a+w /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference 2>/dev/null",
"chmod a+w /sys/class/power_supply/BAT*/charge_control_end_threshold 2>/dev/null",
"chmod a+w /sys/bus/platform/drivers/ideapad_acpi/VPC*/fan_mode 2>/dev/null",
"chmod a+w /sys/bus/platform/drivers/ideapad_acpi/VPC*/conservation_mode 2>/dev/null",
"chmod a+w /sys/devices/platform/asus-nb-wmi/throttle_thermal_policy 2>/dev/null",
"chmod a+w /sys/class/powercap/intel-rapl:0/constraint_*_power_limit_uw 2>/dev/null",
"find /sys/class/hwmon -name \"power1_cap\" -exec chmod a+w {} + 2>/dev/null",
"find /sys/class/input -name \"inhibited\" -exec chmod a+w {} + 2>/dev/null",
];
for cmd in commands {
let _ = Command::new("sh").arg("-c").arg(cmd).status();
if let Ok(s) = status {
if s.success() {
info!("lapctld daemon enabled and started.");
} else {
log::error!("Failed to enable/start lapctld daemon.");
}
}
info!("Udev reload and immediate ACL application complete.");
println!(
"Installation successful! You can now use lapctl power, cooling, and battery commands without sudo."
);
println!("Installation successful! The lapctld daemon is now running.");
println!("You can now use all lapctl commands without sudo.");
}
+51
View File
@@ -2,6 +2,52 @@ use crate::cli::PowerCommands;
use log::{error, info};
use std::fs;
use std::path::Path;
use zbus::Connection;
use zbus::proxy;
#[proxy(
interface = "org.lapctl1",
default_service = "org.lapctl",
default_path = "/org/lapctl"
)]
trait Lapctl {
async fn set_power_profile(&self, profile: String) -> zbus::Result<()>;
async fn set_tdp_limit(&self, watts: u32) -> zbus::Result<()>;
}
fn try_call_daemon(command: &PowerCommands) -> bool {
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 proxy = match LapctlProxy::new(&connection).await {
Ok(p) => p,
Err(_) => return false,
};
match command {
PowerCommands::Performance => proxy
.set_power_profile("performance".to_string())
.await
.is_ok(),
PowerCommands::Balanced => proxy
.set_power_profile("balanced".to_string())
.await
.is_ok(),
PowerCommands::BatterySave => proxy
.set_power_profile("battery-save".to_string())
.await
.is_ok(),
PowerCommands::LimitTdp { watts } => proxy.set_tdp_limit(*watts).await.is_ok(),
}
})
}
fn set_cpu_governor(governor: &str) {
let cpu_dir = Path::new("/sys/devices/system/cpu");
@@ -155,6 +201,11 @@ fn set_platform_profile(profile: &str) {
}
pub fn execute(command: &PowerCommands) {
if try_call_daemon(command) {
println!("Request handled by lapctld daemon.");
return;
}
match command {
PowerCommands::Performance => {
println!("Setting power profile to Performance");
+39
View File
@@ -2,8 +2,47 @@ use crate::cli::TouchpadCommands;
use log::{error, info};
use std::fs;
use std::path::Path;
use zbus::Connection;
use zbus::proxy;
#[proxy(
interface = "org.lapctl1",
default_service = "org.lapctl",
default_path = "/org/lapctl"
)]
trait Lapctl {
async fn set_touchpad_inhibition(&self, inhibited: bool) -> zbus::Result<()>;
}
fn try_call_daemon(command: &TouchpadCommands) -> bool {
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 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(),
}
})
}
pub fn execute(command: &TouchpadCommands) {
if try_call_daemon(command) {
println!("Request handled by lapctld daemon.");
return;
}
let sys_class_input = Path::new("/sys/class/input");
if !sys_class_input.exists() {
error!("Could not find /sys/class/input. Is this a Linux system?");