use std::sync; use tokio::sync::mpsc; use zbus::{fdo, interface}; pub(crate) struct Login1Manager { pub power_allowed: sync::Arc>, pub power_requests: mpsc::Sender, } #[interface(name = "org.freedesktop.login1.Manager")] impl Login1Manager { async fn can_reboot(&self) -> fdo::Result { self.can_control(crate::PowerOperation::Reboot) } async fn can_power_off(&self) -> fdo::Result { self.can_control(crate::PowerOperation::Poweroff) } async fn can_suspend(&self) -> fdo::Result { self.can_control(crate::PowerOperation::Suspend) } async fn can_hibernate(&self) -> fdo::Result { self.can_control(crate::PowerOperation::Hibernate) } async fn reboot(&self, _interactive: bool) -> fdo::Result<()> { self.power(crate::PowerOperation::Reboot).await } async fn power_off(&self, _interactive: bool) -> fdo::Result<()> { self.power(crate::PowerOperation::Poweroff).await } async fn suspend(&self, _interactive: bool) -> fdo::Result<()> { self.power(crate::PowerOperation::Suspend).await } async fn hibernate(&self, _interactive: bool) -> fdo::Result<()> { self.power(crate::PowerOperation::Hibernate).await } } impl Login1Manager { fn can_control(&self, operation: crate::PowerOperation) -> fdo::Result { Ok(if self .power_allowed .lock() .map_err(|_| fdo::Error::Failed("power permission lock poisoned".to_string()))? .allows(operation) { "yes" } else { "no" } .to_string()) } async fn power(&self, operation: crate::PowerOperation) -> fdo::Result<()> { if !self .power_allowed .lock() .map_err(|_| fdo::Error::Failed("power permission lock poisoned".to_string()))? .allows(operation) { return Err(fdo::Error::AccessDenied( "power operation is not currently allowed".to_string(), )); } let (response, result) = tokio::sync::oneshot::channel(); self.power_requests .send(crate::PowerRequest { operation, response, }) .await .map_err(|_| fdo::Error::Failed("sessiond power proxy is unavailable".to_string()))?; result .await .map_err(|_| fdo::Error::Failed("sessiond power proxy disconnected".to_string()))? } }