Commit afc75793f411
tarball3 files changed+94-60
@@ -3,21 +3,20 @@
33
44
use dbus::arg::OwnedFd;55
use dbus::blocking::SyncConnection;6
+use dbus::message::MatchRule;7
+use std::convert::Infallible;68
use std::process::Command;79
use std::sync::Arc;10
+use std::sync::atomic::{AtomicBool, Ordering};811
use std::time::Duration;912
1013
const DEST: &str = "org.freedesktop.login1";1114
const PATH: &str = "/org/freedesktop/login1";1215
const MANAGER: &str = "org.freedesktop.login1.Manager";16
+const BUS: &str = "org.freedesktop.DBus";1317
const TIMEOUT: Duration = Duration::from_secs(2);14
-15
-pub struct Logind {16
- conn: Arc<SyncConnection>,17
- /// Held so that logind leaves the lid switch to us. Closing it releases18
- /// the inhibitor.19
- _lid_lock: OwnedFd,20
-}18
+const RETRY: Duration = Duration::from_secs(10);19
+const IDLE: Duration = Duration::from_secs(3600);2120
2221
#[derive(Clone, Copy)]2322
pub enum Action {@@ -41,52 +40,96 @@ impl Action {
4140
}4241
}4342
44
-impl Logind {45
- pub fn connect() -> Result<Self, dbus::Error> {46
- let conn = Arc::new(SyncConnection::new_system()?);47
- let proxy = conn.with_proxy(DEST, PATH, TIMEOUT);48
- let (fd,): (OwnedFd,) = proxy.method_call(49
- MANAGER,50
- "Inhibit",51
- (52
- "handle-lid-switch",53
- "monolith",54
- "monolith handles the lid",55
- "block",56
- ),57
- )?;58
- Ok(Self {59
- conn,60
- _lid_lock: fd,61
- })43
+/// Keeps logind away from the lid switch for as long as the process lives. The44
+/// connection and the inhibitor are set up on a thread of their own, so a slow45
+/// or absent system bus never holds up startup, and both are taken again when46
+/// the bus drops or logind restarts.47
+pub fn hold_lid() {48
+ let spawned = std::thread::Builder::new()49
+ .name("logind-lid".into())50
+ .spawn(|| {51
+ let mut warned = false;52
+ loop {53
+ let Err(e) = hold(&mut warned);54
+ if !warned {55
+ tracing::warn!("cannot hold the lid switch from logind: {e}");56
+ warned = true;57
+ }58
+ std::thread::sleep(RETRY);59
+ }60
+ });61
+ if let Err(e) = spawned {62
+ tracing::warn!("could not start the logind lid thread: {e}");6263
}64
+}6365
64
- /// Asks logind to run the action without waiting for the reply, and falls65
- /// back to systemctl if the call fails. The call can take as long as the66
- /// timeout, so it stays off the event loop.67
- pub fn act(&self, action: Action) {68
- let conn = self.conn.clone();69
- let spawned = std::thread::Builder::new()70
- .name("logind".into())71
- .spawn(move || {72
- let proxy = conn.with_proxy(DEST, PATH, TIMEOUT);73
- let called: Result<(), dbus::Error> =74
- proxy.method_call(MANAGER, action.method(), (false,));75
- if let Err(e) = called {76
- tracing::warn!("logind {} failed, using systemctl: {e}", action.verb());77
- systemctl(action);78
- }79
- });80
- if let Err(e) = spawned {81
- tracing::warn!("could not start the logind thread: {e}");66
+fn hold(warned: &mut bool) -> Result<Infallible, dbus::Error> {67
+ let conn = SyncConnection::new_system()?;68
+ let mut _lid_lock = inhibit(&conn)?;69
+ let restarted = Arc::new(AtomicBool::new(false));70
+ let seen = restarted.clone();71
+ let rule = MatchRule::new_signal(BUS, "NameOwnerChanged").with_sender(BUS);72
+ conn.add_match(73
+ rule,74
+ move |(name, _, owner): (String, String, String), _, _| {75
+ if name == DEST && !owner.is_empty() {76
+ seen.store(true, Ordering::Relaxed);77
+ }78
+ true79
+ },80
+ )?;81
+ *warned = false;82
+ tracing::info!("holding the lid switch");83
+ loop {84
+ conn.process(IDLE)?;85
+ if restarted.swap(false, Ordering::Relaxed) {86
+ _lid_lock = inhibit(&conn)?;87
+ tracing::info!("logind restarted, took the lid switch again");8288
}8389
}8490
}8591
86
-fn systemctl(action: Action) {87
- match Command::new("systemctl").arg(action.verb()).status() {88
- Ok(s) if !s.success() => tracing::warn!("systemctl {} exited with {s}", action.verb()),89
- Ok(_) => {}90
- Err(e) => tracing::warn!("systemctl {} failed: {e}", action.verb()),92
+/// The returned descriptor keeps the inhibitor alive, and closing it releases93
+/// it.94
+fn inhibit(conn: &SyncConnection) -> Result<OwnedFd, dbus::Error> {95
+ let proxy = conn.with_proxy(DEST, PATH, TIMEOUT);96
+ let (fd,): (OwnedFd,) = proxy.method_call(97
+ MANAGER,98
+ "Inhibit",99
+ (100
+ "handle-lid-switch",101
+ "monolith",102
+ "monolith handles the lid",103
+ "block",104
+ ),105
+ )?;106
+ Ok(fd)107
+}108
+109
+/// Asks logind to run the action and falls back to systemctl if the call110
+/// fails. It happens on a thread, since the call can take as long as the111
+/// timeout, and on a fresh connection, since a long-lived one may have died112
+/// with the bus.113
+pub fn act(action: Action) {114
+ let spawned = std::thread::Builder::new()115
+ .name("logind".into())116
+ .spawn(move || {117
+ let called = SyncConnection::new_system().and_then(|conn| {118
+ conn.with_proxy(DEST, PATH, TIMEOUT)119
+ .method_call::<(), _, _, _>(MANAGER, action.method(), (false,))120
+ });121
+ if let Err(e) = called {122
+ tracing::warn!("logind {} failed, using systemctl: {e}", action.verb());123
+ match Command::new("systemctl").arg(action.verb()).status() {124
+ Ok(s) if !s.success() => {125
+ tracing::warn!("systemctl {} exited with {s}", action.verb());126
+ }127
+ Ok(_) => {}128
+ Err(e) => tracing::warn!("systemctl {} failed: {e}", action.verb()),129
+ }130
+ }131
+ });132
+ if let Err(e) = spawned {133
+ tracing::warn!("could not start the logind thread: {e}");91134
}92135
}@@ -282,10 +282,7 @@ fn run(
282282
xwayland::init(&mut state);283283
state.configure_idle();284284
if gpu.is_some() {285
- match logind::Logind::connect() {286
- Ok(logind) => state.services.power.logind = Some(logind),287
- Err(e) => tracing::warn!("cannot take the lid switch from logind: {e}"),288
- }285
+ logind::hold_lid();289286
}290287
tracing::info!(socket = ?state.core.socket_name, device = state.renderer.device_name(), "monolith running");291288
@@ -2,7 +2,7 @@
22
//! wlr-output-power-management all end up here, and the backend is told what33
//! came out.44
5
-use crate::logind::{Action, Logind};5
+use crate::logind::Action;66
use crate::state::{State, now_ns};77
use monolith_config::LidAction;88
use monolith_protocols::output_power::{OutputPowerHandler, OutputPowerState};@@ -18,8 +18,6 @@ const LID_GRACE: Duration = Duration::from_secs(5);
1818
1919
#[derive(Default)]2020
pub struct Power {21
- /// Absent on the nested backend, or when the system bus is unreachable.22
- pub logind: Option<Logind>,2321
/// Every output is dark until the next input.2422
idle_blanked: bool,2523
/// Outputs switched off by name, which input does not wake.@@ -169,11 +167,7 @@ impl State {
169167
LidAction::Poweroff => Action::Poweroff,170168
LidAction::Ignore => return,171169
};172
- if let Some(logind) = &power.logind {173
- logind.act(action);174
- } else if let Err(e) = self.spawn(&["systemctl".into(), action.verb().into()]) {175
- tracing::warn!("lid close action failed: {e}");176
- }170
+ crate::logind::act(action);177171
}178172
179173
fn wake_idle(&mut self) {